From d8aa3c9044058c9246be454eb3e2acc0a320d2cd Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Thu, 6 Jul 2023 09:32:25 +0200 Subject: [PATCH 5/9] implementation of temporary session variables The temporary variables are created inside temp schema and they are dropped by dropping this schema. For consistency with temp tables, the CREATE TEMP VARIABLE command supports ON COMMIT DROP clause. The temporary variables with this clause are collected in xact_drop_items list. This list should be carefully maintained and can contain only valid entries (not yet dropped). From this reasons now the subcommit and subaborting have to be handled. --- doc/src/sgml/catalogs.sgml | 10 + doc/src/sgml/ref/create_variable.sgml | 15 +- src/backend/access/transam/xact.c | 7 +- src/backend/catalog/pg_variable.c | 24 ++- src/backend/commands/session_variable.c | 191 +++++++++++++++++- src/backend/commands/view.c | 2 +- src/backend/parser/analyze.c | 4 +- src/backend/parser/gram.y | 30 ++- src/backend/parser/parse_relation.c | 22 +- src/bin/psql/describe.c | 10 +- src/bin/psql/tab-complete.c | 3 +- src/include/catalog/pg_variable.h | 9 + src/include/commands/session_variable.h | 7 +- src/include/nodes/parsenodes.h | 1 + src/include/parser/parse_relation.h | 2 +- .../isolation/expected/session-variable.out | 3 +- .../isolation/specs/session-variable.spec | 3 +- src/test/regress/expected/psql.out | 36 ++-- .../regress/expected/session_variables.out | 130 +++++++++++- src/test/regress/sql/session_variables.sql | 66 ++++++ src/tools/pgindent/typedefs.list | 1 + 21 files changed, 515 insertions(+), 61 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index fee3188407..cc7cb6e1bc 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -9773,6 +9773,16 @@ SCRAM-SHA-256$<iteration count>:&l + + + vareoxaction char + + + Action performed at end of transaction: + n = no action, d = drop the variable. + + + varcollation oid diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml index faed05e22b..1915dea3a9 100644 --- a/doc/src/sgml/ref/create_variable.sgml +++ b/doc/src/sgml/ref/create_variable.sgml @@ -26,7 +26,8 @@ PostgreSQL documentation -CREATE VARIABLE [ IF NOT EXISTS ] name [ AS ] data_type ] [ COLLATE collation ] +CREATE [ { TEMPORARY | TEMP } ] VARIABLE [ IF NOT EXISTS ] name [ AS ] data_type ] [ COLLATE collation ] + [ ON COMMIT DROP ] @@ -110,6 +111,18 @@ CREATE VARIABLE [ IF NOT EXISTS ] name + + ON COMMIT DROP + + + The ON COMMIT DROP clause specifies the behaviour of a + temporary session variable at transaction commit. With this clause, the + session variable is dropped at commit time. The clause is only allowed + for temporary variables. + + + + diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 8420cce718..fcd1c906d6 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -2226,7 +2226,7 @@ CommitTransaction(void) smgrDoPendingSyncs(true, is_parallel_worker); /* Remove values of dropped session variables from memory */ - AtPreEOXact_SessionVariables(); + AtPreEOXact_SessionVariables(true); /* close large objects before lower-level cleanup */ AtEOXact_LargeObject(true); @@ -2804,6 +2804,7 @@ AbortTransaction(void) AtAbort_Portals(); smgrDoPendingSyncs(false, is_parallel_worker); AtEOXact_LargeObject(false); + AtPreEOXact_SessionVariables(false); AtAbort_Notify(); AtEOXact_RelationMap(false, is_parallel_worker); AtAbort_Twophase(); @@ -5028,6 +5029,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, @@ -5191,6 +5194,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/catalog/pg_variable.c b/src/backend/catalog/pg_variable.c index d962108671..89495d13ca 100644 --- a/src/backend/catalog/pg_variable.c +++ b/src/backend/catalog/pg_variable.c @@ -36,7 +36,8 @@ static ObjectAddress create_variable(const char *varName, int32 varTypmod, Oid varOwner, Oid varCollation, - bool if_not_exists); + bool if_not_exists, + VariableEOXAction eoxaction); /* @@ -49,7 +50,8 @@ create_variable(const char *varName, int32 varTypmod, Oid varOwner, Oid varCollation, - bool if_not_exists) + bool if_not_exists, + VariableEOXAction eoxaction) { Acl *varacl; NameData varname; @@ -110,6 +112,7 @@ create_variable(const char *varName, values[Anum_pg_variable_vartypmod - 1] = Int32GetDatum(varTypmod); values[Anum_pg_variable_varowner - 1] = ObjectIdGetDatum(varOwner); values[Anum_pg_variable_varcollation - 1] = ObjectIdGetDatum(varCollation); + values[Anum_pg_variable_vareoxaction - 1] = CharGetDatum(eoxaction); varacl = get_user_default_acl(OBJECT_VARIABLE, varOwner, varNamespace); @@ -183,6 +186,13 @@ CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt) Oid typcollation; ObjectAddress variable; + /* Check consistency of arguments */ + if (stmt->eoxaction == VARIABLE_EOX_DROP + && stmt->variable->relpersistence != RELPERSISTENCE_TEMP) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("ON COMMIT DROP can only be used on temporary variables"))); + namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->variable, NoLock, NULL); @@ -222,7 +232,8 @@ CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt) typmod, varowner, collation, - stmt->if_not_exists); + stmt->if_not_exists, + stmt->eoxaction); elog(DEBUG1, "record for session variable \"%s\" (oid:%d) was created in pg_variable", stmt->variable->relname, variable.objectId); @@ -230,6 +241,8 @@ CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt) /* We want SessionVariableCreatePostprocess to see the catalog changes. */ CommandCounterIncrement(); + SessionVariableCreatePostprocess(variable.objectId, stmt->eoxaction); + return variable; } @@ -242,6 +255,7 @@ DropVariable(Oid varid) { Relation rel; HeapTuple tup; + char eoxaction; rel = table_open(VariableRelationId, RowExclusiveLock); @@ -250,6 +264,8 @@ DropVariable(Oid varid) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for variable %u", varid); + eoxaction = ((Form_pg_variable) GETSTRUCT(tup))->vareoxaction; + CatalogTupleDelete(rel, &tup->t_self); ReleaseSysCache(tup); @@ -257,5 +273,5 @@ DropVariable(Oid varid) table_close(rel, RowExclusiveLock); /* Do the necessary cleanup if needed in local memory */ - SessionVariableDropPostprocess(varid); + SessionVariableDropPostprocess(varid, eoxaction); } diff --git a/src/backend/commands/session_variable.c b/src/backend/commands/session_variable.c index 68e28ac6f2..862cf3cc42 100644 --- a/src/backend/commands/session_variable.c +++ b/src/backend/commands/session_variable.c @@ -16,6 +16,8 @@ #include "access/xact.h" #include "catalog/pg_variable.h" +#include "catalog/dependency.h" +#include "catalog/namespace.h" #include "commands/session_variable.h" #include "executor/svariableReceiver.h" #include "funcapi.h" @@ -31,6 +33,19 @@ #include "utils/snapmgr.h" #include "utils/syscache.h" +typedef struct SVariableXActDropItem +{ + Oid varid; /* varid of session variable */ + + /* + * creating_subid is the ID of the creating subxact. If the action was + * unregistered during the current transaction, deleting_subid is the ID + * of the deleting subxact, otherwise InvalidSubTransactionId. + */ + SubTransactionId creating_subid; + SubTransactionId deleting_subid; +} SVariableXActDropItem; + /* * Values of session variables are stored in the backend local memory * inside sessionvars hash table in binary format inside a dedicated memory @@ -105,6 +120,12 @@ static bool needs_validation = false; */ static LocalTransactionId validated_lxid = InvalidLocalTransactionId; +/* list holds fields of SVariableXActDropItem type */ +static List *xact_drop_items = NIL; + +static void register_session_variable_xact_drop(Oid varid); +static void unregister_session_variable_xact_drop(Oid varid); + /* * Callback function for session variable invalidation. * @@ -153,16 +174,45 @@ pg_variable_cache_callback(Datum arg, int cacheid, uint32 hashvalue) } } +/* + * Do the necessary work to setup local memory management of a new + * variable. + * + * Caller should already have created the necessary entry in catalog + * and made them visible. + */ +void +SessionVariableCreatePostprocess(Oid varid, char eoxaction) +{ + /* + * For temporary variables, we need to create a new end of xact action to + * ensure deletion from catalog. + */ + if (eoxaction == VARIABLE_EOX_DROP) + { + Assert(isTempNamespace(get_session_variable_namespace(varid))); + + register_session_variable_xact_drop(varid); + } +} + /* * Handle the local memory cleanup for a DROP VARIABLE command. * * Caller should take care of removing the pg_variable entry first. */ void -SessionVariableDropPostprocess(Oid varid) +SessionVariableDropPostprocess(Oid varid, char eoxaction) { Assert(LocalTransactionIdIsValid(MyProc->lxid)); + if (eoxaction == VARIABLE_EOX_DROP) + { + Assert(isTempNamespace(get_session_variable_namespace(varid))); + + unregister_session_variable_xact_drop(varid); + } + if (sessionvars) { bool found; @@ -181,10 +231,62 @@ SessionVariableDropPostprocess(Oid varid) svar->drop_lxid = MyProc->lxid; needs_validation = true; + } } } +/* + * Registration of actions to be executed on session variables at transaction + * end time. We want to drop temporary session variables with clause ON COMMIT + * DROP. + */ + +/* + * Register a session variable xact action. + */ +static void +register_session_variable_xact_drop(Oid varid) +{ + SVariableXActDropItem *xact_ai; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + + xact_ai = (SVariableXActDropItem *) + palloc(sizeof(SVariableXActDropItem)); + + xact_ai->varid = varid; + + xact_ai->creating_subid = GetCurrentSubTransactionId(); + xact_ai->deleting_subid = InvalidSubTransactionId; + + xact_drop_items = lcons(xact_ai, xact_drop_items); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * Unregister an id of a given session variable from drop list. In this + * moment, the action is just marked as deleted by setting deleting_subid. The + * calling even might be rollbacked, in which case we should not lose this + * action. + */ +static void +unregister_session_variable_xact_drop(Oid varid) +{ + ListCell *l; + + foreach(l, xact_drop_items) + { + SVariableXActDropItem *xact_ai = + (SVariableXActDropItem *) lfirst(l); + + if (xact_ai->varid == varid) + xact_ai->deleting_subid = GetCurrentSubTransactionId(); + } +} + /* * Release stored value, free memory */ @@ -302,15 +404,92 @@ remove_invalid_session_variables(bool atEOX) } /* - * Remove values of dropped session variables from memory. + * Perform ON COMMIT DROP for temporary session variables, + * and remove all dropped variables from memory. */ void -AtPreEOXact_SessionVariables(void) +AtPreEOXact_SessionVariables(bool isCommit) { - /* We cannot to do it when transaction is aborted */ - Assert(IsTransactionState()); + if (isCommit) + { + if (xact_drop_items) + { + ListCell *l; - remove_invalid_session_variables(true); + foreach(l, xact_drop_items) + { + SVariableXActDropItem *xact_ai = + (SVariableXActDropItem *) lfirst(l); + + /* Iterate only over entries that are still pending */ + if (xact_ai->deleting_subid == InvalidSubTransactionId) + { + ObjectAddress object; + + object.classId = VariableRelationId; + object.objectId = xact_ai->varid; + object.objectSubId = 0; + + /* + * Since this is an automatic drop, rather than one + * directly initiated by the user, we pass the + * PERFORM_DELETION_INTERNAL flag. + */ + elog(DEBUG1, "session variable (oid:%u) will be deleted (forced by ON COMMIT DROP clause)", + xact_ai->varid); + + performDeletion(&object, DROP_CASCADE, + PERFORM_DELETION_INTERNAL | + PERFORM_DELETION_QUIETLY); + } + } + } + + remove_invalid_session_variables(true); + } + + /* + * We have to clean xact_drop_items. All related variables are dropped + * now, or lost inside aborted transaction. + */ + list_free_deep(xact_drop_items); + xact_drop_items = NULL; +} + +/* + * Post-subcommit or post-subabort cleanup of xact drop list. + * + * 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) +{ + ListCell *cur_item; + + foreach(cur_item, xact_drop_items) + { + SVariableXActDropItem *xact_ai = + (SVariableXActDropItem *) lfirst(cur_item); + + if (!isCommit && xact_ai->creating_subid == mySubid) + { + /* cur_item must be removed */ + xact_drop_items = foreach_delete_current(xact_drop_items, cur_item); + pfree(xact_ai); + } + else + { + /* cur_item must be preserved */ + if (xact_ai->creating_subid == mySubid) + xact_ai->creating_subid = parentSubid; + if (xact_ai->deleting_subid == mySubid) + xact_ai->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId; + } + } } /* diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 9bd77546b9..24cd6de727 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -489,7 +489,7 @@ DefineView(ViewStmt *stmt, const char *queryString, */ view = copyObject(stmt->view); /* don't corrupt original command */ if (view->relpersistence == RELPERSISTENCE_PERMANENT - && isQueryUsingTempRelation(viewParse)) + && isQueryUsingTempObject(viewParse)) { view->relpersistence = RELPERSISTENCE_TEMP; ereport(NOTICE, diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 632b71a65c..ccd528c79d 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -3300,10 +3300,10 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) * creation query. It would be hard to refresh data or incrementally * maintain it if a source disappeared. */ - if (isQueryUsingTempRelation(query)) + if (isQueryUsingTempObject(query)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("materialized views must not use temporary tables or views"))); + errmsg("materialized views must not use temporary tables, views or session variables"))); /* * A materialized view would either need to save parameters for use in diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index dfe83cd043..9abe5614e1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -477,6 +477,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type OptTemp %type OptNoLog %type OnCommitOption +%type OnEOXActionOption %type for_locking_strength %type for_locking_item @@ -5078,26 +5079,39 @@ create_extension_opt_item: *****************************************************************************/ CreateSessionVarStmt: - CREATE VARIABLE qualified_name opt_as Typename opt_collate_clause + CREATE OptTemp VARIABLE qualified_name opt_as Typename opt_collate_clause OnEOXActionOption { CreateSessionVarStmt *n = makeNode(CreateSessionVarStmt); - n->variable = $3; - n->typeName = $5; - n->collClause = (CollateClause *) $6; + $4->relpersistence = $2; + n->variable = $4; + n->typeName = $6; + n->collClause = (CollateClause *) $7; + n->eoxaction = $8; n->if_not_exists = false; $$ = (Node *) n; } - | CREATE VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename opt_collate_clause + | CREATE OptTemp VARIABLE IF_P NOT EXISTS qualified_name opt_as Typename opt_collate_clause OnEOXActionOption { CreateSessionVarStmt *n = makeNode(CreateSessionVarStmt); - n->variable = $6; - n->typeName = $8; - n->collClause = (CollateClause *) $9; + $7->relpersistence = $2; + n->variable = $7; + n->typeName = $9; + n->collClause = (CollateClause *) $10; + n->eoxaction = $11; n->if_not_exists = true; $$ = (Node *) n; } ; +/* + * Temporary session variables can be dropped on successful + * transaction end like tables. + */ +OnEOXActionOption: ON COMMIT DROP { $$ = VARIABLE_EOX_DROP; } + | /*EMPTY*/ { $$ = VARIABLE_EOX_NOOP; } + ; + + /***************************************************************************** * * ALTER EXTENSION name UPDATE [ TO version ] diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 864ea9b0d5..8afa8d3965 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -101,7 +101,7 @@ static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, static int specialAttNum(const char *attname); static bool rte_visible_if_lateral(ParseState *pstate, RangeTblEntry *rte); static bool rte_visible_if_qualified(ParseState *pstate, RangeTblEntry *rte); -static bool isQueryUsingTempRelation_walker(Node *node, void *context); +static bool isQueryUsingTempObject_walker(Node *node, void *context); /* @@ -3818,13 +3818,13 @@ rte_visible_if_qualified(ParseState *pstate, RangeTblEntry *rte) * the query is a temporary relation (table, view, or materialized view). */ bool -isQueryUsingTempRelation(Query *query) +isQueryUsingTempObject(Query *query) { - return isQueryUsingTempRelation_walker((Node *) query, NULL); + return isQueryUsingTempObject_walker((Node *) query, NULL); } static bool -isQueryUsingTempRelation_walker(Node *node, void *context) +isQueryUsingTempObject_walker(Node *node, void *context) { if (node == NULL) return false; @@ -3850,13 +3850,23 @@ isQueryUsingTempRelation_walker(Node *node, void *context) } return query_tree_walker(query, - isQueryUsingTempRelation_walker, + isQueryUsingTempObject_walker, context, QTW_IGNORE_JOINALIASES); } + else if (IsA(node, Param)) + { + Param *p = (Param *) node; + + if (p->paramkind == PARAM_VARIABLE) + { + if (isAnyTempNamespace(get_session_variable_namespace(p->paramvarid))) + return true; + } + } return expression_tree_walker(node, - isQueryUsingTempRelation_walker, + isQueryUsingTempObject_walker, context); } diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 125f26bc14..1acb902f02 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -5202,7 +5202,7 @@ listVariables(const char *pattern, bool verbose) PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; - static const bool translate_columns[] = {false, false, false, false, false, false, false}; + static const bool translate_columns[] = {false, false, false, false, false, false, false, false}; initPQExpBuffer(&buf); @@ -5212,12 +5212,16 @@ listVariables(const char *pattern, bool verbose) " pg_catalog.format_type(v.vartype, v.vartypmod) as \"%s\",\n" " (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n" " WHERE c.oid = v.varcollation AND bt.oid = v.vartype AND v.varcollation <> bt.typcollation) as \"%s\",\n" - " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\"\n", + " pg_catalog.pg_get_userbyid(v.varowner) as \"%s\",\n" + " CASE v.vareoxaction\n" + " WHEN 'd' THEN 'ON COMMIT DROP'\n" + " END as \"%s\"\n", gettext_noop("Schema"), gettext_noop("Name"), gettext_noop("Type"), gettext_noop("Collation"), - gettext_noop("Owner")); + gettext_noop("Owner"), + gettext_noop("Transactional end action")); if (verbose) { diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 50de480f13..7c5c38a9c0 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3536,7 +3536,8 @@ psql_completion(const char *text, int start, int end) } /* CREATE VARIABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */ /* Complete CREATE VARIABLE with AS */ - else if (TailMatches("CREATE", "VARIABLE", MatchAny)) + else if (TailMatches("CREATE", "VARIABLE", MatchAny) || + TailMatches("TEMP|TEMPORARY", "VARIABLE", MatchAny)) COMPLETE_WITH("AS"); else if (TailMatches("VARIABLE", MatchAny, "AS")) /* Complete CREATE VARIABLE with AS types */ diff --git a/src/include/catalog/pg_variable.h b/src/include/catalog/pg_variable.h index 79776ff04f..c0978b1d4f 100644 --- a/src/include/catalog/pg_variable.h +++ b/src/include/catalog/pg_variable.h @@ -54,6 +54,9 @@ CATALOG(pg_variable,9222,VariableRelationId) /* typmod for variable's type */ int32 vartypmod BKI_DEFAULT(-1); + /* action on transaction end */ + char vareoxaction BKI_DEFAULT(n); + /* variable collation */ Oid varcollation BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_collation); @@ -66,6 +69,12 @@ CATALOG(pg_variable,9222,VariableRelationId) #endif } FormData_pg_variable; +typedef enum VariableEOXAction +{ + VARIABLE_EOX_NOOP = 'n', /* NOOP */ + VARIABLE_EOX_DROP = 'd', /* ON COMMIT DROP */ +} VariableEOXAction; + /* ---------------- * Form_pg_variable corresponds to a pointer to a tuple with * the format of pg_variable relation. diff --git a/src/include/commands/session_variable.h b/src/include/commands/session_variable.h index ad5aa73dc6..b7426a8abf 100644 --- a/src/include/commands/session_variable.h +++ b/src/include/commands/session_variable.h @@ -21,8 +21,11 @@ #include "tcop/cmdtag.h" #include "utils/queryenvironment.h" -extern void SessionVariableDropPostprocess(Oid varid); -extern void AtPreEOXact_SessionVariables(void); +extern void SessionVariableCreatePostprocess(Oid varid, char eoxaction); +extern void SessionVariableDropPostprocess(Oid varid, char eoxaction); +extern void AtPreEOXact_SessionVariables(bool isCommit); +extern void AtEOSubXact_SessionVariables(bool isCommit, SubTransactionId mySubid, + SubTransactionId parentSubid); extern void SetSessionVariable(Oid varid, Datum value, bool isNull); extern void SetSessionVariableWithSecurityCheck(Oid varid, Datum value, bool isNull); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 9acf9d1833..9aeec0b3d9 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3326,6 +3326,7 @@ typedef struct CreateSessionVarStmt TypeName *typeName; /* the type of variable */ CollateClause *collClause; bool if_not_exists; /* do nothing if it already exists */ + char eoxaction; /* on commit action */ } CreateSessionVarStmt; diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 67d9b1e412..6efd1f9cc2 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -124,6 +124,6 @@ extern int attnameAttNum(Relation rd, const char *attname, bool sysColOK); extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); -extern bool isQueryUsingTempRelation(Query *query); +extern bool isQueryUsingTempObject(Query *query); #endif /* PARSE_RELATION_H */ diff --git a/src/test/isolation/expected/session-variable.out b/src/test/isolation/expected/session-variable.out index 2968cce089..9fbbf9057a 100644 --- a/src/test/isolation/expected/session-variable.out +++ b/src/test/isolation/expected/session-variable.out @@ -85,10 +85,11 @@ myvar step sr1: ROLLBACK; -starting permutation: create3 let3 s3 create4 let4 drop4 drop3 inval3 discard sc3 state +starting permutation: create3 let3 s3 o_c_d create4 let4 drop4 drop3 inval3 discard sc3 state step create3: CREATE VARIABLE myvar3 AS text; step let3: LET myvar3 = 'test'; step s3: BEGIN; +step o_c_d: CREATE TEMP VARIABLE myvar_o_c_d AS text ON COMMIT DROP; step create4: CREATE VARIABLE myvar4 AS text; step let4: LET myvar4 = 'test'; step drop4: DROP VARIABLE myvar4; diff --git a/src/test/isolation/specs/session-variable.spec b/src/test/isolation/specs/session-variable.spec index c864fee400..45e65d4085 100644 --- a/src/test/isolation/specs/session-variable.spec +++ b/src/test/isolation/specs/session-variable.spec @@ -24,6 +24,7 @@ step create { CREATE VARIABLE myvar AS text; } session s3 step s3 { BEGIN; } step let3 { LET myvar3 = 'test'; } +step o_c_d { CREATE TEMP VARIABLE myvar_o_c_d AS text ON COMMIT DROP; } step create4 { CREATE VARIABLE myvar4 AS text; } step let4 { LET myvar4 = 'test'; } step drop4 { DROP VARIABLE myvar4; } @@ -47,4 +48,4 @@ permutation let val dbg drop create dbg val # calling the dbg step after the concurrent drop permutation let val s1 dbg drop create dbg val sr1 # test for DISCARD ALL when all internal queues have actions registered -permutation create3 let3 s3 create4 let4 drop4 drop3 inval3 discard sc3 state +permutation create3 let3 s3 o_c_d create4 let4 drop4 drop3 inval3 discard sc3 state diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index c6133e2b20..377b964fb7 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -5819,20 +5819,20 @@ CREATE ROLE regress_variable_owner; SET ROLE TO regress_variable_owner; CREATE VARIABLE var1 AS varchar COLLATE "C"; \dV+ var1 - List of variables - Schema | Name | Type | Collation | Owner | Access privileges | Description ---------+------+-------------------+-----------+------------------------+-------------------+------------- - public | var1 | character varying | C | regress_variable_owner | | + List of variables + Schema | Name | Type | Collation | Owner | Transactional end action | Access privileges | Description +--------+------+-------------------+-----------+------------------------+--------------------------+-------------------+------------- + public | var1 | character varying | C | regress_variable_owner | | | (1 row) GRANT SELECT ON VARIABLE var1 TO PUBLIC; COMMENT ON VARIABLE var1 IS 'some description'; \dV+ var1 - List of variables - Schema | Name | Type | Collation | Owner | Access privileges | Description ---------+------+-------------------+-----------+------------------------+--------------------------------------------------+------------------ - public | var1 | character varying | C | regress_variable_owner | regress_variable_owner=rw/regress_variable_owner+| some description - | | | | | =r/regress_variable_owner | + List of variables + Schema | Name | Type | Collation | Owner | Transactional end action | Access privileges | Description +--------+------+-------------------+-----------+------------------------+--------------------------+--------------------------------------------------+------------------ + public | var1 | character varying | C | regress_variable_owner | | regress_variable_owner=rw/regress_variable_owner+| some description + | | | | | | =r/regress_variable_owner | (1 row) DROP VARIABLE var1; @@ -6300,9 +6300,9 @@ List of schemas (0 rows) \dV "no.such.variable" - List of variables - Schema | Name | Type | Collation | Owner ---------+------+------+-----------+------- + List of variables + Schema | Name | Type | Collation | Owner | Transactional end action +--------+------+------+-----------+-------+-------------------------- (0 rows) -- again, but with dotted schema qualifications. @@ -6475,9 +6475,9 @@ improper qualified name (too many dotted names): "no.such.schema"."no.such.insta \dy "no.such.schema"."no.such.event.trigger" improper qualified name (too many dotted names): "no.such.schema"."no.such.event.trigger" \dV "no.such.schema"."no.such.variable" - List of variables - Schema | Name | Type | Collation | Owner ---------+------+------+-----------+------- + List of variables + Schema | Name | Type | Collation | Owner | Transactional end action +--------+------+------+-----------+-------+-------------------------- (0 rows) -- again, but with current database and dotted schema qualifications. @@ -6614,9 +6614,9 @@ List of text search templates (0 rows) \dV regression."no.such.schema"."no.such.variable" - List of variables - Schema | Name | Type | Collation | Owner ---------+------+------+-----------+------- + List of variables + Schema | Name | Type | Collation | Owner | Transactional end action +--------+------+------+-----------+-------+-------------------------- (0 rows) -- again, but with dotted database and dotted schema qualifications. diff --git a/src/test/regress/expected/session_variables.out b/src/test/regress/expected/session_variables.out index 719cf71a7e..1dadd4c654 100644 --- a/src/test/regress/expected/session_variables.out +++ b/src/test/regress/expected/session_variables.out @@ -45,11 +45,11 @@ SET ROLE TO regress_variable_owner; CREATE VARIABLE svartest.var1 AS int; SET ROLE TO DEFAULT; \dV+ svartest.var1 - List of variables - Schema | Name | Type | Collation | Owner | Access privileges | Description -----------+------+---------+-----------+------------------------+--------------------------------------------------+------------- - svartest | var1 | integer | | regress_variable_owner | regress_variable_owner=rw/regress_variable_owner+| - | | | | | regress_variable_reader=r/regress_variable_owner | + List of variables + Schema | Name | Type | Collation | Owner | Transactional end action | Access privileges | Description +----------+------+---------+-----------+------------------------+--------------------------+--------------------------------------------------+------------- + svartest | var1 | integer | | regress_variable_owner | | regress_variable_owner=rw/regress_variable_owner+| + | | | | | | regress_variable_reader=r/regress_variable_owner | (1 row) DROP VARIABLE svartest.var1; @@ -1236,3 +1236,123 @@ SELECT var1; (1 row) DROP VARIABLE var1, var2; +-- temporary variables +CREATE TEMP VARIABLE var1 AS int; +-- this view should be temporary +CREATE VIEW var_test_view AS SELECT var1; +NOTICE: view "var_test_view" will be a temporary view +DROP VARIABLE var1 CASCADE; +NOTICE: drop cascades to view var_test_view +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + SELECT var1; + var1 +------ + 100 +(1 row) + +COMMIT; +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; + count +------- + 0 +(1 row) + +-- should be zero +SELECT count(*) FROM pg_session_variables(); + count +------- + 0 +(1 row) + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + SELECT var1; + var1 +------ + 100 +(1 row) + +ROLLBACK; +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; + count +------- + 0 +(1 row) + +-- should be zero +SELECT count(*) FROM pg_session_variables(); + count +------- + 0 +(1 row) + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + DROP VARIABLE var1; +COMMIT; +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; + count +------- + 0 +(1 row) + +-- should be zero +SELECT count(*) FROM pg_session_variables(); + count +------- + 0 +(1 row) + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + DROP VARIABLE var1; +ROLLBACK; +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; + count +------- + 0 +(1 row) + +-- should be zero +SELECT count(*) FROM pg_session_variables(); + count +------- + 0 +(1 row) + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + SAVEPOINT s1; + DROP VARIABLE var1; + ROLLBACK TO s1; + SELECT var1; + var1 +------ + 100 +(1 row) + +COMMIT; +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; + count +------- + 0 +(1 row) + +-- should be zero +SELECT count(*) FROM pg_session_variables(); + count +------- + 0 +(1 row) + diff --git a/src/test/regress/sql/session_variables.sql b/src/test/regress/sql/session_variables.sql index 2e5a5bf888..4201e969ae 100644 --- a/src/test/regress/sql/session_variables.sql +++ b/src/test/regress/sql/session_variables.sql @@ -775,6 +775,7 @@ BEGIN; DROP VARIABLE var1; SELECT var2; ROLLBACK; + -- should be ok SELECT var1; @@ -803,3 +804,68 @@ BEGIN; DROP VARIABLE var1; ROLLBACK; SELECT var1; DROP VARIABLE var1, var2; + +-- temporary variables +CREATE TEMP VARIABLE var1 AS int; +-- this view should be temporary +CREATE VIEW var_test_view AS SELECT var1; + +DROP VARIABLE var1 CASCADE; + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + SELECT var1; +COMMIT; + +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; +-- should be zero +SELECT count(*) FROM pg_session_variables(); + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + SELECT var1; +ROLLBACK; + +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; +-- should be zero +SELECT count(*) FROM pg_session_variables(); + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + DROP VARIABLE var1; +COMMIT; + +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; +-- should be zero +SELECT count(*) FROM pg_session_variables(); + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + DROP VARIABLE var1; +ROLLBACK; + +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; +-- should be zero +SELECT count(*) FROM pg_session_variables(); + +BEGIN; + CREATE TEMP VARIABLE var1 AS int ON COMMIT DROP; + LET var1 = 100; + SAVEPOINT s1; + DROP VARIABLE var1; + ROLLBACK TO s1; + SELECT var1; +COMMIT; + +-- should be zero +SELECT count(*) FROM pg_variable WHERE varname = 'var1'; +-- should be zero +SELECT count(*) FROM pg_session_variables(); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 515070f58f..8ccaa170d7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2686,6 +2686,7 @@ SupportRequestWFuncMonotonic SVariable SVariableData SVariableState +SVariableXActDropItem Syn SyncOps SyncRepConfigData -- 2.41.0