From 076fe1e55aed2dc878853723883ee245e8db5451 Mon Sep 17 00:00:00 2001
From: Vasuki M <vasukianand0119@gmail.com>
Date: Tue, 6 Jan 2026 12:07:15 +0530
Subject: [PATCH] [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 | 66 +++++++++++++++++++---------------
 1 file changed, 37 insertions(+), 29 deletions(-)

diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 5174d025531..c5fc2354797 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -2566,40 +2566,48 @@ match_previous_words(int pattern_id,
 	/* ALTER USER/ROLE <name> IN DATABASE <dbname> RESET */
 	else if (Matches("ALTER", "USER|ROLE", MatchAny, "IN", "DATABASE", MatchAny, "RESET"))
 	{
-		/*
-		 * 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 (!pset.db || PQstatus(pset.db) != CONNECTION_OK)
 		{
-			/* 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);
+			/*
+			 * 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);
+			}
+
 		}
 	}
 
-- 
2.43.0

