From 8444e287c12676adee6bd27328bb9cea261f46d1 Mon Sep 17 00:00:00 2001 From: Tatsuo Ishii Date: Thu, 24 Jul 2025 10:34:59 +0900 Subject: [PATCH v3] Allow to accept simple query even if extended queries do not end. Recently pgJDBC has started to issue simple query without ending extended query protocol sequences with sync. This brought a disaster to pgpool. So pgpool refused to accept it. https://git.postgresql.org/gitweb/?p=pgpool2.git;a=commit;h=240c668d120065534b1d298d6facc86839fcbab9 However the situation got worse. Previously pgJDBC issued a simple query without ending extended protocol only when "autosave=always" option is given. But it was reported that pgJDBC has started to use it extensively. https://www.postgresql.org/message-id/CAGXsc%2Baoabb2xxyfckrHfTx4da8%3Ds1L9ai%2BY%2BuAS4cBPRMQD2A%40mail.gmail.com So this commit deals with the situation. When a simple query arrives without finishing extended protocol sequences, before calling SimpleQuery(), ProcessBackendResponse() is called to process pending extended query protocol replies from backend such as parse, bind or command complete messages, instead of rejecting the simple query. After this, SimpleQuery() can process the simple query as usual. Author: Tatsuo Ishii Reported-by: Emond Papegaaij Discussion: https://www.postgresql.org/message-id/CAGXsc%2Baoabb2xxyfckrHfTx4da8%3Ds1L9ai%2BY%2BuAS4cBPRMQD2A%40mail.gmail.com --- src/protocol/pool_proto_modules.c | 61 +++++++++++++------ .../082.guard_against_bad_protocol/test.sh | 5 +- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/protocol/pool_proto_modules.c b/src/protocol/pool_proto_modules.c index 11befe979..fdb41bbf0 100644 --- a/src/protocol/pool_proto_modules.c +++ b/src/protocol/pool_proto_modules.c @@ -221,23 +221,6 @@ SimpleQuery(POOL_CONNECTION *frontend, /* save last query string for logging purpose */ strlcpy(query_string_buffer, contents, sizeof(query_string_buffer)); - /* - * Check if extended query protocol message ended. If not, reject the - * query and raise an error to terminate the session to avoid hanging up. - * However if we are processing a reset query (frontend == NULL), we skip - * the check as we don't want to raise a error. - */ - if (SL_MODE) - { - if (frontend != NULL && - (pool_is_doing_extended_query_message() || - pool_pending_message_head_message())) - - ereport(FATAL, - (errmsg("simple query \"%s\" arrived before ending an extended query message", - query_string_buffer))); - } - /* show ps status */ query_ps_status(contents, backend); @@ -2930,6 +2913,44 @@ ProcessFrontendResponse(POOL_CONNECTION *frontend, ereport(LOG, (errmsg("Query message from frontend."), errdetail("query: \"%s\"", contents))); + + /* + * Check if extended query protocol message ended. If not, process + * any pending response from backend using + * ProcessBackendResponse(). However if we are processing a reset + * query (frontend == NULL), we skip the check as we don't need to + * care about any pending response from backend. + */ + if (SL_MODE) + { + int state; + short num_fields; + + if (frontend != NULL && + (pool_is_doing_extended_query_message() || + pool_pending_message_exists())) + { + ereport(DEBUG1, + (errmsg("simple query \"%s\" arrived before ending an extended query message", + contents))); + + /* if pending message exists, process it */ + while (pool_pending_message_exists()) + { + /* + * read_kind_from_backend requires that query is + * inprogress and doing extended query state because + * it needs to refer to proper query context in + * session context. + */ + pool_set_query_in_progress(); + pool_set_doing_extended_query_message(); + /* process pending responses from backend */ + ProcessBackendResponse(frontend, backend, &state, &num_fields); + } + } + pool_unset_doing_extended_query_message(); + } status = SimpleQuery(frontend, backend, len, contents); break; @@ -3080,6 +3101,12 @@ ProcessFrontendResponse(POOL_CONNECTION *frontend, return status; } +/* + * Read one backend response and process it. + * + * state: used for processing reset query + * num_fields: used in V2 protocol + */ POOL_STATUS ProcessBackendResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, diff --git a/src/test/regression/tests/082.guard_against_bad_protocol/test.sh b/src/test/regression/tests/082.guard_against_bad_protocol/test.sh index f1d16e6f3..c4f6e90b4 100755 --- a/src/test/regression/tests/082.guard_against_bad_protocol/test.sh +++ b/src/test/regression/tests/082.guard_against_bad_protocol/test.sh @@ -22,6 +22,7 @@ echo -n "creating test environment..." $PGPOOL_SETUP || exit 1 echo "done." echo "backend_weight1=0" >> etc/pgpool.conf +echo "client_min_messages=debug1" >> etc/pgpool.conf source ./bashrc.ports ./startall wait_for_pgpool_startup @@ -29,8 +30,8 @@ wait_for_pgpool_startup # test1: # Wait for 1 seconds before pgproto ended. # Usually 1 seconds should be enough to finish pgproto. -# If test suceeded, pgpool emits an error message: -# "FATAL: simple query "SAVEPOINT PGJDBC_AUTOSAVE" arrived before ending an extended query message" +# If the test suceeds, pgpool emits a debug message: +# "simple query "SAVEPOINT PGJDBC_AUTOSAVE" arrived before ending an extended query message" # grep command below should catch the message. timeout 1 $PGPROTO -d $PGDATABASE -p $PGPOOL_PORT -f ../pgproto.data |& grep 'simple query "SAVEPOINT PGJDBC_AUTOSAVE" arrived ' -- 2.25.1