aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-07-26 13:07:03 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2022-07-26 13:07:03 -0400
commita0c632c1dea74de042110e204bd2dc634e87b7ea (patch)
tree67ae4f5d31d4fa5907fb00cf4f3ffa1b040f4356 /src/backend/access/transam
parent7977ac1640a76416eb99c843ad06015bab884bf1 (diff)
downloadpostgresql-a0c632c1dea74de042110e204bd2dc634e87b7ea.tar.gz
postgresql-a0c632c1dea74de042110e204bd2dc634e87b7ea.zip
Force immediate commit after CREATE DATABASE etc in extended protocol.
We have a few commands that "can't run in a transaction block", meaning that if they complete their processing but then we fail to COMMIT, we'll be left with inconsistent on-disk state. However, the existing defenses for this are only watertight for simple query protocol. In extended protocol, we didn't commit until receiving a Sync message. Since the client is allowed to issue another command instead of Sync, we're in trouble if that command fails or is an explicit ROLLBACK. In any case, sitting in an inconsistent state while waiting for a client message that might not come seems pretty risky. This case wasn't reachable via libpq before we introduced pipeline mode, but it's always been an intended aspect of extended query protocol, and likely there are other clients that could reach it before. To fix, set a flag in PreventInTransactionBlock that tells exec_execute_message to force an immediate commit. This seems to be the approach that does least damage to existing working cases while still preventing the undesirable outcomes. While here, add some documentation to protocol.sgml that explicitly says how to use pipelining. That's latent in the existing docs if you know what to look for, but it's better to spell it out; and it provides a place to document this new behavior. Per bug #17434 from Yugo Nagata. It's been wrong for ages, so back-patch to all supported branches. Discussion: https://postgr.es/m/17434-d9f7a064ce2a88a3@postgresql.org
Diffstat (limited to 'src/backend/access/transam')
-rw-r--r--src/backend/access/transam/xact.c14
1 files changed, 13 insertions, 1 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index bd60b55574c..594d8da2cdc 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -3453,6 +3453,9 @@ AbortCurrentTransaction(void)
* could issue more commands and possibly cause a failure after the statement
* completes). Subtransactions are verboten too.
*
+ * We must also set XACT_FLAGS_NEEDIMMEDIATECOMMIT in MyXactFlags, to ensure
+ * that postgres.c follows through by committing after the statement is done.
+ *
* isTopLevel: passed down from ProcessUtility to determine whether we are
* inside a function. (We will always fail if this is false, but it's
* convenient to centralize the check here instead of making callers do it.)
@@ -3494,7 +3497,9 @@ PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
if (CurrentTransactionState->blockState != TBLOCK_DEFAULT &&
CurrentTransactionState->blockState != TBLOCK_STARTED)
elog(FATAL, "cannot prevent transaction chain");
- /* all okay */
+
+ /* All okay. Set the flag to make sure the right thing happens later. */
+ MyXactFlags |= XACT_FLAGS_NEEDIMMEDIATECOMMIT;
}
/*
@@ -3591,6 +3596,13 @@ 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;
}