transaction_cleanup.v4.patch
application/octet-stream
Filename: transaction_cleanup.v4.patch
Type: application/octet-stream
Part: 0
Message:
Discarding DISCARD ALL
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v4
| File | + | − |
|---|---|---|
| src/backend/access/transam/xact.c | 71 | 14 |
| src/backend/commands/async.c | 6 | 0 |
| src/backend/utils/misc/guc.c | 9 | 0 |
| src/include/access/xact.h | 4 | 0 |
| src/test/regress/expected/guc.out | 76 | 1 |
| src/test/regress/sql/guc.sql | 28 | 1 |
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 9cd0b7c11b..6db7b37dad 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -35,6 +35,8 @@
#include "catalog/pg_enum.h"
#include "catalog/storage.h"
#include "commands/async.h"
+#include "commands/prepare.h"
+#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "executor/spi.h"
@@ -80,6 +82,8 @@ bool XactReadOnly;
bool DefaultXactDeferrable = false;
bool XactDeferrable;
+bool transaction_cleanup = false;
+
int synchronous_commit = SYNCHRONOUS_COMMIT_ON;
/*
@@ -336,6 +340,8 @@ static void CommitTransaction(void);
static TransactionId RecordTransactionAbort(bool isSubXact);
static void StartTransaction(void);
+static void PostXactSessionCleanup(void);
+
static void StartSubTransaction(void);
static void CommitSubTransaction(void);
static void AbortSubTransaction(void);
@@ -2132,11 +2138,23 @@ CommitTransaction(void)
/* Shut down the deferred-trigger manager */
AfterTriggerEndXact(true);
- /*
- * Let ON COMMIT management do its thing (must happen after closing
- * cursors, to avoid dangling-reference problems)
- */
- PreCommit_on_commit_actions();
+ /* Apply cleanup consistently across all end of xact actions */
+ if (transaction_cleanup && !IsBootstrapProcessingMode())
+ {
+ MyXactFlags |= XACT_FLAGS_CLEANUP_AT_XACT_END;
+
+ /* Closing portals might run user-defined code, so do that first. */
+ PortalHashTableDeleteAll();
+ ResetTempTableNamespace();
+ }
+ else
+ {
+ /*
+ * Let ON COMMIT management do its thing (must happen after closing
+ * cursors, to avoid dangling-reference problems)
+ */
+ PreCommit_on_commit_actions();
+ }
/*
* Synchronize files that are created and not WAL-logged during this
@@ -2372,11 +2390,23 @@ PrepareTransaction(void)
/* Shut down the deferred-trigger manager */
AfterTriggerEndXact(true);
- /*
- * Let ON COMMIT management do its thing (must happen after closing
- * cursors, to avoid dangling-reference problems)
- */
- PreCommit_on_commit_actions();
+ /* Apply cleanup consistently across all end of xact actions */
+ if (transaction_cleanup && !IsBootstrapProcessingMode())
+ {
+ MyXactFlags |= XACT_FLAGS_CLEANUP_AT_XACT_END;
+
+ /* Closing portals might run user-defined code, so do that first. */
+ PortalHashTableDeleteAll();
+ ResetTempTableNamespace();
+ }
+ else
+ {
+ /*
+ * Let ON COMMIT management do its thing (must happen after closing
+ * cursors, to avoid dangling-reference problems)
+ */
+ PreCommit_on_commit_actions();
+ }
/*
* Synchronize files that are created and not WAL-logged during this
@@ -2413,11 +2443,12 @@ PrepareTransaction(void)
* We must check this after executing any ON COMMIT actions, because they
* might still access a temp relation.
*
- * XXX In principle this could be relaxed to allow some useful special
- * cases, such as a temp table created and dropped all within the
- * transaction. That seems to require much more bookkeeping though.
+ * This can be relaxed if we are using transaction_cleanup since we know
+ * that any temp tables will have been created and dropped all within the
+ * transaction.
*/
- if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
+ if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE) &&
+ !(MyXactFlags & XACT_FLAGS_CLEANUP_AT_XACT_END))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot PREPARE a transaction that has operated on temporary objects")));
@@ -2840,6 +2871,25 @@ CleanupTransaction(void)
s->state = TRANS_DEFAULT;
}
+/*
+ * Perform same actions as DISCARD ALL, but don't try to execute that
+ * directly since it expects to be inside a new transaction.
+ */
+static void
+PostXactSessionCleanup(void)
+{
+ if ((MyXactFlags & XACT_FLAGS_CLEANUP_AT_XACT_END))
+ {
+ SetPGVariable("session_authorization", NIL, false);
+ ResetAllOptions();
+ DropAllPreparedStatements();
+ LockReleaseAll(USER_LOCKMETHOD, true);
+ ResetPlanCache();
+ ResetSequenceCaches();
+ }
+}
+
+
/*
* StartTransactionCommand
*/
@@ -3010,6 +3060,8 @@ CommitTransactionCommand(void)
s->chain = false;
RestoreTransactionCharacteristics();
}
+ else
+ PostXactSessionCleanup();
break;
/*
@@ -3036,6 +3088,8 @@ CommitTransactionCommand(void)
s->chain = false;
RestoreTransactionCharacteristics();
}
+ else
+ PostXactSessionCleanup();
break;
/*
@@ -3054,6 +3108,8 @@ CommitTransactionCommand(void)
s->chain = false;
RestoreTransactionCharacteristics();
}
+ else
+ PostXactSessionCleanup();
break;
/*
@@ -3062,6 +3118,7 @@ CommitTransactionCommand(void)
*/
case TBLOCK_PREPARE:
PrepareTransaction();
+ PostXactSessionCleanup();
s->blockState = TBLOCK_DEFAULT;
break;
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index e04afd9963..ee7e5dba59 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1018,6 +1018,12 @@ AtCommit_Notify(void)
}
}
+ /*
+ * Remove all listenChannels directly, if we need to cleanup
+ */
+ if (amRegisteredListener && transaction_cleanup)
+ Exec_UnlistenAllCommit();
+
/* If no longer listening to anything, get out of listener array */
if (amRegisteredListener && listenChannels == NIL)
asyncQueueUnregister();
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index dabcbb0736..d24a3ff285 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1633,6 +1633,15 @@ static struct config_bool ConfigureNamesBool[] =
false,
NULL, NULL, NULL
},
+ {
+ {"transaction_cleanup", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Whether session cleanup occurs immediately following transaction completion."),
+ NULL
+ },
+ &transaction_cleanup,
+ false,
+ NULL, NULL, NULL
+ },
{
{"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 7320de345c..b1bbac2cb8 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -86,6 +86,8 @@ extern int synchronous_commit;
extern PGDLLIMPORT TransactionId CheckXidAlive;
extern PGDLLIMPORT bool bsysscan;
+extern bool transaction_cleanup;
+
/*
* Miscellaneous flag bits to record events which occur on the top level
* transaction. These flags are only persisted in MyXactFlags and are intended
@@ -107,6 +109,8 @@ extern int MyXactFlags;
*/
#define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK (1U << 1)
+#define XACT_FLAGS_CLEANUP_AT_XACT_END (1U << 2)
+
/*
* start- and end-of-transaction callbacks for dynamically loaded modules
*/
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 811f80a097..1c5552495c 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -530,13 +530,13 @@ SELECT relname FROM pg_class WHERE relname = 'reset_test';
--
-- Test DISCARD ALL
--
+CREATE ROLE regress_guc_user;
-- do changes
DECLARE foo CURSOR WITH HOLD FOR SELECT 1;
PREPARE foo AS SELECT 1;
LISTEN foo_event;
SET vacuum_cost_delay = 13;
CREATE TEMP TABLE tmp_foo (data text) ON COMMIT DELETE ROWS;
-CREATE ROLE regress_guc_user;
SET SESSION AUTHORIZATION regress_guc_user;
-- look changes
SELECT pg_listening_channels();
@@ -610,6 +610,81 @@ SELECT current_user = 'regress_guc_user';
f
(1 row)
+-- do changes, with cleanup
+SET transaction_cleanup = on;
+BEGIN;
+DECLARE foo CURSOR WITH HOLD FOR SELECT 1;
+PREPARE foo AS SELECT 1;
+LISTEN foo_event;
+SET vacuum_cost_delay = 13;
+CREATE TEMP TABLE tmp_foo (data text) ON COMMIT DELETE ROWS;
+SET SESSION AUTHORIZATION regress_guc_user;
+-- look changes
+SELECT name FROM pg_prepared_statements;
+ name
+------
+ foo
+(1 row)
+
+SELECT name FROM pg_cursors;
+ name
+------
+ foo
+(1 row)
+
+SHOW vacuum_cost_delay;
+ vacuum_cost_delay
+-------------------
+ 13ms
+(1 row)
+
+SELECT relname from pg_class where relname = 'tmp_foo';
+ relname
+---------
+ tmp_foo
+(1 row)
+
+SELECT current_user = 'regress_guc_user';
+ ?column?
+----------
+ t
+(1 row)
+
+COMMIT;
+SET transaction_cleanup = off;
+-- look again, should be same as DISCARD ALL
+SELECT pg_listening_channels();
+ pg_listening_channels
+-----------------------
+(0 rows)
+
+SELECT name FROM pg_prepared_statements;
+ name
+------
+(0 rows)
+
+SELECT name FROM pg_cursors;
+ name
+------
+(0 rows)
+
+SHOW vacuum_cost_delay;
+ vacuum_cost_delay
+-------------------
+ 0
+(1 row)
+
+SELECT relname from pg_class where relname = 'tmp_foo';
+ relname
+---------
+(0 rows)
+
+SELECT current_user = 'regress_guc_user';
+ ?column?
+----------
+ f
+(1 row)
+
DROP ROLE regress_guc_user;
--
-- search_path should react to changes in pg_namespace
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index 43dbba3775..e7f4095f82 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -160,13 +160,14 @@ SELECT relname FROM pg_class WHERE relname = 'reset_test';
-- Test DISCARD ALL
--
+CREATE ROLE regress_guc_user;
+
-- do changes
DECLARE foo CURSOR WITH HOLD FOR SELECT 1;
PREPARE foo AS SELECT 1;
LISTEN foo_event;
SET vacuum_cost_delay = 13;
CREATE TEMP TABLE tmp_foo (data text) ON COMMIT DELETE ROWS;
-CREATE ROLE regress_guc_user;
SET SESSION AUTHORIZATION regress_guc_user;
-- look changes
SELECT pg_listening_channels();
@@ -184,6 +185,32 @@ SELECT name FROM pg_cursors;
SHOW vacuum_cost_delay;
SELECT relname from pg_class where relname = 'tmp_foo';
SELECT current_user = 'regress_guc_user';
+
+-- do changes, with cleanup
+SET transaction_cleanup = on;
+BEGIN;
+DECLARE foo CURSOR WITH HOLD FOR SELECT 1;
+PREPARE foo AS SELECT 1;
+LISTEN foo_event;
+SET vacuum_cost_delay = 13;
+CREATE TEMP TABLE tmp_foo (data text) ON COMMIT DELETE ROWS;
+SET SESSION AUTHORIZATION regress_guc_user;
+-- look changes
+SELECT name FROM pg_prepared_statements;
+SELECT name FROM pg_cursors;
+SHOW vacuum_cost_delay;
+SELECT relname from pg_class where relname = 'tmp_foo';
+SELECT current_user = 'regress_guc_user';
+COMMIT;
+SET transaction_cleanup = off;
+-- look again, should be same as DISCARD ALL
+SELECT pg_listening_channels();
+SELECT name FROM pg_prepared_statements;
+SELECT name FROM pg_cursors;
+SHOW vacuum_cost_delay;
+SELECT relname from pg_class where relname = 'tmp_foo';
+SELECT current_user = 'regress_guc_user';
+
DROP ROLE regress_guc_user;
--