diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index e4487cc3223..926a271dc1f 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -5043,10 +5043,11 @@ int PQflush(PGconn *conn);
- While the pipeline API was introduced in
+ While libpq's pipeline API was introduced in
PostgreSQL 14, it is a client-side feature
which doesn't require special server support and works on any server
- that supports the v3 extended query protocol.
+ that supports the v3 extended query protocol. For more information see
+ .
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 27c55a7c812..0770d278c39 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1093,9 +1093,10 @@ SELCT 1/0;
implicit ROLLBACK if they failed. However, there
are a few DDL commands (such as CREATE DATABASE)
that cannot be executed inside a transaction block. If one of
- these is executed in a pipeline, it will, upon success, force an
- immediate commit to preserve database consistency.
- A Sync immediately following one of these has no effect except to
+ these is executed in a pipeline, it will fail unless it is the first
+ command in the pipeline. Furthermore, upon success it will force an
+ immediate commit to preserve database consistency. Thus a Sync
+ immediately following one of these commands has no effect except to
respond with ReadyForQuery.
@@ -1103,7 +1104,7 @@ SELCT 1/0;
When using this method, completion of the pipeline must be determined
by counting ReadyForQuery messages and waiting for that to reach the
number of Syncs sent. Counting command completion responses is
- unreliable, since some of the commands may not be executed and thus not
+ unreliable, since some of the commands may be skipped and thus not
produce a completion message.
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 408c87c6307..d0e5bc26a70 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -3488,6 +3488,16 @@ PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
errmsg("%s cannot run inside a subtransaction",
stmtType)));
+ /*
+ * inside a pipeline that has started an implicit transaction?
+ */
+ if (MyXactFlags & XACT_FLAGS_PIPELINING)
+ ereport(ERROR,
+ (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
+ /* translator: %s represents an SQL statement name */
+ errmsg("%s cannot be executed within a pipeline",
+ stmtType)));
+
/*
* inside a function call?
*/
@@ -3577,9 +3587,11 @@ CheckTransactionBlock(bool isTopLevel, bool throwError, const char *stmtType)
* a transaction block than when running as single commands. ANALYZE is
* currently the only example.
*
- * If this routine returns "false", then the calling statement is
- * guaranteed that if it completes without error, its results will be
- * committed immediately.
+ * If this routine returns "false", then the calling statement is allowed
+ * to perform internal transaction-commit-and-start cycles; there is not a
+ * risk of messing up any transaction already in progress. (Note that this
+ * is not the identical guarantee provided by PreventInTransactionBlock,
+ * since we will not force a post-statement commit.)
*
* isTopLevel: passed down from ProcessUtility to determine whether we are
* inside a function.
@@ -3597,6 +3609,9 @@ IsInTransactionBlock(bool isTopLevel)
if (IsSubTransaction())
return true;
+ if (MyXactFlags & XACT_FLAGS_PIPELINING)
+ return true;
+
if (!isTopLevel)
return true;
@@ -3604,13 +3619,6 @@ IsInTransactionBlock(bool isTopLevel)
CurrentTransactionState->blockState != TBLOCK_STARTED)
return true;
- /*
- * If we tell the caller we're not in a transaction block, then inform
- * postgres.c that it had better commit when the statement is done.
- * Otherwise our report could be a lie.
- */
- MyXactFlags |= XACT_FLAGS_NEEDIMMEDIATECOMMIT;
-
return false;
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 06dfe115e48..2fc49e34455 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -2241,6 +2241,12 @@ exec_execute_message(const char *portal_name, long max_rows)
*/
CommandCounterIncrement();
+ /*
+ * Set XACT_FLAGS_PIPELINING whenever we complete an Execute
+ * message without immediately committing the transaction.
+ */
+ MyXactFlags |= XACT_FLAGS_PIPELINING;
+
/*
* Disable statement timeout whenever we complete an Execute
* message. The next protocol message will start a fresh timeout.
@@ -2256,6 +2262,12 @@ exec_execute_message(const char *portal_name, long max_rows)
/* Portal run not complete, so send PortalSuspended */
if (whereToSendOutput == DestRemote)
pq_putemptymessage('s');
+
+ /*
+ * Set XACT_FLAGS_PIPELINING whenever we suspend an Execute message,
+ * too.
+ */
+ MyXactFlags |= XACT_FLAGS_PIPELINING;
}
/*
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 65616ca2f79..8d46a781bbd 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -113,6 +113,13 @@ extern PGDLLIMPORT int MyXactFlags;
*/
#define XACT_FLAGS_NEEDIMMEDIATECOMMIT (1U << 2)
+/*
+ * XACT_FLAGS_PIPELINING - set when we complete an extended-query-protocol
+ * Execute message. This is useful for detecting that an implicit transaction
+ * block has been created via pipelining.
+ */
+#define XACT_FLAGS_PIPELINING (1U << 3)
+
/*
* start- and end-of-transaction callbacks for dynamically loaded modules
*/