From 6651bc1d9758fa3a0f0fac78fc947cf474bb51d5 Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Thu, 4 Dec 2025 06:17:54 +0100 Subject: [PATCH 8/9] support CREATE IF NOT EXISTS and DROP IF EXISTS --- doc/src/sgml/ref/create_variable.sgml | 12 +++++- doc/src/sgml/ref/drop_variable.sgml | 12 +++++- src/backend/commands/session_variable.c | 43 +++++++++++++------ src/backend/parser/gram.y | 31 ++++++++++++- src/backend/tcop/utility.c | 2 +- src/include/commands/session_variable.h | 2 +- src/include/nodes/parsenodes.h | 4 +- .../expected/session_variables_ddl.out | 6 +++ .../regress/sql/session_variables_ddl.sql | 6 +++ 9 files changed, 97 insertions(+), 21 deletions(-) diff --git a/doc/src/sgml/ref/create_variable.sgml b/doc/src/sgml/ref/create_variable.sgml index 11b1ef74212..286bff62b6c 100644 --- a/doc/src/sgml/ref/create_variable.sgml +++ b/doc/src/sgml/ref/create_variable.sgml @@ -26,7 +26,7 @@ PostgreSQL documentation -CREATE { TEMP | TEMPORAL } VARIABLE name [ AS ] data_type +CREATE { TEMP | TEMPORAL } VARIABLE [ IF NOT EXISTS ] name [ AS ] data_type @@ -68,6 +68,16 @@ CREATE { TEMP | TEMPORAL } VARIABLE name + + IF NOT EXISTS + + + Do not throw an error if the name already exists. A notice is issued in + this case. + + + + name diff --git a/doc/src/sgml/ref/drop_variable.sgml b/doc/src/sgml/ref/drop_variable.sgml index dede42e4ffb..5de6a737493 100644 --- a/doc/src/sgml/ref/drop_variable.sgml +++ b/doc/src/sgml/ref/drop_variable.sgml @@ -26,7 +26,7 @@ PostgreSQL documentation -DROP VARIABLE name +DROP VARIABLE [ IF EXISTS ] name @@ -42,6 +42,16 @@ DROP VARIABLE name Parameters + + IF EXISTS + + + Do not throw an error if the session variable does not exist. A notice is + issued in this case. + + + + name diff --git a/src/backend/commands/session_variable.c b/src/backend/commands/session_variable.c index 474e3482c28..f9fe16f4304 100644 --- a/src/backend/commands/session_variable.c +++ b/src/backend/commands/session_variable.c @@ -97,7 +97,7 @@ create_sessionvars_hashtables(void) * Returns entry of session variable specified by name */ static SVariable -search_variable(char *varname) +search_variable(char *varname, bool missing_ok) { SVariable svar; @@ -107,7 +107,7 @@ search_variable(char *varname) svar = (SVariable) hash_search(sessionvars, varname, HASH_FIND, NULL); - if (!svar) + if (!svar && !missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("session variable \"%s\" doesn't exist", @@ -129,7 +129,7 @@ get_session_variable_type_typmod_collid(char *varname, { SVariable svar; - svar = search_variable(varname); + svar = search_variable(varname, false); /* only owner can set content of variable */ *typid = svar->vartype; @@ -149,7 +149,7 @@ GetSessionVariableWithTypecheck(char *varname, SVariable svar; Datum result; - svar = search_variable(varname); + svar = search_variable(varname, false); if (svar->vartype != typid || svar->vartypmod != typmod) ereport(ERROR, @@ -186,7 +186,7 @@ SetSessionVariableWithTypecheck(char *varname, { SVariable svar; - svar = search_variable(varname); + svar = search_variable(varname, false); if (svar->vartype != typid || svar->vartypmod != typmod) ereport(ERROR, @@ -292,10 +292,18 @@ CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt) HASH_ENTER, &found); if (found) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("session variable \"%s\" already exists", - stmt->name))); + { + if (stmt->if_not_exists) + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("session variable \"%s\" already exists, skipping", + stmt->name))); + else + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("session variable \"%s\" already exists", + stmt->name))); + } namestrcpy(&svar->varname, stmt->name); svar->vartype = typeid; @@ -313,24 +321,31 @@ CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt) * Drop variable by name */ void -DropVariableByName(char *varname) +DropVariableByName(DropSessionVarStmt *stmt) { SVariable svar; - svar = search_variable(varname); + svar = search_variable(stmt->name, stmt->missing_ok); + if (!svar) + { + ereport(NOTICE, + (errmsg("session variable \"%s\" does not exists, skipping", + stmt->name))); + return; + } /* only owner can get content of variable */ if (svar->varowner != GetUserId() && !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be owner of session variable %s", - varname))); + stmt->name))); if (!svar->typbyval && !svar->isnull) pfree(DatumGetPointer(svar->value)); if (hash_search(sessionvars, - varname, + stmt->name, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); @@ -354,7 +369,7 @@ ExecuteLetStmt(ParseState *pstate, char *varname = query->resultVariable; SVariable svar; - svar = search_variable(varname); + svar = search_variable(varname, false); /* only owner can set content of variable */ if (svar->varowner != GetUserId() && !superuser()) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 327ecf88712..8e3725df41c 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -5342,7 +5342,7 @@ create_extension_opt_item: /***************************************************************************** * * QUERY : - * CREATE { TEMP | TEMPORARY } VARIABLE varname [AS] type + * CREATE { TEMP | TEMPORARY } VARIABLE [IF NOT EXISTS ] varname [AS] type * *****************************************************************************/ @@ -5359,14 +5359,31 @@ CreateSessionVarStmt: n->name = $4; n->typeName = $6; + n->if_not_exists = false; + $$ = (Node *) n; + } + | CREATE OptTemp VARIABLE IF_P NOT EXISTS ColId opt_as Typename + { + CreateSessionVarStmt *n = makeNode(CreateSessionVarStmt); + + if ($2 != RELPERSISTENCE_TEMP) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only temporal session variables are supported"), + parser_errposition(@2))); + + n->name = $7; + n->typeName = $9; + n->if_not_exists = true; $$ = (Node *) n; } + ; /***************************************************************************** * * QUERY : - * DROP VARIABLE varname + * DROP VARIABLE [ IF EXISTS ] varname * *****************************************************************************/ @@ -5376,8 +5393,18 @@ DropSessionVarStmt: DropSessionVarStmt *n = makeNode(DropSessionVarStmt); n->name = $3; + n->missing_ok = false; + $$ = (Node *) n; + } + | DROP VARIABLE IF_P EXISTS ColId + { + DropSessionVarStmt *n = makeNode(DropSessionVarStmt); + + n->name = $5; + n->missing_ok = true; $$ = (Node *) n; } + ; /***************************************************************************** diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 3f1c8cb5e89..fd911f5234f 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1073,7 +1073,7 @@ standard_ProcessUtility(PlannedStmt *pstmt, case T_DropSessionVarStmt: /* No event triggers for catalog less session variables */ - DropVariableByName(((DropSessionVarStmt *) parsetree)->name); + DropVariableByName((DropSessionVarStmt *) parsetree); break; case T_LetStmt: diff --git a/src/include/commands/session_variable.h b/src/include/commands/session_variable.h index cc1aa7ce23b..3f07ae55aac 100644 --- a/src/include/commands/session_variable.h +++ b/src/include/commands/session_variable.h @@ -22,7 +22,7 @@ #include "tcop/cmdtag.h" extern void CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt); -extern void DropVariableByName(char *varname); +extern void DropVariableByName(DropSessionVarStmt *stmt); extern Datum GetSessionVariableWithTypecheck(char *varname, Oid typid, int32 typmod, bool *isnull); extern void SetSessionVariableWithTypecheck(char *varname, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 3decc639d81..47941d9dc99 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3592,6 +3592,7 @@ typedef struct CreateSessionVarStmt NodeTag type; char *name; /* the variable to create */ TypeName *typeName; /* the type of variable */ + bool if_not_exists; /* just do nothing if variable already exists? */ } CreateSessionVarStmt; /* ---------------------- @@ -3601,7 +3602,8 @@ typedef struct CreateSessionVarStmt typedef struct DropSessionVarStmt { NodeTag type; - char *name; + char *name; /* the variable name to drop */ + bool missing_ok; /* skip error of variable is missing */ } DropSessionVarStmt; diff --git a/src/test/regress/expected/session_variables_ddl.out b/src/test/regress/expected/session_variables_ddl.out index f12d1f1a6ae..742728033e4 100644 --- a/src/test/regress/expected/session_variables_ddl.out +++ b/src/test/regress/expected/session_variables_ddl.out @@ -48,3 +48,9 @@ ERROR: session variable "x" already exists DISCARD TEMP; -- should be ok CREATE TEMP VARIABLE x AS int; +-- should be ok +CREATE TEMP VARIABLE IF NOT EXISTS x AS int; +NOTICE: session variable "x" already exists, skipping +DROP VARIABLE IF EXISTS x; +DROP VARIABLE IF EXISTS x; +NOTICE: session variable "x" does not exists, skipping diff --git a/src/test/regress/sql/session_variables_ddl.sql b/src/test/regress/sql/session_variables_ddl.sql index c69fd4bfa47..a604f43555c 100644 --- a/src/test/regress/sql/session_variables_ddl.sql +++ b/src/test/regress/sql/session_variables_ddl.sql @@ -64,3 +64,9 @@ DISCARD TEMP; -- should be ok CREATE TEMP VARIABLE x AS int; + +-- should be ok +CREATE TEMP VARIABLE IF NOT EXISTS x AS int; + +DROP VARIABLE IF EXISTS x; +DROP VARIABLE IF EXISTS x; -- 2.52.0