Sorry,it was an incremental patch.
The attached v5 is the fresh patch against Master.

Regards,
Vasuki M
C-DAC,Chennai
From 57e7f3ce7397467466ae705dbe29c783de87e310 Mon Sep 17 00:00:00 2001
From: Vasuki M <[email protected]>
Date: Tue, 6 Jan 2026 14:53:33 +0530
Subject: [PATCH] psql: Add tab-completion support for ALTER ROLE  ... IN 
 DATABASE ... SET/RESET forms .This patch extends psql tab  completion to 
 support the ALTER ROLE <role> IN DATABASE <dbname> command  form.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

After SET, psql completes with the list of configurable GUC variables,
matching the behavior of ALTER ROLE … SET.

After RESET, psql completes with configuration variables actually set
for the given (role, database) pair as recorded in
pg_db_role_setting, plus ALL, mirroring ALTER DATABASE … RESET
behavior.

SQL literal quoting is performed using PQescapeLiteral() via the
implicit connection (pset.db). A safety guard ensures a valid
connection is present before invoking it, falling back to ALL
otherwise.

No TAP tests are added, as this change does not introduce new
libreadline interactions and SQL-based completion paths are not
reliably exercised by the current test harness.

Vasuki M
---
 src/bin/psql/tab-complete.in.c | 70 +++++++++++++++++++++++++++++++++-
 1 file changed, 68 insertions(+), 2 deletions(-)

diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 06edea98f06..3dd46fddb57 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -2536,12 +2536,78 @@ match_previous_words(int pattern_id,
 	else if (Matches("ALTER", "USER|ROLE", MatchAny) &&
 			 !TailMatches("USER", "MAPPING"))
 		COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
-					  "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
+					  "ENCRYPTED PASSWORD", "INHERIT","IN DATABASE", "LOGIN", "NOBYPASSRLS",
 					  "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
 					  "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
 					  "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
 					  "VALID UNTIL", "WITH");
-
+	/* ALTER USER,ROLE <name> IN */
+	else if (Matches("ALTER", "USER|ROLE", MatchAny, "IN"))
+		COMPLETE_WITH("DATABASE");
+	/* ALTER USER/ROLE <name> IN DATABASE */
+	else if (Matches("ALTER", "USER|ROLE", MatchAny, "IN", "DATABASE"))
+	{
+		/* ALTER ROLE bob IN DATABASE <TAB> → list databases */
+		COMPLETE_WITH_QUERY(Query_for_list_of_databases);
+	}
+	/* ALTER USER/ROLE <name> IN DATABASE <dbname> */
+	else if (Matches("ALTER", "USER|ROLE", MatchAny, "IN", "DATABASE", MatchAny))
+	{
+		/* ALTER ROLE bob IN DATABASE mydb <TAB> → SET, RESET */
+		COMPLETE_WITH("SET", "RESET");
+	}
+	/* ALTER USER/ROLE <name> IN DATABASE <dbname> SET */
+	else if (Matches("ALTER", "USER|ROLE", MatchAny, "IN", "DATABASE", MatchAny, "SET"))
+	{
+		/* ALTER ROLE bob IN DATABASE mydb SET <TAB> */
+		COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
+	}
+	/* ALTER USER/ROLE <name> IN DATABASE <dbname> RESET */
+	else if (Matches("ALTER", "USER|ROLE", MatchAny, "IN", "DATABASE", MatchAny, "RESET"))
+	{
+		if (!pset.db || PQstatus(pset.db) != CONNECTION_OK)
+		{
+			COMPLETE_WITH("ALL");
+		}
+		else
+		{
+			/*
+			 * Extract tokens: prev5 = role name prev2 = database name
+			 */
+			char       *role = prev5_wd;
+			char       *dbname = prev2_wd;
+			char       *q_role;
+			char       *q_dbname;
+			char       *query;
+
+			/* Safe SQL literal quoting using libpq */
+			q_role = PQescapeLiteral(pset.db, role, strlen(role));
+			q_dbname = PQescapeLiteral(pset.db, dbname, strlen(dbname));
+			if (!q_role || !q_dbname)
+			{
+				/* If quoting fails, just fall back to ALL */
+				if (q_role)
+					PQfreemem(q_role);
+				if (q_dbname)
+					PQfreemem(q_dbname);
+				COMPLETE_WITH("ALL");
+			}
+			else
+			{
+				query = psprintf(
+								 " SELECT split_part(unnest(setconfig), \'=\', 1) "
+								 "  FROM pg_db_role_setting "
+								 " WHERE setdatabase = "
+								 "       (SELECT oid FROM pg_database WHERE datname = %s) "
+								 "   AND setrole = %s::regrole",
+								 q_dbname, q_role);
+				COMPLETE_WITH_QUERY_PLUS(query, "ALL");
+				PQfreemem(q_role);
+				PQfreemem(q_dbname);
+				pfree(query);
+			}
+		}
+	}
 	/* ALTER USER,ROLE <name> RESET */
 	else if (Matches("ALTER", "USER|ROLE", MatchAny, "RESET"))
 	{
-- 
2.43.0

Reply via email to