From: Justin Pryzby <pryzby@telsasoft.com>
To: Arthur Zakirov <a.zakirov@postgrespro.ru>
To: Edmund Horner <ejrh00@gmail.com>
To: Alvaro Herrera <alvherre@2ndquadrant.com>
Cc: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Subject: Re: adding tab completions
Date: Sun, 3 Jun 2018 18:18:59 -0500
Message-ID: <20180603231859.GV5164@telsasoft.com> (raw)
In-Reply-To: <20180529141227.ygpfqiat5rzikjzx@alvherre.pgsql> <CAMyN-kDUKKtaZRQJu_-ahr258b1P0PC341xyH0CK3CWi7fet7g@mail.gmail.com> <20180529092726.GA6535@zakirov.localdomain>
I finally got back to this; thanks everyone for reviews;
I also added completion for parenthesized explain (...)
d4382c4ae7ea1e272f4fee388aac8ff99421471a
and for ALTER TABLE SET (toast_tuple_target).
c2513365a0a85e77d3c21adb92fe12cfbe0d1897
BTW..should that be toast.tuple_target ??
Note that this also tries to comply with 921059bd66c7fb1230c705d3b1a65940800c4cbb
This is okay in v10 but rejected in v11b1:
postgres=# VACUUM ANALYZE VERBOSE t;
ERROR: syntax error at or near "VERBOSE"
On Tue, May 29, 2018 at 10:12:27AM -0400, Alvaro Herrera wrote:
> Is it just me that finds shocking the notion of vacuuming a foreign
> table?
Fixed, thanks.
On Tue, May 29, 2018 at 12:27:27PM +0300, Arthur Zakirov wrote:
> The patch replaces the variable Query_for_list_of_tmf by
> Query_for_list_of_tpmf. So I think Query_for_list_of_tmf isn't needed
> anymore and can be removed.
Fixed by adding relkind='p' to the existing list rather than making a new
query entry.
> Also I think it could be good to list column names after parentheses,
> but I'm not sure if it easy to implement.
I tried this and nearly gave up, but see attached.
The complication is that table names for which to pull the attributes isn't at a
constant offset; for example:
vacuum analyze a(TAB => needs prev2_wd
but
vacuum analyze a(c, TAB => needs prev3_wd
Maybe that's too much effort to include in tab completion. If so, the
"prev_parens" stanza can be removed from VACUUM and ANALYZE. If it's desired
to keep it, I guess I should avoid the duplicated lines.
On Wed, May 30, 2018 at 12:45:30AM +1200, Edmund Horner wrote:
> I don't believe it's meaningful to match on words with spaces in them,
Excellent point...fixed.
Find attached v6 (there were some "unpublished" versions).
Justin
Attachments:v6-tab-complete-partitions (text/plain, 9.5K) downloaddiff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.cindex 7bb47ea..13d79f8 100644--- a/src/bin/psql/tab-complete.c+++ b/src/bin/psql/tab-complete.c@@ -705,6 +705,7 @@ static const SchemaQuery Query_for_list_of_tmf = {
"pg_catalog.pg_class c",
/* selcondition */
"c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
+ CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_FOREIGN_TABLE) ")",
/* viscondition */
@@ -1282,6 +1283,7 @@ static PGresult *exec_query(const char *query);
static char **get_previous_words(int point, char **buffer, int *nwords);
static char *get_guctype(const char *varname);
+static int prev_parens(int nwords, char **words);
#ifdef NOT_USED
static char *quote_file_name(char *text, int match_type, char *quote_pointer);
@@ -2222,6 +2224,7 @@ psql_completion(const char *text, int start, int end)
"fillfactor",
"parallel_workers",
"log_autovacuum_min_duration",
+ "toast_tuple_target",
"toast.autovacuum_enabled",
"toast.autovacuum_freeze_max_age",
"toast.autovacuum_freeze_min_age",
@@ -2385,7 +2388,7 @@ psql_completion(const char *text, int start, int end)
{"ACCESS METHOD", "CAST", "COLLATION", "CONVERSION", "DATABASE",
"EVENT TRIGGER", "EXTENSION",
"FOREIGN DATA WRAPPER", "FOREIGN TABLE",
- "SERVER", "INDEX", "LANGUAGE", "POLICY", "PUBLICATION", "RULE",+ "SERVER", "INDEX", "LANGUAGE", "POLICY", "PUBLICATION",
"SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION",
"TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
"PROCEDURE", "ROUTINE",
@@ -2703,7 +2706,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_LIST2("TABLE", "MATERIALIZED VIEW");
/* Complete PARTITION BY with RANGE ( or LIST ( or ... */
else if (TailMatches2("PARTITION", "BY"))
- COMPLETE_WITH_LIST2("RANGE (", "LIST (");+ COMPLETE_WITH_LIST3("RANGE (", "LIST (", "HASH (");
/* If we have xxx PARTITION OF, provide a list of partitioned tables */
else if (TailMatches2("PARTITION", "OF"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, "");
@@ -2996,14 +2999,21 @@ psql_completion(const char *text, int start, int end)
else if (Matches1("EXECUTE"))
COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
-/* EXPLAIN */-- /*- * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands- */+/* + * EXPLAIN [ ( option [, ...] ) ] statement+ * EXPLAIN [ ANALYZE ] [ VERBOSE ] statement+ */
else if (Matches1("EXPLAIN"))
- COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",- "ANALYZE", "VERBOSE");+ COMPLETE_WITH_LIST8("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",+ "ANALYZE", "VERBOSE", "(");+ else if (HeadMatches2("EXPLAIN", "("))+ if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))+ COMPLETE_WITH_LIST7("ANALYZE", "VERBOSE", "COSTS", "BUFFERS", "TIMING", "SUMMARY", "FORMAT");+ else+ COMPLETE_WITH_LIST4(",", ")", "ON", "OFF");+ else if (HeadMatches1("EXPLAIN") && previous_words_count==2 && prev_wd[0]=='(' && ends_with(prev_wd, ')'))+ /* If the parenthesis are balanced, the list is apparently parsed as a single word */+ COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
else if (Matches2("EXPLAIN", "ANALYZE"))
COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
"VERBOSE");
@@ -3563,33 +3573,63 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_CONST("OPTIONS");
/*
- * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]- * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]+ * VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ]+ * VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]
*/
else if (Matches1("VACUUM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm,
" UNION SELECT 'FULL'"
" UNION SELECT 'FREEZE'"
" UNION SELECT 'ANALYZE'"
" UNION SELECT 'VERBOSE'");
- else if (Matches2("VACUUM", "FULL|FREEZE"))- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,+ else if (Matches2("VACUUM", "FULL"))+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm,+ " UNION SELECT 'FREEZE'"
" UNION SELECT 'ANALYZE'"
" UNION SELECT 'VERBOSE'");
- else if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE"))- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,- " UNION SELECT 'VERBOSE'");- else if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE"))- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,+ else if (Matches2("VACUUM", "FULL|FREEZE") ||+ Matches3("VACUUM", "FULL", "FREEZE"))+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm,+ " UNION SELECT 'VERBOSE'"
" UNION SELECT 'ANALYZE'");
- else if (Matches2("VACUUM", "VERBOSE"))- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,+ else if (HeadMatches2("VACUUM", "VERBOSE") ||+ Matches3("VACUUM", "FULL|FREEZE", "VERBOSE") ||+ Matches4("VACUUM", "FULL", "FREEZE", "VERBOSE"))+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm,
" UNION SELECT 'ANALYZE'");
- else if (Matches2("VACUUM", "ANALYZE"))- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,- " UNION SELECT 'VERBOSE'");+ else if (HeadMatches2("VACUUM", "ANALYZE"))+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm, NULL);+ else if (HeadMatches2("VACUUM", "("))+ if (ends_with(prev_wd, ',') || ends_with(prev_wd, '('))+ COMPLETE_WITH_LIST5("FULL", "FREEZE", "ANALYZE", "VERBOSE", "DISABLE_PAGE_SKIPPING" );+ else+ COMPLETE_WITH_LIST2(",", ")");++ else if (HeadMatches2("VACUUM", MatchAny) &&+ /* Handle (ANALYZE): */+ !(previous_words_count==2 && prev_wd[0]=='(' && ends_with(prev_wd, ')')) &&+ /* Avoid completing to column names following end of list: VACUUM ANALYZE t(i), ... */+ !(previous_words_count>1 && prev2_wd[0]=='(' && ends_with(prev2_wd, ')')) &&+ -1!=prev_parens(previous_words_count, previous_words)) {+ int attword = prev_parens(previous_words_count, previous_words);+ if (!ends_with(prev_wd, '(') && !ends_with(prev_wd, ','))+ COMPLETE_WITH_CONST(",");+ else+ COMPLETE_WITH_ATTR(previous_words[attword], " UNION SELECT ')' UNION SELECT ','");++ } else if (HeadMatches2("VACUUM", MatchAny) && !ends_with(prev_wd, ',') &&+ !(previous_words_count==2 && prev_wd[0]=='(' && ends_with(prev_wd, ')'))) //&& !ends_with(prev_wd, '('))+ /* Comma to support vacuuming multiple tables+ * Parens to support analyzing a partial column list+ * XXX: this mistakenly completes with tablenames: "vacuum analyze t(a,b,"+ */+ if (ends_with(prev_wd, ')'))+ /* Or maybe do nothing since this could be the end */+ COMPLETE_WITH_CONST(",");+ else+ COMPLETE_WITH_LIST3(",", "(", ")");
else if (HeadMatches1("VACUUM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm, NULL);
/* WITH [RECURSIVE] */
@@ -3600,11 +3640,42 @@ psql_completion(const char *text, int start, int end)
else if (Matches1("WITH"))
COMPLETE_WITH_CONST("RECURSIVE");
-/* ANALYZE */- /* Complete with list of tables */+/*+ * ANALYZE [ ( option [, ...] ) ] [ table_and_columns [, ...] ]+ * ANALYZE [ VERBOSE ] [ table_and_columns [, ...] ]+ */+ else if (Matches2("ANALYZE", "("))+ COMPLETE_WITH_CONST("VERBOSE)");
else if (Matches1("ANALYZE"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf,+ " UNION SELECT 'VERBOSE'"+ " UNION SELECT '('"+ );++ else if (HeadMatches2("ANALYZE", MatchAny) &&+ /* Handle (VERBOSE): */+ !(previous_words_count==2 && prev_wd[0]=='(' && ends_with(prev_wd, ')')) &&+ /* Avoid completing to column names following end of list: ANALYZE t(i), ... */+ !(previous_words_count>1 && prev2_wd[0]=='(' && ends_with(prev2_wd, ')')) &&+ -1!=prev_parens(previous_words_count, previous_words)) {+ int attword = prev_parens(previous_words_count, previous_words);+ if (!ends_with(prev_wd, '(') && !ends_with(prev_wd, ','))+ COMPLETE_WITH_CONST(",");+ else+ COMPLETE_WITH_ATTR(previous_words[attword], " UNION SELECT ')' UNION SELECT ','");++ } else if (HeadMatches2("ANALYZE", MatchAny) &&+ !ends_with(prev_wd, ',') &&+ !(previous_words_count==2 && prev_wd[0]=='(' && ends_with(prev_wd, ')')) &&+ !TailMatches1("VERBOSE"))+ /* Support analyze of multiple tables */+ /* or analyze table(column1, column2) */+ if (ends_with(prev_wd, ')'))+ COMPLETE_WITH_CONST(",");+ else+ COMPLETE_WITH_LIST2(",", "(");+ else if (HeadMatches1("ANALYZE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL);
-
/* WHERE */
/* Simple case of the word before the where being the table name */
else if (TailMatches2(MatchAny, "WHERE"))
@@ -4654,6 +4725,30 @@ get_guctype(const char *varname)
return guctype;
}
+/*+ * Return the word before the beginning of a current parenthesized list,+ * or -1 if none.+ */+int+prev_parens(int nwords, char **words)+{+ int i;+ for (i=0; i<nwords-1; ++i) {+ /* "a b c ( x y" returns: c */+ if (words[i][0]=='(')+ return i+1;++ /* "a b c ( x ) y" fails */+ /* "a b c ) y" fails */+ if (words[i][0]==')')+ return -1;+ }++ /* "a b c" fails */+ return -1;+}++
#ifdef NOT_USED
/*
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: pryzby@telsasoft.com, a.zakirov@postgrespro.ru, ejrh00@gmail.com, alvherre@2ndquadrant.com
Subject: Re: adding tab completions
In-Reply-To: <20180603231859.GV5164@telsasoft.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox