From ea3185cf44bf5b7b56abab742e383116f0a07f3a Mon Sep 17 00:00:00 2001 From: Kiran Kaki Date: Wed, 15 Apr 2026 03:44:15 +0000 Subject: [PATCH] Add psql tab completion for FOR PORTION OF clause Add tab completion support in psql for the FOR PORTION OF clause used in UPDATE and DELETE statements with temporal tables. For both UPDATE and DELETE, completion now guides users through: FOR -> PORTION -> OF -> -> FROM After the FOR PORTION OF ... FROM TO sequence, DELETE offers AS, USING, WHERE and UPDATE offers AS, SET as the next keywords. The existing completions for DELETE FROM
and UPDATE
are extended to include FOR alongside the previously offered keywords. Also adds TAP tests in 010_tab_completion.pl covering the full completion chain for both DELETE and UPDATE with FOR PORTION OF. --- src/bin/psql/t/010_tab_completion.pl | 39 +++++++++++++++++++++++++++- src/bin/psql/tab-complete.in.c | 36 ++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/bin/psql/t/010_tab_completion.pl b/src/bin/psql/t/010_tab_completion.pl index 1d2e5f5..51b5d8c 100644 --- a/src/bin/psql/t/010_tab_completion.pl +++ b/src/bin/psql/t/010_tab_completion.pl @@ -44,7 +44,8 @@ $node->safe_psql('postgres', . "CREATE TABLE mytab246 (f1 int, f2 text);\n" . "CREATE TABLE \"mixedName\" (f1 int, f2 text);\n" . "CREATE TYPE enum1 AS ENUM ('foo', 'bar', 'baz', 'BLACK');\n" - . "CREATE PUBLICATION some_publication;\n"); + . "CREATE PUBLICATION some_publication;\n" + . "CREATE TABLE fpo_test (id int4range, valid_at daterange, name text);\n"); # In a VPATH build, we'll be started in the source directory, but we want # to run in the build directory so that we can use relative paths to @@ -422,6 +423,42 @@ check_completion( clear_line(); +# check tab completion for DELETE ... FOR PORTION OF +check_completion( + "DELETE FROM fpo_test F\t", + qr/FOR /, + "complete DELETE FROM
F to FOR"); + +check_completion("P\t", qr/PORTION /, "complete FOR P to PORTION"); + +check_completion("O\t", qr/OF /, "complete PORTION O to OF"); + +check_completion("v\t", qr/valid_at /, + "complete FOR PORTION OF offers column names"); + +check_completion("FR\t", qr/FROM /, + "complete FOR PORTION OF FR to FROM"); + +clear_query(); + +# check tab completion for UPDATE ... FOR PORTION OF +check_completion( + "UPDATE fpo_test F\t", + qr/FOR /, + "complete UPDATE
F to FOR"); + +check_completion("P\t", qr/PORTION /, "complete FOR P to PORTION"); + +check_completion("O\t", qr/OF /, "complete PORTION O to OF"); + +check_completion("v\t", qr/valid_at /, + "complete FOR PORTION OF offers column names"); + +check_completion("FR\t", qr/FROM /, + "complete FOR PORTION OF FR to FROM"); + +clear_query(); + # send psql an explicit \q to shut it down, else pty won't close properly $h->quit or die "psql returned $?"; diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 9990f81..7c99f0f 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -4362,7 +4362,22 @@ match_previous_words(int pattern_id, COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); /* Complete DELETE FROM
*/ else if (TailMatches("DELETE", "FROM", MatchAny)) - COMPLETE_WITH("USING", "WHERE"); + COMPLETE_WITH("FOR", "USING", "WHERE"); + /* Complete DELETE FROM
FOR with PORTION */ + else if (TailMatches("DELETE", "FROM", MatchAny, "FOR")) + COMPLETE_WITH("PORTION"); + /* Complete DELETE FROM
FOR PORTION with OF */ + else if (TailMatches("DELETE", "FROM", MatchAny, "FOR", "PORTION")) + COMPLETE_WITH("OF"); + /* Complete DELETE FROM
FOR PORTION OF with column names */ + else if (TailMatches("DELETE", "FROM", MatchAny, "FOR", "PORTION", "OF")) + COMPLETE_WITH_ATTR(prev4_wd); + /* Complete DELETE FROM
FOR PORTION OF with FROM */ + else if (TailMatches("DELETE", "FROM", MatchAny, "FOR", "PORTION", "OF", MatchAny)) + COMPLETE_WITH("FROM"); + /* Complete DELETE FROM
FOR PORTION OF ... TO with AS, USING, WHERE */ + else if (TailMatches("DELETE", "FROM", MatchAny, "FOR", "PORTION", "OF", MatchAnyN, "TO", MatchAny)) + COMPLETE_WITH("AS", "USING", "WHERE"); /* Complete DELETE FROM
USING with relations supporting SELECT */ else if (TailMatches("DELETE", "FROM", MatchAny, "USING")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables); @@ -5434,9 +5449,24 @@ match_previous_words(int pattern_id, /* If prev. word is UPDATE suggest a list of tables */ else if (TailMatches("UPDATE")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables); - /* Complete UPDATE
with "SET" */ + /* Complete UPDATE
with "SET" or "FOR" (for FOR PORTION OF) */ else if (TailMatches("UPDATE", MatchAny)) - COMPLETE_WITH("SET"); + COMPLETE_WITH("FOR", "SET"); + /* Complete UPDATE
FOR with PORTION */ + else if (TailMatches("UPDATE", MatchAny, "FOR")) + COMPLETE_WITH("PORTION"); + /* Complete UPDATE
FOR PORTION with OF */ + else if (TailMatches("UPDATE", MatchAny, "FOR", "PORTION")) + COMPLETE_WITH("OF"); + /* Complete UPDATE
FOR PORTION OF with column names */ + else if (TailMatches("UPDATE", MatchAny, "FOR", "PORTION", "OF")) + COMPLETE_WITH_ATTR(prev4_wd); + /* Complete UPDATE
FOR PORTION OF with FROM */ + else if (TailMatches("UPDATE", MatchAny, "FOR", "PORTION", "OF", MatchAny)) + COMPLETE_WITH("FROM"); + /* Complete UPDATE
FOR PORTION OF ... TO with AS, SET */ + else if (TailMatches("UPDATE", MatchAny, "FOR", "PORTION", "OF", MatchAnyN, "TO", MatchAny)) + COMPLETE_WITH("AS", "SET"); /* Complete UPDATE
SET with list of attributes */ else if (TailMatches("UPDATE", MatchAny, "SET")) COMPLETE_WITH_ATTR(prev2_wd); -- 2.43.0