In Pgpool-II 4.0 or earlier, if we set "disable_load_balance_on_write = transaction",
when a write query is issued inside an explicit truncation,
subsequent queries should be sent to primary only until the
end of this transactionin in order to avoid the replication
delay.
However, the SELECTs having write functions specified in black_function_list
are not regarded as a write query and the subsequent read queries are still load balanced.
This commit will disable load balance after a SELECT having functions
specified in black/white function list.
<!--
transaction has not issued a write query yet (until a write
query is issued, load balance is possible. Here "write query"
- means non SELECT DML or DDL. SELECTs having write functions as
- specified in black or white function list is not regarded as
- a write query. This may be changed in the future.)
+ means non SELECT DML or DDL. <EMPHASIS>Before <productname>Pgpool-II</> 4.1</>,
+ SELECTs having write functions as specified in black or
+ white function list is not regarded as a write query.)
-->
トランザクション内で更新を伴うクエリが実行されていない
(更新を伴うクエリが実行されるまでは負荷分散されます。
ここで「更新を伴うクエリ」とは、SELECT以外のDDLやDMLを指します。
-black/white function listで指定される更新関数を含むSELECTは更新を伴うクエリとは見なされません。
-ã\81\93ã\81®ä»\95æ§\98ã\81¯å°\86æ\9d¥å¤\89æ\9b´ã\81\95ã\82\8cã\82\8bå\8f¯è\83½æ\80§ã\81\8cã\81\82ã\82\8aã\81¾ã\81\99)
+<EMPHASIS><productname>Pgpool-II</>4.1以前</>のバージョンでは、black/white function list
+ã\81§æ\8c\87å®\9aã\81\95ã\82\8cã\82\8bæ\9b´æ\96°é\96¢æ\95°ã\82\92å\90«ã\82\80SELECTã\81¯æ\9b´æ\96°ã\82\92ä¼´ã\81\86ã\82¯ã\82¨ã\83ªã\81¨ã\81¯è¦\8bã\81ªã\81\95ã\82\8cã\81¾ã\81\9bã\82\93ã\80\82)
</para>
</listitem>
<listitem>
<para>
transaction has not issued a write query yet (until a write
query is issued, load balance is possible. Here "write query"
- means non SELECT DML or DDL. SELECTs having write functions as
- specified in black or white function list is not regarded as
- a write query. This may be changed in the future.)
+ means non SELECT DML or DDL. <EMPHASIS>Before <productname>Pgpool-II</> 4.1</>,
+ SELECTs having write functions as specified in black or
+ white function list is not regarded as a write query.)
</para>
</listitem>
<listitem>
/*
* Take care of "writing transaction" flag.
*/
- if (!is_select_query(node, query) && !is_start_transaction_query(node) &&
- !is_commit_or_rollback_query(node))
+ if ((!is_select_query(node, query) || pool_has_function_call(node)) &&
+ !is_start_transaction_query(node) &&
+ !is_commit_or_rollback_query(node))
{
ereport(DEBUG1,
(errmsg("Execute: TSTATE:%c",
close_standby_transactions(frontend, backend);
}
- else if (!is_select_query(node, query))
+ else if (!is_select_query(node, query) || pool_has_function_call(node))
{
/*
* If the query was not READ SELECT, and we are in an explicit
--- /dev/null
+FE=> Query (query="DROP FUNCTION IF EXISTS f1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M function f1() does not exist, skipping F dropcmds.c L 483 R does_not_exist_skipping )
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL")
+<= BE CommandComplete(CREATE FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="SELECT f1(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query (query="DROP FUNCTION f1")
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query (query="DROP FUNCTION IF EXISTS f1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M function f1() does not exist, skipping F dropcmds.c L 483 R does_not_exist_skipping )
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL")
+<= BE CommandComplete(CREATE FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="SELECT f1(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query (query="DROP FUNCTION f1")
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query (query="DROP FUNCTION IF EXISTS f1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M function f1() does not exist, skipping F dropcmds.c L 483 R does_not_exist_skipping )
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL")
+<= BE CommandComplete(CREATE FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="SELECT f1(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query (query="DROP FUNCTION f1")
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query (query="DROP FUNCTION IF EXISTS f1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M function f1() does not exist, skipping F dropcmds.c L 483 R does_not_exist_skipping )
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL")
+<= BE CommandComplete(CREATE FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query (query="SELECT 1")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query (query="SELECT f1(1)")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query (query="SELECT 2")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query (query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query (query="SELECT 3")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Query (query="BEGIN")
+<= BE CommandComplete(BEGIN)
+<= BE ReadyForQuery(T)
+FE=> Query (query="SELECT 4")
+<= BE RowDescription
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(T)
+FE=> Query (query="COMMIT")
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Query (query="DROP FUNCTION f1")
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query (query="DROP FUNCTION IF EXISTS f1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M function f1() does not exist, skipping F dropcmds.c L 483 R does_not_exist_skipping )
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL")
+<= BE CommandComplete(CREATE FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="SELECT f1(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query (query="DROP FUNCTION f1")
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+FE=> Query (query="DROP FUNCTION IF EXISTS f1")
+<= BE NoticeResponse(S NOTICE V NOTICE C 00000 M function f1() does not exist, skipping F dropcmds.c L 483 R does_not_exist_skipping )
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Query (query="CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL")
+<= BE CommandComplete(CREATE FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="S1", query="SELECT 1")
+FE=> Parse(stmt="", query="SELECT f1(1)")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 2")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE ParseComplete
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="SELECT 3")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ReadyForQuery(I)
+FE=> Parse(stmt="", query="BEGIN")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Bind(stmt="S1", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="SELECT 4")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Parse(stmt="", query="COMMIT")
+FE=> Bind(stmt="", portal="")
+FE=> Execute(portal="")
+FE=> Close(stmt="S1")
+FE=> Sync
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(BEGIN)
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE DataRow
+<= BE CommandComplete(SELECT 1)
+<= BE ParseComplete
+<= BE BindComplete
+<= BE CommandComplete(COMMIT)
+<= BE CloseComplete
+<= BE ReadyForQuery(I)
+FE=> Query (query="DROP FUNCTION f1")
+<= BE CommandComplete(DROP FUNCTION)
+<= BE ReadyForQuery(I)
+FE=> Terminate
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Create: 0 Parse: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# Parse: 0 Execute: 0 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+set 0 0 0 0 0 0 0 0 0 0 0 0
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Create: 0 Parse: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# Parse: 1 Execute: 1
+# Execute: 0 Parse: 0 Execute: 0
+set 0 1 0 0 0 0 0 0 1 1 0 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Create: 0 Parse: 1 Parse: 0 Execute: 0 Parse: 1 Execute: 1 Parse: 1 Execute: 1
+# Parse: 1 Execute: 1
+# Execute: 1 Parse: 1 Execute: 1
+set 0 1 0 0 1 1 1 1 1 1 1 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: CREATE: 0 SELECT: 1 SELECT: 0 SELECT: 0 SELECT: 1 SELECT: 1
+set 0 1 0 0 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Create: 0 Parse: 1 Parse: 0 Execute: 0 Parse: 0 Execute: 0 Parse: 0 Execute: 0
+# Parse: 1 Execute: 1
+# Execute: 0 Parse: 0 Execute: 0
+set 0 1 0 0 0 0 0 0 1 1 0 0 0
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+#!/usr/bin/env bash
+
+# expected results: Create: 0 Parse: 1 parse: 1 Execute: 1 Parse: 1 Execute: 1 Parse: 1 Execute: 1
+# Parse: 1 Execute: 1
+# Parse: 1 Execute: 1 Parse: 1 Execute: 1
+set 0 1 1 1 1 1 1 1 1 1 1 1 1 1
+
+grep "SELECT" $PGPOOLLOG |awk '{print $9}' | while read node
+do
+ if [ $1 -ne $node ]
+ then
+ echo "expected: $1 result: $node"
+ exit 99
+ fi
+ shift
+done
+
+if [ $? = 99 ]
+then
+ exit 1
+fi
+
+exit 0
+
--- /dev/null
+# Test for load_balance_on_write function feature. disable_load_balance_on_write = 'always'
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+##black_function_list = 'f1'
+
+# disable_load_balance_on_write = 'always'
+# Disable load balance after a WRITE function is issued.
+
+## disable_load_balance_on_write = 'always'
+
+# Create test white function
+'Q' "DROP FUNCTION IF EXISTS f1"
+'Y'
+'Q' "CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Execute function f1. This should be sent to primary node.
+'P' "" "SELECT f1(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node because
+# disable_load_balance_on_write = 'always'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+
+# DROP test table
+'Q' "DROP FUNCTION f1"
+'Y'
+
+'X'
--- /dev/null
+# Test for load_balance_on_write function feature. disable_load_balance_on_write = 'transaction'
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+##black_function_list = 'f1'
+
+# Disable load balance in current transaction only. (disable_load_balance_on_write = 'transaction')
+# (default behavior)
+
+# Create test white function
+'Q' "DROP FUNCTION IF EXISTS f1"
+'Y'
+'Q' "CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Execute function f1. This should be sent to primary node.
+'P' "" "SELECT f1(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to node 1 because
+# disable_load_balance_on_write = 'transaction'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to node 1.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+
+# DROP test table
+'Q' "DROP FUNCTION f1"
+'Y'
+
+'X'
--- /dev/null
+# Test for load_balance_on_write function feature. disable_load_balance_on_write = 'off'
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+##black_function_list = 'f1'
+
+# disable_load_balance_on_write = 'off'
+# Don't disable load balance even if a WRITE function is issued.
+
+## disable_load_balance_on_write = 'off'
+
+
+# Create test white function
+'Q' "DROP FUNCTION IF EXISTS f1"
+'Y'
+'Q' "CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Execute function f1. This should be sent to primary node.
+'P' "" "SELECT f1(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to standby node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to standby node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to standby node.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to standby node because
+# disable_load_balance_on_write = 'off'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to standby node.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+
+# DROP test table
+'Q' "DROP FUNCTION f1"
+'Y'
+
+'X'
--- /dev/null
+# Test for disable_load_balance_on_write on write function feature
+# using simple queries.
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+##black_function_list = 'f1'
+
+# Disable load balance in current transaction only. (disable_load_balance_on_write = 'transaction')
+# (default behavior)
+
+# Create test white function
+'Q' "DROP FUNCTION IF EXISTS f1"
+'Y'
+'Q' "CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue Parse SELECT. This should be sent to node 1.
+'Q' "SELECT 1"
+'Y'
+
+# Issue INSERT
+'Q' "SELECT f1(1)"
+'Y'
+
+# Issue another SELECT. This should be sent to primary node.
+'Q' "SELECT 2"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'Q' "SELECT 3"
+'Y'
+
+# Start a transaction
+'Q' "BEGIN"
+'Y'
+
+# Issue another SELECT. This should be sent to standby node.
+'Q' "SELECT 4"
+'Y'
+
+# Issue COMMIT
+'Q' "COMMIT"
+'Y'
+
+# DROP test table
+'Q' "DROP FUNCTION f1"
+'Y'
+
+'X'
--- /dev/null
+# Test for load_balance_on_write function feature. disable_load_balance_on_write = 'trans_transaction'
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+##black_function_list = 'f1'
+
+# disable_load_balance_on_write = 'trans_transaction'
+# Disable load balance in next transaction
+
+## disable_load_balance_on_write = 'trans_transaction'
+
+# Create test white function
+'Q' "DROP FUNCTION IF EXISTS f1"
+'Y'
+'Q' "CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL"
+'Y'
+
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Execute function f1. This should be sent to primary node.
+'P' "" "SELECT f1(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to primary node because
+# disable_load_balance_on_write = 'trans_transaction'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to primary node.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+
+# DROP test table
+'Q' "DROP FUNCTION f1"
+'Y'
+
+'X'
--- /dev/null
+# Test for load_balance_on_read function feature.
+#
+
+# Force load balance node to 1.
+##backend_weight0 = 0
+##backend_weight1 = 1
+
+# Disable load balance in current transaction only. (disable_load_balance_on_write = 'transaction')
+# (default behavior)
+
+# Create test white function
+'Q' "DROP FUNCTION IF EXISTS f1"
+'Y'
+'Q' "CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL"
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Parse SELECT. This should be sent to node 1.
+'P' "S1" "SELECT 1" 0
+
+# Execute function f1. This should be sent to node 1.
+'P' "" "SELECT f1(1)" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to node 1.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to node 1.
+'P' "" "SELECT 2"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Issue another SELECT. This should be sent to node 1 because outside a transaction.
+'P' "" "SELECT 3"
+'B' "" "" 0 0 0
+'E' "" 0
+'S'
+'Y'
+
+# Start a transaction
+'P' "" "BEGIN" 0
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue Bind/Execute SELECT. This should be sent to node 1 because
+# disable_load_balance_on_write = 'transaction'.
+'B' "" "S1" 0 0 0
+'E' "" 0
+
+# Issue another SELECT. This should be sent to node 1.
+'P' "" "SELECT 4"
+'B' "" "" 0 0 0
+'E' "" 0
+
+# Issue COMMIT
+'P' "" "COMMIT" 0
+'B' "" "" 0 0 0
+'E' "" 0
+'C' 'S' "S1"
+'S'
+'Y'
+
+
+# DROP test table
+'Q' "DROP FUNCTION f1"
+'Y'
+
+'X'