From a1e0f57df1c4d838b3303a73cf5c19671ee1ef33 Mon Sep 17 00:00:00 2001
From: "okbob@github.com" <pavel.stehule@gmail.com>
Date: Thu, 4 Dec 2025 18:49:06 +0100
Subject: [PATCH 09/11] use names of currently used temp variables for tab
 complete of DROP VARIABLE, LET and VARIABLE()

---
 src/backend/commands/session_variable.c       | 37 +++++++++++++++++++
 src/bin/psql/tab-complete.in.c                | 17 +++++++++
 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(+)

diff --git a/src/backend/commands/session_variable.c b/src/backend/commands/session_variable.c
index 861a9317686..824410a3235 100644
--- a/src/backend/commands/session_variable.c
+++ b/src/backend/commands/session_variable.c
@@ -19,6 +19,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"
@@ -29,6 +30,7 @@
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
+#include "utils/tuplestore.h"
 
 /*
  * The session variables are stored in the backend's private memory (data,
@@ -449,3 +451,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 69f348c129e..aafd2136b62 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -1208,6 +1208,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", \
@@ -4506,6 +4511,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);
@@ -4955,6 +4964,8 @@ match_previous_words(int pattern_id,
 
 /* LET */
 	/* Complete LET <variable> with "=" */
+	else if (Matches("LET"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_temporary_session_variables);
 	else if (TailMatches("LET", MatchAny))
 		COMPLETE_WITH("=");
 
@@ -5566,6 +5577,12 @@ match_previous_words(int pattern_id,
 			COMPLETE_WITH("'standby_replay'", "'standby_write'", "'standby_flush'", "'primary_flush'");
 	}
 
+/*
+ * 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 acf16254b21..0b24cde208b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12860,4 +12860,9 @@
   proname => 'hashoid8extended', prorettype => 'int8',
   proargtypes => 'oid8 int8', prosrc => 'hashoid8extended' },
 
+# 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 9f5b088de72..758ce582fca 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 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 60f78671e3b..c4cbfd17169 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 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.53.0

