From 0b5bb4074d3c7e3d52e325cc7a6ae0d433388624 Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Mon, 8 Dec 2025 05:00:12 +0100 Subject: [PATCH 11/11] subtransaction support for session variables DDL (CREATE, DROP) If we support transactional DDL for CREATE, DROP session variables, we should to support subtransactions too. Implementation is simple. Any value has two new flags: created_subid and dropped_subid. At the subtransaction end for rollback we clean entries from the stack related to subtransactions. When commit we update created_subid and dropped_subid for parent subtransaction. --- src/backend/access/transam/xact.c | 4 + src/backend/commands/session_variable.c | 109 ++++++++++++++++++ src/include/commands/session_variable.h | 3 + .../expected/session_variables_ddl.out | 21 ++++ .../regress/sql/session_variables_ddl.sql | 12 ++ 5 files changed, 149 insertions(+) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 8a80e9c00af..57ee8da6d1f 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -5208,6 +5208,8 @@ CommitSubTransaction(void) AtEOSubXact_SPI(true, s->subTransactionId); AtEOSubXact_on_commit_actions(true, s->subTransactionId, s->parent->subTransactionId); + AtEOSubXact_SessionVariables(true, s->subTransactionId, + s->parent->subTransactionId); AtEOSubXact_Namespace(true, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_Files(true, s->subTransactionId, @@ -5377,6 +5379,8 @@ AbortSubTransaction(void) AtEOSubXact_SPI(false, s->subTransactionId); AtEOSubXact_on_commit_actions(false, s->subTransactionId, s->parent->subTransactionId); + AtEOSubXact_SessionVariables(false, s->subTransactionId, + s->parent->subTransactionId); AtEOSubXact_Namespace(false, s->subTransactionId, s->parent->subTransactionId); AtEOSubXact_Files(false, s->subTransactionId, diff --git a/src/backend/commands/session_variable.c b/src/backend/commands/session_variable.c index 76121b24363..1790f2ca4f3 100644 --- a/src/backend/commands/session_variable.c +++ b/src/backend/commands/session_variable.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "access/xact.h" #include "catalog/pg_language.h" #include "commands/session_variable.h" #include "executor/executor.h" @@ -59,6 +60,8 @@ typedef struct SVariableData bool stacked; LocalTransactionId created_lxid; LocalTransactionId dropped_lxid; + SubTransactionId created_subid; + SubTransactionId dropped_subid; } SVariableData; typedef SVariableData *SVariable; @@ -349,6 +352,8 @@ CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt) svar->stacked = false; svar->dropped_lxid = InvalidLocalTransactionId; svar->created_lxid = MyProc->vxid.lxid; + svar->dropped_subid = InvalidSubTransactionId; + svar->created_subid = GetCurrentSubTransactionId(); created_or_dropped_lxid = MyProc->vxid.lxid; } @@ -385,6 +390,7 @@ DropVariableByName(DropSessionVarStmt *stmt) stmt->name))); svar->dropped_lxid = MyProc->vxid.lxid; + svar->dropped_subid = GetCurrentSubTransactionId(); created_or_dropped_lxid = MyProc->vxid.lxid; } @@ -454,6 +460,7 @@ AtPreEOXact_SessionVariables(bool isCommit) free_stacked_svars(svar->prev); svar->prev = NULL; svar->created_lxid = InvalidLocalTransactionId; + svar->created_subid = InvalidSubTransactionId; } } else @@ -500,6 +507,7 @@ AtPreEOXact_SessionVariables(bool isCommit) /* revert dropped flag */ svar->dropped_lxid = InvalidLocalTransactionId; + svar->dropped_subid = InvalidSubTransactionId; } } } @@ -509,6 +517,107 @@ AtPreEOXact_SessionVariables(bool isCommit) } } +/* + * Post-subcommit or post-subabort cleanup + * + * During subabort, we can immediately remove entries created during this + * subtransaction. During subcommit, just transfer entries marked during + * this subtransaction as being the parent's responsibility. + */ +void +AtEOSubXact_SessionVariables(bool isCommit, + SubTransactionId mySubid, + SubTransactionId parentSubid) +{ + if (created_or_dropped_lxid != InvalidLocalTransactionId) + { + HASH_SEQ_STATUS status; + SVariable svar; + + Assert(created_or_dropped_lxid == MyProc->vxid.lxid); + Assert(sessionvars); + + hash_seq_init(&status, sessionvars); + + while ((svar = (SVariable) hash_seq_search(&status)) != NULL) + { + if ((svar->dropped_lxid != InvalidLocalTransactionId) || + (svar->created_lxid != InvalidLocalTransactionId)) + { + if (!isCommit) + { + SVariable iterator = svar; + SVariable last = NULL; + SVariable first = NULL; + + /* remove entries or flags by current subtransactions */ + while (iterator) + { + SVariable current = iterator; + + iterator = current->prev; + + if (current->dropped_subid == mySubid) + { + current->dropped_lxid = InvalidLocalTransactionId; + current->dropped_subid = InvalidSubTransactionId; + } + + if (current->created_subid == mySubid) + { + free_svar_value(current); + if (current->stacked) + pfree(current); + } + else + { + /* remember first not deleted svar */ + if (first == NULL) + first = current; + + if (last) + last->prev = current; + + last = current; + } + } + + /* Some svars was removed - set hashtab entry or remove it */ + if (!first) + { + /* we have to remove entry from hash table */ + (void) hash_search(sessionvars, + NameStr(svar->varname), + HASH_REMOVE, + NULL); + } + else if (first->stacked) + { + memcpy(svar, first, sizeof(SVariableData)); + svar->stacked = false; + pfree(first); + } + } + else + { + SVariable iterator = svar; + + /* transfer responsibility to parent */ + while (iterator) + { + if (iterator->dropped_subid == mySubid) + iterator->dropped_subid = parentSubid; + if (iterator->created_subid == mySubid) + iterator->created_subid = parentSubid; + + iterator = iterator->prev; + } + } + } + } + } +} + /* * Assign the result of the evaluated expression to the session variable */ diff --git a/src/include/commands/session_variable.h b/src/include/commands/session_variable.h index 1218c566767..45ccbe2f046 100644 --- a/src/include/commands/session_variable.h +++ b/src/include/commands/session_variable.h @@ -40,5 +40,8 @@ extern void ExecuteLetStmt(ParseState *pstate, LetStmt *stmt, ParamListInfo para extern void ResetSessionVariables(void); extern void AtPreEOXact_SessionVariables(bool isCommit); +extern void AtEOSubXact_SessionVariables(bool isCommit, + SubTransactionId mySubid, + SubTransactionId parentSubid); #endif diff --git a/src/test/regress/expected/session_variables_ddl.out b/src/test/regress/expected/session_variables_ddl.out index 2d00471da96..9031b5c384c 100644 --- a/src/test/regress/expected/session_variables_ddl.out +++ b/src/test/regress/expected/session_variables_ddl.out @@ -107,4 +107,25 @@ SELECT VARIABLE(x); Hi (1 row) +BEGIN; +SAVEPOINT s1; +DROP VARIABLE x; +CREATE TEMP VARIABLE x AS varchar; +DROP VARIABLE x; +CREATE TEMP VARIABLE x AS varchar; +LET x = 'Hello'; +SELECT VARIABLE(x); + x +------- + Hello +(1 row) + +ROLLBACK TO s1; +SELECT VARIABLE(x); + x +---- + Hi +(1 row) + +COMMIT; DROP VARIABLE x; diff --git a/src/test/regress/sql/session_variables_ddl.sql b/src/test/regress/sql/session_variables_ddl.sql index 7335f15ed39..5ec412ad7c9 100644 --- a/src/test/regress/sql/session_variables_ddl.sql +++ b/src/test/regress/sql/session_variables_ddl.sql @@ -98,4 +98,16 @@ SELECT VARIABLE(x); ROLLBACK; SELECT VARIABLE(x); +BEGIN; +SAVEPOINT s1; +DROP VARIABLE x; +CREATE TEMP VARIABLE x AS varchar; +DROP VARIABLE x; +CREATE TEMP VARIABLE x AS varchar; +LET x = 'Hello'; +SELECT VARIABLE(x); +ROLLBACK TO s1; +SELECT VARIABLE(x); +COMMIT; + DROP VARIABLE x; -- 2.52.0