From d4138e09db42494626761cb65e4b65e97d61efe3 Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Thu, 4 Dec 2025 18:49:06 +0100 Subject: [PATCH 9/9] use names of currently used temp variables for tab complete of DROP VARIABLE, LET and VARIABLE() --- src/backend/commands/session_variable.c | 36 +++++++++++++++++++ src/bin/psql/tab-complete.in.c | 19 +++++++++- src/include/catalog/pg_proc.dat | 5 +++ .../expected/session_variables_ddl.out | 16 +++++++++ .../regress/sql/session_variables_ddl.sql | 8 +++++ 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/backend/commands/session_variable.c b/src/backend/commands/session_variable.c index f9fe16f4304..97ff3e37b8c 100644 --- a/src/backend/commands/session_variable.c +++ b/src/backend/commands/session_variable.c @@ -21,6 +21,7 @@ #include "commands/session_variable.h" #include "executor/executor.h" #include "executor/svariableReceiver.h" +#include "funcapi.h" #include "miscadmin.h" #include "parser/parse_type.h" #include "rewrite/rewriteHandler.h" @@ -451,3 +452,38 @@ ResetSessionVariables(void) if (SVariableMemoryContext != NULL) MemoryContextReset(SVariableMemoryContext); } + +/* + * pg_get_temporary_session_variables_names + * + * Returns list of temporary session variables. It is used by psql's + * tab complete for DROP VARIABLE and LET commands. + */ +Datum +pg_get_temporary_session_variables_names(PG_FUNCTION_ARGS) +{ + InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC); + + if (sessionvars) + { + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + HASH_SEQ_STATUS status; + SVariable svar; + + hash_seq_init(&status, sessionvars); + + while ((svar = (SVariable) hash_seq_search(&status)) != NULL) + { + Datum values[1]; + bool nulls[1]; + + values[0] = CStringGetTextDatum((NameStr(svar->varname))); + nulls[0] = false; + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, + values, nulls); + } + } + + return (Datum) 0; +} diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 4451959b81e..bef22a331ed 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -1200,6 +1200,11 @@ Keywords_for_list_of_owner_roles, "PUBLIC" " FROM pg_catalog.pg_timezone_names() "\ " WHERE pg_catalog.quote_literal(pg_catalog.lower(name)) LIKE pg_catalog.lower('%s')" +#define Query_for_list_of_temporary_session_variables \ +"SELECT varname "\ +" FROM pg_catalog.pg_get_temporary_session_variables_names() AS varname "\ +" WHERE varname LIKE '%s'" + /* Privilege options shared between GRANT and REVOKE */ #define Privilege_options_of_grant_and_revoke \ "SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", \ @@ -4360,6 +4365,10 @@ match_previous_words(int pattern_id, else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE", MatchAny)) COMPLETE_WITH("CASCADE", "RESTRICT"); + /* DROP VARIABLE */ + else if (Matches("DROP", "VARIABLE")) + COMPLETE_WITH_QUERY(Query_for_list_of_temporary_session_variables); + /* EXECUTE */ else if (Matches("EXECUTE")) COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements); @@ -4800,6 +4809,8 @@ match_previous_words(int pattern_id, /* LET */ /* Complete LET with "=" */ + else if (Matches("LET")) + COMPLETE_WITH_QUERY(Query_for_list_of_temporary_session_variables); else if (TailMatches("LET", MatchAny)) COMPLETE_WITH("="); @@ -5292,7 +5303,7 @@ match_previous_words(int pattern_id, COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables, "VERBOSE", "ANALYZE", - "ONLY"); + "ONLY"); else if (Matches("VACUUM", MatchAnyN, "VERBOSE")) COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables, "ANALYZE", @@ -5359,6 +5370,12 @@ match_previous_words(int pattern_id, */ } +/* + * VARIABLE fence + */ + else if (TailMatches("VARIABLE", "(")) + COMPLETE_WITH_QUERY(Query_for_list_of_temporary_session_variables); + /* WITH [RECURSIVE] */ /* diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 66af2d96d67..097de5da861 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12612,4 +12612,9 @@ proargnames => '{pid,io_id,io_generation,state,operation,off,length,target,handle_data_len,raw_result,result,target_desc,f_sync,f_localmem,f_buffered}', prosrc => 'pg_get_aios' }, +# Session variables support +{ oid => '8068', descr => 'returns names of temporary session variables', + proname => 'pg_get_temporary_session_variables_names', prorows => '1000', proretset => 't', + provolatile => 'v', proparallel => 'r', prorettype => 'text', proargtypes => '', + prosrc => 'pg_get_temporary_session_variables_names' }, ] diff --git a/src/test/regress/expected/session_variables_ddl.out b/src/test/regress/expected/session_variables_ddl.out index 742728033e4..c8757f4cbbd 100644 --- a/src/test/regress/expected/session_variables_ddl.out +++ b/src/test/regress/expected/session_variables_ddl.out @@ -54,3 +54,19 @@ 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 +CREATE TEMP VARIABLE x AS int; +CREATE TEMP VARIABLE y AS int; +SELECT * FROM pg_get_temporary_session_variables_names(); + pg_get_temporary_session_variables_names +------------------------------------------ + y + x +(2 rows) + +DROP VARIABLE x; +DROP VARIABLE y; +SELECT * FROM pg_get_temporary_session_variables_names(); + pg_get_temporary_session_variables_names +------------------------------------------ +(0 rows) + diff --git a/src/test/regress/sql/session_variables_ddl.sql b/src/test/regress/sql/session_variables_ddl.sql index a604f43555c..0c51b0a2f40 100644 --- a/src/test/regress/sql/session_variables_ddl.sql +++ b/src/test/regress/sql/session_variables_ddl.sql @@ -70,3 +70,11 @@ CREATE TEMP VARIABLE IF NOT EXISTS x AS int; DROP VARIABLE IF EXISTS x; DROP VARIABLE IF EXISTS x; + +CREATE TEMP VARIABLE x AS int; +CREATE TEMP VARIABLE y AS int; +SELECT * FROM pg_get_temporary_session_variables_names(); + +DROP VARIABLE x; +DROP VARIABLE y; +SELECT * FROM pg_get_temporary_session_variables_names(); -- 2.52.0