@@ -1131,9 +1131,8 @@ initialize_readline(void)
11311131 * If pattern is NULL, it's a wild card that matches any word.
11321132 * If pattern begins with '!', the result is negated, ie we check that 'word'
11331133 * does *not* match any alternative appearing in the rest of 'pattern'.
1134- * Any alternative can end with '*' which is a wild card, i.e., it means
1135- * match any word that matches the characters so far. (We do not currently
1136- * support '*' elsewhere than the end of an alternative.)
1134+ * Any alternative can contain '*' which is a wild card, i.e., it can match
1135+ * any substring; however, we allow at most one '*' per alternative.
11371136 *
11381137 * For readability, callers should use the macros MatchAny and MatchAnyExcept
11391138 * to invoke those two special cases for 'pattern'. (But '|' and '*' must
@@ -1147,8 +1146,10 @@ word_matches_internal(const char *pattern,
11471146 const char * word ,
11481147 bool case_sensitive )
11491148{
1150- size_t wordlen ,
1151- patternlen ;
1149+ size_t wordlen ;
1150+
1151+ #define cimatch (s1 , s2 , n ) \
1152+ (case_sensitive ? strncmp(s1, s2, n) == 0 : pg_strncasecmp(s1, s2, n) == 0)
11521153
11531154 /* NULL pattern matches anything. */
11541155 if (pattern == NULL )
@@ -1162,31 +1163,34 @@ word_matches_internal(const char *pattern,
11621163 wordlen = strlen (word );
11631164 for (;;)
11641165 {
1166+ const char * star = NULL ;
11651167 const char * c ;
11661168
1167- /* Find end of current alternative. */
1169+ /* Find end of current alternative, and locate any wild card . */
11681170 c = pattern ;
11691171 while (* c != '\0' && * c != '|' )
1172+ {
1173+ if (* c == '*' )
1174+ star = c ;
11701175 c ++ ;
1171- /* Was there a wild card? (Assumes first alternative is not empty) */
1172- if (c [-1 ] == '*' )
1176+ }
1177+ /* Was there a wild card? */
1178+ if (star )
11731179 {
11741180 /* Yes, wildcard match? */
1175- patternlen = c - pattern - 1 ;
1176- if (wordlen >= patternlen &&
1177- (case_sensitive ?
1178- strncmp (word , pattern , patternlen ) == 0 :
1179- pg_strncasecmp (word , pattern , patternlen ) == 0 ))
1181+ size_t beforelen = star - pattern ,
1182+ afterlen = c - star - 1 ;
1183+
1184+ if (wordlen >= (beforelen + afterlen ) &&
1185+ cimatch (word , pattern , beforelen ) &&
1186+ cimatch (word + wordlen - afterlen , star + 1 , afterlen ))
11801187 return true;
11811188 }
11821189 else
11831190 {
11841191 /* No, plain match? */
1185- patternlen = c - pattern ;
1186- if (wordlen == patternlen &&
1187- (case_sensitive ?
1188- strncmp (word , pattern , wordlen ) == 0 :
1189- pg_strncasecmp (word , pattern , wordlen ) == 0 ))
1192+ if (wordlen == (c - pattern ) &&
1193+ cimatch (word , pattern , wordlen ))
11901194 return true;
11911195 }
11921196 /* Out of alternatives? */
@@ -2158,6 +2162,24 @@ psql_completion(const char *text, int start, int end)
21582162 else if (Matches5 ("ALTER" , "TYPE" , MatchAny , "RENAME" , "VALUE" ))
21592163 COMPLETE_WITH_ENUM_VALUE (prev3_wd );
21602164
2165+ /*
2166+ * ANALYZE [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
2167+ * ANALYZE [ VERBOSE ] [ table_and_columns [, ...] ]
2168+ *
2169+ * Currently the only allowed option is VERBOSE, so we can be skimpier on
2170+ * the option processing than VACUUM has to be.
2171+ */
2172+ else if (Matches1 ("ANALYZE" ))
2173+ COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_analyzables ,
2174+ " UNION SELECT 'VERBOSE'" );
2175+ else if (Matches2 ("ANALYZE" , "(" ))
2176+ COMPLETE_WITH_CONST ("VERBOSE)" );
2177+ else if (HeadMatches1 ("ANALYZE" ) && TailMatches1 ("(" ))
2178+ /* "ANALYZE (" should be caught above, so assume we want columns */
2179+ COMPLETE_WITH_ATTR (prev2_wd , "" );
2180+ else if (HeadMatches1 ("ANALYZE" ))
2181+ COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_analyzables , NULL );
2182+
21612183/* BEGIN */
21622184 else if (Matches1 ("BEGIN" ))
21632185 COMPLETE_WITH_LIST6 ("WORK" , "TRANSACTION" , "ISOLATION LEVEL" , "READ" , "DEFERRABLE" , "NOT DEFERRABLE" );
@@ -2817,18 +2839,34 @@ psql_completion(const char *text, int start, int end)
28172839 else if (Matches1 ("EXECUTE" ))
28182840 COMPLETE_WITH_QUERY (Query_for_list_of_prepared_statements );
28192841
2820- /* EXPLAIN */
2821-
2822- /*
2823- * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
2824- */
2842+ /*
2843+ * EXPLAIN [ ( option [, ...] ) ] statement
2844+ * EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
2845+ */
28252846 else if (Matches1 ("EXPLAIN" ))
28262847 COMPLETE_WITH_LIST7 ("SELECT" , "INSERT" , "DELETE" , "UPDATE" , "DECLARE" ,
28272848 "ANALYZE" , "VERBOSE" );
2849+ else if (HeadMatches2 ("EXPLAIN" , "(*" ) &&
2850+ !HeadMatches2 ("EXPLAIN" , "(*)" ))
2851+ {
2852+ /*
2853+ * This fires if we're in an unfinished parenthesized option list.
2854+ * get_previous_words treats a completed parenthesized option list as
2855+ * one word, so the above test is correct.
2856+ */
2857+ if (ends_with (prev_wd , '(' ) || ends_with (prev_wd , ',' ))
2858+ COMPLETE_WITH_LIST7 ("ANALYZE" , "VERBOSE" , "COSTS" , "BUFFERS" ,
2859+ "TIMING" , "SUMMARY" , "FORMAT" );
2860+ else if (TailMatches1 ("ANALYZE|VERBOSE|COSTS|BUFFERS|TIMING|SUMMARY" ))
2861+ COMPLETE_WITH_LIST2 ("ON" , "OFF" );
2862+ else if (TailMatches1 ("FORMAT" ))
2863+ COMPLETE_WITH_LIST4 ("TEXT" , "XML" , "JSON" , "YAML" );
2864+ }
28282865 else if (Matches2 ("EXPLAIN" , "ANALYZE" ))
28292866 COMPLETE_WITH_LIST6 ("SELECT" , "INSERT" , "DELETE" , "UPDATE" , "DECLARE" ,
28302867 "VERBOSE" );
2831- else if (Matches2 ("EXPLAIN" , "VERBOSE" ) ||
2868+ else if (Matches2 ("EXPLAIN" , "(*)" ) ||
2869+ Matches2 ("EXPLAIN" , "VERBOSE" ) ||
28322870 Matches3 ("EXPLAIN" , "ANALYZE" , "VERBOSE" ))
28332871 COMPLETE_WITH_LIST5 ("SELECT" , "INSERT" , "DELETE" , "UPDATE" , "DECLARE" );
28342872
@@ -3383,31 +3421,45 @@ psql_completion(const char *text, int start, int end)
33833421 COMPLETE_WITH_CONST ("OPTIONS" );
33843422
33853423/*
3386- * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
3387- * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
3424+ * VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
3425+ * VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]
33883426 */
33893427 else if (Matches1 ("VACUUM" ))
33903428 COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables ,
33913429 " UNION SELECT 'FULL'"
33923430 " UNION SELECT 'FREEZE'"
33933431 " UNION SELECT 'ANALYZE'"
33943432 " UNION SELECT 'VERBOSE'" );
3395- else if (Matches2 ("VACUUM" , "FULL|FREEZE " ))
3433+ else if (Matches2 ("VACUUM" , "FULL" ))
33963434 COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables ,
3435+ " UNION SELECT 'FREEZE'"
33973436 " UNION SELECT 'ANALYZE'"
33983437 " UNION SELECT 'VERBOSE'" );
3399- else if (Matches3 ("VACUUM" , "FULL|FREEZE" , "ANALYZE" ))
3400- COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables ,
3401- " UNION SELECT 'VERBOSE'" );
3402- else if (Matches3 ("VACUUM" , "FULL|FREEZE" , "VERBOSE" ))
3438+ else if (Matches2 ("VACUUM" , "FREEZE" ) ||
3439+ Matches3 ("VACUUM" , "FULL" , "FREEZE" ))
34033440 COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables ,
3441+ " UNION SELECT 'VERBOSE'"
34043442 " UNION SELECT 'ANALYZE'" );
3405- else if (Matches2 ("VACUUM" , "VERBOSE" ))
3443+ else if (Matches2 ("VACUUM" , "VERBOSE" ) ||
3444+ Matches3 ("VACUUM" , "FULL|FREEZE" , "VERBOSE" ) ||
3445+ Matches4 ("VACUUM" , "FULL" , "FREEZE" , "VERBOSE" ))
34063446 COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables ,
34073447 " UNION SELECT 'ANALYZE'" );
3408- else if (Matches2 ("VACUUM" , "ANALYZE" ))
3409- COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables ,
3410- " UNION SELECT 'VERBOSE'" );
3448+ else if (HeadMatches2 ("VACUUM" , "(*" ) &&
3449+ !HeadMatches2 ("VACUUM" , "(*)" ))
3450+ {
3451+ /*
3452+ * This fires if we're in an unfinished parenthesized option list.
3453+ * get_previous_words treats a completed parenthesized option list as
3454+ * one word, so the above test is correct.
3455+ */
3456+ if (ends_with (prev_wd , '(' ) || ends_with (prev_wd , ',' ))
3457+ COMPLETE_WITH_LIST5 ("FULL" , "FREEZE" , "ANALYZE" , "VERBOSE" ,
3458+ "DISABLE_PAGE_SKIPPING" );
3459+ }
3460+ else if (HeadMatches1 ("VACUUM" ) && TailMatches1 ("(" ))
3461+ /* "VACUUM (" should be caught above, so assume we want columns */
3462+ COMPLETE_WITH_ATTR (prev2_wd , "" );
34113463 else if (HeadMatches1 ("VACUUM" ))
34123464 COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_vacuumables , NULL );
34133465
@@ -3420,11 +3472,6 @@ psql_completion(const char *text, int start, int end)
34203472 else if (Matches1 ("WITH" ))
34213473 COMPLETE_WITH_CONST ("RECURSIVE" );
34223474
3423- /* ANALYZE */
3424- /* Complete with list of appropriate relations */
3425- else if (Matches1 ("ANALYZE" ))
3426- COMPLETE_WITH_SCHEMA_QUERY (Query_for_list_of_analyzables , NULL );
3427-
34283475/* WHERE */
34293476 /* Simple case of the word before the where being the table name */
34303477 else if (TailMatches2 (MatchAny , "WHERE" ))
0 commit comments