</listitem>
</varlistentry>
- <varlistentry>
- <term>PREPARE/EXECUTE/DEALLOCATE</term>
- <listitem>
- <para>
- たとえPREPAREが読み取り専用のSELECTを処理の対象にしていても、PREPARE/EXECUTE/DEALLOCATEは常にプライマリサーバに送られます。
- </para>
- </listitem>
- </varlistentry>
-
</variablelist>
</para>
</sect1>
</listitem>
</varlistentry>
- <varlistentry>
- <term>PREPARE/EXECUTE/DEALLOCATE</term>
- <listitem>
- <para>
- PREPARE/EXECUTE/DEALLOCATE are always sent to primary server
- even if the query PREPARE is processing is read only SELECT.
- </para>
- </listitem>
- </varlistentry>
-
</variablelist>
</para>
</sect1>
session_context = pool_get_session_context(false);
backend = session_context->backend;
+ /*
+ * Check to see if we can load balance the SELECT (or any read only query
+ * from syntactical point of view).
+ */
+ elog(DEBUG1, "Maybe: load balance mode: %d is_select_query: %d",
+ pool_config->load_balance_mode, is_select_query(node, query));
+
if (pool_config->load_balance_mode &&
is_select_query(node, query) &&
MAJOR(backend) == PROTO_MAJOR_V3)
}
/*
- * If a writing function call is used or replicate_select is true,
- * we prefer to send to all nodes.
+ * If a writing function call is used or replicate_select is true, we
+ * have to send to all nodes since the function may modify database.
*/
+ elog(DEBUG1, "Maybe sent to all node: pool_has_function_call: %d pool_config->replicate_select: %d",
+ pool_has_function_call(node), pool_config->replicate_select);
if (pool_has_function_call(node) || pool_config->replicate_select)
{
pool_setall_node_to_be_sent(query_context);
!pool_is_failed_transaction() &&
pool_get_transaction_isolation() != POOL_SERIALIZABLE))
{
+ elog(DEBUG1, "load balance TSTATE: %c pool_is_writing_transaction: %d pool_is_failed_transaction: %d pool_get_transaction_isolation: %d",
+ TSTATE(backend, MAIN_NODE_ID),
+ pool_is_writing_transaction(),
+ pool_is_failed_transaction(),
+ pool_get_transaction_isolation());
set_load_balance_info(query_context);
}
else
{
/* only send to main node */
+ elog(DEBUG1, "unable to load balance");
pool_set_node_to_be_sent(query_context, REAL_MAIN_NODE_ID);
}
}
*
* For followings this function returns true:
* - SELECT/WITH without FOR UPDATE/SHARE
+ * (for PREPARE, this function checks its "query" member of PrepareStmt)
* - COPY TO STDOUT
* - EXPLAIN
* - EXPLAIN ANALYZE and query is SELECT not including writing functions
bool
is_select_query(Node *node, char *sql)
{
+ bool prepare = false;
+
if (node == NULL)
return false;
- /*
- * 2009/5/1 Tatsuo says: This test is not bogus. As of 2.2, pgpool sets
- * Portal->sql_string to NULL for SQL command PREPARE. Usually this is ok,
- * since in most cases SQL command EXECUTE follows anyway. Problem is,
- * some applications mix PREPARE with extended protocol command "EXECUTE"
- * and so on. Execute() seems to think this never happens but it is not
- * real. Someday we should extract actual query string from
- * PrepareStmt->query and set it to Portal->sql_string.
- */
if (sql == NULL)
+ {
+ elog(WARNING, "is_select_query called without SQL string");
return false;
+ }
if (!pool_config->allow_sql_comments && pool_config->ignore_leading_white_space)
{
sql++;
}
+ /*
+ * If it's PREPARE, then we check the actual query specified in "AS"
+ * clause.
+ */
+ if (IsA(node, PrepareStmt))
+ {
+ PrepareStmt *prepare_statement = (PrepareStmt *) node;
+ prepare = true;
+ node = (Node *) (prepare_statement->query);
+ }
+
if (IsA(node, SelectStmt))
{
SelectStmt *select_stmt;
}
}
+ /*
+ * If SQL comment is not allowed, the query must start with cetain characters.
+ * However if it's PREPARE, we should skip the check.
+ */
if (!pool_config->allow_sql_comments)
/* '\0' and ';' signify empty query */
return (*sql == 's' || *sql == 'S' || *sql == '(' ||
*sql == 'w' || *sql == 'W' || *sql == 't' || *sql == 'T' ||
- *sql == '\0' || *sql == ';');
+ *sql == '\0' || *sql == ';' ||
+ prepare);
else
return true;
}
POOL_CONNECTION_POOL * backend,
POOL_SENT_MESSAGE * message,
POOL_SENT_MESSAGE * bind_message);
+static POOL_STATUS send_prepare(POOL_CONNECTION * frontend,
+ POOL_CONNECTION_POOL * backend,
+ POOL_SENT_MESSAGE * message);
static int *find_victim_nodes(int *ntuples, int nmembers, int main_node, int *number_of_nodes);
static POOL_STATUS close_standby_transactions(POOL_CONNECTION * frontend,
POOL_CONNECTION_POOL * backend);
if (node)
{
+ /*
+ * Take care of PREPARE/EXECUTE.
+ */
POOL_SENT_MESSAGE *msg = NULL;
if (IsA(node, PrepareStmt))
}
else if (IsA(node, ExecuteStmt))
{
+ /*
+ * EXECUTE needs to refer to information of PREPARE.
+ */
msg = pool_get_sent_message('Q', ((ExecuteStmt *) node)->name, POOL_SENT_MESSAGE_CREATED);
if (!msg)
msg = pool_get_sent_message('P', ((ExecuteStmt *) node)->name, POOL_SENT_MESSAGE_CREATED);
+ else
+ {
+ /*
+ * Take care the case when the previous PREPARE
+ * message has been sent to other than primary node in
+ * SL mode. In this case, we send a PREPARE message to
+ * the primary node to keep up the
+ * disable_load_balance_on_write rule.
+ */
+ if (SL_MODE && pool_config->load_balance_mode && pool_is_writing_transaction() &&
+ TSTATE(backend, MAIN_REPLICA ? PRIMARY_NODE_ID : REAL_MAIN_NODE_ID) == 'T' &&
+ pool_config->disable_load_balance_on_write != DLBOW_OFF)
+ {
+ if (send_prepare(frontend, backend, msg) != POOL_CONTINUE)
+ return POOL_END;
+ }
+ }
}
/* rewrite `now()' to timestamp literal */
return POOL_CONTINUE;
}
+/*
+ * Send PREPARE message to primary node and wait for reply if particular
+ * message is not yet PREPAREd on the primary node but PREPAREd on other
+ * node. Caller must provide the PREPARED message information as "message"
+ * argument.
+ */
+static POOL_STATUS send_prepare(POOL_CONNECTION * frontend,
+ POOL_CONNECTION_POOL * backend,
+ POOL_SENT_MESSAGE * message)
+{
+ int node_id;
+ bool backup[MAX_NUM_BACKENDS];
+ POOL_QUERY_CONTEXT *qc, *new_qc;
+ char qbuf[1024];
+ POOL_SELECT_RESULT *res;
+
+ elog(DEBUG1, "send_prepare called");
+
+ if (!SL_MODE)
+ {
+ elog(DEBUG1, "send_prepare: not SL_MODE");
+ return POOL_CONTINUE;
+ }
+
+ /* set target backend node id */
+ node_id = PRIMARY_NODE_ID;
+
+ /* create copy of where_to_send map */
+ qc = message->query_context;
+ memcpy(backup, qc->where_to_send, sizeof(qc->where_to_send));
+
+ if (message->kind != 'Q' || qc->where_to_send[node_id])
+ {
+ ereport(DEBUG1,
+ (errmsg("send_prepare"),
+ errdetail("no need to re-send PREPARE kind: %c where_to_send: %d", message->kind,
+ qc->where_to_send[node_id])));
+ return POOL_CONTINUE;
+ }
+
+ /*
+ * we are in streaming replication mode and the PREPARE message has
+ * not been sent to primary yet.
+ */
+
+ /*
+ * Prepare modified query context This is a copy of original PREPARE
+ * query context except the query sending destination is changed to
+ * primary node.
+ */
+ new_qc = pool_query_context_shallow_copy(qc);
+ memset(new_qc->where_to_send, 0, sizeof(new_qc->where_to_send));
+ new_qc->where_to_send[node_id] = 1;
+ new_qc->virtual_main_node_id = node_id;
+ new_qc->load_balance_node_id = node_id;
+
+ /* named statement? */
+ if (message->name[0] != '\0')
+ {
+ /*
+ * Before sending the PREPARE message to the primary, we need to
+ * DEALLOCATE the named statement. Otherwise we will get an error
+ * from backend if an identical named statement already exists.
+ */
+
+ /* check to see if the named statement exists on primary node */
+ snprintf(qbuf, sizeof(qbuf), "SELECT count(*) FROM pg_prepared_statements WHERE name = '%s'",
+ message->name);
+
+ elog(DEBUG1, "send_prepare: %s to backend: %d", qbuf, node_id);
+ do_query(CONNECTION(backend, node_id), qbuf, &res, MAJOR(backend));
+
+ if (res && res->data[0] && strcmp(res->data[0], "0"))
+ {
+ free_select_result(res);
+ /*
+ * The same named statement exists, We need to send DEALLOCATE
+ * message
+ */
+ snprintf(qbuf, sizeof(qbuf), "DEALLOCATE %s", message->name);
+
+ /* send DEALLOCATE message to primary node */
+ elog(DEBUG1, "send_prepare: %s to backend: %d", qbuf, node_id);
+ do_query(CONNECTION(backend, node_id), qbuf, &res, MAJOR(backend));
+ }
+ free_select_result(res);
+ }
+
+ /* send PREPARE message to primary node */
+ elog(DEBUG1, "send_prepare: sending PREPARE to primary node");
+ do_query(CONNECTION(backend, node_id), message->query_context->original_query,
+ &res, MAJOR(backend));
+ free_select_result(res);
+
+ /* replace the query context of PREPARE message with new query context */
+ message->query_context = new_qc;
+
+ /*
+ * Replace query contex in the session context with the new query context
+ * so that subsequent EXECUTE will be sent to primary node.
+ */
+ pool_get_session_context(true)->query_context = new_qc;
+
+ /* recover where_to_send map */
+ memcpy(qc->where_to_send, backup, sizeof(backup));
+ return POOL_CONTINUE;
+}
+
/*
* Find victim nodes by "decide by majority" rule and returns array
* of victim node ids. If no victim is found, return NULL.
CREATE FUNCTION f2(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL;
NOTICE: DB node id: 0 statement: CREATE FUNCTION f2(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL;
NOTICE: DB node id: 1 statement: CREATE FUNCTION f2(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL;
+CREATE FUNCTION f3(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+NOTICE: DB node id: 0 statement: CREATE FUNCTION f3(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+NOTICE: DB node id: 1 statement: CREATE FUNCTION f3(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+CREATE FUNCTION f4(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+NOTICE: DB node id: 0 statement: CREATE FUNCTION f4(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+NOTICE: DB node id: 1 statement: CREATE FUNCTION f4(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
SELECT * FROM t1; -- this load balances
NOTICE: DB node id: 1 statement: SELECT * FROM t1;
i
NOTICE: DB node id: 0 statement: CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL;
CREATE FUNCTION f2(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL;
NOTICE: DB node id: 0 statement: CREATE FUNCTION f2(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL;
+CREATE FUNCTION f3(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+NOTICE: DB node id: 0 statement: CREATE FUNCTION f3(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+CREATE FUNCTION f4(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
+NOTICE: DB node id: 0 statement: CREATE FUNCTION f4(INTEGER) returns INTEGER AS 'SELECT $1' LANGUAGE SQL STABLE;
SELECT * FROM t1; -- this load balances
NOTICE: DB node id: 1 statement: SELECT * FROM t1;
i
-SELECT f1(1);
+SELECT f1(1); -- no load balance because volatile function
NOTICE: DB node id: 0 statement: SELECT f1(1);
NOTICE: DB node id: 1 statement: SELECT f1(1);
f1
1
(1 row)
-SELECT public.f2(1);
+SELECT public.f2(1); -- no load balance because volatile function
NOTICE: DB node id: 0 statement: SELECT public.f2(1);
NOTICE: DB node id: 1 statement: SELECT public.f2(1);
f2
1
(1 row)
+SELECT f3(1); -- load balance because statble function
+NOTICE: DB node id: 1 statement: SELECT f3(1);
+ f3
+----
+ 1
+(1 row)
+
+SELECT public.f4(1); -- load balance because stable function
+NOTICE: DB node id: 1 statement: SELECT public.f4(1);
+ f4
+----
+ 1
+(1 row)
+
+PREPARE p1 AS SELECT f1(1); -- no load balance because volatile function
+NOTICE: DB node id: 0 statement: PREPARE p1 AS SELECT f1(1);
+NOTICE: DB node id: 1 statement: PREPARE p1 AS SELECT f1(1);
+EXECUTE p1; -- no load balance because volatile function
+NOTICE: DB node id: 0 statement: EXECUTE p1;
+NOTICE: DB node id: 1 statement: EXECUTE p1;
+ f1
+----
+ 1
+(1 row)
+
+DEALLOCATE p1; -- no load balance because volatile function
+NOTICE: DB node id: 0 statement: DEALLOCATE p1;
+NOTICE: DB node id: 1 statement: DEALLOCATE p1;
+PREPARE p2 AS SELECT f3(1); -- load balance because stable function
+NOTICE: DB node id: 1 statement: PREPARE p2 AS SELECT f3(1);
+EXECUTE p2; -- load balance because stable function
+NOTICE: DB node id: 1 statement: EXECUTE p2;
+ f3
+----
+ 1
+(1 row)
+
+DEALLOCATE p2; -- load balance because stable function
+NOTICE: DB node id: 1 statement: DEALLOCATE p2;
+-- PREPARE in transaction test
+BEGIN;
+NOTICE: DB node id: 0 statement: BEGIN;
+NOTICE: DB node id: 1 statement: BEGIN;
+PREPARE p3 AS SELECT 1; -- load balance
+NOTICE: DB node id: 1 statement: PREPARE p3 AS SELECT 1;
+EXECUTE p3; -- load balance
+NOTICE: DB node id: 1 statement: EXECUTE p3;
+ ?column?
+----------
+ 1
+(1 row)
+
+DEALLOCATE p3; -- load balance
+NOTICE: DB node id: 1 statement: DEALLOCATE p3;
+END;
+NOTICE: DB node id: 1 statement: END;
+NOTICE: DB node id: 0 statement: END;
+-- PREPARE in writing transaction test
+BEGIN;
+NOTICE: DB node id: 0 statement: BEGIN;
+NOTICE: DB node id: 1 statement: BEGIN;
+PREPARE p3 AS SELECT 1; -- load balance
+NOTICE: DB node id: 1 statement: PREPARE p3 AS SELECT 1;
+SELECT f1(1); -- no load balance. writing transaction is set
+NOTICE: DB node id: 0 statement: SELECT f1(1);
+NOTICE: DB node id: 1 statement: SELECT f1(1);
+ f1
+----
+ 1
+(1 row)
+
+-- PREPARE is re-execute and EXECUTE no load balance in SL_MODE.
+-- in other mode, load balance
+EXECUTE p3;
+NOTICE: DB node id: 1 statement: EXECUTE p3;
+ ?column?
+----------
+ 1
+(1 row)
+
+-- no load balance in SL_MODE.
+-- in other mode, load balance
+DEALLOCATE p3;
+NOTICE: DB node id: 1 statement: DEALLOCATE p3;
+END;
+NOTICE: DB node id: 1 statement: END;
+NOTICE: DB node id: 0 statement: END;
-SELECT f1(1);
+SELECT f1(1); -- no load balance because volatile function
NOTICE: DB node id: 0 statement: SELECT f1(1);
f1
----
1
(1 row)
-SELECT public.f2(1);
+SELECT public.f2(1); -- no load balance because volatile function
NOTICE: DB node id: 0 statement: SELECT public.f2(1);
f2
----
1
(1 row)
+SELECT f3(1); -- load balance because statble function
+NOTICE: DB node id: 1 statement: SELECT f3(1);
+ f3
+----
+ 1
+(1 row)
+
+SELECT public.f4(1); -- load balance because stable function
+NOTICE: DB node id: 1 statement: SELECT public.f4(1);
+ f4
+----
+ 1
+(1 row)
+
+PREPARE p1 AS SELECT f1(1); -- no load balance because volatile function
+NOTICE: DB node id: 0 statement: PREPARE p1 AS SELECT f1(1);
+EXECUTE p1; -- no load balance because volatile function
+NOTICE: DB node id: 0 statement: EXECUTE p1;
+ f1
+----
+ 1
+(1 row)
+
+DEALLOCATE p1; -- no load balance because volatile function
+NOTICE: DB node id: 0 statement: DEALLOCATE p1;
+PREPARE p2 AS SELECT f3(1); -- load balance because stable function
+NOTICE: DB node id: 1 statement: PREPARE p2 AS SELECT f3(1);
+EXECUTE p2; -- load balance because stable function
+NOTICE: DB node id: 1 statement: EXECUTE p2;
+ f3
+----
+ 1
+(1 row)
+
+DEALLOCATE p2; -- load balance because stable function
+NOTICE: DB node id: 1 statement: DEALLOCATE p2;
+-- PREPARE in transaction test
+BEGIN;
+NOTICE: DB node id: 0 statement: BEGIN;
+NOTICE: DB node id: 1 statement: BEGIN;
+PREPARE p3 AS SELECT 1; -- load balance
+NOTICE: DB node id: 1 statement: PREPARE p3 AS SELECT 1;
+EXECUTE p3; -- load balance
+NOTICE: DB node id: 1 statement: EXECUTE p3;
+ ?column?
+----------
+ 1
+(1 row)
+
+DEALLOCATE p3; -- load balance
+NOTICE: DB node id: 1 statement: DEALLOCATE p3;
+END;
+NOTICE: DB node id: 1 statement: END;
+NOTICE: DB node id: 0 statement: END;
+-- PREPARE in writing transaction test
+BEGIN;
+NOTICE: DB node id: 0 statement: BEGIN;
+NOTICE: DB node id: 1 statement: BEGIN;
+PREPARE p3 AS SELECT 1; -- load balance
+NOTICE: DB node id: 1 statement: PREPARE p3 AS SELECT 1;
+SELECT f1(1); -- no load balance. writing transaction is set
+NOTICE: DB node id: 0 statement: SELECT f1(1);
+ f1
+----
+ 1
+(1 row)
+
+-- PREPARE is re-execute and EXECUTE no load balance in SL_MODE.
+-- in other mode, load balance
+EXECUTE p3;
+NOTICE: DB node id: 0 statement: EXECUTE p3;
+ ?column?
+----------
+ 1
+(1 row)
+
+-- no load balance in SL_MODE.
+-- in other mode, load balance
+DEALLOCATE p3;
+NOTICE: DB node id: 0 statement: DEALLOCATE p3;
+END;
+NOTICE: DB node id: 1 statement: END;
+NOTICE: DB node id: 0 statement: END;
-- PREPARE
PREPARE foo AS SELECT 2;
NOTICE: DB node id: 0 statement: PREPARE foo AS SELECT 2;
-NOTICE: DB node id: 1 statement: PREPARE foo AS SELECT 2;
-- EXECUTE
EXECUTE foo;
NOTICE: DB node id: 0 statement: EXECUTE foo;
-NOTICE: DB node id: 1 statement: EXECUTE foo;
?column?
----------
2
-- DEALLOCATE
DEALLOCATE foo;
NOTICE: DB node id: 0 statement: DEALLOCATE foo;
-NOTICE: DB node id: 1 statement: DEALLOCATE foo;
-- ROLLBACK TO
ROLLBACK TO a;
NOTICE: DB node id: 1 statement: ROLLBACK TO a;
-- PREPARE
PREPARE foo AS SELECT 2;
NOTICE: DB node id: 0 statement: PREPARE foo AS SELECT 2;
-NOTICE: DB node id: 1 statement: PREPARE foo AS SELECT 2;
-- EXECUTE
EXECUTE foo;
NOTICE: DB node id: 0 statement: EXECUTE foo;
-NOTICE: DB node id: 1 statement: EXECUTE foo;
?column?
----------
2
-- DEALLOCATE
DEALLOCATE foo;
NOTICE: DB node id: 0 statement: DEALLOCATE foo;
-NOTICE: DB node id: 1 statement: DEALLOCATE foo;
-- ROLLBACK TO
ROLLBACK TO a;
NOTICE: DB node id: 1 statement: ROLLBACK TO a;
--- /dev/null
+SELECT f1(1); -- no load balance because volatile function
+SELECT public.f2(1); -- no load balance because volatile function
+SELECT f3(1); -- load balance because statble function
+SELECT public.f4(1); -- load balance because stable function
+PREPARE p1 AS SELECT f1(1); -- no load balance because volatile function
+EXECUTE p1; -- no load balance because volatile function
+DEALLOCATE p1; -- no load balance because volatile function
+PREPARE p2 AS SELECT f3(1); -- load balance because stable function
+EXECUTE p2; -- load balance because stable function
+DEALLOCATE p2; -- load balance because stable function
+-- PREPARE in transaction test
+BEGIN;
+PREPARE p3 AS SELECT 1; -- load balance
+EXECUTE p3; -- load balance
+DEALLOCATE p3; -- load balance
+END;
+-- PREPARE in writing transaction test
+BEGIN;
+PREPARE p3 AS SELECT 1; -- load balance
+SELECT f1(1); -- no load balance. writing transaction is set
+-- PREPARE is re-execute and EXECUTE no load balance in SL_MODE.
+-- in other mode, load balance
+EXECUTE p3;
+-- no load balance in SL_MODE.
+-- in other mode, load balance
+DEALLOCATE p3;
+END;
CREATE TABLE t2(i INTEGER);
CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT \$1' LANGUAGE SQL;
CREATE FUNCTION f2(INTEGER) returns INTEGER AS 'SELECT \$1' LANGUAGE SQL;
+CREATE FUNCTION f3(INTEGER) returns INTEGER AS 'SELECT \$1' LANGUAGE SQL STABLE;
+CREATE FUNCTION f4(INTEGER) returns INTEGER AS 'SELECT \$1' LANGUAGE SQL STABLE;
SELECT * FROM t1; -- this load balances
SELECT f1(1); -- this does not load balance
SELECT public.f2(1); -- this does not load balance
echo "read_only_function_list = ''" >> etc/pgpool.conf
echo "write_function_list = ''" >> etc/pgpool.conf
echo "statement_level_load_balance = on" >> etc/pgpool.conf
- echo "log_min_messages = debug1" >> etc/pgpool.conf
+ echo "log_min_messages = debug5" >> etc/pgpool.conf
./startall
sleep $st
# function is regarded doing writes.
# Since f1() and f2() were declared without volatility property, they are regarded
# as volatile functions.
+#
+# Also check PREPARE/EXECUTE/DEALLOCATE in this test.
# -------------------------------------------------------------------------------
echo "load_balance_mode = on" >> etc/pgpool.conf
echo "write_function_list = ''" >> etc/pgpool.conf
echo "read_only_function_list = ''" >> etc/pgpool.conf
- ./pgpool_reload
- sleep $st
+ ./shutdownall
+ ./startall
+ wait_for_pgpool_startup
- $PSQL $PSQLOPTS > result6 2>&1 <<EOF
-SELECT f1(1);
-SELECT public.f2(1);
-EOF
- check_result 6 $PSQLVERSION
+ $PSQL $PSQLOPTS < ../sql/6.sql > result6 2>&1
+ check_result 6
echo "=== test7 started ==="
# -------------------------------------------------------------------------------
# So following does not work.
#echo "primary_routing_query_pattern_list = ''" >> etc/pgpool.conf
sed -i '/^primary_routing_query_pattern_list/d' etc/pgpool.conf
- #echo "log_min_messages = debug5" >> etc/pgpool.conf
./shutdownall
./startall
* Return true if this SELECT has function calls *and* supposed to
* modify database. We check write/read_only function list to determine
* whether the function modifies database.
+ * If node is PrepareStmt, we look into PrepareStmt->query instead.
*/
bool
pool_has_function_call(Node *node)
{
SelectContext ctx;
+ if (IsA(node, PrepareStmt))
+ {
+ PrepareStmt *prepare_statement = (PrepareStmt *) node;
+ node = (Node *) (prepare_statement->query);
+ }
+
if (!IsA(node, SelectStmt))
return false;