1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

psql: Improve tab completion for COPY ... STDIN/STDOUT.

This commit enhances tab completion for both COPY FROM and COPY TO
commands to suggest STDIN and STDOUT, respectively.

To make suggesting both file names and keywords easier, it introduces
a new COMPLETE_WITH_FILES_PLUS() macro.

Author: Yugo Nagata <nagata@sraoss.co.jp>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Discussion: https://postgr.es/m/20250605100835.b396f9d656df1018f65a4556@sraoss.co.jp
This commit is contained in:
Masahiko Sawada
2025-11-04 10:40:58 -08:00
parent be9efd4929
commit 02fd47dbfa

View File

@@ -443,13 +443,23 @@ do { \
matches = rl_completion_matches(text, complete_from_schema_query); \ matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0) } while (0)
#define COMPLETE_WITH_FILES(escape, force_quote) \ #define COMPLETE_WITH_FILES_LIST(escape, force_quote, list) \
do { \ do { \
completion_charp = escape; \ completion_charp = escape; \
completion_charpp = list; \
completion_force_quote = force_quote; \ completion_force_quote = force_quote; \
matches = rl_completion_matches(text, complete_from_files); \ matches = rl_completion_matches(text, complete_from_files); \
} while (0) } while (0)
#define COMPLETE_WITH_FILES(escape, force_quote) \
COMPLETE_WITH_FILES_LIST(escape, force_quote, NULL)
#define COMPLETE_WITH_FILES_PLUS(escape, force_quote, ...) \
do { \
static const char *const list[] = { __VA_ARGS__, NULL }; \
COMPLETE_WITH_FILES_LIST(escape, force_quote, list); \
} while (0)
#define COMPLETE_WITH_GENERATOR(generator) \ #define COMPLETE_WITH_GENERATOR(generator) \
matches = rl_completion_matches(text, generator) matches = rl_completion_matches(text, generator)
@@ -1485,6 +1495,7 @@ static void append_variable_names(char ***varnames, int *nvars,
static char **complete_from_variables(const char *text, static char **complete_from_variables(const char *text,
const char *prefix, const char *suffix, bool need_value); const char *prefix, const char *suffix, bool need_value);
static char *complete_from_files(const char *text, int state); static char *complete_from_files(const char *text, int state);
static char *_complete_from_files(const char *text, int state);
static char *pg_strdup_keyword_case(const char *s, const char *ref); static char *pg_strdup_keyword_case(const char *s, const char *ref);
static char *escape_string(const char *text); static char *escape_string(const char *text);
@@ -3325,11 +3336,17 @@ match_previous_words(int pattern_id,
/* Complete COPY <sth> */ /* Complete COPY <sth> */
else if (Matches("COPY|\\copy", MatchAny)) else if (Matches("COPY|\\copy", MatchAny))
COMPLETE_WITH("FROM", "TO"); COMPLETE_WITH("FROM", "TO");
/* Complete COPY <sth> FROM|TO with filename */ /* Complete COPY|\copy <sth> FROM|TO with filename or STDIN/STDOUT */
else if (Matches("COPY", MatchAny, "FROM|TO")) else if (Matches("COPY|\\copy", MatchAny, "FROM|TO"))
COMPLETE_WITH_FILES("", true); /* COPY requires quoted filename */ {
else if (Matches("\\copy", MatchAny, "FROM|TO")) /* COPY requires quoted filename */
COMPLETE_WITH_FILES("", false); bool force_quote = HeadMatches("COPY");
if (TailMatches("FROM"))
COMPLETE_WITH_FILES_PLUS("", force_quote, "STDIN");
else
COMPLETE_WITH_FILES_PLUS("", force_quote, "STDOUT");
}
/* Complete COPY <sth> TO <sth> */ /* Complete COPY <sth> TO <sth> */
else if (Matches("COPY|\\copy", MatchAny, "TO", MatchAny)) else if (Matches("COPY|\\copy", MatchAny, "TO", MatchAny))
@@ -6250,6 +6267,59 @@ complete_from_variables(const char *text, const char *prefix, const char *suffix
} }
/*
* This function returns in order one of a fixed, NULL pointer terminated list
* of string that matches file names or optionally specified list of keywords.
*
* If completion_charpp is set to a null-terminated array of literal keywords,
* those keywords are added to the completion results alongside filenames if
* they case-insensitively match the current input.
*/
static char *
complete_from_files(const char *text, int state)
{
static int list_index;
static bool files_done;
const char *item;
/* Initialization */
if (state == 0)
{
list_index = 0;
files_done = false;
}
if (!files_done)
{
char *result = _complete_from_files(text, state);
/* Return a filename that matches */
if (result)
return result;
/* There are no more matching files */
files_done = true;
}
if (!completion_charpp)
return NULL;
/*
* Check for hard-wired keywords. These will only be returned if they
* match the input-so-far, ignoring case.
*/
while ((item = completion_charpp[list_index++]))
{
if (pg_strncasecmp(text, item, strlen(text)) == 0)
{
completion_force_quote = false;
return pg_strdup_keyword_case(item, text);
}
}
return NULL;
}
/* /*
* This function wraps rl_filename_completion_function() to strip quotes from * This function wraps rl_filename_completion_function() to strip quotes from
* the input before searching for matches and to quote any matches for which * the input before searching for matches and to quote any matches for which
@@ -6264,7 +6334,7 @@ complete_from_variables(const char *text, const char *prefix, const char *suffix
* quotes around the result. (The SQL COPY command requires that.) * quotes around the result. (The SQL COPY command requires that.)
*/ */
static char * static char *
complete_from_files(const char *text, int state) _complete_from_files(const char *text, int state)
{ {
#ifdef USE_FILENAME_QUOTING_FUNCTIONS #ifdef USE_FILENAME_QUOTING_FUNCTIONS