Rebase.

Regards,
Dmitrii Bondar.
From 5d49526ee4e802b67c5b21fd473d5e02c4554048 Mon Sep 17 00:00:00 2001
From: Dmitrii Bondar <[email protected]>
Date: Mon, 2 Mar 2026 18:08:30 +0700
Subject: [PATCH v3] Remove synchronous prepare from pgbench

Pgbench waits for the result of a prepare packet synchronously
when the prepared protocol flag is provided. For this reason
it is impossible to use it with some poolers (such as PgBouncer)
in the session pooling mode.
It is replaced by the full sequence of parse/bind/execute/sync
packets. This behaviour is similar to pgbench with the extended
protocol flag.
---
 doc/src/sgml/libpq.sgml                      | 30 +++++++++++++
 src/bin/pgbench/pgbench.c                    | 22 ++++++++--
 src/bin/pgbench/t/001_pgbench_with_server.pl |  1 -
 src/interfaces/libpq/exports.txt             |  1 +
 src/interfaces/libpq/fe-exec.c               | 46 ++++++++++++++++++++
 src/interfaces/libpq/libpq-fe.h              |  9 ++++
 6 files changed, 104 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 6db823808fc..ff890e739ac 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -5329,6 +5329,7 @@ unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);
    <xref linkend="libpq-PQsendQueryParams"/>,
    <xref linkend="libpq-PQsendPrepare"/>,
    <xref linkend="libpq-PQsendQueryPrepared"/>,
+   <xref linkend="libpq-PQsendPBES"/>,
    <xref linkend="libpq-PQsendDescribePrepared"/>,
    <xref linkend="libpq-PQsendDescribePortal"/>,
    <xref linkend="libpq-PQsendClosePrepared"/>, and
@@ -5450,6 +5451,34 @@ int PQsendQueryPrepared(PGconn *conn,
      </listitem>
     </varlistentry>
 
+    <varlistentry id="libpq-PQsendPBES">
+     <term><function>PQsendPBES</function><indexterm><primary>PQsendPBES</primary></indexterm></term>
+
+     <listitem>
+      <para>
+       Sends a request to prepare and execute a statement with given
+       parameters, without waiting for the result(s).
+<synopsis>
+int PQsendPBES(PGconn *conn,
+               const char *stmtName,
+               const char *query,
+               int nParams,
+               const Oid *paramTypes,
+               const char *const *paramValues,
+               const int *paramLengths,
+               const int *paramFormats,
+               int resultFormat);
+</synopsis>
+
+       This is similar to <xref linkend="libpq-PQsendQueryPrepared"/>, but the
+       statement is prepared as part of the same request using the supplied
+       query string. Internally, it sends a Parse, Bind, Execute, and Sync
+       message sequence. The function's parameters are handled identically to
+       <xref linkend="libpq-PQexecParams"/>.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry id="libpq-PQsendDescribePrepared">
      <term><function>PQsendDescribePrepared</function><indexterm><primary>PQsendDescribePrepared</primary></indexterm></term>
 
@@ -5540,6 +5569,7 @@ int PQsendClosePortal(PGconn *conn, const char *portalName);
        <xref linkend="libpq-PQsendQueryParams"/>,
        <xref linkend="libpq-PQsendPrepare"/>,
        <xref linkend="libpq-PQsendQueryPrepared"/>,
+       <xref linkend="libpq-PQsendPBES"/>,
        <xref linkend="libpq-PQsendDescribePrepared"/>,
        <xref linkend="libpq-PQsendDescribePortal"/>,
        <xref linkend="libpq-PQsendClosePrepared"/>,
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 1dae918cc09..749b9347a6e 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -3179,12 +3179,26 @@ sendCommand(CState *st, Command *command)
 	{
 		const char *params[MAX_ARGS];
 
-		prepareCommand(st, st->command);
 		getQueryParams(&st->variables, command, params);
 
-		pg_log_debug("client %d sending %s", st->id, command->prepname);
-		r = PQsendQueryPrepared(st->con, command->prepname, command->argc - 1,
-								params, NULL, NULL, 0);
+		if (!st->prepared)
+			allocCStatePrepared(st);
+
+		if (!st->prepared[st->use_file][st->command])
+		{
+			r = PQsendPBES(st->con, command->prepname,
+						   command->argv[0], command->argc - 1, NULL,
+						   params, NULL, NULL, 0);
+			if (!r)
+				pg_log_error("%s", PQerrorMessage(st->con));
+			st->prepared[st->use_file][st->command] = true;
+		}
+		else
+		{
+			pg_log_debug("client %d sending %s", st->id, command->prepname);
+			r = PQsendQueryPrepared(st->con, command->prepname, command->argc - 1,
+									params, NULL, NULL, 0);
+		}
 	}
 	else						/* unknown sql mode */
 		r = 0;
diff --git a/src/bin/pgbench/t/001_pgbench_with_server.pl b/src/bin/pgbench/t/001_pgbench_with_server.pl
index b7685ea5d20..c047b5d0460 100644
--- a/src/bin/pgbench/t/001_pgbench_with_server.pl
+++ b/src/bin/pgbench/t/001_pgbench_with_server.pl
@@ -1231,7 +1231,6 @@ my @errors = (
 		2,
 		[
 			qr{ERROR:  syntax error},
-			qr{prepared statement .* does not exist}
 		],
 		q{-- SQL syntax error
     SELECT 1 + ;
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 1e3d5bd5867..cca19c0193d 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -211,3 +211,4 @@ PQdefaultAuthDataHook     208
 PQfullProtocolVersion     209
 appendPQExpBufferVA       210
 PQgetThreadLock           211
+PQsendPBES                212
\ No newline at end of file
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 203d388bdbf..19e4381e6bb 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1682,6 +1682,52 @@ PQsendQueryPrepared(PGconn *conn,
 						   resultFormat);
 }
 
+/* PQsendPBES
+ *		Like PQsendQueryPrepared, but it also prepares a query
+ */
+int
+PQsendPBES(PGconn *conn,
+		   const char *stmtName,
+		   const char *query,
+		   int nParams,
+		   const Oid *paramTypes,
+		   const char *const *paramValues,
+		   const int *paramLengths,
+		   const int *paramFormats,
+		   int resultFormat)
+{
+	if (!PQsendQueryStart(conn, true))
+		return 0;
+
+	/* check the arguments */
+	if (!stmtName)
+	{
+		libpq_append_conn_error(conn, "statement name is a null pointer");
+		return 0;
+	}
+	if (!query)
+	{
+		libpq_append_conn_error(conn, "command string is a null pointer");
+		return 0;
+	}
+	if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
+	{
+		libpq_append_conn_error(conn, "number of parameters must be between 0 and %d",
+								PQ_QUERY_PARAM_MAX_LIMIT);
+		return 0;
+	}
+
+	return PQsendQueryGuts(conn,
+						   query,
+						   stmtName,
+						   nParams,
+						   paramTypes,
+						   paramValues,
+						   paramLengths,
+						   paramFormats,
+						   resultFormat);
+}
+
 /*
  * PQsendQueryStart
  *	Common startup code for PQsendQuery and sibling routines
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index f06e7a972c3..e12a6e79ca1 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -535,6 +535,15 @@ extern int	PQsendQueryPrepared(PGconn *conn,
 								const int *paramLengths,
 								const int *paramFormats,
 								int resultFormat);
+extern int	PQsendPBES(PGconn *conn,
+					   const char *stmtName,
+					   const char *query,
+					   int nParams,
+					   const Oid *paramTypes,
+					   const char *const *paramValues,
+					   const int *paramLengths,
+					   const int *paramFormats,
+					   int resultFormat);
 extern int	PQsetSingleRowMode(PGconn *conn);
 extern int	PQsetChunkedRowsMode(PGconn *conn, int chunkSize);
 extern PGresult *PQgetResult(PGconn *conn);
-- 
2.52.0

Reply via email to