From cf42fc6247e3a20db77f7d6e8e552d4e6a1a8248 Mon Sep 17 00:00:00 2001 From: Haiying Tang Date: Thu, 22 Apr 2021 21:31:19 +0900 Subject: [PATCH] Support tab completion with a query result for upper character inputs in psql diff --git a/src/bin/psql/t/010_tab_completion.pl b/src/bin/psql/t/010_tab_completion.pl index c27f216d39..70c3482b6e 100644 --- a/src/bin/psql/t/010_tab_completion.pl +++ b/src/bin/psql/t/010_tab_completion.pl @@ -38,8 +38,10 @@ $node->start; # set up a few database objects $node->safe_psql('postgres', "CREATE TABLE tab1 (f1 int, f2 text);\n" + . "CREATE TABLE onetab1 (f1 int);\n" . "CREATE TABLE mytab123 (f1 int, f2 text);\n" - . "CREATE TABLE mytab246 (f1 int, f2 text);\n"); + . "CREATE TABLE mytab246 (f1 int, f2 text);\n" + . "CREATE TABLE \"myTAB123\" (\"aF1\" int, f2 text);\n"); # Developers would not appreciate this test adding a bunch of junk to # their ~/.psql_history, so be sure to redirect history into a temp file. @@ -142,6 +144,51 @@ check_completion("SEL\t", qr/SELECT /, "complete SEL to SELECT"); clear_query(); +# check set query command(upper case) completion for upper character inputs +check_completion("set BYT\t", qr/set BYTEA_OUTPUT /, "complete set BYT to set BYTEA_OUTPUT"); + +clear_query(); + +# check set query command(lower case) completion for upper character inputs +check_completion("set bYT\t", qq/set bYT\b\bytea_output /, "complete set bYT to set bytea_output"); + +clear_query(); + +# check query command(upper case) completion for empty input +check_completion("update onetab1 \t", qr/update onetab1 SET /, "complete SQL key words for onetab1 with empty input"); + +clear_query(); + +# check query command(lower case) completion for empty input +check_completion("update onetab1 SET \t", qr/update onetab1 SET f1 /, "complete column name for onetab1 with empty input"); + +clear_query(); + +# check query command completion for upper character relation name +check_completion("update TAB1 SET \t", qr/update TAB1 SET \af/, "complete column name for TAB1"); + +clear_query(); + +# check quoted identifiers in table +check_completion("update \"my\t", qr/update \"myTAB123\" /, "complete quoted string1"); + +clear_query(); + +# check quoted identifiers in column +check_completion("update \"myTAB123\" SET \"aF\t", qr/update \"myTAB123\" SET \"aF1\" /, "complete quoted string2"); + +clear_query(); + +# check schema query(lower case) which is case-insensitive +check_completion("select oid from pg_Cla\t", qq/select oid from pg_Cla\b\b\bclass /, "complete schema query with lower case string"); + +clear_query(); + +# check schema query(upper case) which is case-insensitive +check_completion("select oid from Pg_cla\t", qq/select oid from Pg_cla\b\b\b\b\bG_CLASS /, "complete schema query with uppper case string"); + +clear_query(); + # check case variation is honored check_completion("sel\t", qr/select /, "complete sel to select"); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index ed84b3789c..c3bd75e797 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -197,18 +197,21 @@ static bool completion_force_quote; /* true to force-quote filenames */ */ #define COMPLETE_WITH_QUERY(query) \ do { \ + completion_case_sensitive = false; \ completion_charp = query; \ matches = rl_completion_matches(text, complete_from_query); \ } while (0) #define COMPLETE_WITH_VERSIONED_QUERY(query) \ do { \ + completion_case_sensitive = false; \ completion_vquery = query; \ matches = rl_completion_matches(text, complete_from_versioned_query); \ } while (0) #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ do { \ + completion_case_sensitive = false; \ completion_squery = &(query); \ completion_charp = addon; \ matches = rl_completion_matches(text, complete_from_schema_query); \ @@ -216,6 +219,7 @@ do { \ #define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \ do { \ + completion_case_sensitive = false; \ completion_squery = query; \ completion_vquery = addon; \ matches = rl_completion_matches(text, complete_from_versioned_schema_query); \ @@ -1179,6 +1183,7 @@ static char *complete_from_files(const char *text, int state); static char *pg_strdup_keyword_case(const char *s, const char *ref); static char *escape_string(const char *text); +static char *pg_string_tolower(const char *text); static PGresult *exec_query(const char *query); static char **get_previous_words(int point, char **buffer, int *nwords); @@ -4402,6 +4407,7 @@ _complete_from_query(const char *simple_query, char *e_text; char *e_info_charp; char *e_info_charp2; + char *le_str; const char *pstr = text; int char_length = 0; @@ -4422,16 +4428,46 @@ _complete_from_query(const char *simple_query, PQclear(result); result = NULL; - /* Set up suitably-escaped copies of textual inputs */ + /* Set up suitably-escaped copies of textual inputs, + * if the inputs are not quoted identifiers, + * then change the textual inputs to lower case. + */ e_text = escape_string(text); - + if(e_text[0] == '"') + completion_case_sensitive = true; + else + { + le_str = pg_string_tolower(e_text); + free(e_text); + e_text = le_str; + } if (completion_info_charp) + { e_info_charp = escape_string(completion_info_charp); + if(e_info_charp[0] == '"') + completion_case_sensitive = true; + else + { + le_str = pg_string_tolower(e_info_charp); + free(e_info_charp); + e_info_charp = le_str; + } + } else e_info_charp = NULL; if (completion_info_charp2) + { e_info_charp2 = escape_string(completion_info_charp2); + if(e_info_charp2[0] == '"') + completion_case_sensitive = true; + else + { + le_str = pg_string_tolower(e_info_charp2); + free(e_info_charp2); + e_info_charp2 = le_str; + } + } else e_info_charp2 = NULL; @@ -4558,7 +4594,17 @@ _complete_from_query(const char *simple_query, while (list_index < PQntuples(result) && (item = PQgetvalue(result, list_index++, 0))) if (pg_strncasecmp(text, item, byte_length) == 0) - return pg_strdup(item); + { + if (byte_length == 0 || completion_case_sensitive) + return pg_strdup(item); + else + + /* + * If case insensitive matching was requested initially, + * adjust the case according to setting. + */ + return pg_strdup_keyword_case(item, text); + } } /* If nothing matches, free the db structure and return null */ @@ -4895,6 +4941,24 @@ escape_string(const char *text) } +/* + * pg_string_tolower - Fold a string to lower case. + */ +static char * +pg_string_tolower(const char *text) +{ + char *ret, + *p; + + ret = pg_strdup(text); + + for (p = ret; *p; p++) + *p = pg_tolower((unsigned char) *p); + + return ret; +} + + /* * Execute a query and report any errors. This should be the preferred way of * talking to the database in this file. -- 2.30.0.windows.2