From e84fe9e5a8d93b7b0e347ee2c7d03a26d2dd7296 Mon Sep 17 00:00:00 2001 From: Tatsuo Ishii Date: Fri, 29 Sep 2023 07:29:28 +0900 Subject: [PATCH] Fix Describe() so that it does not abort with portal created by DECLARE. When DECLARE foo CURSOR FOR is executed, a portal named "foo" is automatically created by PostgreSQL. As the portal is not managed by Pgpool-II, "Describe foo" message failed with "unable to execute Describe. unable to get the bind message" error. To fix this, make Describe() creates a dummy write query context so that the describe message is sent to primary (streaming replication mode) or all nodes (replication/snapshot isolation mode). Problem analysis by Heather Lapointe. Discussion: https://www.pgpool.net/pipermail/pgpool-general/2023-September/008995.html --- src/protocol/pool_proto_modules.c | 48 +++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/protocol/pool_proto_modules.c b/src/protocol/pool_proto_modules.c index b1ed1c499..553aebe55 100644 --- a/src/protocol/pool_proto_modules.c +++ b/src/protocol/pool_proto_modules.c @@ -112,6 +112,8 @@ static bool multi_statement_query(char *buf); static void check_prepare(List *parse_tree_list, int len, char *contents); +static POOL_QUERY_CONTEXT *create_dummy_query_context(void); + /* * This is the workhorse of processing the pg_terminate_backend function to * make sure that the use of function should not trigger the backend node failover. @@ -1784,14 +1786,22 @@ Describe(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend, (errmsg("Describe message from frontend."), errdetail("portal: \"%s\"", contents + 1))); msg = pool_get_sent_message('B', contents + 1, POOL_SENT_MESSAGE_CREATED); - if (!msg) - ereport(FATAL, - (return_code(2), - errmsg("unable to execute Describe"), - errdetail("unable to get the bind message"))); } - query_context = msg->query_context; + query_context = NULL; + + if (msg) + query_context = msg->query_context; + + else if (!msg && *contents == 'P') + { + /* + * It is possible that client wants to use the portal created by + * DECLARE CUSOR or pl/pgSQL function. If so, the describe message + * should only be sent to primary node. + */ + query_context = create_dummy_query_context(); + } if (query_context == NULL) ereport(FATAL, @@ -4888,3 +4898,29 @@ check_prepare(List *parse_tree_list, int len, char *contents) pool_add_sent_message(message); /* add it to the sent message list */ } } + +/* + * Create a dummy query context using get_dummy_insert_query_node(). The + * query destination is also set. As a side effect, query in progress flag is + * set. + */ +static +POOL_QUERY_CONTEXT *create_dummy_query_context(void) +{ + POOL_QUERY_CONTEXT *query_context; + Node *node; + MemoryContext old_context; + char *query = "UNKNOWN QUERY"; + + query_context = pool_init_query_context(); + old_context = MemoryContextSwitchTo(query_context->memory_context); + + node = get_dummy_insert_query_node(); + pool_start_query(query_context, query, strlen(query), node); + pool_where_to_send(query_context, query_context->original_query, + query_context->parse_tree); + + MemoryContextSwitchTo(old_context); + + return query_context; +} -- 2.39.5