From ee8eb6b699966d648d7c2ef1f3ce06255e96cc07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Mon, 9 Jun 2025 20:39:15 +0100 Subject: [PATCH 1/5] Add tab completion for ALTER TABLE ... ALTER COLUMN ... RESET Unlike SET, it only takes parenthesised attribute options. --- src/bin/psql/tab-complete.in.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 316a2dafbf1..879b8fdeb96 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2936,9 +2936,13 @@ match_previous_words(int pattern_id, "STATISTICS", "STORAGE", /* a subset of ALTER SEQUENCE options */ "INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE"); - /* ALTER TABLE ALTER [COLUMN] SET ( */ - else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") || - Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "(")) + /* ALTER TABLE ALTER [COLUMN] RESET */ + else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "RESET") || + Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "RESET")) + COMPLETE_WITH("("); + /* ALTER TABLE ALTER [COLUMN] SET|RESET ( */ + else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET|RESET", "(") || + Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET|RESET", "(")) COMPLETE_WITH("n_distinct", "n_distinct_inherited"); /* ALTER TABLE ALTER [COLUMN] SET COMPRESSION */ else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION") || From 2b047a5f3ed02074f05ffba5d1595f8b2f4ff562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Mon, 9 Jun 2025 20:41:29 +0100 Subject: [PATCH 2/5] Add tab completion for ALTER FOREIGN TABLE ... SET The schema is the only thing that can be set for foreign tables. --- src/bin/psql/tab-complete.in.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 879b8fdeb96..2343c87c49a 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2458,6 +2458,10 @@ match_previous_words(int pattern_id, COMPLETE_WITH("ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE", "INHERIT", "NO INHERIT", "OPTIONS", "OWNER TO", "RENAME", "SET", "VALIDATE CONSTRAINT"); + else if (Matches("ALTER", "FOREIGN", "TABLE", MatchAny, "SET")) + COMPLETE_WITH("SCHEMA"); + else if (Matches("ALTER", "FOREIGN", "TABLE", MatchAny, "SET", "SCHEMA")) + COMPLETE_WITH_QUERY(Query_for_list_of_schemas); /* ALTER INDEX */ else if (Matches("ALTER", "INDEX")) From 2f467501a4751206eb20424dc1610a02fca14b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Fri, 8 Aug 2025 23:06:36 +0100 Subject: [PATCH 3/5] Improve tab completion for RESET Only complete variables that have been set in the current session, plus the keywords ALL, ROLE and SESSION (which will subsequently be completed with AUTHORIZATION). Because we're using Matches() insted of TailMatches(), we can also get rid of the guards against ALTER DATABASE|USER|ROLE ... RESET. --- src/bin/psql/tab-complete.in.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 2343c87c49a..523927abae5 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -1067,6 +1067,11 @@ static const SchemaQuery Query_for_trigger_of_table = { " WHERE context IN ('user', 'superuser') "\ " AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')" +#define Query_for_list_of_session_vars \ +"SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings "\ +" WHERE source = 'session' "\ +" AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')" + #define Query_for_list_of_show_vars \ "SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings "\ " WHERE pg_catalog.lower(name) LIKE pg_catalog.lower('%s')" @@ -5068,9 +5073,8 @@ match_previous_words(int pattern_id, /* SET, RESET, SHOW */ /* Complete with a variable name */ - else if (TailMatches("SET|RESET") && - !TailMatches("UPDATE", MatchAny, "SET") && - !TailMatches("ALTER", "DATABASE|USER|ROLE", MatchAny, "RESET")) + else if (TailMatches("SET") && + !TailMatches("UPDATE", MatchAny, "SET")) COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars, "CONSTRAINTS", "TRANSACTION", @@ -5078,6 +5082,12 @@ match_previous_words(int pattern_id, "ROLE", "TABLESPACE", "ALL"); + /* Complete with variables set in the current session */ + else if (Matches("RESET")) + COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_session_vars, + "ALL", + "ROLE", + "SESSION"); else if (Matches("SHOW")) COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_show_vars, "SESSION AUTHORIZATION", From ad5f64e2ec500321226df58193646f820fff76e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Fri, 8 Aug 2025 23:07:39 +0100 Subject: [PATCH 4/5] Improve tab completion for ALTER SYSTEM RESET Only complete variables where sourcefile is `postgresql.conf.auto` in the data directory. --- src/bin/psql/tab-complete.in.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 523927abae5..324c87273d6 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -1062,6 +1062,13 @@ static const SchemaQuery Query_for_trigger_of_table = { " WHERE context != 'internal' "\ " AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')" +#define Query_for_list_of_alter_system_reset_vars \ +"SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings " \ +" WHERE sourcefile LIKE pg_catalog.regexp_replace( " \ +" pg_catalog.current_setting('data_directory'), " \ +" '[_%%\\\\]', '\\\\\\&') || '_postgresql.auto.conf' " \ +" AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')" + #define Query_for_list_of_set_vars \ "SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings "\ " WHERE context IN ('user', 'superuser') "\ @@ -2647,8 +2654,10 @@ match_previous_words(int pattern_id, /* ALTER SYSTEM SET, RESET, RESET ALL */ else if (Matches("ALTER", "SYSTEM")) COMPLETE_WITH("SET", "RESET"); - else if (Matches("ALTER", "SYSTEM", "SET|RESET")) - COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_set_vars, + else if (Matches("ALTER", "SYSTEM", "SET")) + COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars); + else if (Matches("ALTER", "SYSTEM", "RESET")) + COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_reset_vars, "ALL"); else if (Matches("ALTER", "SYSTEM", "SET", MatchAny)) COMPLETE_WITH("TO"); From 0a043873e057d595b35e5d8284966dc70244f379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= Date: Sat, 9 Aug 2025 00:29:39 +0100 Subject: [PATCH 5/5] Improve tab completion for ALTER SYSTEM RESET for non-superusers Non-superusers can only use ALTER SYSTEM on variables they've explicitly been GRANTed access to, and can't read the data_directory setting or pg_settings.sourceline column, so the existing tab completion for ALTER SYSTEM isn't very useful for them. Instead, query pg_parameter_acl to show the variables they do have permission to set via ALTER SYSTEM. --- src/bin/psql/tab-complete.in.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 324c87273d6..8571b283409 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -1069,6 +1069,13 @@ static const SchemaQuery Query_for_trigger_of_table = { " '[_%%\\\\]', '\\\\\\&') || '_postgresql.auto.conf' " \ " AND pg_catalog.lower(name) LIKE pg_catalog.lower('%s')" +#define Query_for_list_of_alter_system_granted_vars \ +"SELECT pg_catalog.lower(parname) FROM pg_catalog.pg_parameter_acl " \ +" WHERE EXISTS (SELECT FROM pg_catalog.aclexplode(paracl) " \ +" WHERE pg_has_role(current_role, grantee, 'usage') " \ +" AND privilege_type = 'ALTER SYSTEM') " \ +" AND pg_catalog.lower(parname) LIKE pg_catalog.lower('%s')" + #define Query_for_list_of_set_vars \ "SELECT pg_catalog.lower(name) FROM pg_catalog.pg_settings "\ " WHERE context IN ('user', 'superuser') "\ @@ -2655,10 +2662,16 @@ match_previous_words(int pattern_id, else if (Matches("ALTER", "SYSTEM")) COMPLETE_WITH("SET", "RESET"); else if (Matches("ALTER", "SYSTEM", "SET")) - COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars); + if (is_superuser()) + COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars); + else + COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_granted_vars); else if (Matches("ALTER", "SYSTEM", "RESET")) - COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_reset_vars, - "ALL"); + if (is_superuser()) + COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_reset_vars, + "ALL"); + else + COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_granted_vars); else if (Matches("ALTER", "SYSTEM", "SET", MatchAny)) COMPLETE_WITH("TO"); /* ALTER VIEW */