From 8b6e84a22146b323c606849c3ae1b901c184c237 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Tue, 14 Jul 2026 11:18:55 +1000 Subject: [PATCH v1] psql - tab-completion of pub/sub options. For commands: CREATE PUBLICATION ... WITH (options) ALTER PUBLICATION ... SET (options) CREATE SUBSCRIPTION ... WITH (options) ALTER SUBSCRIPTION ... SET (options) Now: - handles multiple options of the WITH/SET comma-separated lists - enum options show their allowed values Author: Peter Smith --- src/bin/psql/tab-complete.in.c | 143 ++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 20 deletions(-) diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 1cacc8c3ea2..484cd82a512 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2341,9 +2341,20 @@ match_previous_words(int pattern_id, COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas " AND nspname NOT LIKE E'pg\\\\_%%'", "CURRENT_SCHEMA"); - /* ALTER PUBLICATION SET ( */ - else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "(")) - COMPLETE_WITH("publish", "publish_generated_columns", "publish_via_partition_root"); + /* ALTER PUBLICATION SET ( */ + else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "SET", "(")) + { + if (ends_with(prev_wd, '(') || ends_with(prev_wd, ',')) + COMPLETE_WITH("publish", "publish_generated_columns =", + "publish_via_partition_root"); + + /* Complete "ALTER PUBLICATION SET ( =" */ + + else if (TailMatches("publish_generated_columns")) + COMPLETE_WITH("="); + else if (TailMatches("publish_generated_columns", "=")) + COMPLETE_WITH("none", "stored"); + } /* ALTER SUBSCRIPTION */ else if (Matches("ALTER", "SUBSCRIPTION", MatchAny)) COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO", @@ -2361,13 +2372,38 @@ match_previous_words(int pattern_id, else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET")) COMPLETE_WITH("(", "PUBLICATION"); /* ALTER SUBSCRIPTION SET ( */ - else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "(")) - COMPLETE_WITH("binary", "conflict_log_destination", "disable_on_error", - "failover", "max_retention_duration", "origin", - "password_required", "retain_dead_tuples", - "run_as_owner", "slot_name", "streaming", - "synchronous_commit", "two_phase", + else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny, "SET", "(")) + { + if (ends_with(prev_wd, '(') || ends_with(prev_wd, ',')) + COMPLETE_WITH("binary", "conflict_log_destination =", + "disable_on_error", "failover", "max_retention_duration", + "origin =", "password_required", "retain_dead_tuples", + "run_as_owner", "slot_name", "streaming =", + "synchronous_commit =", "two_phase", "wal_receiver_timeout"); + + /* ALTER SUBSCRIPTION SET ( = */ + + else if (TailMatches("conflict_log_destination")) + COMPLETE_WITH("="); + else if (TailMatches("conflict_log_destination", "=")) + COMPLETE_WITH("all", "log", "table"); + + else if (TailMatches("origin")) + COMPLETE_WITH("="); + else if (TailMatches("origin", "=")) + COMPLETE_WITH("any", "none"); + + else if (TailMatches("streaming")) + COMPLETE_WITH("="); + else if (TailMatches("streaming", "=")) + COMPLETE_WITH("off", "on", "parallel"); + + else if (TailMatches("synchronous_commit")) + COMPLETE_WITH("="); + else if (TailMatches("synchronous_commit", "=")) + COMPLETE_WITH("local", "off", "on", "remote_apply", "remote_write"); + } /* ALTER SUBSCRIPTION SKIP ( */ else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "(")) COMPLETE_WITH("lsn"); @@ -3776,9 +3812,34 @@ match_previous_words(int pattern_id, "CURRENT_SCHEMA"); else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ','))) COMPLETE_WITH("WITH ("); - /* Complete "CREATE PUBLICATION [...] WITH" */ - else if (Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "(")) - COMPLETE_WITH("publish", "publish_generated_columns", "publish_via_partition_root"); + /* Complete "CREATE PUBLICATION ... WITH ( " */ + else if (HeadMatches("CREATE", "PUBLICATION")) + { + bool seen_with = false; + + for (int i = 0; i < previous_words_count; i++) + { + if (pg_strcasecmp(previous_words[i], "WITH") == 0) + { + seen_with = true; + break; + } + } + + if (seen_with) + { + if (ends_with(prev_wd, '(') || ends_with(prev_wd, ',')) + COMPLETE_WITH("publish", "publish_generated_columns =", + "publish_via_partition_root"); + + /* Complete "CREATE PUBLICATION ... WITH ( =" */ + + else if (TailMatches("publish_generated_columns")) + COMPLETE_WITH("="); + else if (TailMatches("publish_generated_columns", "=")) + COMPLETE_WITH("none", "stored"); + } + } /* CREATE RULE */ /* Complete "CREATE [ OR REPLACE ] RULE " with "AS ON" */ @@ -3945,15 +4006,57 @@ match_previous_words(int pattern_id, } else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny)) COMPLETE_WITH("WITH ("); + else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH")) + COMPLETE_WITH("("); /* Complete "CREATE SUBSCRIPTION ... WITH ( " */ - else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "(")) - COMPLETE_WITH("binary", "conflict_log_destination", "connect", "copy_data", - "create_slot", "disable_on_error", "enabled", "failover", - "max_retention_duration", "origin", - "password_required", "retain_dead_tuples", - "run_as_owner", "slot_name", "streaming", - "synchronous_commit", "two_phase", - "wal_receiver_timeout"); + else if (HeadMatches("CREATE", "SUBSCRIPTION")) + { + bool seen_with = false; + + for (int i = 0; i < previous_words_count; i++) + { + if (pg_strcasecmp(previous_words[i], "WITH") == 0) + { + seen_with = true; + break; + } + } + + if (seen_with) + { + if (ends_with(prev_wd, '(') || ends_with(prev_wd, ',')) + COMPLETE_WITH("binary", "conflict_log_destination =", + "connect", "copy_data", "create_slot", + "disable_on_error", "enabled", "failover", + "max_retention_duration", "origin =", + "password_required", "retain_dead_tuples", + "run_as_owner", "slot_name", "streaming =", + "synchronous_commit =", "two_phase", + "wal_receiver_timeout"); + + /* Complete "CREATE SUBSCRIPTION ... WITH ( =" */ + + else if (TailMatches("conflict_log_destination")) + COMPLETE_WITH("="); + else if (TailMatches("conflict_log_destination", "=")) + COMPLETE_WITH("all", "log", "table"); + + else if (TailMatches("origin")) + COMPLETE_WITH("="); + else if (TailMatches("origin", "=")) + COMPLETE_WITH("any", "none"); + + else if (TailMatches("streaming")) + COMPLETE_WITH("="); + else if (TailMatches("streaming", "=")) + COMPLETE_WITH("off", "on", "parallel"); + + else if (TailMatches("synchronous_commit")) + COMPLETE_WITH("="); + else if (TailMatches("synchronous_commit", "=")) + COMPLETE_WITH("local", "off", "on", "remote_apply", "remote_write"); + } + } /* CREATE TRIGGER --- is allowed inside CREATE SCHEMA, so use TailMatches */ -- 2.47.3