From ee77f6a6fa20a3d356d47090dd64b8e44fccfabc Mon Sep 17 00:00:00 2001 From: Suraj Kharage Date: Fri, 30 Oct 2026 08:11:25 +0530 Subject: [PATCH v1] psql: Fix tab completion of COPY FORMAT and HEADER values. Tab completion offered "json" after COPY ... FROM ... (FORMAT, and "MATCH" after COPY ... TO ... (HEADER, though the server rejects both. Make these completions depend on the COPY direction, as is already done for the option names. Add tests for both cases. --- src/bin/psql/t/010_tab_completion.pl | 30 ++++++++++++++++++++++++++++ src/bin/psql/tab-complete.in.c | 16 +++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/bin/psql/t/010_tab_completion.pl b/src/bin/psql/t/010_tab_completion.pl index 1d2e5f5b92a..4920e65b1fb 100644 --- a/src/bin/psql/t/010_tab_completion.pl +++ b/src/bin/psql/t/010_tab_completion.pl @@ -422,6 +422,36 @@ check_completion( clear_line(); +# check COPY FORMAT json completion: supported for TO but not for FROM +check_completion( + "COPY foo TO stdout WITH ( FORMAT j\t", + qr/json /, + "COPY TO with FORMAT json completion"); + +clear_line(); + +check_completion( + "COPY foo FROM stdin WITH ( FORMAT \t\t", + qr/binary\s+csv\s+text\s+postgres=# /i, + "COPY FROM FORMAT completion does not offer json"); + +clear_line(); + +# check COPY HEADER match completion: supported for FROM but not for TO +check_completion( + "COPY foo FROM stdin WITH ( HEADER M\t", + qr/MATCH /, + "COPY FROM with HEADER MATCH completion"); + +clear_line(); + +check_completion( + "COPY foo TO stdout WITH ( HEADER \t\t", + qr/false\s+true\s+postgres=# /i, + "COPY TO HEADER completion does not offer MATCH"); + +clear_line(); + # 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 cab52f5ba08..3daa840516e 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -3436,7 +3436,13 @@ match_previous_words(int pattern_id, /* Complete COPY FROM|TO filename WITH (FORMAT */ else if (TailMatches("FORMAT")) - COMPLETE_WITH("binary", "csv", "text", "json"); + { + /* json is supported for COPY TO only */ + if (HeadMatches("COPY|\\copy", MatchAny, "FROM")) + COMPLETE_WITH("binary", "csv", "text"); + else + COMPLETE_WITH("binary", "csv", "text", "json"); + } /* Complete COPY FROM|TO filename WITH (FREEZE */ else if (TailMatches("FREEZE")) @@ -3444,7 +3450,13 @@ match_previous_words(int pattern_id, /* Complete COPY FROM|TO filename WITH (HEADER */ else if (TailMatches("HEADER")) - COMPLETE_WITH("true", "false", "MATCH"); + { + /* MATCH is supported for COPY FROM only */ + if (HeadMatches("COPY|\\copy", MatchAny, "FROM")) + COMPLETE_WITH("true", "false", "MATCH"); + else + COMPLETE_WITH("true", "false"); + } /* Complete COPY FROM filename WITH (ON_ERROR */ else if (TailMatches("ON_ERROR")) -- 2.47.3