From a2ec0d2326a562576905f14d75391d22a0a070b3 Mon Sep 17 00:00:00 2001
From: Tatsuo Ishii <ishii@postgresql.org>
Date: Wed, 23 Jul 2025 16:53:03 +0900
Subject: [PATCH v2] 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 refuses 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, SimpleQuery() calls
ProcessBackendResponse() 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()
process a simple query as usual.

Discussion: https://www.postgresql.org/message-id/CAGXsc%2Baoabb2xxyfckrHfTx4da8%3Ds1L9ai%2BY%2BuAS4cBPRMQD2A%40mail.gmail.com
---
 src/protocol/pool_proto_modules.c | 61 ++++++++++++++++++++++---------
 1 file changed, 44 insertions(+), 17 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,
-- 
2.25.1

