LCOV - code coverage report
Current view: top level - src/bin/psql - tab-complete.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 976 1933 50.5 %
Date: 2021-12-04 23:09:10 Functions: 16 28 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * psql - the PostgreSQL interactive terminal
       3             :  *
       4             :  * Copyright (c) 2000-2021, PostgreSQL Global Development Group
       5             :  *
       6             :  * src/bin/psql/tab-complete.c
       7             :  */
       8             : 
       9             : /*----------------------------------------------------------------------
      10             :  * This file implements a somewhat more sophisticated readline "TAB
      11             :  * completion" in psql. It is not intended to be AI, to replace
      12             :  * learning SQL, or to relieve you from thinking about what you're
      13             :  * doing. Also it does not always give you all the syntactically legal
      14             :  * completions, only those that are the most common or the ones that
      15             :  * the programmer felt most like implementing.
      16             :  *
      17             :  * CAVEAT: Tab completion causes queries to be sent to the backend.
      18             :  * The number of tuples returned gets limited, in most default
      19             :  * installations to 1000, but if you still don't like this prospect,
      20             :  * you can turn off tab completion in your ~/.inputrc (or else
      21             :  * ${INPUTRC}) file so:
      22             :  *
      23             :  *   $if psql
      24             :  *   set disable-completion on
      25             :  *   $endif
      26             :  *
      27             :  * See `man 3 readline' or `info readline' for the full details.
      28             :  *
      29             :  * BUGS:
      30             :  * - Quotes, parentheses, and other funny characters are not handled
      31             :  *   all that gracefully.
      32             :  *----------------------------------------------------------------------
      33             :  */
      34             : 
      35             : #include "postgres_fe.h"
      36             : 
      37             : #include "input.h"
      38             : #include "tab-complete.h"
      39             : 
      40             : /* If we don't have this, we might as well forget about the whole thing: */
      41             : #ifdef USE_READLINE
      42             : 
      43             : #include <ctype.h>
      44             : #include <sys/stat.h>
      45             : 
      46             : #include "catalog/pg_am_d.h"
      47             : #include "catalog/pg_class_d.h"
      48             : #include "common.h"
      49             : #include "libpq-fe.h"
      50             : #include "pqexpbuffer.h"
      51             : #include "settings.h"
      52             : #include "stringutils.h"
      53             : 
      54             : /*
      55             :  * Ancient versions of libedit provide filename_completion_function()
      56             :  * instead of rl_filename_completion_function().  Likewise for
      57             :  * [rl_]completion_matches().
      58             :  */
      59             : #ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION
      60             : #define rl_filename_completion_function filename_completion_function
      61             : #endif
      62             : 
      63             : #ifndef HAVE_RL_COMPLETION_MATCHES
      64             : #define rl_completion_matches completion_matches
      65             : #endif
      66             : 
      67             : /*
      68             :  * Currently we assume that rl_filename_dequoting_function exists if
      69             :  * rl_filename_quoting_function does.  If that proves not to be the case,
      70             :  * we'd need to test for the former, or possibly both, in configure.
      71             :  */
      72             : #ifdef HAVE_RL_FILENAME_QUOTING_FUNCTION
      73             : #define USE_FILENAME_QUOTING_FUNCTIONS 1
      74             : #endif
      75             : 
      76             : /* word break characters */
      77             : #define WORD_BREAKS     "\t\n@$><=;|&{() "
      78             : 
      79             : /*
      80             :  * Since readline doesn't let us pass any state through to the tab completion
      81             :  * callback, we have to use this global variable to let get_previous_words()
      82             :  * get at the previous lines of the current command.  Ick.
      83             :  */
      84             : PQExpBuffer tab_completion_query_buf = NULL;
      85             : 
      86             : /*
      87             :  * In some situations, the query to find out what names are available to
      88             :  * complete with must vary depending on server version.  We handle this by
      89             :  * storing a list of queries, each tagged with the minimum server version
      90             :  * it will work for.  Each list must be stored in descending server version
      91             :  * order, so that the first satisfactory query is the one to use.
      92             :  *
      93             :  * When the query string is otherwise constant, an array of VersionedQuery
      94             :  * suffices.  Terminate the array with an entry having min_server_version = 0.
      95             :  * That entry's query string can be a query that works in all supported older
      96             :  * server versions, or NULL to give up and do no completion.
      97             :  */
      98             : typedef struct VersionedQuery
      99             : {
     100             :     int         min_server_version;
     101             :     const char *query;
     102             : } VersionedQuery;
     103             : 
     104             : /*
     105             :  * This struct is used to define "schema queries", which are custom-built
     106             :  * to obtain possibly-schema-qualified names of database objects.  There is
     107             :  * enough similarity in the structure that we don't want to repeat it each
     108             :  * time.  So we put the components of each query into this struct and
     109             :  * assemble them with the common boilerplate in _complete_from_query().
     110             :  *
     111             :  * As with VersionedQuery, we can use an array of these if the query details
     112             :  * must vary across versions.
     113             :  */
     114             : typedef struct SchemaQuery
     115             : {
     116             :     /*
     117             :      * If not zero, minimum server version this struct applies to.  If not
     118             :      * zero, there should be a following struct with a smaller minimum server
     119             :      * version; use catname == NULL in the last entry if we should do nothing.
     120             :      */
     121             :     int         min_server_version;
     122             : 
     123             :     /*
     124             :      * Name of catalog or catalogs to be queried, with alias, eg.
     125             :      * "pg_catalog.pg_class c".  Note that "pg_namespace n" will be added.
     126             :      */
     127             :     const char *catname;
     128             : 
     129             :     /*
     130             :      * Selection condition --- only rows meeting this condition are candidates
     131             :      * to display.  If catname mentions multiple tables, include the necessary
     132             :      * join condition here.  For example, this might look like "c.relkind = "
     133             :      * CppAsString2(RELKIND_RELATION).  Write NULL (not an empty string) if
     134             :      * not needed.
     135             :      */
     136             :     const char *selcondition;
     137             : 
     138             :     /*
     139             :      * Visibility condition --- which rows are visible without schema
     140             :      * qualification?  For example, "pg_catalog.pg_table_is_visible(c.oid)".
     141             :      */
     142             :     const char *viscondition;
     143             : 
     144             :     /*
     145             :      * Namespace --- name of field to join to pg_namespace.oid. For example,
     146             :      * "c.relnamespace".
     147             :      */
     148             :     const char *namespace;
     149             : 
     150             :     /*
     151             :      * Result --- the appropriately-quoted name to return, in the case of an
     152             :      * unqualified name.  For example, "pg_catalog.quote_ident(c.relname)".
     153             :      */
     154             :     const char *result;
     155             : 
     156             :     /*
     157             :      * In some cases a different result must be used for qualified names.
     158             :      * Enter that here, or write NULL if result can be used.
     159             :      */
     160             :     const char *qualresult;
     161             : } SchemaQuery;
     162             : 
     163             : 
     164             : /* Store maximum number of records we want from database queries
     165             :  * (implemented via SELECT ... LIMIT xx).
     166             :  */
     167             : static int  completion_max_records;
     168             : 
     169             : /*
     170             :  * Communication variables set by psql_completion (mostly in COMPLETE_WITH_FOO
     171             :  * macros) and then used by the completion callback functions.  Ugly but there
     172             :  * is no better way.
     173             :  */
     174             : static char completion_last_char;   /* last char of input word */
     175             : static const char *completion_charp;    /* to pass a string */
     176             : static const char *const *completion_charpp;    /* to pass a list of strings */
     177             : static const char *completion_info_charp;   /* to pass a second string */
     178             : static const char *completion_info_charp2;  /* to pass a third string */
     179             : static const VersionedQuery *completion_vquery; /* to pass a VersionedQuery */
     180             : static const SchemaQuery *completion_squery;    /* to pass a SchemaQuery */
     181             : static bool completion_case_sensitive;  /* completion is case sensitive */
     182             : static bool completion_force_quote; /* true to force-quote filenames */
     183             : 
     184             : /*
     185             :  * A few macros to ease typing. You can use these to complete the given
     186             :  * string with
     187             :  * 1) The results from a query you pass it. (Perhaps one of those below?)
     188             :  *    We support both simple and versioned queries.
     189             :  * 2) The results from a schema query you pass it.
     190             :  *    We support both simple and versioned schema queries.
     191             :  * 3) The items from a null-pointer-terminated list (with or without
     192             :  *    case-sensitive comparison); if the list is constant you can build it
     193             :  *    with COMPLETE_WITH() or COMPLETE_WITH_CS().
     194             :  * 4) The list of attributes of the given table (possibly schema-qualified).
     195             :  * 5) The list of arguments to the given function (possibly schema-qualified).
     196             :  */
     197             : #define COMPLETE_WITH_QUERY(query) \
     198             : do { \
     199             :     completion_charp = query; \
     200             :     matches = rl_completion_matches(text, complete_from_query); \
     201             : } while (0)
     202             : 
     203             : #define COMPLETE_WITH_VERSIONED_QUERY(query) \
     204             : do { \
     205             :     completion_vquery = query; \
     206             :     matches = rl_completion_matches(text, complete_from_versioned_query); \
     207             : } while (0)
     208             : 
     209             : #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
     210             : do { \
     211             :     completion_squery = &(query); \
     212             :     completion_charp = addon; \
     213             :     matches = rl_completion_matches(text, complete_from_schema_query); \
     214             : } while (0)
     215             : 
     216             : #define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \
     217             : do { \
     218             :     completion_squery = query; \
     219             :     completion_vquery = addon; \
     220             :     matches = rl_completion_matches(text, complete_from_versioned_schema_query); \
     221             : } while (0)
     222             : 
     223             : /*
     224             :  * Caution: COMPLETE_WITH_CONST is not for general-purpose use; you probably
     225             :  * want COMPLETE_WITH() with one element, instead.
     226             :  */
     227             : #define COMPLETE_WITH_CONST(cs, con) \
     228             : do { \
     229             :     completion_case_sensitive = (cs); \
     230             :     completion_charp = (con); \
     231             :     matches = rl_completion_matches(text, complete_from_const); \
     232             : } while (0)
     233             : 
     234             : #define COMPLETE_WITH_LIST_INT(cs, list) \
     235             : do { \
     236             :     completion_case_sensitive = (cs); \
     237             :     completion_charpp = (list); \
     238             :     matches = rl_completion_matches(text, complete_from_list); \
     239             : } while (0)
     240             : 
     241             : #define COMPLETE_WITH_LIST(list) COMPLETE_WITH_LIST_INT(false, list)
     242             : #define COMPLETE_WITH_LIST_CS(list) COMPLETE_WITH_LIST_INT(true, list)
     243             : 
     244             : #define COMPLETE_WITH(...) \
     245             : do { \
     246             :     static const char *const list[] = { __VA_ARGS__, NULL }; \
     247             :     COMPLETE_WITH_LIST(list); \
     248             : } while (0)
     249             : 
     250             : #define COMPLETE_WITH_CS(...) \
     251             : do { \
     252             :     static const char *const list[] = { __VA_ARGS__, NULL }; \
     253             :     COMPLETE_WITH_LIST_CS(list); \
     254             : } while (0)
     255             : 
     256             : #define COMPLETE_WITH_ATTR(relation, addon) \
     257             : do { \
     258             :     char   *_completion_schema; \
     259             :     char   *_completion_table; \
     260             : \
     261             :     _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \
     262             :                                  false, false, pset.encoding); \
     263             :     (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
     264             :                    false, false, pset.encoding); \
     265             :     _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
     266             :                                 false, false, pset.encoding); \
     267             :     if (_completion_table == NULL) \
     268             :     { \
     269             :         completion_charp = Query_for_list_of_attributes  addon; \
     270             :         completion_info_charp = relation; \
     271             :     } \
     272             :     else \
     273             :     { \
     274             :         completion_charp = Query_for_list_of_attributes_with_schema  addon; \
     275             :         completion_info_charp = _completion_table; \
     276             :         completion_info_charp2 = _completion_schema; \
     277             :     } \
     278             :     matches = rl_completion_matches(text, complete_from_query); \
     279             : } while (0)
     280             : 
     281             : #define COMPLETE_WITH_ENUM_VALUE(type) \
     282             : do { \
     283             :     char   *_completion_schema; \
     284             :     char   *_completion_type; \
     285             : \
     286             :     _completion_schema = strtokx(type, " \t\n\r", ".", "\"", 0, \
     287             :                                  false, false, pset.encoding); \
     288             :     (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
     289             :                    false, false, pset.encoding); \
     290             :     _completion_type = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
     291             :                                false, false, pset.encoding);  \
     292             :     if (_completion_type == NULL)\
     293             :     { \
     294             :         completion_charp = Query_for_list_of_enum_values; \
     295             :         completion_info_charp = type; \
     296             :     } \
     297             :     else \
     298             :     { \
     299             :         completion_charp = Query_for_list_of_enum_values_with_schema; \
     300             :         completion_info_charp = _completion_type; \
     301             :         completion_info_charp2 = _completion_schema; \
     302             :     } \
     303             :     matches = rl_completion_matches(text, complete_from_query); \
     304             : } while (0)
     305             : 
     306             : #define COMPLETE_WITH_FUNCTION_ARG(function) \
     307             : do { \
     308             :     char   *_completion_schema; \
     309             :     char   *_completion_function; \
     310             : \
     311             :     _completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \
     312             :                                  false, false, pset.encoding); \
     313             :     (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
     314             :                    false, false, pset.encoding); \
     315             :     _completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
     316             :                                    false, false, pset.encoding); \
     317             :     if (_completion_function == NULL) \
     318             :     { \
     319             :         completion_charp = Query_for_list_of_arguments; \
     320             :         completion_info_charp = function; \
     321             :     } \
     322             :     else \
     323             :     { \
     324             :         completion_charp = Query_for_list_of_arguments_with_schema; \
     325             :         completion_info_charp = _completion_function; \
     326             :         completion_info_charp2 = _completion_schema; \
     327             :     } \
     328             :     matches = rl_completion_matches(text, complete_from_query); \
     329             : } while (0)
     330             : 
     331             : /*
     332             :  * Assembly instructions for schema queries
     333             :  *
     334             :  * Note that toast tables are not included in those queries to avoid
     335             :  * unnecessary bloat in the completions generated.
     336             :  */
     337             : 
     338             : static const SchemaQuery Query_for_list_of_aggregates[] = {
     339             :     {
     340             :         .min_server_version = 110000,
     341             :         .catname = "pg_catalog.pg_proc p",
     342             :         .selcondition = "p.prokind = 'a'",
     343             :         .viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
     344             :         .namespace = "p.pronamespace",
     345             :         .result = "pg_catalog.quote_ident(p.proname)",
     346             :     },
     347             :     {
     348             :         .catname = "pg_catalog.pg_proc p",
     349             :         .selcondition = "p.proisagg",
     350             :         .viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
     351             :         .namespace = "p.pronamespace",
     352             :         .result = "pg_catalog.quote_ident(p.proname)",
     353             :     }
     354             : };
     355             : 
     356             : static const SchemaQuery Query_for_list_of_datatypes = {
     357             :     .catname = "pg_catalog.pg_type t",
     358             :     /* selcondition --- ignore table rowtypes and array types */
     359             :     .selcondition = "(t.typrelid = 0 "
     360             :     " OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
     361             :     "     FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
     362             :     "AND t.typname !~ '^_'",
     363             :     .viscondition = "pg_catalog.pg_type_is_visible(t.oid)",
     364             :     .namespace = "t.typnamespace",
     365             :     .result = "pg_catalog.format_type(t.oid, NULL)",
     366             :     .qualresult = "pg_catalog.quote_ident(t.typname)",
     367             : };
     368             : 
     369             : static const SchemaQuery Query_for_list_of_composite_datatypes = {
     370             :     .catname = "pg_catalog.pg_type t",
     371             :     /* selcondition --- only get composite types */
     372             :     .selcondition = "(SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE)
     373             :     " FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid) "
     374             :     "AND t.typname !~ '^_'",
     375             :     .viscondition = "pg_catalog.pg_type_is_visible(t.oid)",
     376             :     .namespace = "t.typnamespace",
     377             :     .result = "pg_catalog.format_type(t.oid, NULL)",
     378             :     .qualresult = "pg_catalog.quote_ident(t.typname)",
     379             : };
     380             : 
     381             : static const SchemaQuery Query_for_list_of_domains = {
     382             :     .catname = "pg_catalog.pg_type t",
     383             :     .selcondition = "t.typtype = 'd'",
     384             :     .viscondition = "pg_catalog.pg_type_is_visible(t.oid)",
     385             :     .namespace = "t.typnamespace",
     386             :     .result = "pg_catalog.quote_ident(t.typname)",
     387             : };
     388             : 
     389             : /* Note: this intentionally accepts aggregates as well as plain functions */
     390             : static const SchemaQuery Query_for_list_of_functions[] = {
     391             :     {
     392             :         .min_server_version = 110000,
     393             :         .catname = "pg_catalog.pg_proc p",
     394             :         .selcondition = "p.prokind != 'p'",
     395             :         .viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
     396             :         .namespace = "p.pronamespace",
     397             :         .result = "pg_catalog.quote_ident(p.proname)",
     398             :     },
     399             :     {
     400             :         .catname = "pg_catalog.pg_proc p",
     401             :         .viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
     402             :         .namespace = "p.pronamespace",
     403             :         .result = "pg_catalog.quote_ident(p.proname)",
     404             :     }
     405             : };
     406             : 
     407             : static const SchemaQuery Query_for_list_of_procedures[] = {
     408             :     {
     409             :         .min_server_version = 110000,
     410             :         .catname = "pg_catalog.pg_proc p",
     411             :         .selcondition = "p.prokind = 'p'",
     412             :         .viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
     413             :         .namespace = "p.pronamespace",
     414             :         .result = "pg_catalog.quote_ident(p.proname)",
     415             :     },
     416             :     {
     417             :         /* not supported in older versions */
     418             :         .catname = NULL,
     419             :     }
     420             : };
     421             : 
     422             : static const SchemaQuery Query_for_list_of_routines = {
     423             :     .catname = "pg_catalog.pg_proc p",
     424             :     .viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
     425             :     .namespace = "p.pronamespace",
     426             :     .result = "pg_catalog.quote_ident(p.proname)",
     427             : };
     428             : 
     429             : static const SchemaQuery Query_for_list_of_sequences = {
     430             :     .catname = "pg_catalog.pg_class c",
     431             :     .selcondition = "c.relkind IN (" CppAsString2(RELKIND_SEQUENCE) ")",
     432             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     433             :     .namespace = "c.relnamespace",
     434             :     .result = "pg_catalog.quote_ident(c.relname)",
     435             : };
     436             : 
     437             : static const SchemaQuery Query_for_list_of_foreign_tables = {
     438             :     .catname = "pg_catalog.pg_class c",
     439             :     .selcondition = "c.relkind IN (" CppAsString2(RELKIND_FOREIGN_TABLE) ")",
     440             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     441             :     .namespace = "c.relnamespace",
     442             :     .result = "pg_catalog.quote_ident(c.relname)",
     443             : };
     444             : 
     445             : static const SchemaQuery Query_for_list_of_tables = {
     446             :     .catname = "pg_catalog.pg_class c",
     447             :     .selcondition =
     448             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     449             :     CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
     450             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     451             :     .namespace = "c.relnamespace",
     452             :     .result = "pg_catalog.quote_ident(c.relname)",
     453             : };
     454             : 
     455             : static const SchemaQuery Query_for_list_of_partitioned_tables = {
     456             :     .catname = "pg_catalog.pg_class c",
     457             :     .selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
     458             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     459             :     .namespace = "c.relnamespace",
     460             :     .result = "pg_catalog.quote_ident(c.relname)",
     461             : };
     462             : 
     463             : static const SchemaQuery Query_for_list_of_views = {
     464             :     .catname = "pg_catalog.pg_class c",
     465             :     .selcondition = "c.relkind IN (" CppAsString2(RELKIND_VIEW) ")",
     466             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     467             :     .namespace = "c.relnamespace",
     468             :     .result = "pg_catalog.quote_ident(c.relname)",
     469             : };
     470             : 
     471             : static const SchemaQuery Query_for_list_of_matviews = {
     472             :     .catname = "pg_catalog.pg_class c",
     473             :     .selcondition = "c.relkind IN (" CppAsString2(RELKIND_MATVIEW) ")",
     474             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     475             :     .namespace = "c.relnamespace",
     476             :     .result = "pg_catalog.quote_ident(c.relname)",
     477             : };
     478             : 
     479             : static const SchemaQuery Query_for_list_of_indexes = {
     480             :     .catname = "pg_catalog.pg_class c",
     481             :     .selcondition =
     482             :     "c.relkind IN (" CppAsString2(RELKIND_INDEX) ", "
     483             :     CppAsString2(RELKIND_PARTITIONED_INDEX) ")",
     484             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     485             :     .namespace = "c.relnamespace",
     486             :     .result = "pg_catalog.quote_ident(c.relname)",
     487             : };
     488             : 
     489             : static const SchemaQuery Query_for_list_of_partitioned_indexes = {
     490             :     .catname = "pg_catalog.pg_class c",
     491             :     .selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
     492             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     493             :     .namespace = "c.relnamespace",
     494             :     .result = "pg_catalog.quote_ident(c.relname)",
     495             : };
     496             : 
     497             : 
     498             : /* All relations */
     499             : static const SchemaQuery Query_for_list_of_relations = {
     500             :     .catname = "pg_catalog.pg_class c",
     501             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     502             :     .namespace = "c.relnamespace",
     503             :     .result = "pg_catalog.quote_ident(c.relname)",
     504             : };
     505             : 
     506             : /* partitioned relations */
     507             : static const SchemaQuery Query_for_list_of_partitioned_relations = {
     508             :     .catname = "pg_catalog.pg_class c",
     509             :     .selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE)
     510             :     ", " CppAsString2(RELKIND_PARTITIONED_INDEX) ")",
     511             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     512             :     .namespace = "c.relnamespace",
     513             :     .result = "pg_catalog.quote_ident(c.relname)",
     514             : };
     515             : 
     516             : static const SchemaQuery Query_for_list_of_operator_families = {
     517             :     .catname = "pg_catalog.pg_opfamily c",
     518             :     .viscondition = "pg_catalog.pg_opfamily_is_visible(c.oid)",
     519             :     .namespace = "c.opfnamespace",
     520             :     .result = "pg_catalog.quote_ident(c.opfname)",
     521             : };
     522             : 
     523             : /* Relations supporting INSERT, UPDATE or DELETE */
     524             : static const SchemaQuery Query_for_list_of_updatables = {
     525             :     .catname = "pg_catalog.pg_class c",
     526             :     .selcondition =
     527             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     528             :     CppAsString2(RELKIND_FOREIGN_TABLE) ", "
     529             :     CppAsString2(RELKIND_VIEW) ", "
     530             :     CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
     531             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     532             :     .namespace = "c.relnamespace",
     533             :     .result = "pg_catalog.quote_ident(c.relname)",
     534             : };
     535             : 
     536             : /* Relations supporting SELECT */
     537             : static const SchemaQuery Query_for_list_of_selectables = {
     538             :     .catname = "pg_catalog.pg_class c",
     539             :     .selcondition =
     540             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     541             :     CppAsString2(RELKIND_SEQUENCE) ", "
     542             :     CppAsString2(RELKIND_VIEW) ", "
     543             :     CppAsString2(RELKIND_MATVIEW) ", "
     544             :     CppAsString2(RELKIND_FOREIGN_TABLE) ", "
     545             :     CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
     546             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     547             :     .namespace = "c.relnamespace",
     548             :     .result = "pg_catalog.quote_ident(c.relname)",
     549             : };
     550             : 
     551             : /* Relations supporting TRUNCATE */
     552             : static const SchemaQuery Query_for_list_of_truncatables = {
     553             :     .catname = "pg_catalog.pg_class c",
     554             :     .selcondition =
     555             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     556             :     CppAsString2(RELKIND_FOREIGN_TABLE) ", "
     557             :     CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
     558             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     559             :     .namespace = "c.relnamespace",
     560             :     .result = "pg_catalog.quote_ident(c.relname)",
     561             : };
     562             : 
     563             : /* Relations supporting GRANT are currently same as those supporting SELECT */
     564             : #define Query_for_list_of_grantables Query_for_list_of_selectables
     565             : 
     566             : /* Relations supporting ANALYZE */
     567             : static const SchemaQuery Query_for_list_of_analyzables = {
     568             :     .catname = "pg_catalog.pg_class c",
     569             :     .selcondition =
     570             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     571             :     CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
     572             :     CppAsString2(RELKIND_MATVIEW) ", "
     573             :     CppAsString2(RELKIND_FOREIGN_TABLE) ")",
     574             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     575             :     .namespace = "c.relnamespace",
     576             :     .result = "pg_catalog.quote_ident(c.relname)",
     577             : };
     578             : 
     579             : /* Relations supporting index creation */
     580             : static const SchemaQuery Query_for_list_of_indexables = {
     581             :     .catname = "pg_catalog.pg_class c",
     582             :     .selcondition =
     583             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     584             :     CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
     585             :     CppAsString2(RELKIND_MATVIEW) ")",
     586             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     587             :     .namespace = "c.relnamespace",
     588             :     .result = "pg_catalog.quote_ident(c.relname)",
     589             : };
     590             : 
     591             : /*
     592             :  * Relations supporting VACUUM are currently same as those supporting
     593             :  * indexing.
     594             :  */
     595             : #define Query_for_list_of_vacuumables Query_for_list_of_indexables
     596             : 
     597             : /* Relations supporting CLUSTER */
     598             : static const SchemaQuery Query_for_list_of_clusterables = {
     599             :     .catname = "pg_catalog.pg_class c",
     600             :     .selcondition =
     601             :     "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", "
     602             :     CppAsString2(RELKIND_MATVIEW) ")",
     603             :     .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
     604             :     .namespace = "c.relnamespace",
     605             :     .result = "pg_catalog.quote_ident(c.relname)",
     606             : };
     607             : 
     608             : static const SchemaQuery Query_for_list_of_constraints_with_schema = {
     609             :     .catname = "pg_catalog.pg_constraint c",
     610             :     .selcondition = "c.conrelid <> 0",
     611             :     .viscondition = "true",       /* there is no pg_constraint_is_visible */
     612             :     .namespace = "c.connamespace",
     613             :     .result = "pg_catalog.quote_ident(c.conname)",
     614             : };
     615             : 
     616             : static const SchemaQuery Query_for_list_of_statistics = {
     617             :     .catname = "pg_catalog.pg_statistic_ext s",
     618             :     .viscondition = "pg_catalog.pg_statistics_obj_is_visible(s.oid)",
     619             :     .namespace = "s.stxnamespace",
     620             :     .result = "pg_catalog.quote_ident(s.stxname)",
     621             : };
     622             : 
     623             : static const SchemaQuery Query_for_list_of_collations = {
     624             :     .catname = "pg_catalog.pg_collation c",
     625             :     .selcondition = "c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))",
     626             :     .viscondition = "pg_catalog.pg_collation_is_visible(c.oid)",
     627             :     .namespace = "c.collnamespace",
     628             :     .result = "pg_catalog.quote_ident(c.collname)",
     629             : };
     630             : 
     631             : 
     632             : /*
     633             :  * Queries to get lists of names of various kinds of things, possibly
     634             :  * restricted to names matching a partially entered name.  In these queries,
     635             :  * the first %s will be replaced by the text entered so far (suitably escaped
     636             :  * to become a SQL literal string).  %d will be replaced by the length of the
     637             :  * string (in unescaped form).  A second and third %s, if present, will be
     638             :  * replaced by a suitably-escaped version of the string provided in
     639             :  * completion_info_charp.  A fourth and fifth %s are similarly replaced by
     640             :  * completion_info_charp2.
     641             :  *
     642             :  * Beware that the allowed sequences of %s and %d are determined by
     643             :  * _complete_from_query().
     644             :  */
     645             : 
     646             : #define Query_for_list_of_attributes \
     647             : "SELECT pg_catalog.quote_ident(attname) "\
     648             : "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
     649             : " WHERE c.oid = a.attrelid "\
     650             : "   AND a.attnum > 0 "\
     651             : "   AND NOT a.attisdropped "\
     652             : "   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
     653             : "   AND (pg_catalog.quote_ident(relname)='%s' "\
     654             : "        OR '\"' || relname || '\"'='%s') "\
     655             : "   AND pg_catalog.pg_table_is_visible(c.oid)"
     656             : 
     657             : #define Query_for_list_of_attribute_numbers \
     658             : "SELECT attnum "\
     659             : "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
     660             : " WHERE c.oid = a.attrelid "\
     661             : "   AND a.attnum > 0 "\
     662             : "   AND NOT a.attisdropped "\
     663             : "   AND substring(attnum::pg_catalog.text,1,%d)='%s' "\
     664             : "   AND (pg_catalog.quote_ident(relname)='%s' "\
     665             : "        OR '\"' || relname || '\"'='%s') "\
     666             : "   AND pg_catalog.pg_table_is_visible(c.oid)"
     667             : 
     668             : #define Query_for_list_of_attributes_with_schema \
     669             : "SELECT pg_catalog.quote_ident(attname) "\
     670             : "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
     671             : " WHERE c.oid = a.attrelid "\
     672             : "   AND n.oid = c.relnamespace "\
     673             : "   AND a.attnum > 0 "\
     674             : "   AND NOT a.attisdropped "\
     675             : "   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
     676             : "   AND (pg_catalog.quote_ident(relname)='%s' "\
     677             : "        OR '\"' || relname || '\"' ='%s') "\
     678             : "   AND (pg_catalog.quote_ident(nspname)='%s' "\
     679             : "        OR '\"' || nspname || '\"' ='%s') "
     680             : 
     681             : #define Query_for_list_of_enum_values \
     682             : "SELECT pg_catalog.quote_literal(enumlabel) "\
     683             : "  FROM pg_catalog.pg_enum e, pg_catalog.pg_type t "\
     684             : " WHERE t.oid = e.enumtypid "\
     685             : "   AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\
     686             : "   AND (pg_catalog.quote_ident(typname)='%s' "\
     687             : "        OR '\"' || typname || '\"'='%s') "\
     688             : "   AND pg_catalog.pg_type_is_visible(t.oid)"
     689             : 
     690             : #define Query_for_list_of_enum_values_with_schema \
     691             : "SELECT pg_catalog.quote_literal(enumlabel) "\
     692             : "  FROM pg_catalog.pg_enum e, pg_catalog.pg_type t, pg_catalog.pg_namespace n "\
     693             : " WHERE t.oid = e.enumtypid "\
     694             : "   AND n.oid = t.typnamespace "\
     695             : "   AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\
     696             : "   AND (pg_catalog.quote_ident(typname)='%s' "\
     697             : "        OR '\"' || typname || '\"'='%s') "\
     698             : "   AND (pg_catalog.quote_ident(nspname)='%s' "\
     699             : "        OR '\"' || nspname || '\"' ='%s') "
     700             : 
     701             : #define Query_for_list_of_template_databases \
     702             : "SELECT pg_catalog.quote_ident(d.datname) "\
     703             : "  FROM pg_catalog.pg_database d "\
     704             : " WHERE substring(pg_catalog.quote_ident(d.datname),1,%d)='%s' "\
     705             : "   AND (d.datistemplate OR pg_catalog.pg_has_role(d.datdba, 'USAGE'))"
     706             : 
     707             : #define Query_for_list_of_databases \
     708             : "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
     709             : " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"
     710             : 
     711             : #define Query_for_list_of_tablespaces \
     712             : "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\
     713             : " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"
     714             : 
     715             : #define Query_for_list_of_encodings \
     716             : " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
     717             : "   FROM pg_catalog.pg_conversion "\
     718             : "  WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
     719             : 
     720             : #define Query_for_list_of_languages \
     721             : "SELECT pg_catalog.quote_ident(lanname) "\
     722             : "  FROM pg_catalog.pg_language "\
     723             : " WHERE lanname != 'internal' "\
     724             : "   AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'"
     725             : 
     726             : #define Query_for_list_of_schemas \
     727             : "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\
     728             : " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"
     729             : 
     730             : #define Query_for_list_of_alter_system_set_vars \
     731             : "SELECT name FROM "\
     732             : " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
     733             : "  WHERE context != 'internal' "\
     734             : "  UNION ALL SELECT 'all') ss "\
     735             : " WHERE substring(name,1,%d)='%s'"
     736             : 
     737             : #define Query_for_list_of_set_vars \
     738             : "SELECT name FROM "\
     739             : " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
     740             : "  WHERE context IN ('user', 'superuser') "\
     741             : "  UNION ALL SELECT 'constraints' "\
     742             : "  UNION ALL SELECT 'transaction' "\
     743             : "  UNION ALL SELECT 'session' "\
     744             : "  UNION ALL SELECT 'role' "\
     745             : "  UNION ALL SELECT 'tablespace' "\
     746             : "  UNION ALL SELECT 'all') ss "\
     747             : " WHERE substring(name,1,%d)='%s'"
     748             : 
     749             : #define Query_for_list_of_show_vars \
     750             : "SELECT name FROM "\
     751             : " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
     752             : "  UNION ALL SELECT 'session authorization' "\
     753             : "  UNION ALL SELECT 'all') ss "\
     754             : " WHERE substring(name,1,%d)='%s'"
     755             : 
     756             : #define Query_for_list_of_roles \
     757             : " SELECT pg_catalog.quote_ident(rolname) "\
     758             : "   FROM pg_catalog.pg_roles "\
     759             : "  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"
     760             : 
     761             : #define Query_for_list_of_grant_roles \
     762             : " SELECT pg_catalog.quote_ident(rolname) "\
     763             : "   FROM pg_catalog.pg_roles "\
     764             : "  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\
     765             : " UNION ALL SELECT 'PUBLIC'"\
     766             : " UNION ALL SELECT 'CURRENT_ROLE'"\
     767             : " UNION ALL SELECT 'CURRENT_USER'"\
     768             : " UNION ALL SELECT 'SESSION_USER'"
     769             : 
     770             : /* the silly-looking length condition is just to eat up the current word */
     771             : #define Query_for_index_of_table \
     772             : "SELECT pg_catalog.quote_ident(c2.relname) "\
     773             : "  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
     774             : " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
     775             : "       and (%d = pg_catalog.length('%s'))"\
     776             : "       and pg_catalog.quote_ident(c1.relname)='%s'"\
     777             : "       and pg_catalog.pg_table_is_visible(c2.oid)"
     778             : 
     779             : #define Query_for_unique_index_of_table \
     780             : Query_for_index_of_table \
     781             : "       and i.indisunique"
     782             : 
     783             : /* the silly-looking length condition is just to eat up the current word */
     784             : #define Query_for_constraint_of_table \
     785             : "SELECT pg_catalog.quote_ident(conname) "\
     786             : "  FROM pg_catalog.pg_class c1, pg_catalog.pg_constraint con "\
     787             : " WHERE c1.oid=conrelid and (%d = pg_catalog.length('%s'))"\
     788             : "       and pg_catalog.quote_ident(c1.relname)='%s'"\
     789             : "       and pg_catalog.pg_table_is_visible(c1.oid)"
     790             : 
     791             : #define Query_for_all_table_constraints \
     792             : "SELECT pg_catalog.quote_ident(conname) "\
     793             : "  FROM pg_catalog.pg_constraint c "\
     794             : " WHERE c.conrelid <> 0 "
     795             : 
     796             : /* the silly-looking length condition is just to eat up the current word */
     797             : #define Query_for_constraint_of_type \
     798             : "SELECT pg_catalog.quote_ident(conname) "\
     799             : "  FROM pg_catalog.pg_type t, pg_catalog.pg_constraint con "\
     800             : " WHERE t.oid=contypid and (%d = pg_catalog.length('%s'))"\
     801             : "       and pg_catalog.quote_ident(t.typname)='%s'"\
     802             : "       and pg_catalog.pg_type_is_visible(t.oid)"
     803             : 
     804             : /* the silly-looking length condition is just to eat up the current word */
     805             : #define Query_for_list_of_tables_for_constraint \
     806             : "SELECT pg_catalog.quote_ident(relname) "\
     807             : "  FROM pg_catalog.pg_class"\
     808             : " WHERE (%d = pg_catalog.length('%s'))"\
     809             : "   AND oid IN "\
     810             : "       (SELECT conrelid FROM pg_catalog.pg_constraint "\
     811             : "         WHERE pg_catalog.quote_ident(conname)='%s')"
     812             : 
     813             : /* the silly-looking length condition is just to eat up the current word */
     814             : #define Query_for_rule_of_table \
     815             : "SELECT pg_catalog.quote_ident(rulename) "\
     816             : "  FROM pg_catalog.pg_class c1, pg_catalog.pg_rewrite "\
     817             : " WHERE c1.oid=ev_class and (%d = pg_catalog.length('%s'))"\
     818             : "       and pg_catalog.quote_ident(c1.relname)='%s'"\
     819             : "       and pg_catalog.pg_table_is_visible(c1.oid)"
     820             : 
     821             : /* the silly-looking length condition is just to eat up the current word */
     822             : #define Query_for_list_of_tables_for_rule \
     823             : "SELECT pg_catalog.quote_ident(relname) "\
     824             : "  FROM pg_catalog.pg_class"\
     825             : " WHERE (%d = pg_catalog.length('%s'))"\
     826             : "   AND oid IN "\
     827             : "       (SELECT ev_class FROM pg_catalog.pg_rewrite "\
     828             : "         WHERE pg_catalog.quote_ident(rulename)='%s')"
     829             : 
     830             : /* the silly-looking length condition is just to eat up the current word */
     831             : #define Query_for_trigger_of_table \
     832             : "SELECT pg_catalog.quote_ident(tgname) "\
     833             : "  FROM pg_catalog.pg_class c1, pg_catalog.pg_trigger "\
     834             : " WHERE c1.oid=tgrelid and (%d = pg_catalog.length('%s'))"\
     835             : "       and pg_catalog.quote_ident(c1.relname)='%s'"\
     836             : "       and pg_catalog.pg_table_is_visible(c1.oid)"\
     837             : "       and not tgisinternal"
     838             : 
     839             : /* the silly-looking length condition is just to eat up the current word */
     840             : #define Query_for_list_of_tables_for_trigger \
     841             : "SELECT pg_catalog.quote_ident(relname) "\
     842             : "  FROM pg_catalog.pg_class"\
     843             : " WHERE (%d = pg_catalog.length('%s'))"\
     844             : "   AND oid IN "\
     845             : "       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
     846             : "         WHERE pg_catalog.quote_ident(tgname)='%s')"
     847             : 
     848             : #define Query_for_list_of_ts_configurations \
     849             : "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
     850             : " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
     851             : 
     852             : #define Query_for_list_of_ts_dictionaries \
     853             : "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\
     854             : " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'"
     855             : 
     856             : #define Query_for_list_of_ts_parsers \
     857             : "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\
     858             : " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'"
     859             : 
     860             : #define Query_for_list_of_ts_templates \
     861             : "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\
     862             : " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'"
     863             : 
     864             : #define Query_for_list_of_fdws \
     865             : " SELECT pg_catalog.quote_ident(fdwname) "\
     866             : "   FROM pg_catalog.pg_foreign_data_wrapper "\
     867             : "  WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'"
     868             : 
     869             : #define Query_for_list_of_servers \
     870             : " SELECT pg_catalog.quote_ident(srvname) "\
     871             : "   FROM pg_catalog.pg_foreign_server "\
     872             : "  WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'"
     873             : 
     874             : #define Query_for_list_of_user_mappings \
     875             : " SELECT pg_catalog.quote_ident(usename) "\
     876             : "   FROM pg_catalog.pg_user_mappings "\
     877             : "  WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"
     878             : 
     879             : #define Query_for_list_of_access_methods \
     880             : " SELECT pg_catalog.quote_ident(amname) "\
     881             : "   FROM pg_catalog.pg_am "\
     882             : "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
     883             : 
     884             : #define Query_for_list_of_index_access_methods \
     885             : " SELECT pg_catalog.quote_ident(amname) "\
     886             : "   FROM pg_catalog.pg_am "\
     887             : "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s' AND "\
     888             : "   amtype=" CppAsString2(AMTYPE_INDEX)
     889             : 
     890             : #define Query_for_list_of_table_access_methods \
     891             : " SELECT pg_catalog.quote_ident(amname) "\
     892             : "   FROM pg_catalog.pg_am "\
     893             : "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s' AND "\
     894             : "   amtype=" CppAsString2(AMTYPE_TABLE)
     895             : 
     896             : /* the silly-looking length condition is just to eat up the current word */
     897             : #define Query_for_list_of_arguments \
     898             : "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
     899             : "  FROM pg_catalog.pg_proc "\
     900             : " WHERE (%d = pg_catalog.length('%s'))"\
     901             : "   AND (pg_catalog.quote_ident(proname)='%s'"\
     902             : "        OR '\"' || proname || '\"'='%s') "\
     903             : "   AND (pg_catalog.pg_function_is_visible(pg_proc.oid))"
     904             : 
     905             : /* the silly-looking length condition is just to eat up the current word */
     906             : #define Query_for_list_of_arguments_with_schema \
     907             : "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
     908             : "  FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
     909             : " WHERE (%d = pg_catalog.length('%s'))"\
     910             : "   AND n.oid = p.pronamespace "\
     911             : "   AND (pg_catalog.quote_ident(proname)='%s' "\
     912             : "        OR '\"' || proname || '\"' ='%s') "\
     913             : "   AND (pg_catalog.quote_ident(nspname)='%s' "\
     914             : "        OR '\"' || nspname || '\"' ='%s') "
     915             : 
     916             : #define Query_for_list_of_extensions \
     917             : " SELECT pg_catalog.quote_ident(extname) "\
     918             : "   FROM pg_catalog.pg_extension "\
     919             : "  WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'"
     920             : 
     921             : #define Query_for_list_of_available_extensions \
     922             : " SELECT pg_catalog.quote_ident(name) "\
     923             : "   FROM pg_catalog.pg_available_extensions "\
     924             : "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL"
     925             : 
     926             : /* the silly-looking length condition is just to eat up the current word */
     927             : #define Query_for_list_of_available_extension_versions \
     928             : " SELECT pg_catalog.quote_ident(version) "\
     929             : "   FROM pg_catalog.pg_available_extension_versions "\
     930             : "  WHERE (%d = pg_catalog.length('%s'))"\
     931             : "    AND pg_catalog.quote_ident(name)='%s'"
     932             : 
     933             : /* the silly-looking length condition is just to eat up the current word */
     934             : #define Query_for_list_of_available_extension_versions_with_TO \
     935             : " SELECT 'TO ' || pg_catalog.quote_ident(version) "\
     936             : "   FROM pg_catalog.pg_available_extension_versions "\
     937             : "  WHERE (%d = pg_catalog.length('%s'))"\
     938             : "    AND pg_catalog.quote_ident(name)='%s'"
     939             : 
     940             : #define Query_for_list_of_prepared_statements \
     941             : " SELECT pg_catalog.quote_ident(name) "\
     942             : "   FROM pg_catalog.pg_prepared_statements "\
     943             : "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"
     944             : 
     945             : #define Query_for_list_of_event_triggers \
     946             : " SELECT pg_catalog.quote_ident(evtname) "\
     947             : "   FROM pg_catalog.pg_event_trigger "\
     948             : "  WHERE substring(pg_catalog.quote_ident(evtname),1,%d)='%s'"
     949             : 
     950             : #define Query_for_list_of_tablesample_methods \
     951             : " SELECT pg_catalog.quote_ident(proname) "\
     952             : "   FROM pg_catalog.pg_proc "\
     953             : "  WHERE prorettype = 'pg_catalog.tsm_handler'::pg_catalog.regtype AND "\
     954             : "        proargtypes[0] = 'pg_catalog.internal'::pg_catalog.regtype AND "\
     955             : "        substring(pg_catalog.quote_ident(proname),1,%d)='%s'"
     956             : 
     957             : #define Query_for_list_of_policies \
     958             : " SELECT pg_catalog.quote_ident(polname) "\
     959             : "   FROM pg_catalog.pg_policy "\
     960             : "  WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'"
     961             : 
     962             : #define Query_for_list_of_tables_for_policy \
     963             : "SELECT pg_catalog.quote_ident(relname) "\
     964             : "  FROM pg_catalog.pg_class"\
     965             : " WHERE (%d = pg_catalog.length('%s'))"\
     966             : "   AND oid IN "\
     967             : "       (SELECT polrelid FROM pg_catalog.pg_policy "\
     968             : "         WHERE pg_catalog.quote_ident(polname)='%s')"
     969             : 
     970             : #define Query_for_enum \
     971             : " SELECT name FROM ( "\
     972             : "   SELECT pg_catalog.quote_ident(pg_catalog.unnest(enumvals)) AS name "\
     973             : "     FROM pg_catalog.pg_settings "\
     974             : "    WHERE pg_catalog.lower(name)=pg_catalog.lower('%s') "\
     975             : "    UNION ALL " \
     976             : "   SELECT 'DEFAULT' ) ss "\
     977             : "  WHERE pg_catalog.substring(name,1,%%d)='%%s'"
     978             : 
     979             : /* the silly-looking length condition is just to eat up the current word */
     980             : #define Query_for_partition_of_table \
     981             : "SELECT pg_catalog.quote_ident(c2.relname) "\
     982             : "  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i"\
     983             : " WHERE c1.oid=i.inhparent and i.inhrelid=c2.oid"\
     984             : "       and (%d = pg_catalog.length('%s'))"\
     985             : "       and pg_catalog.quote_ident(c1.relname)='%s'"\
     986             : "       and pg_catalog.pg_table_is_visible(c2.oid)"\
     987             : "       and c2.relispartition = 'true'"
     988             : 
     989             : #define Query_for_list_of_cursors \
     990             : " SELECT pg_catalog.quote_ident(name) "\
     991             : "   FROM pg_catalog.pg_cursors "\
     992             : "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"
     993             : 
     994             : /*
     995             :  * These object types were introduced later than our support cutoff of
     996             :  * server version 7.4.  We use the VersionedQuery infrastructure so that
     997             :  * we don't send certain-to-fail queries to older servers.
     998             :  */
     999             : 
    1000             : static const VersionedQuery Query_for_list_of_publications[] = {
    1001             :     {100000,
    1002             :         " SELECT pg_catalog.quote_ident(pubname) "
    1003             :         "   FROM pg_catalog.pg_publication "
    1004             :         "  WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'"
    1005             :     },
    1006             :     {0, NULL}
    1007             : };
    1008             : 
    1009             : static const VersionedQuery Query_for_list_of_subscriptions[] = {
    1010             :     {100000,
    1011             :         " SELECT pg_catalog.quote_ident(s.subname) "
    1012             :         "   FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "
    1013             :         "  WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' "
    1014             :         "    AND d.datname = pg_catalog.current_database() "
    1015             :         "    AND s.subdbid = d.oid"
    1016             :     },
    1017             :     {0, NULL}
    1018             : };
    1019             : 
    1020             : /*
    1021             :  * This is a list of all "things" in Pgsql, which can show up after CREATE or
    1022             :  * DROP; and there is also a query to get a list of them.
    1023             :  */
    1024             : 
    1025             : typedef struct
    1026             : {
    1027             :     const char *name;
    1028             :     const char *query;          /* simple query, or NULL */
    1029             :     const VersionedQuery *vquery;   /* versioned query, or NULL */
    1030             :     const SchemaQuery *squery;  /* schema query, or NULL */
    1031             :     const bits32 flags;         /* visibility flags, see below */
    1032             : } pgsql_thing_t;
    1033             : 
    1034             : #define THING_NO_CREATE     (1 << 0)  /* should not show up after CREATE */
    1035             : #define THING_NO_DROP       (1 << 1)  /* should not show up after DROP */
    1036             : #define THING_NO_ALTER      (1 << 2)  /* should not show up after ALTER */
    1037             : #define THING_NO_SHOW       (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)
    1038             : 
    1039             : static const pgsql_thing_t words_after_create[] = {
    1040             :     {"ACCESS METHOD", NULL, NULL, NULL, THING_NO_ALTER},
    1041             :     {"AGGREGATE", NULL, NULL, Query_for_list_of_aggregates},
    1042             :     {"CAST", NULL, NULL, NULL}, /* Casts have complex structures for names, so
    1043             :                                  * skip it */
    1044             :     {"COLLATION", NULL, NULL, &Query_for_list_of_collations},
    1045             : 
    1046             :     /*
    1047             :      * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
    1048             :      * to be used only by pg_dump.
    1049             :      */
    1050             :     {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, NULL, THING_NO_SHOW},
    1051             :     {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
    1052             :     {"DATABASE", Query_for_list_of_databases},
    1053             :     {"DEFAULT PRIVILEGES", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
    1054             :     {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, THING_NO_SHOW},
    1055             :     {"DOMAIN", NULL, NULL, &Query_for_list_of_domains},
    1056             :     {"EVENT TRIGGER", NULL, NULL, NULL},
    1057             :     {"EXTENSION", Query_for_list_of_extensions},
    1058             :     {"FOREIGN DATA WRAPPER", NULL, NULL, NULL},
    1059             :     {"FOREIGN TABLE", NULL, NULL, NULL},
    1060             :     {"FUNCTION", NULL, NULL, Query_for_list_of_functions},
    1061             :     {"GROUP", Query_for_list_of_roles},
    1062             :     {"INDEX", NULL, NULL, &Query_for_list_of_indexes},
    1063             :     {"LANGUAGE", Query_for_list_of_languages},
    1064             :     {"LARGE OBJECT", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
    1065             :     {"MATERIALIZED VIEW", NULL, NULL, &Query_for_list_of_matviews},
    1066             :     {"OPERATOR", NULL, NULL, NULL}, /* Querying for this is probably not such
    1067             :                                      * a good idea. */
    1068             :     {"OR REPLACE", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},
    1069             :     {"OWNED", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_ALTER},    /* for DROP OWNED BY ... */
    1070             :     {"PARSER", Query_for_list_of_ts_parsers, NULL, NULL, THING_NO_SHOW},
    1071             :     {"POLICY", NULL, NULL, NULL},
    1072             :     {"PROCEDURE", NULL, NULL, Query_for_list_of_procedures},
    1073             :     {"PUBLICATION", NULL, Query_for_list_of_publications},
    1074             :     {"ROLE", Query_for_list_of_roles},
    1075             :     {"ROUTINE", NULL, NULL, &Query_for_list_of_routines, THING_NO_CREATE},
    1076             :     {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
    1077             :     {"SCHEMA", Query_for_list_of_schemas},
    1078             :     {"SEQUENCE", NULL, NULL, &Query_for_list_of_sequences},
    1079             :     {"SERVER", Query_for_list_of_servers},
    1080             :     {"STATISTICS", NULL, NULL, &Query_for_list_of_statistics},
    1081             :     {"SUBSCRIPTION", NULL, Query_for_list_of_subscriptions},
    1082             :     {"SYSTEM", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
    1083             :     {"TABLE", NULL, NULL, &Query_for_list_of_tables},
    1084             :     {"TABLESPACE", Query_for_list_of_tablespaces},
    1085             :     {"TEMP", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE
    1086             :                                                                  * ... */
    1087             :     {"TEMPLATE", Query_for_list_of_ts_templates, NULL, NULL, THING_NO_SHOW},
    1088             :     {"TEMPORARY", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},  /* for CREATE TEMPORARY
    1089             :                                                                          * TABLE ... */
    1090             :     {"TEXT SEARCH", NULL, NULL, NULL},
    1091             :     {"TRANSFORM", NULL, NULL, NULL, THING_NO_ALTER},
    1092             :     {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s' AND NOT tgisinternal"},
    1093             :     {"TYPE", NULL, NULL, &Query_for_list_of_datatypes},
    1094             :     {"UNIQUE", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE
    1095             :                                                                      * INDEX ... */
    1096             :     {"UNLOGGED", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED
    1097             :                                                                      * TABLE ... */
    1098             :     {"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
    1099             :     {"USER MAPPING FOR", NULL, NULL, NULL},
    1100             :     {"VIEW", NULL, NULL, &Query_for_list_of_views},
    1101             :     {NULL}                      /* end of list */
    1102             : };
    1103             : 
    1104             : /* Storage parameters for CREATE TABLE and ALTER TABLE */
    1105             : static const char *const table_storage_parameters[] = {
    1106             :     "autovacuum_analyze_scale_factor",
    1107             :     "autovacuum_analyze_threshold",
    1108             :     "autovacuum_enabled",
    1109             :     "autovacuum_freeze_max_age",
    1110             :     "autovacuum_freeze_min_age",
    1111             :     "autovacuum_freeze_table_age",
    1112             :     "autovacuum_multixact_freeze_max_age",
    1113             :     "autovacuum_multixact_freeze_min_age",
    1114             :     "autovacuum_multixact_freeze_table_age",
    1115             :     "autovacuum_vacuum_cost_delay",
    1116             :     "autovacuum_vacuum_cost_limit",
    1117             :     "autovacuum_vacuum_insert_scale_factor",
    1118             :     "autovacuum_vacuum_insert_threshold",
    1119             :     "autovacuum_vacuum_scale_factor",
    1120             :     "autovacuum_vacuum_threshold",
    1121             :     "fillfactor",
    1122             :     "log_autovacuum_min_duration",
    1123             :     "parallel_workers",
    1124             :     "toast.autovacuum_enabled",
    1125             :     "toast.autovacuum_freeze_max_age",
    1126             :     "toast.autovacuum_freeze_min_age",
    1127             :     "toast.autovacuum_freeze_table_age",
    1128             :     "toast.autovacuum_multixact_freeze_max_age",
    1129             :     "toast.autovacuum_multixact_freeze_min_age",
    1130             :     "toast.autovacuum_multixact_freeze_table_age",
    1131             :     "toast.autovacuum_vacuum_cost_delay",
    1132             :     "toast.autovacuum_vacuum_cost_limit",
    1133             :     "toast.autovacuum_vacuum_insert_scale_factor",
    1134             :     "toast.autovacuum_vacuum_insert_threshold",
    1135             :     "toast.autovacuum_vacuum_scale_factor",
    1136             :     "toast.autovacuum_vacuum_threshold",
    1137             :     "toast.log_autovacuum_min_duration",
    1138             :     "toast.vacuum_index_cleanup",
    1139             :     "toast.vacuum_truncate",
    1140             :     "toast_tuple_target",
    1141             :     "user_catalog_table",
    1142             :     "vacuum_index_cleanup",
    1143             :     "vacuum_truncate",
    1144             :     NULL
    1145             : };
    1146             : 
    1147             : 
    1148             : /* Forward declaration of functions */
    1149             : static char **psql_completion(const char *text, int start, int end);
    1150             : static char *create_command_generator(const char *text, int state);
    1151             : static char *drop_command_generator(const char *text, int state);
    1152             : static char *alter_command_generator(const char *text, int state);
    1153             : static char *complete_from_query(const char *text, int state);
    1154             : static char *complete_from_versioned_query(const char *text, int state);
    1155             : static char *complete_from_schema_query(const char *text, int state);
    1156             : static char *complete_from_versioned_schema_query(const char *text, int state);
    1157             : static char *_complete_from_query(const char *simple_query,
    1158             :                                   const SchemaQuery *schema_query,
    1159             :                                   const char *text, int state);
    1160             : static char *complete_from_list(const char *text, int state);
    1161             : static char *complete_from_const(const char *text, int state);
    1162             : static void append_variable_names(char ***varnames, int *nvars,
    1163             :                                   int *maxvars, const char *varname,
    1164             :                                   const char *prefix, const char *suffix);
    1165             : static char **complete_from_variables(const char *text,
    1166             :                                       const char *prefix, const char *suffix, bool need_value);
    1167             : static char *complete_from_files(const char *text, int state);
    1168             : 
    1169             : static char *pg_strdup_keyword_case(const char *s, const char *ref);
    1170             : static char *escape_string(const char *text);
    1171             : static PGresult *exec_query(const char *query);
    1172             : 
    1173             : static char **get_previous_words(int point, char **buffer, int *nwords);
    1174             : 
    1175             : static char *get_guctype(const char *varname);
    1176             : 
    1177             : #ifdef USE_FILENAME_QUOTING_FUNCTIONS
    1178             : static char *quote_file_name(char *fname, int match_type, char *quote_pointer);
    1179             : static char *dequote_file_name(char *fname, int quote_char);
    1180             : #endif
    1181             : 
    1182             : 
    1183             : /*
    1184             :  * Initialize the readline library for our purposes.
    1185             :  */
    1186             : void
    1187           2 : initialize_readline(void)
    1188             : {
    1189           2 :     rl_readline_name = (char *) pset.progname;
    1190           2 :     rl_attempted_completion_function = psql_completion;
    1191             : 
    1192             : #ifdef USE_FILENAME_QUOTING_FUNCTIONS
    1193           2 :     rl_filename_quoting_function = quote_file_name;
    1194           2 :     rl_filename_dequoting_function = dequote_file_name;
    1195             : #endif
    1196             : 
    1197           2 :     rl_basic_word_break_characters = WORD_BREAKS;
    1198             : 
    1199             :     /*
    1200             :      * We should include '"' in rl_completer_quote_characters too, but that
    1201             :      * will require some upgrades to how we handle quoted identifiers, so
    1202             :      * that's for another day.
    1203             :      */
    1204           2 :     rl_completer_quote_characters = "'";
    1205             : 
    1206             :     /*
    1207             :      * Set rl_filename_quote_characters to "all possible characters",
    1208             :      * otherwise Readline will skip filename quoting if it thinks a filename
    1209             :      * doesn't need quoting.  Readline actually interprets this as bytes, so
    1210             :      * there are no encoding considerations here.
    1211             :      */
    1212             : #ifdef HAVE_RL_FILENAME_QUOTE_CHARACTERS
    1213             :     {
    1214           2 :         unsigned char *fqc = (unsigned char *) pg_malloc(256);
    1215             : 
    1216         512 :         for (int i = 0; i < 255; i++)
    1217         510 :             fqc[i] = (unsigned char) (i + 1);
    1218           2 :         fqc[255] = '\0';
    1219           2 :         rl_filename_quote_characters = (const char *) fqc;
    1220             :     }
    1221             : #endif
    1222             : 
    1223           2 :     completion_max_records = 1000;
    1224             : 
    1225             :     /*
    1226             :      * There is a variable rl_completion_query_items for this but apparently
    1227             :      * it's not defined everywhere.
    1228             :      */
    1229           2 : }
    1230             : 
    1231             : /*
    1232             :  * Check if 'word' matches any of the '|'-separated strings in 'pattern',
    1233             :  * using case-insensitive or case-sensitive comparisons.
    1234             :  *
    1235             :  * If pattern is NULL, it's a wild card that matches any word.
    1236             :  * If pattern begins with '!', the result is negated, ie we check that 'word'
    1237             :  * does *not* match any alternative appearing in the rest of 'pattern'.
    1238             :  * Any alternative can contain '*' which is a wild card, i.e., it can match
    1239             :  * any substring; however, we allow at most one '*' per alternative.
    1240             :  *
    1241             :  * For readability, callers should use the macros MatchAny and MatchAnyExcept
    1242             :  * to invoke those two special cases for 'pattern'.  (But '|' and '*' must
    1243             :  * just be written directly in patterns.)
    1244             :  */
    1245             : #define MatchAny  NULL
    1246             : #define MatchAnyExcept(pattern)  ("!" pattern)
    1247             : 
    1248             : static bool
    1249        2882 : word_matches(const char *pattern,
    1250             :              const char *word,
    1251             :              bool case_sensitive)
    1252             : {
    1253             :     size_t      wordlen;
    1254             : 
    1255             : #define cimatch(s1, s2, n) \
    1256             :     (case_sensitive ? strncmp(s1, s2, n) == 0 : pg_strncasecmp(s1, s2, n) == 0)
    1257             : 
    1258             :     /* NULL pattern matches anything. */
    1259        2882 :     if (pattern == NULL)
    1260          18 :         return true;
    1261             : 
    1262             :     /* Handle negated patterns from the MatchAnyExcept macro. */
    1263        2864 :     if (*pattern == '!')
    1264           0 :         return !word_matches(pattern + 1, word, case_sensitive);
    1265             : 
    1266             :     /* Else consider each alternative in the pattern. */
    1267        2864 :     wordlen = strlen(word);
    1268             :     for (;;)
    1269         298 :     {
    1270        3162 :         const char *star = NULL;
    1271             :         const char *c;
    1272             : 
    1273             :         /* Find end of current alternative, and locate any wild card. */
    1274        3162 :         c = pattern;
    1275       20800 :         while (*c != '\0' && *c != '|')
    1276             :         {
    1277       17638 :             if (*c == '*')
    1278         140 :                 star = c;
    1279       17638 :             c++;
    1280             :         }
    1281             :         /* Was there a wild card? */
    1282        3162 :         if (star)
    1283             :         {
    1284             :             /* Yes, wildcard match? */
    1285         140 :             size_t      beforelen = star - pattern,
    1286         140 :                         afterlen = c - star - 1;
    1287             : 
    1288         280 :             if (wordlen >= (beforelen + afterlen) &&
    1289         156 :                 cimatch(word, pattern, beforelen) &&
    1290           8 :                 cimatch(word + wordlen - afterlen, star + 1, afterlen))
    1291           8 :                 return true;
    1292             :         }
    1293             :         else
    1294             :         {
    1295             :             /* No, plain match? */
    1296        3628 :             if (wordlen == (c - pattern) &&
    1297         606 :                 cimatch(word, pattern, wordlen))
    1298          30 :                 return true;
    1299             :         }
    1300             :         /* Out of alternatives? */
    1301        3124 :         if (*c == '\0')
    1302        2826 :             break;
    1303             :         /* Nope, try next alternative. */
    1304         298 :         pattern = c + 1;
    1305             :     }
    1306             : 
    1307        2826 :     return false;
    1308             : }
    1309             : 
    1310             : /*
    1311             :  * Implementation of TailMatches and TailMatchesCS macros: do the last N words
    1312             :  * in previous_words match the variadic arguments?
    1313             :  *
    1314             :  * The array indexing might look backwards, but remember that
    1315             :  * previous_words[0] contains the *last* word on the line, not the first.
    1316             :  */
    1317             : static bool
    1318        1850 : TailMatchesImpl(bool case_sensitive,
    1319             :                 int previous_words_count, char **previous_words,
    1320             :                 int narg,...)
    1321             : {
    1322             :     va_list     args;
    1323             : 
    1324        1850 :     if (previous_words_count < narg)
    1325        1062 :         return false;
    1326             : 
    1327         788 :     va_start(args, narg);
    1328             : 
    1329         820 :     for (int argno = 0; argno < narg; argno++)
    1330             :     {
    1331         798 :         const char *arg = va_arg(args, const char *);
    1332             : 
    1333         798 :         if (!word_matches(arg, previous_words[narg - argno - 1],
    1334             :                           case_sensitive))
    1335             :         {
    1336         766 :             va_end(args);
    1337         766 :             return false;
    1338             :         }
    1339             :     }
    1340             : 
    1341          22 :     va_end(args);
    1342             : 
    1343          22 :     return true;
    1344             : }
    1345             : 
    1346             : /*
    1347             :  * Implementation of Matches and MatchesCS macros: do all of the words
    1348             :  * in previous_words match the variadic arguments?
    1349             :  */
    1350             : static bool
    1351        8412 : MatchesImpl(bool case_sensitive,
    1352             :             int previous_words_count, char **previous_words,
    1353             :             int narg,...)
    1354             : {
    1355             :     va_list     args;
    1356             : 
    1357        8412 :     if (previous_words_count != narg)
    1358        7084 :         return false;
    1359             : 
    1360        1328 :     va_start(args, narg);
    1361             : 
    1362        1352 :     for (int argno = 0; argno < narg; argno++)
    1363             :     {
    1364        1344 :         const char *arg = va_arg(args, const char *);
    1365             : 
    1366        1344 :         if (!word_matches(arg, previous_words[narg - argno - 1],
    1367             :                           case_sensitive))
    1368             :         {
    1369        1320 :             va_end(args);
    1370        1320 :             return false;
    1371             :         }
    1372             :     }
    1373             : 
    1374           8 :     va_end(args);
    1375             : 
    1376           8 :     return true;
    1377             : }
    1378             : 
    1379             : /*
    1380             :  * Implementation of HeadMatches and HeadMatchesCS macros: do the first N
    1381             :  * words in previous_words match the variadic arguments?
    1382             :  */
    1383             : static bool
    1384        1118 : HeadMatchesImpl(bool case_sensitive,
    1385             :                 int previous_words_count, char **previous_words,
    1386             :                 int narg,...)
    1387             : {
    1388             :     va_list     args;
    1389             : 
    1390        1118 :     if (previous_words_count < narg)
    1391         378 :         return false;
    1392             : 
    1393         740 :     va_start(args, narg);
    1394             : 
    1395         740 :     for (int argno = 0; argno < narg; argno++)
    1396             :     {
    1397         740 :         const char *arg = va_arg(args, const char *);
    1398             : 
    1399         740 :         if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
    1400             :                           case_sensitive))
    1401             :         {
    1402         740 :             va_end(args);
    1403         740 :             return false;
    1404             :         }
    1405             :     }
    1406             : 
    1407           0 :     va_end(args);
    1408             : 
    1409           0 :     return true;
    1410             : }
    1411             : 
    1412             : /*
    1413             :  * Check if the final character of 's' is 'c'.
    1414             :  */
    1415             : static bool
    1416           0 : ends_with(const char *s, char c)
    1417             : {
    1418           0 :     size_t      length = strlen(s);
    1419             : 
    1420           0 :     return (length > 0 && s[length - 1] == c);
    1421             : }
    1422             : 
    1423             : /*
    1424             :  * The completion function.
    1425             :  *
    1426             :  * According to readline spec this gets passed the text entered so far and its
    1427             :  * start and end positions in the readline buffer. The return value is some
    1428             :  * partially obscure list format that can be generated by readline's
    1429             :  * rl_completion_matches() function, so we don't have to worry about it.
    1430             :  */
    1431             : static char **
    1432          28 : psql_completion(const char *text, int start, int end)
    1433             : {
    1434             :     /* This is the variable we'll return. */
    1435          28 :     char      **matches = NULL;
    1436             : 
    1437             :     /* Workspace for parsed words. */
    1438             :     char       *words_buffer;
    1439             : 
    1440             :     /* This array will contain pointers to parsed words. */
    1441             :     char      **previous_words;
    1442             : 
    1443             :     /* The number of words found on the input line. */
    1444             :     int         previous_words_count;
    1445             : 
    1446             :     /*
    1447             :      * For compactness, we use these macros to reference previous_words[].
    1448             :      * Caution: do not access a previous_words[] entry without having checked
    1449             :      * previous_words_count to be sure it's valid.  In most cases below, that
    1450             :      * check is implicit in a TailMatches() or similar macro, but in some
    1451             :      * places we have to check it explicitly.
    1452             :      */
    1453             : #define prev_wd   (previous_words[0])
    1454             : #define prev2_wd  (previous_words[1])
    1455             : #define prev3_wd  (previous_words[2])
    1456             : #define prev4_wd  (previous_words[3])
    1457             : #define prev5_wd  (previous_words[4])
    1458             : #define prev6_wd  (previous_words[5])
    1459             : #define prev7_wd  (previous_words[6])
    1460             : #define prev8_wd  (previous_words[7])
    1461             : #define prev9_wd  (previous_words[8])
    1462             : 
    1463             :     /* Match the last N words before point, case-insensitively. */
    1464             : #define TailMatches(...) \
    1465             :     TailMatchesImpl(false, previous_words_count, previous_words, \
    1466             :                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
    1467             : 
    1468             :     /* Match the last N words before point, case-sensitively. */
    1469             : #define TailMatchesCS(...) \
    1470             :     TailMatchesImpl(true, previous_words_count, previous_words, \
    1471             :                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
    1472             : 
    1473             :     /* Match N words representing all of the line, case-insensitively. */
    1474             : #define Matches(...) \
    1475             :     MatchesImpl(false, previous_words_count, previous_words, \
    1476             :                 VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
    1477             : 
    1478             :     /* Match N words representing all of the line, case-sensitively. */
    1479             : #define MatchesCS(...) \
    1480             :     MatchesImpl(true, previous_words_count, previous_words, \
    1481             :                 VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
    1482             : 
    1483             :     /* Match the first N words on the line, case-insensitively. */
    1484             : #define HeadMatches(...) \
    1485             :     HeadMatchesImpl(false, previous_words_count, previous_words, \
    1486             :                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
    1487             : 
    1488             :     /* Match the first N words on the line, case-sensitively. */
    1489             : #define HeadMatchesCS(...) \
    1490             :     HeadMatchesImpl(true, previous_words_count, previous_words, \
    1491             :                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
    1492             : 
    1493             :     /* Known command-starting keywords. */
    1494             :     static const char *const sql_commands[] = {
    1495             :         "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
    1496             :         "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
    1497             :         "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
    1498             :         "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
    1499             :         "MOVE", "NOTIFY", "PREPARE",
    1500             :         "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
    1501             :         "RESET", "REVOKE", "ROLLBACK",
    1502             :         "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
    1503             :         "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
    1504             :         NULL
    1505             :     };
    1506             : 
    1507             :     /* psql's backslash commands. */
    1508             :     static const char *const backslash_commands[] = {
    1509             :         "\\a",
    1510             :         "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy",
    1511             :         "\\copyright", "\\crosstabview",
    1512             :         "\\d", "\\da", "\\dA", "\\dAc", "\\dAf", "\\dAo", "\\dAp",
    1513             :         "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD",
    1514             :         "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df",
    1515             :         "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
    1516             :         "\\dm", "\\dn", "\\do", "\\dO", "\\dp", "\\dP", "\\dPi", "\\dPt",
    1517             :         "\\drds", "\\dRs", "\\dRp", "\\ds",
    1518             :         "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dX", "\\dy",
    1519             :         "\\echo", "\\edit", "\\ef", "\\elif", "\\else", "\\encoding",
    1520             :         "\\endif", "\\errverbose", "\\ev",
    1521             :         "\\f",
    1522             :         "\\g", "\\gdesc", "\\gexec", "\\gset", "\\gx",
    1523             :         "\\help", "\\html",
    1524             :         "\\if", "\\include", "\\include_relative", "\\ir",
    1525             :         "\\list", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
    1526             :         "\\out",
    1527             :         "\\password", "\\print", "\\prompt", "\\pset",
    1528             :         "\\qecho", "\\quit",
    1529             :         "\\reset",
    1530             :         "\\s", "\\set", "\\setenv", "\\sf", "\\sv",
    1531             :         "\\t", "\\T", "\\timing",
    1532             :         "\\unset",
    1533             :         "\\x",
    1534             :         "\\warn", "\\watch", "\\write",
    1535             :         "\\z",
    1536             :         "\\!", "\\?",
    1537             :         NULL
    1538             :     };
    1539             : 
    1540             :     /*
    1541             :      * Temporary workaround for a bug in recent (2019) libedit: it incorrectly
    1542             :      * de-escapes the input "text", causing us to fail to recognize backslash
    1543             :      * commands.  So get the string to look at from rl_line_buffer instead.
    1544             :      */
    1545          28 :     char       *text_copy = pnstrdup(rl_line_buffer + start, end - start);
    1546          28 :     text = text_copy;
    1547             : 
    1548             :     /* Remember last char of the given input word. */
    1549          28 :     completion_last_char = (end > start) ? text[end - start - 1] : '\0';
    1550             : 
    1551             :     /* We usually want the append character to be a space. */
    1552             : #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
    1553          28 :     rl_completion_append_character = ' ';
    1554             : #endif
    1555             : 
    1556             :     /* Clear a few things. */
    1557          28 :     completion_charp = NULL;
    1558          28 :     completion_charpp = NULL;
    1559          28 :     completion_info_charp = NULL;
    1560          28 :     completion_info_charp2 = NULL;
    1561             : 
    1562             :     /*
    1563             :      * Scan the input line to extract the words before our current position.
    1564             :      * According to those we'll make some smart decisions on what the user is
    1565             :      * probably intending to type.
    1566             :      */
    1567          28 :     previous_words = get_previous_words(start,
    1568             :                                         &words_buffer,
    1569             :                                         &previous_words_count);
    1570             : 
    1571             :     /* If current word is a backslash command, offer completions for that */
    1572          28 :     if (text[0] == '\\')
    1573           2 :         COMPLETE_WITH_LIST_CS(backslash_commands);
    1574             : 
    1575             :     /* If current word is a variable interpolation, handle that case */
    1576          26 :     else if (text[0] == ':' && text[1] != ':')
    1577             :     {
    1578           0 :         if (text[1] == '\'')
    1579           0 :             matches = complete_from_variables(text, ":'", "'", true);
    1580           0 :         else if (text[1] == '"')
    1581           0 :             matches = complete_from_variables(text, ":\"", "\"", true);
    1582             :         else
    1583           0 :             matches = complete_from_variables(text, ":", "", true);
    1584             :     }
    1585             : 
    1586             :     /* If no previous word, suggest one of the basic sql commands */
    1587          26 :     else if (previous_words_count == 0)
    1588           4 :         COMPLETE_WITH_LIST(sql_commands);
    1589             : 
    1590             : /* CREATE */
    1591             :     /* complete with something you can create */
    1592          22 :     else if (TailMatches("CREATE"))
    1593           0 :         matches = rl_completion_matches(text, create_command_generator);
    1594             : 
    1595             :     /* complete with something you can create or replace */
    1596          22 :     else if (TailMatches("CREATE", "OR", "REPLACE"))
    1597           0 :         COMPLETE_WITH("FUNCTION", "PROCEDURE", "LANGUAGE", "RULE", "VIEW",
    1598             :                       "AGGREGATE", "TRANSFORM", "TRIGGER");
    1599             : 
    1600             : /* DROP, but not DROP embedded in other commands */
    1601             :     /* complete with something you can drop */
    1602          22 :     else if (Matches("DROP"))
    1603           0 :         matches = rl_completion_matches(text, drop_command_generator);
    1604             : 
    1605             : /* ALTER */
    1606             : 
    1607             :     /* ALTER TABLE */
    1608          22 :     else if (Matches("ALTER", "TABLE"))
    1609           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
    1610             :                                    "UNION SELECT 'ALL IN TABLESPACE'");
    1611             : 
    1612             :     /* ALTER something */
    1613          22 :     else if (Matches("ALTER"))
    1614           0 :         matches = rl_completion_matches(text, alter_command_generator);
    1615             :     /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
    1616          22 :     else if (TailMatches("ALL", "IN", "TABLESPACE", MatchAny))
    1617           0 :         COMPLETE_WITH("SET TABLESPACE", "OWNED BY");
    1618             :     /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */
    1619          22 :     else if (TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY"))
    1620           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    1621             :     /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */
    1622          22 :     else if (TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny))
    1623           0 :         COMPLETE_WITH("SET TABLESPACE");
    1624             :     /* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> */
    1625          22 :     else if (Matches("ALTER", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
    1626           0 :         COMPLETE_WITH("(");
    1627             :     /* ALTER AGGREGATE <name> (...) */
    1628          22 :     else if (Matches("ALTER", "AGGREGATE", MatchAny, MatchAny))
    1629             :     {
    1630           0 :         if (ends_with(prev_wd, ')'))
    1631           0 :             COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
    1632             :         else
    1633           0 :             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
    1634             :     }
    1635             :     /* ALTER FUNCTION,PROCEDURE,ROUTINE <name> (...) */
    1636          22 :     else if (Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny))
    1637             :     {
    1638           0 :         if (ends_with(prev_wd, ')'))
    1639           0 :             COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA",
    1640             :                           "DEPENDS ON EXTENSION", "NO DEPENDS ON EXTENSION");
    1641             :         else
    1642           0 :             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
    1643             :     }
    1644             : 
    1645             :     /* ALTER PUBLICATION <name> */
    1646          22 :     else if (Matches("ALTER", "PUBLICATION", MatchAny))
    1647           0 :         COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
    1648             :     /* ALTER PUBLICATION <name> ADD */
    1649          22 :     else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
    1650           0 :         COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
    1651             :     /* ALTER PUBLICATION <name> DROP */
    1652          22 :     else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP"))
    1653           0 :         COMPLETE_WITH("ALL TABLES IN SCHEMA", "TABLE");
    1654             :     /* ALTER PUBLICATION <name> SET */
    1655          22 :     else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
    1656           0 :         COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
    1657          22 :     else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
    1658           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_schemas
    1659             :                             " AND nspname != 'pg_catalog' "
    1660             :                             " AND nspname not like 'pg\\_toast%%' "
    1661             :                             " AND nspname not like 'pg\\_temp%%' "
    1662             :                             " UNION SELECT 'CURRENT_SCHEMA'");
    1663             :     /* ALTER PUBLICATION <name> SET ( */
    1664          22 :     else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("SET", "("))
    1665           0 :         COMPLETE_WITH("publish", "publish_via_partition_root");
    1666             :     /* ALTER SUBSCRIPTION <name> */
    1667          22 :     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny))
    1668           0 :         COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO",
    1669             :                       "RENAME TO", "REFRESH PUBLICATION", "SET",
    1670             :                       "ADD PUBLICATION", "DROP PUBLICATION");
    1671             :     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
    1672          22 :     else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
    1673           0 :              TailMatches("REFRESH", "PUBLICATION"))
    1674           0 :         COMPLETE_WITH("WITH (");
    1675             :     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
    1676          22 :     else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
    1677           0 :              TailMatches("REFRESH", "PUBLICATION", "WITH", "("))
    1678           0 :         COMPLETE_WITH("copy_data");
    1679             :     /* ALTER SUBSCRIPTION <name> SET */
    1680          22 :     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"))
    1681           0 :         COMPLETE_WITH("(", "PUBLICATION");
    1682             :     /* ALTER SUBSCRIPTION <name> SET ( */
    1683          22 :     else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "("))
    1684           0 :         COMPLETE_WITH("binary", "slot_name", "streaming", "synchronous_commit");
    1685             :     /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
    1686          22 :     else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "PUBLICATION"))
    1687             :     {
    1688             :         /* complete with nothing here as this refers to remote publications */
    1689             :     }
    1690             :     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
    1691          22 :     else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
    1692           0 :              TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny))
    1693           0 :         COMPLETE_WITH("WITH (");
    1694             :     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
    1695          22 :     else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
    1696           0 :              TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
    1697           0 :         COMPLETE_WITH("copy_data", "refresh");
    1698             : 
    1699             :     /* ALTER SCHEMA <name> */
    1700          22 :     else if (Matches("ALTER", "SCHEMA", MatchAny))
    1701           0 :         COMPLETE_WITH("OWNER TO", "RENAME TO");
    1702             : 
    1703             :     /* ALTER COLLATION <name> */
    1704          22 :     else if (Matches("ALTER", "COLLATION", MatchAny))
    1705           0 :         COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
    1706             : 
    1707             :     /* ALTER CONVERSION <name> */
    1708          22 :     else if (Matches("ALTER", "CONVERSION", MatchAny))
    1709           0 :         COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
    1710             : 
    1711             :     /* ALTER DATABASE <name> */
    1712          22 :     else if (Matches("ALTER", "DATABASE", MatchAny))
    1713           0 :         COMPLETE_WITH("RESET", "SET", "OWNER TO", "RENAME TO",
    1714             :                       "IS_TEMPLATE", "ALLOW_CONNECTIONS",
    1715             :                       "CONNECTION LIMIT");
    1716             : 
    1717             :     /* ALTER DATABASE <name> SET TABLESPACE */
    1718          22 :     else if (Matches("ALTER", "DATABASE", MatchAny, "SET", "TABLESPACE"))
    1719           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
    1720             : 
    1721             :     /* ALTER EVENT TRIGGER */
    1722          22 :     else if (Matches("ALTER", "EVENT", "TRIGGER"))
    1723           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
    1724             : 
    1725             :     /* ALTER EVENT TRIGGER <name> */
    1726          22 :     else if (Matches("ALTER", "EVENT", "TRIGGER", MatchAny))
    1727           0 :         COMPLETE_WITH("DISABLE", "ENABLE", "OWNER TO", "RENAME TO");
    1728             : 
    1729             :     /* ALTER EVENT TRIGGER <name> ENABLE */
    1730          22 :     else if (Matches("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"))
    1731           0 :         COMPLETE_WITH("REPLICA", "ALWAYS");
    1732             : 
    1733             :     /* ALTER EXTENSION <name> */
    1734          22 :     else if (Matches("ALTER", "EXTENSION", MatchAny))
    1735           0 :         COMPLETE_WITH("ADD", "DROP", "UPDATE", "SET SCHEMA");
    1736             : 
    1737             :     /* ALTER EXTENSION <name> UPDATE */
    1738          22 :     else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE"))
    1739             :     {
    1740           0 :         completion_info_charp = prev2_wd;
    1741           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions_with_TO);
    1742             :     }
    1743             : 
    1744             :     /* ALTER EXTENSION <name> UPDATE TO */
    1745          22 :     else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"))
    1746             :     {
    1747           0 :         completion_info_charp = prev3_wd;
    1748           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
    1749             :     }
    1750             : 
    1751             :     /* ALTER FOREIGN */
    1752          22 :     else if (Matches("ALTER", "FOREIGN"))
    1753           0 :         COMPLETE_WITH("DATA WRAPPER", "TABLE");
    1754             : 
    1755             :     /* ALTER FOREIGN DATA WRAPPER <name> */
    1756          22 :     else if (Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
    1757           0 :         COMPLETE_WITH("HANDLER", "VALIDATOR", "NO",
    1758             :                       "OPTIONS", "OWNER TO", "RENAME TO");
    1759          22 :     else if (Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny, "NO"))
    1760           0 :         COMPLETE_WITH("HANDLER", "VALIDATOR");
    1761             : 
    1762             :     /* ALTER FOREIGN TABLE <name> */
    1763          22 :     else if (Matches("ALTER", "FOREIGN", "TABLE", MatchAny))
    1764           0 :         COMPLETE_WITH("ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE",
    1765             :                       "INHERIT", "NO INHERIT", "OPTIONS", "OWNER TO",
    1766             :                       "RENAME", "SET", "VALIDATE CONSTRAINT");
    1767             : 
    1768             :     /* ALTER INDEX */
    1769          22 :     else if (Matches("ALTER", "INDEX"))
    1770           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
    1771             :                                    "UNION SELECT 'ALL IN TABLESPACE'");
    1772             :     /* ALTER INDEX <name> */
    1773          22 :     else if (Matches("ALTER", "INDEX", MatchAny))
    1774           0 :         COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET",
    1775             :                       "RESET", "ATTACH PARTITION",
    1776             :                       "DEPENDS ON EXTENSION", "NO DEPENDS ON EXTENSION");
    1777          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH"))
    1778           0 :         COMPLETE_WITH("PARTITION");
    1779          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION"))
    1780           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
    1781             :     /* ALTER INDEX <name> ALTER */
    1782          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ALTER"))
    1783           0 :         COMPLETE_WITH("COLUMN");
    1784             :     /* ALTER INDEX <name> ALTER COLUMN */
    1785          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN"))
    1786             :     {
    1787           0 :         completion_info_charp = prev3_wd;
    1788           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_attribute_numbers);
    1789             :     }
    1790             :     /* ALTER INDEX <name> ALTER COLUMN <colnum> */
    1791          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny))
    1792           0 :         COMPLETE_WITH("SET STATISTICS");
    1793             :     /* ALTER INDEX <name> ALTER COLUMN <colnum> SET */
    1794          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET"))
    1795           0 :         COMPLETE_WITH("STATISTICS");
    1796             :     /* ALTER INDEX <name> ALTER COLUMN <colnum> SET STATISTICS */
    1797          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS"))
    1798             :     {
    1799             :         /* Enforce no completion here, as an integer has to be specified */
    1800             :     }
    1801             :     /* ALTER INDEX <name> SET */
    1802          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "SET"))
    1803           0 :         COMPLETE_WITH("(", "TABLESPACE");
    1804             :     /* ALTER INDEX <name> RESET */
    1805          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "RESET"))
    1806           0 :         COMPLETE_WITH("(");
    1807             :     /* ALTER INDEX <foo> SET|RESET ( */
    1808          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "RESET", "("))
    1809           0 :         COMPLETE_WITH("fillfactor",
    1810             :                       "deduplicate_items",    /* BTREE */
    1811             :                       "fastupdate", "gin_pending_list_limit",   /* GIN */
    1812             :                       "buffering",    /* GiST */
    1813             :                       "pages_per_range", "autosummarize"    /* BRIN */
    1814             :             );
    1815          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "SET", "("))
    1816           0 :         COMPLETE_WITH("fillfactor =",
    1817             :                       "deduplicate_items =",  /* BTREE */
    1818             :                       "fastupdate =", "gin_pending_list_limit =",   /* GIN */
    1819             :                       "buffering =",  /* GiST */
    1820             :                       "pages_per_range =", "autosummarize ="    /* BRIN */
    1821             :             );
    1822          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "NO", "DEPENDS"))
    1823           0 :         COMPLETE_WITH("ON EXTENSION");
    1824          22 :     else if (Matches("ALTER", "INDEX", MatchAny, "DEPENDS"))
    1825           0 :         COMPLETE_WITH("ON EXTENSION");
    1826             : 
    1827             :     /* ALTER LANGUAGE <name> */
    1828          22 :     else if (Matches("ALTER", "LANGUAGE", MatchAny))
    1829           0 :         COMPLETE_WITH("OWNER TO", "RENAME TO");
    1830             : 
    1831             :     /* ALTER LARGE OBJECT <oid> */
    1832          22 :     else if (Matches("ALTER", "LARGE", "OBJECT", MatchAny))
    1833           0 :         COMPLETE_WITH("OWNER TO");
    1834             : 
    1835             :     /* ALTER MATERIALIZED VIEW */
    1836          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW"))
    1837           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
    1838             :                                    "UNION SELECT 'ALL IN TABLESPACE'");
    1839             : 
    1840             :     /* ALTER USER,ROLE <name> */
    1841          22 :     else if (Matches("ALTER", "USER|ROLE", MatchAny) &&
    1842           0 :              !TailMatches("USER", "MAPPING"))
    1843           0 :         COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
    1844             :                       "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
    1845             :                       "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
    1846             :                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
    1847             :                       "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
    1848             :                       "VALID UNTIL", "WITH");
    1849             : 
    1850             :     /* ALTER USER,ROLE <name> WITH */
    1851          22 :     else if (Matches("ALTER", "USER|ROLE", MatchAny, "WITH"))
    1852             :         /* Similar to the above, but don't complete "WITH" again. */
    1853           0 :         COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
    1854             :                       "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
    1855             :                       "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
    1856             :                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
    1857             :                       "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
    1858             :                       "VALID UNTIL");
    1859             : 
    1860             :     /* ALTER DEFAULT PRIVILEGES */
    1861          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES"))
    1862           0 :         COMPLETE_WITH("FOR ROLE", "IN SCHEMA");
    1863             :     /* ALTER DEFAULT PRIVILEGES FOR */
    1864          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR"))
    1865           0 :         COMPLETE_WITH("ROLE");
    1866             :     /* ALTER DEFAULT PRIVILEGES IN */
    1867          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN"))
    1868           0 :         COMPLETE_WITH("SCHEMA");
    1869             :     /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... */
    1870          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER",
    1871             :                      MatchAny))
    1872           0 :         COMPLETE_WITH("GRANT", "REVOKE", "IN SCHEMA");
    1873             :     /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... */
    1874          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA",
    1875             :                      MatchAny))
    1876           0 :         COMPLETE_WITH("GRANT", "REVOKE", "FOR ROLE");
    1877             :     /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR */
    1878          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA",
    1879             :                      MatchAny, "FOR"))
    1880           0 :         COMPLETE_WITH("ROLE");
    1881             :     /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... IN SCHEMA ... */
    1882             :     /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR ROLE|USER ... */
    1883          22 :     else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER",
    1884          22 :                      MatchAny, "IN", "SCHEMA", MatchAny) ||
    1885          22 :              Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA",
    1886             :                      MatchAny, "FOR", "ROLE|USER", MatchAny))
    1887           0 :         COMPLETE_WITH("GRANT", "REVOKE");
    1888             :     /* ALTER DOMAIN <name> */
    1889          22 :     else if (Matches("ALTER", "DOMAIN", MatchAny))
    1890           0 :         COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME", "SET",
    1891             :                       "VALIDATE CONSTRAINT");
    1892             :     /* ALTER DOMAIN <sth> DROP */
    1893          22 :     else if (Matches("ALTER", "DOMAIN", MatchAny, "DROP"))
    1894           0 :         COMPLETE_WITH("CONSTRAINT", "DEFAULT", "NOT NULL");
    1895             :     /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
    1896          22 :     else if (Matches("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"))
    1897             :     {
    1898           0 :         completion_info_charp = prev3_wd;
    1899           0 :         COMPLETE_WITH_QUERY(Query_for_constraint_of_type);
    1900             :     }
    1901             :     /* ALTER DOMAIN <sth> RENAME */
    1902          22 :     else if (Matches("ALTER", "DOMAIN", MatchAny, "RENAME"))
    1903           0 :         COMPLETE_WITH("CONSTRAINT", "TO");
    1904             :     /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
    1905          22 :     else if (Matches("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny))
    1906           0 :         COMPLETE_WITH("TO");
    1907             : 
    1908             :     /* ALTER DOMAIN <sth> SET */
    1909          22 :     else if (Matches("ALTER", "DOMAIN", MatchAny, "SET"))
    1910           0 :         COMPLETE_WITH("DEFAULT", "NOT NULL", "SCHEMA");
    1911             :     /* ALTER SEQUENCE <name> */
    1912          22 :     else if (Matches("ALTER", "SEQUENCE", MatchAny))
    1913           0 :         COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART",
    1914             :                       "NO", "CACHE", "CYCLE", "SET SCHEMA", "OWNED BY",
    1915             :                       "OWNER TO", "RENAME TO");
    1916             :     /* ALTER SEQUENCE <name> AS */
    1917          22 :     else if (TailMatches("ALTER", "SEQUENCE", MatchAny, "AS"))
    1918           0 :         COMPLETE_WITH_CS("smallint", "integer", "bigint");
    1919             :     /* ALTER SEQUENCE <name> NO */
    1920          22 :     else if (Matches("ALTER", "SEQUENCE", MatchAny, "NO"))
    1921           0 :         COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
    1922             :     /* ALTER SERVER <name> */
    1923          22 :     else if (Matches("ALTER", "SERVER", MatchAny))
    1924           0 :         COMPLETE_WITH("VERSION", "OPTIONS", "OWNER TO", "RENAME TO");
    1925             :     /* ALTER SERVER <name> VERSION <version> */
    1926          22 :     else if (Matches("ALTER", "SERVER", MatchAny, "VERSION", MatchAny))
    1927           0 :         COMPLETE_WITH("OPTIONS");
    1928             :     /* ALTER SYSTEM SET, RESET, RESET ALL */
    1929          22 :     else if (Matches("ALTER", "SYSTEM"))
    1930           0 :         COMPLETE_WITH("SET", "RESET");
    1931          22 :     else if (Matches("ALTER", "SYSTEM", "SET|RESET"))
    1932           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
    1933          22 :     else if (Matches("ALTER", "SYSTEM", "SET", MatchAny))
    1934           0 :         COMPLETE_WITH("TO");
    1935             :     /* ALTER VIEW <name> */
    1936          22 :     else if (Matches("ALTER", "VIEW", MatchAny))
    1937           0 :         COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME",
    1938             :                       "SET SCHEMA");
    1939             :     /* ALTER VIEW xxx RENAME */
    1940          22 :     else if (Matches("ALTER", "VIEW", MatchAny, "RENAME"))
    1941           0 :         COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'TO'");
    1942          22 :     else if (Matches("ALTER", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"))
    1943           0 :         COMPLETE_WITH_ATTR(prev3_wd, "");
    1944             :     /* ALTER VIEW xxx ALTER [ COLUMN ] yyy */
    1945          44 :     else if (Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny) ||
    1946          22 :              Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny))
    1947           0 :         COMPLETE_WITH("SET DEFAULT", "DROP DEFAULT");
    1948             :     /* ALTER VIEW xxx RENAME yyy */
    1949          22 :     else if (Matches("ALTER", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")))
    1950           0 :         COMPLETE_WITH("TO");
    1951             :     /* ALTER VIEW xxx RENAME COLUMN yyy */
    1952          22 :     else if (Matches("ALTER", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")))
    1953           0 :         COMPLETE_WITH("TO");
    1954             : 
    1955             :     /* ALTER MATERIALIZED VIEW <name> */
    1956          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny))
    1957           0 :         COMPLETE_WITH("ALTER COLUMN", "CLUSTER ON", "DEPENDS ON EXTENSION",
    1958             :                       "NO DEPENDS ON EXTENSION", "OWNER TO", "RENAME",
    1959             :                       "RESET (", "SET");
    1960             :     /* ALTER MATERIALIZED VIEW xxx RENAME */
    1961          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME"))
    1962           0 :         COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'TO'");
    1963          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"))
    1964           0 :         COMPLETE_WITH_ATTR(prev3_wd, "");
    1965             :     /* ALTER MATERIALIZED VIEW xxx RENAME yyy */
    1966          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")))
    1967           0 :         COMPLETE_WITH("TO");
    1968             :     /* ALTER MATERIALIZED VIEW xxx RENAME COLUMN yyy */
    1969          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")))
    1970           0 :         COMPLETE_WITH("TO");
    1971             :     /* ALTER MATERIALIZED VIEW xxx SET */
    1972          22 :     else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET"))
    1973           0 :         COMPLETE_WITH("(", "SCHEMA", "TABLESPACE", "WITHOUT CLUSTER");
    1974             :     /* ALTER POLICY <name> */
    1975          22 :     else if (Matches("ALTER", "POLICY"))
    1976           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_policies);
    1977             :     /* ALTER POLICY <name> ON */
    1978          22 :     else if (Matches("ALTER", "POLICY", MatchAny))
    1979           0 :         COMPLETE_WITH("ON");
    1980             :     /* ALTER POLICY <name> ON <table> */
    1981          22 :     else if (Matches("ALTER", "POLICY", MatchAny, "ON"))
    1982             :     {
    1983           0 :         completion_info_charp = prev2_wd;
    1984           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);
    1985             :     }
    1986             :     /* ALTER POLICY <name> ON <table> - show options */
    1987          22 :     else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny))
    1988           0 :         COMPLETE_WITH("RENAME TO", "TO", "USING (", "WITH CHECK (");
    1989             :     /* ALTER POLICY <name> ON <table> TO <role> */
    1990          22 :     else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"))
    1991           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
    1992             :     /* ALTER POLICY <name> ON <table> USING ( */
    1993          22 :     else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
    1994           0 :         COMPLETE_WITH("(");
    1995             :     /* ALTER POLICY <name> ON <table> WITH CHECK ( */
    1996          22 :     else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
    1997           0 :         COMPLETE_WITH("(");
    1998             : 
    1999             :     /* ALTER RULE <name>, add ON */
    2000          22 :     else if (Matches("ALTER", "RULE", MatchAny))
    2001           0 :         COMPLETE_WITH("ON");
    2002             : 
    2003             :     /* If we have ALTER RULE <name> ON, then add the correct tablename */
    2004          22 :     else if (Matches("ALTER", "RULE", MatchAny, "ON"))
    2005             :     {
    2006           0 :         completion_info_charp = prev2_wd;
    2007           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
    2008             :     }
    2009             : 
    2010             :     /* ALTER RULE <name> ON <name> */
    2011          22 :     else if (Matches("ALTER", "RULE", MatchAny, "ON", MatchAny))
    2012           0 :         COMPLETE_WITH("RENAME TO");
    2013             : 
    2014             :     /* ALTER STATISTICS <name> */
    2015          22 :     else if (Matches("ALTER", "STATISTICS", MatchAny))
    2016           0 :         COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA", "SET STATISTICS");
    2017             : 
    2018             :     /* ALTER TRIGGER <name>, add ON */
    2019          22 :     else if (Matches("ALTER", "TRIGGER", MatchAny))
    2020           0 :         COMPLETE_WITH("ON");
    2021             : 
    2022          22 :     else if (Matches("ALTER", "TRIGGER", MatchAny, MatchAny))
    2023             :     {
    2024           0 :         completion_info_charp = prev2_wd;
    2025           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
    2026             :     }
    2027             : 
    2028             :     /*
    2029             :      * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
    2030             :      */
    2031          22 :     else if (Matches("ALTER", "TRIGGER", MatchAny, "ON"))
    2032           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    2033             : 
    2034             :     /* ALTER TRIGGER <name> ON <name> */
    2035          22 :     else if (Matches("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
    2036           0 :         COMPLETE_WITH("RENAME TO", "DEPENDS ON EXTENSION",
    2037             :                       "NO DEPENDS ON EXTENSION");
    2038             : 
    2039             :     /*
    2040             :      * If we detect ALTER TABLE <name>, suggest sub commands
    2041             :      */
    2042          22 :     else if (Matches("ALTER", "TABLE", MatchAny))
    2043           0 :         COMPLETE_WITH("ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP",
    2044             :                       "ENABLE", "INHERIT", "NO", "RENAME", "RESET",
    2045             :                       "OWNER TO", "SET", "VALIDATE CONSTRAINT",
    2046             :                       "REPLICA IDENTITY", "ATTACH PARTITION",
    2047             :                       "DETACH PARTITION", "FORCE ROW LEVEL SECURITY");
    2048             :     /* ALTER TABLE xxx ADD */
    2049          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD"))
    2050             :     {
    2051             :         /* make sure to keep this list and the !Matches() below in sync */
    2052           0 :         COMPLETE_WITH("COLUMN", "CONSTRAINT", "CHECK", "UNIQUE", "PRIMARY KEY",
    2053             :                       "EXCLUDE", "FOREIGN KEY");
    2054             :     }
    2055             :     /* ATER TABLE xxx ADD [COLUMN] yyy */
    2056          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny) ||
    2057          22 :              (Matches("ALTER", "TABLE", MatchAny, "ADD", MatchAny) &&
    2058           0 :               !Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
    2059           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    2060             :     /* ALTER TABLE xxx ADD CONSTRAINT yyy */
    2061          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny))
    2062           0 :         COMPLETE_WITH("CHECK", "UNIQUE", "PRIMARY KEY", "EXCLUDE", "FOREIGN KEY");
    2063             :     /* ALTER TABLE xxx ADD [CONSTRAINT yyy] (PRIMARY KEY|UNIQUE) */
    2064          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY") ||
    2065          44 :              Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE") ||
    2066          44 :              Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "PRIMARY", "KEY") ||
    2067          22 :              Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "UNIQUE"))
    2068           0 :         COMPLETE_WITH("(", "USING INDEX");
    2069             :     /* ALTER TABLE xxx ADD PRIMARY KEY USING INDEX */
    2070          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY", "USING", "INDEX"))
    2071             :     {
    2072           0 :         completion_info_charp = prev6_wd;
    2073           0 :         COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);
    2074             :     }
    2075             :     /* ALTER TABLE xxx ADD UNIQUE USING INDEX */
    2076          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE", "USING", "INDEX"))
    2077             :     {
    2078           0 :         completion_info_charp = prev5_wd;
    2079           0 :         COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);
    2080             :     }
    2081             :     /* ALTER TABLE xxx ADD CONSTRAINT yyy PRIMARY KEY USING INDEX */
    2082          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny,
    2083             :                      "PRIMARY", "KEY", "USING", "INDEX"))
    2084             :     {
    2085           0 :         completion_info_charp = prev8_wd;
    2086           0 :         COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);
    2087             :     }
    2088             :     /* ALTER TABLE xxx ADD CONSTRAINT yyy UNIQUE USING INDEX */
    2089          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny,
    2090             :                      "UNIQUE", "USING", "INDEX"))
    2091             :     {
    2092           0 :         completion_info_charp = prev7_wd;
    2093           0 :         COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);
    2094             :     }
    2095             :     /* ALTER TABLE xxx ENABLE */
    2096          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE"))
    2097           0 :         COMPLETE_WITH("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE",
    2098             :                       "TRIGGER");
    2099          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"))
    2100           0 :         COMPLETE_WITH("RULE", "TRIGGER");
    2101          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
    2102             :     {
    2103           0 :         completion_info_charp = prev3_wd;
    2104           0 :         COMPLETE_WITH_QUERY(Query_for_rule_of_table);
    2105             :     }
    2106          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
    2107             :     {
    2108           0 :         completion_info_charp = prev4_wd;
    2109           0 :         COMPLETE_WITH_QUERY(Query_for_rule_of_table);
    2110             :     }
    2111          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
    2112             :     {
    2113           0 :         completion_info_charp = prev3_wd;
    2114           0 :         COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
    2115             :     }
    2116          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"))
    2117             :     {
    2118           0 :         completion_info_charp = prev4_wd;
    2119           0 :         COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
    2120             :     }
    2121             :     /* ALTER TABLE xxx INHERIT */
    2122          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "INHERIT"))
    2123           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
    2124             :     /* ALTER TABLE xxx NO */
    2125          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "NO"))
    2126           0 :         COMPLETE_WITH("FORCE ROW LEVEL SECURITY", "INHERIT");
    2127             :     /* ALTER TABLE xxx NO INHERIT */
    2128          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
    2129           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
    2130             :     /* ALTER TABLE xxx DISABLE */
    2131          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE"))
    2132           0 :         COMPLETE_WITH("ROW LEVEL SECURITY", "RULE", "TRIGGER");
    2133          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
    2134             :     {
    2135           0 :         completion_info_charp = prev3_wd;
    2136           0 :         COMPLETE_WITH_QUERY(Query_for_rule_of_table);
    2137             :     }
    2138          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
    2139             :     {
    2140           0 :         completion_info_charp = prev3_wd;
    2141           0 :         COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
    2142             :     }
    2143             : 
    2144             :     /* ALTER TABLE xxx ALTER */
    2145          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER"))
    2146           0 :         COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'");
    2147             : 
    2148             :     /* ALTER TABLE xxx RENAME */
    2149          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "RENAME"))
    2150           0 :         COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'");
    2151          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
    2152           0 :         COMPLETE_WITH_ATTR(prev3_wd, "");
    2153             : 
    2154             :     /* ALTER TABLE xxx RENAME yyy */
    2155          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")))
    2156           0 :         COMPLETE_WITH("TO");
    2157             : 
    2158             :     /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
    2159          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")))
    2160           0 :         COMPLETE_WITH("TO");
    2161             : 
    2162             :     /* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
    2163          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DROP"))
    2164           0 :         COMPLETE_WITH("COLUMN", "CONSTRAINT");
    2165             :     /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
    2166          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
    2167           0 :         COMPLETE_WITH_ATTR(prev3_wd, "");
    2168             : 
    2169             :     /*
    2170             :      * If we have ALTER TABLE <sth> ALTER|DROP|RENAME|VALIDATE CONSTRAINT,
    2171             :      * provide list of constraints
    2172             :      */
    2173          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME|VALIDATE", "CONSTRAINT"))
    2174             :     {
    2175           0 :         completion_info_charp = prev3_wd;
    2176           0 :         COMPLETE_WITH_QUERY(Query_for_constraint_of_table);
    2177             :     }
    2178             :     /* ALTER TABLE ALTER [COLUMN] <foo> */
    2179          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny) ||
    2180          22 :              Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny))
    2181           0 :         COMPLETE_WITH("TYPE", "SET", "RESET", "RESTART", "ADD", "DROP");
    2182             :     /* ALTER TABLE ALTER [COLUMN] <foo> SET */
    2183          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
    2184          22 :              Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
    2185           0 :         COMPLETE_WITH("(", "COMPRESSION", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
    2186             :     /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
    2187          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
    2188          22 :              Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
    2189           0 :         COMPLETE_WITH("n_distinct", "n_distinct_inherited");
    2190             :     /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
    2191          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
    2192          22 :              Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
    2193           0 :         COMPLETE_WITH("PLAIN", "EXTERNAL", "EXTENDED", "MAIN");
    2194             :     /* ALTER TABLE ALTER [COLUMN] <foo> SET STATISTICS */
    2195          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS") ||
    2196          22 :              Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STATISTICS"))
    2197             :     {
    2198             :         /* Enforce no completion here, as an integer has to be specified */
    2199             :     }
    2200             :     /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
    2201          44 :     else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP") ||
    2202          22 :              Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "DROP"))
    2203           0 :         COMPLETE_WITH("DEFAULT", "EXPRESSION", "IDENTITY", "NOT NULL");
    2204          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER"))
    2205           0 :         COMPLETE_WITH("ON");
    2206          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))
    2207             :     {
    2208           0 :         completion_info_charp = prev3_wd;
    2209           0 :         COMPLETE_WITH_QUERY(Query_for_index_of_table);
    2210             :     }
    2211             :     /* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
    2212          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "SET"))
    2213           0 :         COMPLETE_WITH("(", "ACCESS METHOD", "LOGGED", "SCHEMA",
    2214             :                       "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT");
    2215             : 
    2216             :     /*
    2217             :      * If we have ALTER TABLE <sth> SET ACCESS METHOD provide a list of table
    2218             :      * AMs.
    2219             :      */
    2220          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "SET", "ACCESS", "METHOD"))
    2221           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
    2222             : 
    2223             :     /*
    2224             :      * If we have ALTER TABLE <sth> SET TABLESPACE provide a list of
    2225             :      * tablespaces
    2226             :      */
    2227          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"))
    2228           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
    2229             :     /* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
    2230          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"))
    2231           0 :         COMPLETE_WITH("CLUSTER", "OIDS");
    2232             :     /* ALTER TABLE <foo> RESET */
    2233          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "RESET"))
    2234           0 :         COMPLETE_WITH("(");
    2235             :     /* ALTER TABLE <foo> SET|RESET ( */
    2236          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "SET|RESET", "("))
    2237           0 :         COMPLETE_WITH_LIST(table_storage_parameters);
    2238          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
    2239             :     {
    2240           0 :         completion_info_charp = prev5_wd;
    2241           0 :         COMPLETE_WITH_QUERY(Query_for_index_of_table);
    2242             :     }
    2243          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
    2244           0 :         COMPLETE_WITH("INDEX");
    2245          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"))
    2246           0 :         COMPLETE_WITH("FULL", "NOTHING", "DEFAULT", "USING");
    2247          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA"))
    2248           0 :         COMPLETE_WITH("IDENTITY");
    2249             : 
    2250             :     /*
    2251             :      * If we have ALTER TABLE <foo> ATTACH PARTITION, provide a list of
    2252             :      * tables.
    2253             :      */
    2254          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "ATTACH", "PARTITION"))
    2255           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
    2256             :     /* Limited completion support for partition bound specification */
    2257          22 :     else if (TailMatches("ATTACH", "PARTITION", MatchAny))
    2258           0 :         COMPLETE_WITH("FOR VALUES", "DEFAULT");
    2259          22 :     else if (TailMatches("FOR", "VALUES"))
    2260           0 :         COMPLETE_WITH("FROM (", "IN (", "WITH (");
    2261             : 
    2262             :     /*
    2263             :      * If we have ALTER TABLE <foo> DETACH PARTITION, provide a list of
    2264             :      * partitions of <foo>.
    2265             :      */
    2266          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION"))
    2267             :     {
    2268           0 :         completion_info_charp = prev3_wd;
    2269           0 :         COMPLETE_WITH_QUERY(Query_for_partition_of_table);
    2270             :     }
    2271          22 :     else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny))
    2272           0 :         COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
    2273             : 
    2274             :     /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
    2275          22 :     else if (Matches("ALTER", "TABLESPACE", MatchAny))
    2276           0 :         COMPLETE_WITH("RENAME TO", "OWNER TO", "SET", "RESET");
    2277             :     /* ALTER TABLESPACE <foo> SET|RESET */
    2278          22 :     else if (Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET"))
    2279           0 :         COMPLETE_WITH("(");
    2280             :     /* ALTER TABLESPACE <foo> SET|RESET ( */
    2281          22 :     else if (Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("))
    2282           0 :         COMPLETE_WITH("seq_page_cost", "random_page_cost",
    2283             :                       "effective_io_concurrency", "maintenance_io_concurrency");
    2284             : 
    2285             :     /* ALTER TEXT SEARCH */
    2286          22 :     else if (Matches("ALTER", "TEXT", "SEARCH"))
    2287           0 :         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
    2288          22 :     else if (Matches("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny))
    2289           0 :         COMPLETE_WITH("RENAME TO", "SET SCHEMA");
    2290          22 :     else if (Matches("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny))
    2291           0 :         COMPLETE_WITH("(", "OWNER TO", "RENAME TO", "SET SCHEMA");
    2292          22 :     else if (Matches("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
    2293           0 :         COMPLETE_WITH("ADD MAPPING FOR", "ALTER MAPPING",
    2294             :                       "DROP MAPPING FOR",
    2295             :                       "OWNER TO", "RENAME TO", "SET SCHEMA");
    2296             : 
    2297             :     /* complete ALTER TYPE <foo> with actions */
    2298          22 :     else if (Matches("ALTER", "TYPE", MatchAny))
    2299           0 :         COMPLETE_WITH("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE",
    2300             :                       "DROP ATTRIBUTE",
    2301             :                       "OWNER TO", "RENAME", "SET SCHEMA", "SET (");
    2302             :     /* complete ALTER TYPE <foo> ADD with actions */
    2303          22 :     else if (Matches("ALTER", "TYPE", MatchAny, "ADD"))
    2304           0 :         COMPLETE_WITH("ATTRIBUTE", "VALUE");
    2305             :     /* ALTER TYPE <foo> RENAME    */
    2306          22 :     else if (Matches("ALTER", "TYPE", MatchAny, "RENAME"))
    2307           0 :         COMPLETE_WITH("ATTRIBUTE", "TO", "VALUE");
    2308             :     /* ALTER TYPE xxx RENAME (ATTRIBUTE|VALUE) yyy */
    2309          22 :     else if (Matches("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE|VALUE", MatchAny))
    2310           0 :         COMPLETE_WITH("TO");
    2311             : 
    2312             :     /*
    2313             :      * If we have ALTER TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list
    2314             :      * of attributes
    2315             :      */
    2316          22 :     else if (Matches("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"))
    2317           0 :         COMPLETE_WITH_ATTR(prev3_wd, "");
    2318             :     /* ALTER TYPE ALTER ATTRIBUTE <foo> */
    2319          22 :     else if (Matches("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny))
    2320           0 :         COMPLETE_WITH("TYPE");
    2321             :     /* complete ALTER GROUP <foo> */
    2322          22 :     else if (Matches("ALTER", "GROUP", MatchAny))
    2323           0 :         COMPLETE_WITH("ADD USER", "DROP USER", "RENAME TO");
    2324             :     /* complete ALTER GROUP <foo> ADD|DROP with USER */
    2325          22 :     else if (Matches("ALTER", "GROUP", MatchAny, "ADD|DROP"))
    2326           0 :         COMPLETE_WITH("USER");
    2327             :     /* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
    2328          22 :     else if (Matches("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"))
    2329           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    2330             : 
    2331             :     /*
    2332             :      * If we have ALTER TYPE <sth> RENAME VALUE, provide list of enum values
    2333             :      */
    2334          22 :     else if (Matches("ALTER", "TYPE", MatchAny, "RENAME", "VALUE"))
    2335           0 :         COMPLETE_WITH_ENUM_VALUE(prev3_wd);
    2336             : 
    2337             : /*
    2338             :  * ANALYZE [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
    2339             :  * ANALYZE [ VERBOSE ] [ table_and_columns [, ...] ]
    2340             :  */
    2341          22 :     else if (Matches("ANALYZE"))
    2342           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables,
    2343             :                                    " UNION SELECT 'VERBOSE'");
    2344          22 :     else if (HeadMatches("ANALYZE", "(*") &&
    2345           0 :              !HeadMatches("ANALYZE", "(*)"))
    2346             :     {
    2347             :         /*
    2348             :          * This fires if we're in an unfinished parenthesized option list.
    2349             :          * get_previous_words treats a completed parenthesized option list as
    2350             :          * one word, so the above test is correct.
    2351             :          */
    2352           0 :         if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    2353           0 :             COMPLETE_WITH("VERBOSE", "SKIP_LOCKED");
    2354           0 :         else if (TailMatches("VERBOSE|SKIP_LOCKED"))
    2355           0 :             COMPLETE_WITH("ON", "OFF");
    2356             :     }
    2357          22 :     else if (HeadMatches("ANALYZE") && TailMatches("("))
    2358             :         /* "ANALYZE (" should be caught above, so assume we want columns */
    2359           0 :         COMPLETE_WITH_ATTR(prev2_wd, "");
    2360          22 :     else if (HeadMatches("ANALYZE"))
    2361           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables, NULL);
    2362             : 
    2363             : /* BEGIN */
    2364          22 :     else if (Matches("BEGIN"))
    2365           0 :         COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
    2366             : /* END, ABORT */
    2367          22 :     else if (Matches("END|ABORT"))
    2368           0 :         COMPLETE_WITH("AND", "WORK", "TRANSACTION");
    2369             : /* COMMIT */
    2370          22 :     else if (Matches("COMMIT"))
    2371           0 :         COMPLETE_WITH("AND", "WORK", "TRANSACTION", "PREPARED");
    2372             : /* RELEASE SAVEPOINT */
    2373          22 :     else if (Matches("RELEASE"))
    2374           0 :         COMPLETE_WITH("SAVEPOINT");
    2375             : /* ROLLBACK */
    2376          22 :     else if (Matches("ROLLBACK"))
    2377           0 :         COMPLETE_WITH("AND", "WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
    2378          22 :     else if (Matches("ABORT|END|COMMIT|ROLLBACK", "AND"))
    2379           0 :         COMPLETE_WITH("CHAIN");
    2380             : /* CALL */
    2381          22 :     else if (Matches("CALL"))
    2382           0 :         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
    2383          22 :     else if (Matches("CALL", MatchAny))
    2384           0 :         COMPLETE_WITH("(");
    2385             : /* CLOSE */
    2386          22 :     else if (Matches("CLOSE"))
    2387           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_cursors
    2388             :                             " UNION SELECT 'ALL'");
    2389             : /* CLUSTER */
    2390          22 :     else if (Matches("CLUSTER"))
    2391           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, "UNION SELECT 'VERBOSE'");
    2392          44 :     else if (Matches("CLUSTER", "VERBOSE") ||
    2393          22 :              Matches("CLUSTER", "(*)"))
    2394           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, NULL);
    2395             :     /* If we have CLUSTER <sth>, then add "USING" */
    2396          22 :     else if (Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON|(|(*)")))
    2397           0 :         COMPLETE_WITH("USING");
    2398             :     /* If we have CLUSTER VERBOSE <sth>, then add "USING" */
    2399          22 :     else if (Matches("CLUSTER", "VERBOSE|(*)", MatchAny))
    2400           0 :         COMPLETE_WITH("USING");
    2401             :     /* If we have CLUSTER <sth> USING, then add the index as well */
    2402          44 :     else if (Matches("CLUSTER", MatchAny, "USING") ||
    2403          22 :              Matches("CLUSTER", "VERBOSE|(*)", MatchAny, "USING"))
    2404             :     {
    2405           0 :         completion_info_charp = prev2_wd;
    2406           0 :         COMPLETE_WITH_QUERY(Query_for_index_of_table);
    2407             :     }
    2408          22 :     else if (HeadMatches("CLUSTER", "(*") &&
    2409           0 :              !HeadMatches("CLUSTER", "(*)"))
    2410             :     {
    2411             :         /*
    2412             :          * This fires if we're in an unfinished parenthesized option list.
    2413             :          * get_previous_words treats a completed parenthesized option list as
    2414             :          * one word, so the above test is correct.
    2415             :          */
    2416           0 :         if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    2417           0 :             COMPLETE_WITH("VERBOSE");
    2418             :     }
    2419             : 
    2420             : /* COMMENT */
    2421          22 :     else if (Matches("COMMENT"))
    2422           0 :         COMPLETE_WITH("ON");
    2423          22 :     else if (Matches("COMMENT", "ON"))
    2424           0 :         COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
    2425             :                       "COLUMN", "CONSTRAINT", "CONVERSION", "DATABASE",
    2426             :                       "DOMAIN", "EXTENSION", "EVENT TRIGGER",
    2427             :                       "FOREIGN DATA WRAPPER", "FOREIGN TABLE",
    2428             :                       "FUNCTION", "INDEX", "LANGUAGE", "LARGE OBJECT",
    2429             :                       "MATERIALIZED VIEW", "OPERATOR", "POLICY",
    2430             :                       "PROCEDURE", "PROCEDURAL LANGUAGE", "PUBLICATION", "ROLE",
    2431             :                       "ROUTINE", "RULE", "SCHEMA", "SEQUENCE", "SERVER",
    2432             :                       "STATISTICS", "SUBSCRIPTION", "TABLE",
    2433             :                       "TABLESPACE", "TEXT SEARCH", "TRANSFORM FOR",
    2434             :                       "TRIGGER", "TYPE", "VIEW");
    2435          22 :     else if (Matches("COMMENT", "ON", "ACCESS", "METHOD"))
    2436           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
    2437          22 :     else if (Matches("COMMENT", "ON", "CONSTRAINT"))
    2438           0 :         COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
    2439          22 :     else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny))
    2440           0 :         COMPLETE_WITH("ON");
    2441          22 :     else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
    2442             :     {
    2443           0 :         completion_info_charp = prev2_wd;
    2444           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint
    2445             :                             " UNION SELECT 'DOMAIN'");
    2446             :     }
    2447          22 :     else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON", "DOMAIN"))
    2448           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
    2449          22 :     else if (Matches("COMMENT", "ON", "EVENT", "TRIGGER"))
    2450           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
    2451          22 :     else if (Matches("COMMENT", "ON", "FOREIGN"))
    2452           0 :         COMPLETE_WITH("DATA WRAPPER", "TABLE");
    2453          22 :     else if (Matches("COMMENT", "ON", "FOREIGN", "TABLE"))
    2454           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
    2455          22 :     else if (Matches("COMMENT", "ON", "MATERIALIZED", "VIEW"))
    2456           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
    2457          22 :     else if (Matches("COMMENT", "ON", "POLICY"))
    2458           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_policies);
    2459          22 :     else if (Matches("COMMENT", "ON", "POLICY", MatchAny))
    2460           0 :         COMPLETE_WITH("ON");
    2461          22 :     else if (Matches("COMMENT", "ON", "POLICY", MatchAny, "ON"))
    2462             :     {
    2463           0 :         completion_info_charp = prev2_wd;
    2464           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);
    2465             :     }
    2466          22 :     else if (Matches("COMMENT", "ON", "PROCEDURAL", "LANGUAGE"))
    2467           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
    2468          22 :     else if (Matches("COMMENT", "ON", "RULE", MatchAny))
    2469           0 :         COMPLETE_WITH("ON");
    2470          22 :     else if (Matches("COMMENT", "ON", "RULE", MatchAny, "ON"))
    2471             :     {
    2472           0 :         completion_info_charp = prev2_wd;
    2473           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
    2474             :     }
    2475          22 :     else if (Matches("COMMENT", "ON", "TEXT", "SEARCH"))
    2476           0 :         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
    2477          22 :     else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "CONFIGURATION"))
    2478           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
    2479          22 :     else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "DICTIONARY"))
    2480           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
    2481          22 :     else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "PARSER"))
    2482           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
    2483          22 :     else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "TEMPLATE"))
    2484           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
    2485          22 :     else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR"))
    2486           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    2487          22 :     else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny))
    2488           0 :         COMPLETE_WITH("LANGUAGE");
    2489          22 :     else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
    2490             :     {
    2491           0 :         completion_info_charp = prev2_wd;
    2492           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
    2493             :     }
    2494          22 :     else if (Matches("COMMENT", "ON", "TRIGGER", MatchAny))
    2495           0 :         COMPLETE_WITH("ON");
    2496          22 :     else if (Matches("COMMENT", "ON", "TRIGGER", MatchAny, "ON"))
    2497             :     {
    2498           0 :         completion_info_charp = prev2_wd;
    2499           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
    2500             :     }
    2501          44 :     else if (Matches("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
    2502          44 :              Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) ||
    2503          44 :              Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")) ||
    2504          22 :              Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")))
    2505           0 :         COMPLETE_WITH("IS");
    2506             : 
    2507             : /* COPY */
    2508             : 
    2509             :     /*
    2510             :      * If we have COPY, offer list of tables or "(" (Also cover the analogous
    2511             :      * backslash command).
    2512             :      */
    2513          22 :     else if (Matches("COPY|\\copy"))
    2514           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
    2515             :                                    " UNION ALL SELECT '('");
    2516             :     /* Complete COPY ( with legal query commands */
    2517          22 :     else if (Matches("COPY|\\copy", "("))
    2518           0 :         COMPLETE_WITH("SELECT", "TABLE", "VALUES", "INSERT INTO", "UPDATE", "DELETE FROM", "WITH");
    2519             :     /* Complete COPY <sth> */
    2520          22 :     else if (Matches("COPY|\\copy", MatchAny))
    2521           0 :         COMPLETE_WITH("FROM", "TO");
    2522             :     /* Complete COPY <sth> FROM|TO with filename */
    2523          22 :     else if (Matches("COPY", MatchAny, "FROM|TO"))
    2524             :     {
    2525           8 :         completion_charp = "";
    2526           8 :         completion_force_quote = true;  /* COPY requires quoted filename */
    2527           8 :         matches = rl_completion_matches(text, complete_from_files);
    2528             :     }
    2529          14 :     else if (Matches("\\copy", MatchAny, "FROM|TO"))
    2530             :     {
    2531           0 :         completion_charp = "";
    2532           0 :         completion_force_quote = false;
    2533           0 :         matches = rl_completion_matches(text, complete_from_files);
    2534             :     }
    2535             : 
    2536             :     /* Complete COPY <sth> TO <sth> */
    2537          14 :     else if (Matches("COPY|\\copy", MatchAny, "TO", MatchAny))
    2538           0 :         COMPLETE_WITH("WITH (");
    2539             : 
    2540             :     /* Complete COPY <sth> FROM <sth> */
    2541          14 :     else if (Matches("COPY|\\copy", MatchAny, "FROM", MatchAny))
    2542           0 :         COMPLETE_WITH("WITH (", "WHERE");
    2543             : 
    2544             :     /* Complete COPY <sth> FROM|TO filename WITH ( */
    2545          14 :     else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
    2546           0 :         COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
    2547             :                       "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
    2548             :                       "FORCE_NOT_NULL", "FORCE_NULL", "ENCODING");
    2549             : 
    2550             :     /* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
    2551          14 :     else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "FORMAT"))
    2552           0 :         COMPLETE_WITH("binary", "csv", "text");
    2553             : 
    2554             :     /* Complete COPY <sth> FROM <sth> WITH (<options>) */
    2555          14 :     else if (Matches("COPY|\\copy", MatchAny, "FROM", MatchAny, "WITH", MatchAny))
    2556           0 :         COMPLETE_WITH("WHERE");
    2557             : 
    2558             :     /* CREATE ACCESS METHOD */
    2559             :     /* Complete "CREATE ACCESS METHOD <name>" */
    2560          14 :     else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny))
    2561           0 :         COMPLETE_WITH("TYPE");
    2562             :     /* Complete "CREATE ACCESS METHOD <name> TYPE" */
    2563          14 :     else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"))
    2564           0 :         COMPLETE_WITH("INDEX", "TABLE");
    2565             :     /* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
    2566          14 :     else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
    2567           0 :         COMPLETE_WITH("HANDLER");
    2568             : 
    2569             :     /* CREATE COLLATION */
    2570          14 :     else if (Matches("CREATE", "COLLATION", MatchAny))
    2571           0 :         COMPLETE_WITH("(", "FROM");
    2572          14 :     else if (Matches("CREATE", "COLLATION", MatchAny, "FROM"))
    2573           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations, NULL);
    2574          14 :     else if (HeadMatches("CREATE", "COLLATION", MatchAny, "(*"))
    2575             :     {
    2576           0 :         if (TailMatches("(|*,"))
    2577           0 :             COMPLETE_WITH("LOCALE =", "LC_COLLATE =", "LC_CTYPE =",
    2578             :                           "PROVIDER =", "DETERMINISTIC =");
    2579           0 :         else if (TailMatches("PROVIDER", "="))
    2580           0 :             COMPLETE_WITH("libc", "icu");
    2581           0 :         else if (TailMatches("DETERMINISTIC", "="))
    2582           0 :             COMPLETE_WITH("true", "false");
    2583             :     }
    2584             : 
    2585             :     /* CREATE DATABASE */
    2586          14 :     else if (Matches("CREATE", "DATABASE", MatchAny))
    2587           0 :         COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
    2588             :                       "IS_TEMPLATE",
    2589             :                       "ALLOW_CONNECTIONS", "CONNECTION LIMIT",
    2590             :                       "LC_COLLATE", "LC_CTYPE", "LOCALE");
    2591             : 
    2592          14 :     else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
    2593           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
    2594             : 
    2595             :     /* CREATE DOMAIN */
    2596          14 :     else if (Matches("CREATE", "DOMAIN", MatchAny))
    2597           0 :         COMPLETE_WITH("AS");
    2598          14 :     else if (Matches("CREATE", "DOMAIN", MatchAny, "AS"))
    2599           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    2600          14 :     else if (Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny))
    2601           0 :         COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
    2602             :                       "NOT NULL", "NULL", "CHECK (");
    2603          14 :     else if (Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"))
    2604           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations, NULL);
    2605             : 
    2606             :     /* CREATE EXTENSION */
    2607             :     /* Complete with available extensions rather than installed ones. */
    2608          14 :     else if (Matches("CREATE", "EXTENSION"))
    2609           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
    2610             :     /* CREATE EXTENSION <name> */
    2611          14 :     else if (Matches("CREATE", "EXTENSION", MatchAny))
    2612           0 :         COMPLETE_WITH("WITH SCHEMA", "CASCADE", "VERSION");
    2613             :     /* CREATE EXTENSION <name> VERSION */
    2614          14 :     else if (Matches("CREATE", "EXTENSION", MatchAny, "VERSION"))
    2615             :     {
    2616           0 :         completion_info_charp = prev2_wd;
    2617           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
    2618             :     }
    2619             : 
    2620             :     /* CREATE FOREIGN */
    2621          14 :     else if (Matches("CREATE", "FOREIGN"))
    2622           0 :         COMPLETE_WITH("DATA WRAPPER", "TABLE");
    2623             : 
    2624             :     /* CREATE FOREIGN DATA WRAPPER */
    2625          14 :     else if (Matches("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
    2626           0 :         COMPLETE_WITH("HANDLER", "VALIDATOR", "OPTIONS");
    2627             : 
    2628             :     /* CREATE INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */
    2629             :     /* First off we complete CREATE UNIQUE with "INDEX" */
    2630          14 :     else if (TailMatches("CREATE", "UNIQUE"))
    2631           0 :         COMPLETE_WITH("INDEX");
    2632             : 
    2633             :     /*
    2634             :      * If we have CREATE|UNIQUE INDEX, then add "ON", "CONCURRENTLY", and
    2635             :      * existing indexes
    2636             :      */
    2637          14 :     else if (TailMatches("CREATE|UNIQUE", "INDEX"))
    2638           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
    2639             :                                    " UNION SELECT 'ON'"
    2640             :                                    " UNION SELECT 'CONCURRENTLY'");
    2641             : 
    2642             :     /*
    2643             :      * Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of relations
    2644             :      * that indexes can be created on
    2645             :      */
    2646          28 :     else if (TailMatches("INDEX|CONCURRENTLY", MatchAny, "ON") ||
    2647          14 :              TailMatches("INDEX|CONCURRENTLY", "ON"))
    2648           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables, NULL);
    2649             : 
    2650             :     /*
    2651             :      * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing
    2652             :      * indexes
    2653             :      */
    2654          14 :     else if (TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY"))
    2655           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
    2656             :                                    " UNION SELECT 'ON'");
    2657             :     /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
    2658          28 :     else if (TailMatches("CREATE|UNIQUE", "INDEX", MatchAny) ||
    2659          14 :              TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny))
    2660           0 :         COMPLETE_WITH("ON");
    2661             : 
    2662             :     /*
    2663             :      * Complete INDEX <name> ON <table> with a list of table columns (which
    2664             :      * should really be in parens)
    2665             :      */
    2666          28 :     else if (TailMatches("INDEX", MatchAny, "ON", MatchAny) ||
    2667          14 :              TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny))
    2668           0 :         COMPLETE_WITH("(", "USING");
    2669          28 :     else if (TailMatches("INDEX", MatchAny, "ON", MatchAny, "(") ||
    2670          14 :              TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny, "("))
    2671           0 :         COMPLETE_WITH_ATTR(prev2_wd, "");
    2672             :     /* same if you put in USING */
    2673          14 :     else if (TailMatches("ON", MatchAny, "USING", MatchAny, "("))
    2674           0 :         COMPLETE_WITH_ATTR(prev4_wd, "");
    2675             :     /* Complete USING with an index method */
    2676          28 :     else if (TailMatches("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") ||
    2677          28 :              TailMatches("INDEX", MatchAny, "ON", MatchAny, "USING") ||
    2678          14 :              TailMatches("INDEX", "ON", MatchAny, "USING"))
    2679           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_index_access_methods);
    2680          14 :     else if (TailMatches("ON", MatchAny, "USING", MatchAny) &&
    2681           0 :              !TailMatches("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) &&
    2682           0 :              !TailMatches("FOR", MatchAny, MatchAny, MatchAny))
    2683           0 :         COMPLETE_WITH("(");
    2684             : 
    2685             :     /* CREATE OR REPLACE */
    2686          14 :     else if (Matches("CREATE", "OR"))
    2687           0 :         COMPLETE_WITH("REPLACE");
    2688             : 
    2689             :     /* CREATE POLICY */
    2690             :     /* Complete "CREATE POLICY <name> ON" */
    2691          14 :     else if (Matches("CREATE", "POLICY", MatchAny))
    2692           0 :         COMPLETE_WITH("ON");
    2693             :     /* Complete "CREATE POLICY <name> ON <table>" */
    2694          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON"))
    2695           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    2696             :     /* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */
    2697          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny))
    2698           0 :         COMPLETE_WITH("AS", "FOR", "TO", "USING (", "WITH CHECK (");
    2699             :     /* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */
    2700          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS"))
    2701           0 :         COMPLETE_WITH("PERMISSIVE", "RESTRICTIVE");
    2702             : 
    2703             :     /*
    2704             :      * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
    2705             :      * FOR|TO|USING|WITH CHECK
    2706             :      */
    2707          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny))
    2708           0 :         COMPLETE_WITH("FOR", "TO", "USING", "WITH CHECK");
    2709             :     /* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE */
    2710          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"))
    2711           0 :         COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
    2712             :     /* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */
    2713          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"))
    2714           0 :         COMPLETE_WITH("TO", "WITH CHECK (");
    2715             :     /* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE TO|USING" */
    2716          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"))
    2717           0 :         COMPLETE_WITH("TO", "USING (");
    2718             :     /* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */
    2719          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"))
    2720           0 :         COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
    2721             :     /* Complete "CREATE POLICY <name> ON <table> TO <role>" */
    2722          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"))
    2723           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
    2724             :     /* Complete "CREATE POLICY <name> ON <table> USING (" */
    2725          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
    2726           0 :         COMPLETE_WITH("(");
    2727             : 
    2728             :     /*
    2729             :      * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
    2730             :      * ALL|SELECT|INSERT|UPDATE|DELETE
    2731             :      */
    2732          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR"))
    2733           0 :         COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
    2734             : 
    2735             :     /*
    2736             :      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
    2737             :      * INSERT TO|WITH CHECK"
    2738             :      */
    2739          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "INSERT"))
    2740           0 :         COMPLETE_WITH("TO", "WITH CHECK (");
    2741             : 
    2742             :     /*
    2743             :      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
    2744             :      * SELECT|DELETE TO|USING"
    2745             :      */
    2746          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "SELECT|DELETE"))
    2747           0 :         COMPLETE_WITH("TO", "USING (");
    2748             : 
    2749             :     /*
    2750             :      * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
    2751             :      * ALL|UPDATE TO|USING|WITH CHECK
    2752             :      */
    2753          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "ALL|UPDATE"))
    2754           0 :         COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
    2755             : 
    2756             :     /*
    2757             :      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE TO
    2758             :      * <role>"
    2759             :      */
    2760          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "TO"))
    2761           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
    2762             : 
    2763             :     /*
    2764             :      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
    2765             :      * USING ("
    2766             :      */
    2767          14 :     else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "USING"))
    2768           0 :         COMPLETE_WITH("(");
    2769             : 
    2770             : 
    2771             : /* CREATE PUBLICATION */
    2772          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny))
    2773           0 :         COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR ALL TABLES IN SCHEMA", "WITH (");
    2774          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR"))
    2775           0 :         COMPLETE_WITH("TABLE", "ALL TABLES", "ALL TABLES IN SCHEMA");
    2776          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"))
    2777           0 :         COMPLETE_WITH("TABLES", "TABLES IN SCHEMA");
    2778          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"))
    2779           0 :         COMPLETE_WITH("IN SCHEMA", "WITH (");
    2780          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny))
    2781           0 :         COMPLETE_WITH("WITH (");
    2782             :     /* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
    2783          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
    2784           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    2785             : 
    2786             :     /*
    2787             :      * Complete "CREATE PUBLICATION <name> FOR ALL TABLES IN SCHEMA <schema>,
    2788             :      * ..."
    2789             :      */
    2790          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA"))
    2791           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_schemas
    2792             :                             " AND nspname != 'pg_catalog' "
    2793             :                             " AND nspname not like 'pg\\_toast%%' "
    2794             :                             " AND nspname not like 'pg\\_temp%%' "
    2795             :                             " UNION SELECT 'CURRENT_SCHEMA' ");
    2796          14 :     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ',')))
    2797           0 :         COMPLETE_WITH("WITH (");
    2798             :     /* Complete "CREATE PUBLICATION <name> [...] WITH" */
    2799          14 :     else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))
    2800           0 :         COMPLETE_WITH("publish", "publish_via_partition_root");
    2801             : 
    2802             : /* CREATE RULE */
    2803             :     /* Complete "CREATE [ OR REPLACE ] RULE <sth>" with "AS ON" */
    2804          28 :     else if (Matches("CREATE", "RULE", MatchAny) ||
    2805          14 :              Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny))
    2806           0 :         COMPLETE_WITH("AS ON");
    2807             :     /* Complete "CREATE [ OR REPLACE ] RULE <sth> AS" with "ON" */
    2808          28 :     else if (Matches("CREATE", "RULE", MatchAny, "AS") ||
    2809          14 :              Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS"))
    2810           0 :         COMPLETE_WITH("ON");
    2811             : 
    2812             :     /*
    2813             :      * Complete "CREATE [ OR REPLACE ] RULE <sth> AS ON" with
    2814             :      * SELECT|UPDATE|INSERT|DELETE
    2815             :      */
    2816          28 :     else if (Matches("CREATE", "RULE", MatchAny, "AS", "ON") ||
    2817          14 :              Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS", "ON"))
    2818           0 :         COMPLETE_WITH("SELECT", "UPDATE", "INSERT", "DELETE");
    2819             :     /* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */
    2820          14 :     else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE"))
    2821           0 :         COMPLETE_WITH("TO");
    2822             :     /* Complete "AS ON <sth> TO" with a table name */
    2823          14 :     else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
    2824           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    2825             : 
    2826             : /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
    2827          28 :     else if (TailMatches("CREATE", "SEQUENCE", MatchAny) ||
    2828          14 :              TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
    2829           0 :         COMPLETE_WITH("AS", "INCREMENT BY", "MINVALUE", "MAXVALUE", "NO",
    2830             :                       "CACHE", "CYCLE", "OWNED BY", "START WITH");
    2831          28 :     else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
    2832          14 :              TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"))
    2833           0 :         COMPLETE_WITH_CS("smallint", "integer", "bigint");
    2834          28 :     else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "NO") ||
    2835          14 :              TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
    2836           0 :         COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
    2837             : 
    2838             : /* CREATE SERVER <name> */
    2839          14 :     else if (Matches("CREATE", "SERVER", MatchAny))
    2840           0 :         COMPLETE_WITH("TYPE", "VERSION", "FOREIGN DATA WRAPPER");
    2841             : 
    2842             : /* CREATE STATISTICS <name> */
    2843          14 :     else if (Matches("CREATE", "STATISTICS", MatchAny))
    2844           0 :         COMPLETE_WITH("(", "ON");
    2845          14 :     else if (Matches("CREATE", "STATISTICS", MatchAny, "("))
    2846           0 :         COMPLETE_WITH("ndistinct", "dependencies", "mcv");
    2847          14 :     else if (Matches("CREATE", "STATISTICS", MatchAny, "(*)"))
    2848           0 :         COMPLETE_WITH("ON");
    2849          14 :     else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
    2850           0 :              TailMatches("FROM"))
    2851           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    2852             : 
    2853             : /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
    2854             :     /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
    2855          14 :     else if (TailMatches("CREATE", "TEMP|TEMPORARY"))
    2856           0 :         COMPLETE_WITH("SEQUENCE", "TABLE", "VIEW");
    2857             :     /* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */
    2858          14 :     else if (TailMatches("CREATE", "UNLOGGED"))
    2859           0 :         COMPLETE_WITH("TABLE", "MATERIALIZED VIEW");
    2860             :     /* Complete PARTITION BY with RANGE ( or LIST ( or ... */
    2861          14 :     else if (TailMatches("PARTITION", "BY"))
    2862           0 :         COMPLETE_WITH("RANGE (", "LIST (", "HASH (");
    2863             :     /* If we have xxx PARTITION OF, provide a list of partitioned tables */
    2864          14 :     else if (TailMatches("PARTITION", "OF"))
    2865           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, "");
    2866             :     /* Limited completion support for partition bound specification */
    2867          14 :     else if (TailMatches("PARTITION", "OF", MatchAny))
    2868           0 :         COMPLETE_WITH("FOR VALUES", "DEFAULT");
    2869             :     /* Complete CREATE TABLE <name> with '(', OF or PARTITION OF */
    2870          28 :     else if (TailMatches("CREATE", "TABLE", MatchAny) ||
    2871          14 :              TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny))
    2872           0 :         COMPLETE_WITH("(", "OF", "PARTITION OF");
    2873             :     /* Complete CREATE TABLE <name> OF with list of composite types */
    2874          28 :     else if (TailMatches("CREATE", "TABLE", MatchAny, "OF") ||
    2875          14 :              TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "OF"))
    2876           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes, NULL);
    2877             :     /* Complete CREATE TABLE name (...) with supported options */
    2878          28 :     else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)") ||
    2879          14 :              TailMatches("CREATE", "UNLOGGED", "TABLE", MatchAny, "(*)"))
    2880           0 :         COMPLETE_WITH("INHERITS (", "PARTITION BY", "USING", "TABLESPACE", "WITH (");
    2881          14 :     else if (TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)"))
    2882           0 :         COMPLETE_WITH("INHERITS (", "ON COMMIT", "PARTITION BY",
    2883             :                       "TABLESPACE", "WITH (");
    2884             :     /* Complete CREATE TABLE (...) USING with table access methods */
    2885          28 :     else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)", "USING") ||
    2886          14 :              TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "USING"))
    2887           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
    2888             :     /* Complete CREATE TABLE (...) WITH with storage parameters */
    2889          28 :     else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)", "WITH", "(") ||
    2890          14 :              TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "WITH", "("))
    2891           0 :         COMPLETE_WITH_LIST(table_storage_parameters);
    2892             :     /* Complete CREATE TABLE ON COMMIT with actions */
    2893          14 :     else if (TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)", "ON", "COMMIT"))
    2894           0 :         COMPLETE_WITH("DELETE ROWS", "DROP", "PRESERVE ROWS");
    2895             : 
    2896             : /* CREATE TABLESPACE */
    2897          14 :     else if (Matches("CREATE", "TABLESPACE", MatchAny))
    2898           0 :         COMPLETE_WITH("OWNER", "LOCATION");
    2899             :     /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
    2900          14 :     else if (Matches("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny))
    2901           0 :         COMPLETE_WITH("LOCATION");
    2902             : 
    2903             : /* CREATE TEXT SEARCH */
    2904          14 :     else if (Matches("CREATE", "TEXT", "SEARCH"))
    2905           0 :         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
    2906          14 :     else if (Matches("CREATE", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
    2907           0 :         COMPLETE_WITH("(");
    2908             : 
    2909             : /* CREATE TRANSFORM */
    2910          28 :     else if (Matches("CREATE", "TRANSFORM") ||
    2911          14 :              Matches("CREATE", "OR", "REPLACE", "TRANSFORM"))
    2912           0 :         COMPLETE_WITH("FOR");
    2913          28 :     else if (Matches("CREATE", "TRANSFORM", "FOR") ||
    2914          14 :              Matches("CREATE","OR", "REPLACE", "TRANSFORM", "FOR"))
    2915           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    2916          28 :     else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny) ||
    2917          14 :              Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny))
    2918           0 :         COMPLETE_WITH("LANGUAGE");
    2919          28 :     else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE") ||
    2920          14 :              Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
    2921             :     {
    2922           0 :         completion_info_charp = prev2_wd;
    2923           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
    2924             :     }
    2925             : 
    2926             : /* CREATE SUBSCRIPTION */
    2927          14 :     else if (Matches("CREATE", "SUBSCRIPTION", MatchAny))
    2928           0 :         COMPLETE_WITH("CONNECTION");
    2929          14 :     else if (Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny))
    2930           0 :         COMPLETE_WITH("PUBLICATION");
    2931          14 :     else if (Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION",
    2932             :                      MatchAny, "PUBLICATION"))
    2933             :     {
    2934             :         /* complete with nothing here as this refers to remote publications */
    2935             :     }
    2936          14 :     else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("PUBLICATION", MatchAny))
    2937           0 :         COMPLETE_WITH("WITH (");
    2938             :     /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
    2939          14 :     else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("WITH", "("))
    2940           0 :         COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
    2941             :                       "enabled", "slot_name", "streaming",
    2942             :                       "synchronous_commit", "two_phase");
    2943             : 
    2944             : /* CREATE TRIGGER --- is allowed inside CREATE SCHEMA, so use TailMatches */
    2945             : 
    2946             :     /*
    2947             :      * Complete CREATE [ OR REPLACE ] TRIGGER <name> with BEFORE|AFTER|INSTEAD
    2948             :      * OF.
    2949             :      */
    2950          28 :     else if (TailMatches("CREATE", "TRIGGER", MatchAny) ||
    2951          14 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny))
    2952           0 :         COMPLETE_WITH("BEFORE", "AFTER", "INSTEAD OF");
    2953             : 
    2954             :     /*
    2955             :      * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER with an
    2956             :      * event.
    2957             :      */
    2958          28 :     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER") ||
    2959          14 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER"))
    2960           0 :         COMPLETE_WITH("INSERT", "DELETE", "UPDATE", "TRUNCATE");
    2961             :     /* Complete CREATE [ OR REPLACE ] TRIGGER <name> INSTEAD OF with an event */
    2962          28 :     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF") ||
    2963          14 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF"))
    2964           0 :         COMPLETE_WITH("INSERT", "DELETE", "UPDATE");
    2965             : 
    2966             :     /*
    2967             :      * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER sth with
    2968             :      * OR|ON.
    2969             :      */
    2970          28 :     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
    2971          28 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
    2972          28 :              TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny) ||
    2973          14 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny))
    2974           0 :         COMPLETE_WITH("ON", "OR");
    2975             : 
    2976             :     /*
    2977             :      * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER event ON
    2978             :      * with a list of tables.  EXECUTE FUNCTION is the recommended grammar
    2979             :      * instead of EXECUTE PROCEDURE in version 11 and upwards.
    2980             :      */
    2981          28 :     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON") ||
    2982          14 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
    2983           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    2984             : 
    2985             :     /*
    2986             :      * Complete CREATE [ OR REPLACE ] TRIGGER ... INSTEAD OF event ON with a
    2987             :      * list of views.
    2988             :      */
    2989          28 :     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON") ||
    2990          14 :              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
    2991           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
    2992          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    2993          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    2994           0 :              TailMatches("ON", MatchAny))
    2995             :     {
    2996           0 :         if (pset.sversion >= 110000)
    2997           0 :             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
    2998             :                           "REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
    2999             :         else
    3000           0 :             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
    3001             :                           "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
    3002             :     }
    3003          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3004          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3005           0 :              (TailMatches("DEFERRABLE") || TailMatches("INITIALLY", "IMMEDIATE|DEFERRED")))
    3006             :     {
    3007           0 :         if (pset.sversion >= 110000)
    3008           0 :             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
    3009             :         else
    3010           0 :             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
    3011             :     }
    3012          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3013          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3014           0 :              TailMatches("REFERENCING"))
    3015           0 :         COMPLETE_WITH("OLD TABLE", "NEW TABLE");
    3016          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3017          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3018           0 :              TailMatches("OLD|NEW", "TABLE"))
    3019           0 :         COMPLETE_WITH("AS");
    3020          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3021          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3022           0 :              (TailMatches("REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
    3023           0 :               TailMatches("REFERENCING", "OLD", "TABLE", MatchAny)))
    3024             :     {
    3025           0 :         if (pset.sversion >= 110000)
    3026           0 :             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
    3027             :         else
    3028           0 :             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
    3029             :     }
    3030          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3031          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3032           0 :              (TailMatches("REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
    3033           0 :               TailMatches("REFERENCING", "NEW", "TABLE", MatchAny)))
    3034             :     {
    3035           0 :         if (pset.sversion >= 110000)
    3036           0 :             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
    3037             :         else
    3038           0 :             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
    3039             :     }
    3040          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3041          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3042           0 :              (TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
    3043           0 :               TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
    3044           0 :               TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
    3045           0 :               TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny)))
    3046             :     {
    3047           0 :         if (pset.sversion >= 110000)
    3048           0 :             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
    3049             :         else
    3050           0 :             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
    3051             :     }
    3052          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3053          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3054           0 :              TailMatches("FOR"))
    3055           0 :         COMPLETE_WITH("EACH", "ROW", "STATEMENT");
    3056          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3057          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3058           0 :              TailMatches("FOR", "EACH"))
    3059           0 :         COMPLETE_WITH("ROW", "STATEMENT");
    3060          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3061          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3062           0 :              (TailMatches("FOR", "EACH", "ROW|STATEMENT") ||
    3063           0 :               TailMatches("FOR", "ROW|STATEMENT")))
    3064             :     {
    3065           0 :         if (pset.sversion >= 110000)
    3066           0 :             COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
    3067             :         else
    3068           0 :             COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
    3069             :     }
    3070          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3071          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3072           0 :              TailMatches("WHEN", "(*)"))
    3073             :     {
    3074           0 :         if (pset.sversion >= 110000)
    3075           0 :             COMPLETE_WITH("EXECUTE FUNCTION");
    3076             :         else
    3077           0 :             COMPLETE_WITH("EXECUTE PROCEDURE");
    3078             :     }
    3079             : 
    3080             :     /*
    3081             :      * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
    3082             :      * PROCEDURE|FUNCTION.
    3083             :      */
    3084          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3085          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3086           0 :              TailMatches("EXECUTE"))
    3087             :     {
    3088           0 :         if (pset.sversion >= 110000)
    3089           0 :             COMPLETE_WITH("FUNCTION");
    3090             :         else
    3091           0 :             COMPLETE_WITH("PROCEDURE");
    3092             :     }
    3093          28 :     else if ((HeadMatches("CREATE", "TRIGGER") ||
    3094          14 :               HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
    3095           0 :              TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
    3096           0 :         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
    3097             : 
    3098             : /* CREATE ROLE,USER,GROUP <name> */
    3099          14 :     else if (Matches("CREATE", "ROLE|GROUP|USER", MatchAny) &&
    3100           0 :              !TailMatches("USER", "MAPPING"))
    3101           0 :         COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
    3102             :                       "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
    3103             :                       "LOGIN", "NOBYPASSRLS",
    3104             :                       "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
    3105             :                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
    3106             :                       "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
    3107             :                       "VALID UNTIL", "WITH");
    3108             : 
    3109             : /* CREATE ROLE,USER,GROUP <name> WITH */
    3110          14 :     else if (Matches("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"))
    3111             :         /* Similar to the above, but don't complete "WITH" again. */
    3112           0 :         COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
    3113             :                       "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
    3114             :                       "LOGIN", "NOBYPASSRLS",
    3115             :                       "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
    3116             :                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
    3117             :                       "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
    3118             :                       "VALID UNTIL");
    3119             : 
    3120             :     /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
    3121          14 :     else if (Matches("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
    3122           0 :         COMPLETE_WITH("GROUP", "ROLE");
    3123             : 
    3124             : /* CREATE TYPE */
    3125          14 :     else if (Matches("CREATE", "TYPE", MatchAny))
    3126           0 :         COMPLETE_WITH("(", "AS");
    3127          14 :     else if (Matches("CREATE", "TYPE", MatchAny, "AS"))
    3128           0 :         COMPLETE_WITH("ENUM", "RANGE", "(");
    3129          14 :     else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "("))
    3130             :     {
    3131           0 :         if (TailMatches("(|*,", MatchAny))
    3132           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    3133           0 :         else if (TailMatches("(|*,", MatchAny, MatchAnyExcept("*)")))
    3134           0 :             COMPLETE_WITH("COLLATE", ",", ")");
    3135             :     }
    3136          14 :     else if (Matches("CREATE", "TYPE", MatchAny, "AS", "ENUM|RANGE"))
    3137           0 :         COMPLETE_WITH("(");
    3138          14 :     else if (HeadMatches("CREATE", "TYPE", MatchAny, "("))
    3139             :     {
    3140           0 :         if (TailMatches("(|*,"))
    3141           0 :             COMPLETE_WITH("INPUT", "OUTPUT", "RECEIVE", "SEND",
    3142             :                           "TYPMOD_IN", "TYPMOD_OUT", "ANALYZE", "SUBSCRIPT",
    3143             :                           "INTERNALLENGTH", "PASSEDBYVALUE", "ALIGNMENT",
    3144             :                           "STORAGE", "LIKE", "CATEGORY", "PREFERRED",
    3145             :                           "DEFAULT", "ELEMENT", "DELIMITER",
    3146             :                           "COLLATABLE");
    3147           0 :         else if (TailMatches("(*|*,", MatchAnyExcept("*=")))
    3148           0 :             COMPLETE_WITH("=");
    3149           0 :         else if (TailMatches("=", MatchAnyExcept("*)")))
    3150           0 :             COMPLETE_WITH(",", ")");
    3151             :     }
    3152          14 :     else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "RANGE", "("))
    3153             :     {
    3154           0 :         if (TailMatches("(|*,"))
    3155           0 :             COMPLETE_WITH("SUBTYPE", "SUBTYPE_OPCLASS", "COLLATION",
    3156             :                           "CANONICAL", "SUBTYPE_DIFF",
    3157             :                           "MULTIRANGE_TYPE_NAME");
    3158           0 :         else if (TailMatches("(*|*,", MatchAnyExcept("*=")))
    3159           0 :             COMPLETE_WITH("=");
    3160           0 :         else if (TailMatches("=", MatchAnyExcept("*)")))
    3161           0 :             COMPLETE_WITH(",", ")");
    3162             :     }
    3163             : 
    3164             : /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
    3165             :     /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS */
    3166          28 :     else if (TailMatches("CREATE", "VIEW", MatchAny) ||
    3167          14 :              TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny))
    3168           0 :         COMPLETE_WITH("AS");
    3169             :     /* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */
    3170          28 :     else if (TailMatches("CREATE", "VIEW", MatchAny, "AS") ||
    3171          14 :              TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS"))
    3172           0 :         COMPLETE_WITH("SELECT");
    3173             : 
    3174             : /* CREATE MATERIALIZED VIEW */
    3175          14 :     else if (Matches("CREATE", "MATERIALIZED"))
    3176           0 :         COMPLETE_WITH("VIEW");
    3177             :     /* Complete CREATE MATERIALIZED VIEW <name> with AS */
    3178          14 :     else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny))
    3179           0 :         COMPLETE_WITH("AS");
    3180             :     /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
    3181          14 :     else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"))
    3182           0 :         COMPLETE_WITH("SELECT");
    3183             : 
    3184             : /* CREATE EVENT TRIGGER */
    3185          14 :     else if (Matches("CREATE", "EVENT"))
    3186           0 :         COMPLETE_WITH("TRIGGER");
    3187             :     /* Complete CREATE EVENT TRIGGER <name> with ON */
    3188          14 :     else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny))
    3189           0 :         COMPLETE_WITH("ON");
    3190             :     /* Complete CREATE EVENT TRIGGER <name> ON with event_type */
    3191          14 :     else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
    3192           0 :         COMPLETE_WITH("ddl_command_start", "ddl_command_end", "sql_drop");
    3193             : 
    3194             :     /*
    3195             :      * Complete CREATE EVENT TRIGGER <name> ON <event_type>.  EXECUTE FUNCTION
    3196             :      * is the recommended grammar instead of EXECUTE PROCEDURE in version 11
    3197             :      * and upwards.
    3198             :      */
    3199          14 :     else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON", MatchAny))
    3200             :     {
    3201           0 :         if (pset.sversion >= 110000)
    3202           0 :             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE FUNCTION");
    3203             :         else
    3204           0 :             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
    3205             :     }
    3206          14 :     else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
    3207           0 :              TailMatches("WHEN|AND", MatchAny, "IN", "(*)"))
    3208             :     {
    3209           0 :         if (pset.sversion >= 110000)
    3210           0 :             COMPLETE_WITH("EXECUTE FUNCTION");
    3211             :         else
    3212           0 :             COMPLETE_WITH("EXECUTE PROCEDURE");
    3213             :     }
    3214          14 :     else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
    3215           0 :              TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
    3216           0 :         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
    3217             : 
    3218             : /* DEALLOCATE */
    3219          14 :     else if (Matches("DEALLOCATE"))
    3220           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements
    3221             :                             " UNION SELECT 'ALL'");
    3222             : 
    3223             : /* DECLARE */
    3224             : 
    3225             :     /*
    3226             :      * Complete DECLARE <name> with one of BINARY, ASENSITIVE, INSENSITIVE,
    3227             :      * SCROLL, NO SCROLL, and CURSOR.
    3228             :      */
    3229          14 :     else if (Matches("DECLARE", MatchAny))
    3230           0 :         COMPLETE_WITH("BINARY", "ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL",
    3231             :                       "CURSOR");
    3232             : 
    3233             :     /*
    3234             :      * Complete DECLARE ... <option> with other options. The PostgreSQL parser
    3235             :      * allows DECLARE options to be specified in any order. But the
    3236             :      * tab-completion follows the ordering of them that the SQL standard
    3237             :      * provides, like the syntax of DECLARE command in the documentation
    3238             :      * indicates.
    3239             :      */
    3240          14 :     else if (HeadMatches("DECLARE") && TailMatches("BINARY"))
    3241           0 :         COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
    3242          14 :     else if (HeadMatches("DECLARE") && TailMatches("ASENSITIVE|INSENSITIVE"))
    3243           0 :         COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
    3244          14 :     else if (HeadMatches("DECLARE") && TailMatches("SCROLL"))
    3245           0 :         COMPLETE_WITH("CURSOR");
    3246             :     /* Complete DECLARE ... [options] NO with SCROLL */
    3247          14 :     else if (HeadMatches("DECLARE") && TailMatches("NO"))
    3248           0 :         COMPLETE_WITH("SCROLL");
    3249             : 
    3250             :     /*
    3251             :      * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and
    3252             :      * FOR
    3253             :      */
    3254          14 :     else if (HeadMatches("DECLARE") && TailMatches("CURSOR"))
    3255           0 :         COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
    3256             :     /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
    3257          14 :     else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT"))
    3258           0 :         COMPLETE_WITH("HOLD");
    3259             :     /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
    3260          14 :     else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT", "HOLD"))
    3261           0 :         COMPLETE_WITH("FOR");
    3262             : 
    3263             : /* DELETE --- can be inside EXPLAIN, RULE, etc */
    3264             :     /* Complete DELETE with "FROM" */
    3265          14 :     else if (Matches("DELETE"))
    3266           0 :         COMPLETE_WITH("FROM");
    3267             :     /* Complete DELETE FROM with a list of tables */
    3268          14 :     else if (TailMatches("DELETE", "FROM"))
    3269           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
    3270             :     /* Complete DELETE FROM <table> */
    3271          14 :     else if (TailMatches("DELETE", "FROM", MatchAny))
    3272           0 :         COMPLETE_WITH("USING", "WHERE");
    3273             :     /* XXX: implement tab completion for DELETE ... USING */
    3274             : 
    3275             : /* DISCARD */
    3276          14 :     else if (Matches("DISCARD"))
    3277           0 :         COMPLETE_WITH("ALL", "PLANS", "SEQUENCES", "TEMP");
    3278             : 
    3279             : /* DO */
    3280          14 :     else if (Matches("DO"))
    3281           0 :         COMPLETE_WITH("LANGUAGE");
    3282             : 
    3283             : /* DROP */
    3284             :     /* Complete DROP object with CASCADE / RESTRICT */
    3285          14 :     else if (Matches("DROP",
    3286             :                      "COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
    3287          14 :                      MatchAny) ||
    3288          28 :              Matches("DROP", "ACCESS", "METHOD", MatchAny) ||
    3289          14 :              (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
    3290          14 :               ends_with(prev_wd, ')')) ||
    3291          28 :              Matches("DROP", "EVENT", "TRIGGER", MatchAny) ||
    3292          28 :              Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
    3293          28 :              Matches("DROP", "FOREIGN", "TABLE", MatchAny) ||
    3294          14 :              Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
    3295           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3296             : 
    3297             :     /* help completing some of the variants */
    3298          14 :     else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
    3299           0 :         COMPLETE_WITH("(");
    3300          14 :     else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, "("))
    3301           0 :         COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
    3302          14 :     else if (Matches("DROP", "FOREIGN"))
    3303           0 :         COMPLETE_WITH("DATA WRAPPER", "TABLE");
    3304          14 :     else if (Matches("DROP", "DATABASE", MatchAny))
    3305           0 :         COMPLETE_WITH("WITH (");
    3306          14 :     else if (HeadMatches("DROP", "DATABASE") && (ends_with(prev_wd, '(')))
    3307           0 :         COMPLETE_WITH("FORCE");
    3308             : 
    3309             :     /* DROP INDEX */
    3310          14 :     else if (Matches("DROP", "INDEX"))
    3311           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
    3312             :                                    " UNION SELECT 'CONCURRENTLY'");
    3313          14 :     else if (Matches("DROP", "INDEX", "CONCURRENTLY"))
    3314           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
    3315          14 :     else if (Matches("DROP", "INDEX", MatchAny))
    3316           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3317          14 :     else if (Matches("DROP", "INDEX", "CONCURRENTLY", MatchAny))
    3318           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3319             : 
    3320             :     /* DROP MATERIALIZED VIEW */
    3321          14 :     else if (Matches("DROP", "MATERIALIZED"))
    3322           0 :         COMPLETE_WITH("VIEW");
    3323          14 :     else if (Matches("DROP", "MATERIALIZED", "VIEW"))
    3324           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
    3325          14 :     else if (Matches("DROP", "MATERIALIZED", "VIEW", MatchAny))
    3326           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3327             : 
    3328             :     /* DROP OWNED BY */
    3329          14 :     else if (Matches("DROP", "OWNED"))
    3330           0 :         COMPLETE_WITH("BY");
    3331          14 :     else if (Matches("DROP", "OWNED", "BY"))
    3332           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    3333          14 :     else if (Matches("DROP", "OWNED", "BY", MatchAny))
    3334           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3335             : 
    3336             :     /* DROP TEXT SEARCH */
    3337          14 :     else if (Matches("DROP", "TEXT", "SEARCH"))
    3338           0 :         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
    3339             : 
    3340             :     /* DROP TRIGGER */
    3341          14 :     else if (Matches("DROP", "TRIGGER", MatchAny))
    3342           0 :         COMPLETE_WITH("ON");
    3343          14 :     else if (Matches("DROP", "TRIGGER", MatchAny, "ON"))
    3344             :     {
    3345           0 :         completion_info_charp = prev2_wd;
    3346           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
    3347             :     }
    3348          14 :     else if (Matches("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
    3349           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3350             : 
    3351             :     /* DROP ACCESS METHOD */
    3352          14 :     else if (Matches("DROP", "ACCESS"))
    3353           0 :         COMPLETE_WITH("METHOD");
    3354          14 :     else if (Matches("DROP", "ACCESS", "METHOD"))
    3355           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
    3356             : 
    3357             :     /* DROP EVENT TRIGGER */
    3358          14 :     else if (Matches("DROP", "EVENT"))
    3359           0 :         COMPLETE_WITH("TRIGGER");
    3360          14 :     else if (Matches("DROP", "EVENT", "TRIGGER"))
    3361           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
    3362             : 
    3363             :     /* DROP POLICY <name>  */
    3364          14 :     else if (Matches("DROP", "POLICY"))
    3365           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_policies);
    3366             :     /* DROP POLICY <name> ON */
    3367          14 :     else if (Matches("DROP", "POLICY", MatchAny))
    3368           0 :         COMPLETE_WITH("ON");
    3369             :     /* DROP POLICY <name> ON <table> */
    3370          14 :     else if (Matches("DROP", "POLICY", MatchAny, "ON"))
    3371             :     {
    3372           0 :         completion_info_charp = prev2_wd;
    3373           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);
    3374             :     }
    3375          14 :     else if (Matches("DROP", "POLICY", MatchAny, "ON", MatchAny))
    3376           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3377             : 
    3378             :     /* DROP RULE */
    3379          14 :     else if (Matches("DROP", "RULE", MatchAny))
    3380           0 :         COMPLETE_WITH("ON");
    3381          14 :     else if (Matches("DROP", "RULE", MatchAny, "ON"))
    3382             :     {
    3383           0 :         completion_info_charp = prev2_wd;
    3384           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
    3385             :     }
    3386          14 :     else if (Matches("DROP", "RULE", MatchAny, "ON", MatchAny))
    3387           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3388             : 
    3389             :     /* DROP TRANSFORM */
    3390          14 :     else if (Matches("DROP", "TRANSFORM"))
    3391           0 :         COMPLETE_WITH("FOR");
    3392          14 :     else if (Matches("DROP", "TRANSFORM", "FOR"))
    3393           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    3394          14 :     else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny))
    3395           0 :         COMPLETE_WITH("LANGUAGE");
    3396          14 :     else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
    3397             :     {
    3398           0 :         completion_info_charp = prev2_wd;
    3399           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
    3400             :     }
    3401          14 :     else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE", MatchAny))
    3402           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    3403             : 
    3404             : /* EXECUTE */
    3405          14 :     else if (Matches("EXECUTE"))
    3406           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
    3407             : 
    3408             : /*
    3409             :  * EXPLAIN [ ( option [, ...] ) ] statement
    3410             :  * EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
    3411             :  */
    3412          14 :     else if (Matches("EXPLAIN"))
    3413           0 :         COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
    3414             :                       "EXECUTE", "ANALYZE", "VERBOSE");
    3415          14 :     else if (HeadMatches("EXPLAIN", "(*") &&
    3416           0 :              !HeadMatches("EXPLAIN", "(*)"))
    3417             :     {
    3418             :         /*
    3419             :          * This fires if we're in an unfinished parenthesized option list.
    3420             :          * get_previous_words treats a completed parenthesized option list as
    3421             :          * one word, so the above test is correct.
    3422             :          */
    3423           0 :         if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    3424           0 :             COMPLETE_WITH("ANALYZE", "VERBOSE", "COSTS", "SETTINGS",
    3425             :                           "BUFFERS", "WAL", "TIMING", "SUMMARY", "FORMAT");
    3426           0 :         else if (TailMatches("ANALYZE|VERBOSE|COSTS|SETTINGS|BUFFERS|WAL|TIMING|SUMMARY"))
    3427           0 :             COMPLETE_WITH("ON", "OFF");
    3428           0 :         else if (TailMatches("FORMAT"))
    3429           0 :             COMPLETE_WITH("TEXT", "XML", "JSON", "YAML");
    3430             :     }
    3431          14 :     else if (Matches("EXPLAIN", "ANALYZE"))
    3432           0 :         COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
    3433             :                       "EXECUTE", "VERBOSE");
    3434          28 :     else if (Matches("EXPLAIN", "(*)") ||
    3435          28 :              Matches("EXPLAIN", "VERBOSE") ||
    3436          14 :              Matches("EXPLAIN", "ANALYZE", "VERBOSE"))
    3437           0 :         COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
    3438             :                       "EXECUTE");
    3439             : 
    3440             : /* FETCH && MOVE */
    3441             : 
    3442             :     /*
    3443             :      * Complete FETCH with one of ABSOLUTE, BACKWARD, FORWARD, RELATIVE, ALL,
    3444             :      * NEXT, PRIOR, FIRST, LAST, FROM, IN, and a list of cursors
    3445             :      */
    3446          14 :     else if (Matches("FETCH|MOVE"))
    3447           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_cursors
    3448             :                             " UNION SELECT 'ABSOLUTE'"
    3449             :                             " UNION SELECT 'BACKWARD'"
    3450             :                             " UNION SELECT 'FORWARD'"
    3451             :                             " UNION SELECT 'RELATIVE'"
    3452             :                             " UNION SELECT 'ALL'"
    3453             :                             " UNION SELECT 'NEXT'"
    3454             :                             " UNION SELECT 'PRIOR'"
    3455             :                             " UNION SELECT 'FIRST'"
    3456             :                             " UNION SELECT 'LAST'"
    3457             :                             " UNION SELECT 'FROM'"
    3458             :                             " UNION SELECT 'IN'");
    3459             : 
    3460             :     /*
    3461             :      * Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN, and a
    3462             :      * list of cursors
    3463             :      */
    3464          14 :     else if (Matches("FETCH|MOVE", "BACKWARD|FORWARD"))
    3465           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_cursors
    3466             :                             " UNION SELECT 'ALL'"
    3467             :                             " UNION SELECT 'FROM'"
    3468             :                             " UNION SELECT 'IN'");
    3469             : 
    3470             :     /*
    3471             :      * Complete FETCH <direction> with "FROM" or "IN". These are equivalent,
    3472             :      * but we may as well tab-complete both: perhaps some users prefer one
    3473             :      * variant or the other.
    3474             :      */
    3475          14 :     else if (Matches("FETCH|MOVE", "ABSOLUTE|BACKWARD|FORWARD|RELATIVE",
    3476          14 :                      MatchAnyExcept("FROM|IN")) ||
    3477          14 :              Matches("FETCH|MOVE", "ALL|NEXT|PRIOR|FIRST|LAST"))
    3478           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_cursors
    3479             :                             " UNION SELECT 'FROM'"
    3480             :                             " UNION SELECT 'IN'");
    3481             :     /* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
    3482          14 :     else if (HeadMatches("FETCH|MOVE") &&
    3483           0 :              TailMatches("FROM|IN"))
    3484           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_cursors);
    3485             : 
    3486             : /* FOREIGN DATA WRAPPER */
    3487             :     /* applies in ALTER/DROP FDW and in CREATE SERVER */
    3488          14 :     else if (TailMatches("FOREIGN", "DATA", "WRAPPER") &&
    3489           0 :              !TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
    3490           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
    3491             :     /* applies in CREATE SERVER */
    3492          14 :     else if (TailMatches("FOREIGN", "DATA", "WRAPPER", MatchAny) &&
    3493           0 :              HeadMatches("CREATE", "SERVER"))
    3494           0 :         COMPLETE_WITH("OPTIONS");
    3495             : 
    3496             : /* FOREIGN TABLE */
    3497          14 :     else if (TailMatches("FOREIGN", "TABLE") &&
    3498           0 :              !TailMatches("CREATE", MatchAny, MatchAny))
    3499           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
    3500             : 
    3501             : /* FOREIGN SERVER */
    3502          14 :     else if (TailMatches("FOREIGN", "SERVER"))
    3503           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_servers);
    3504             : 
    3505             : /*
    3506             :  * GRANT and REVOKE are allowed inside CREATE SCHEMA and
    3507             :  * ALTER DEFAULT PRIVILEGES, so use TailMatches
    3508             :  */
    3509             :     /* Complete GRANT/REVOKE with a list of roles and privileges */
    3510          14 :     else if (TailMatches("GRANT|REVOKE"))
    3511             :     {
    3512             :         /*
    3513             :          * With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
    3514             :          * privileges (can't grant roles)
    3515             :          */
    3516           0 :         if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
    3517           0 :             COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
    3518             :                           "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
    3519             :                           "EXECUTE", "USAGE", "ALL");
    3520             :         else
    3521           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_roles
    3522             :                                 " UNION SELECT 'SELECT'"
    3523             :                                 " UNION SELECT 'INSERT'"
    3524             :                                 " UNION SELECT 'UPDATE'"
    3525             :                                 " UNION SELECT 'DELETE'"
    3526             :                                 " UNION SELECT 'TRUNCATE'"
    3527             :                                 " UNION SELECT 'REFERENCES'"
    3528             :                                 " UNION SELECT 'TRIGGER'"
    3529             :                                 " UNION SELECT 'CREATE'"
    3530             :                                 " UNION SELECT 'CONNECT'"
    3531             :                                 " UNION SELECT 'TEMPORARY'"
    3532             :                                 " UNION SELECT 'EXECUTE'"
    3533             :                                 " UNION SELECT 'USAGE'"
    3534             :                                 " UNION SELECT 'ALL'");
    3535             :     }
    3536             : 
    3537             :     /*
    3538             :      * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
    3539             :      * TO/FROM
    3540             :      */
    3541          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny))
    3542             :     {
    3543           0 :         if (TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
    3544           0 :             COMPLETE_WITH("ON");
    3545           0 :         else if (TailMatches("GRANT", MatchAny))
    3546           0 :             COMPLETE_WITH("TO");
    3547             :         else
    3548           0 :             COMPLETE_WITH("FROM");
    3549             :     }
    3550             : 
    3551             :     /*
    3552             :      * Complete GRANT/REVOKE <sth> ON with a list of appropriate relations.
    3553             :      *
    3554             :      * Keywords like DATABASE, FUNCTION, LANGUAGE and SCHEMA added to query
    3555             :      * result via UNION; seems to work intuitively.
    3556             :      *
    3557             :      * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
    3558             :      * here will only work if the privilege list contains exactly one
    3559             :      * privilege.
    3560             :      */
    3561          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON"))
    3562             :     {
    3563             :         /*
    3564             :          * With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds of
    3565             :          * objects supported.
    3566             :          */
    3567           0 :         if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
    3568           0 :             COMPLETE_WITH("TABLES", "SEQUENCES", "FUNCTIONS", "PROCEDURES", "ROUTINES", "TYPES", "SCHEMAS");
    3569             :         else
    3570           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables,
    3571             :                                        " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'"
    3572             :                                        " UNION SELECT 'ALL PROCEDURES IN SCHEMA'"
    3573             :                                        " UNION SELECT 'ALL ROUTINES IN SCHEMA'"
    3574             :                                        " UNION SELECT 'ALL SEQUENCES IN SCHEMA'"
    3575             :                                        " UNION SELECT 'ALL TABLES IN SCHEMA'"
    3576             :                                        " UNION SELECT 'DATABASE'"
    3577             :                                        " UNION SELECT 'DOMAIN'"
    3578             :                                        " UNION SELECT 'FOREIGN DATA WRAPPER'"
    3579             :                                        " UNION SELECT 'FOREIGN SERVER'"
    3580             :                                        " UNION SELECT 'FUNCTION'"
    3581             :                                        " UNION SELECT 'LANGUAGE'"
    3582             :                                        " UNION SELECT 'LARGE OBJECT'"
    3583             :                                        " UNION SELECT 'PROCEDURE'"
    3584             :                                        " UNION SELECT 'ROUTINE'"
    3585             :                                        " UNION SELECT 'SCHEMA'"
    3586             :                                        " UNION SELECT 'SEQUENCE'"
    3587             :                                        " UNION SELECT 'TABLE'"
    3588             :                                        " UNION SELECT 'TABLESPACE'"
    3589             :                                        " UNION SELECT 'TYPE'");
    3590             :     }
    3591          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL"))
    3592           0 :         COMPLETE_WITH("FUNCTIONS IN SCHEMA",
    3593             :                       "PROCEDURES IN SCHEMA",
    3594             :                       "ROUTINES IN SCHEMA",
    3595             :                       "SEQUENCES IN SCHEMA",
    3596             :                       "TABLES IN SCHEMA");
    3597          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
    3598           0 :         COMPLETE_WITH("DATA WRAPPER", "SERVER");
    3599             : 
    3600             :     /*
    3601             :      * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of
    3602             :      * appropriate objects.
    3603             :      *
    3604             :      * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
    3605             :      */
    3606          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny))
    3607             :     {
    3608           0 :         if (TailMatches("DATABASE"))
    3609           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_databases);
    3610           0 :         else if (TailMatches("DOMAIN"))
    3611           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
    3612           0 :         else if (TailMatches("FUNCTION"))
    3613           0 :             COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
    3614           0 :         else if (TailMatches("LANGUAGE"))
    3615           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_languages);
    3616           0 :         else if (TailMatches("PROCEDURE"))
    3617           0 :             COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
    3618           0 :         else if (TailMatches("ROUTINE"))
    3619           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);
    3620           0 :         else if (TailMatches("SCHEMA"))
    3621           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
    3622           0 :         else if (TailMatches("SEQUENCE"))
    3623           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
    3624           0 :         else if (TailMatches("TABLE"))
    3625           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
    3626           0 :         else if (TailMatches("TABLESPACE"))
    3627           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
    3628           0 :         else if (TailMatches("TYPE"))
    3629           0 :             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    3630           0 :         else if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny))
    3631           0 :             COMPLETE_WITH("TO");
    3632             :         else
    3633           0 :             COMPLETE_WITH("FROM");
    3634             :     }
    3635             : 
    3636             :     /*
    3637             :      * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
    3638             :      * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
    3639             :      */
    3640          28 :     else if ((HeadMatches("GRANT") && TailMatches("TO")) ||
    3641          14 :              (HeadMatches("REVOKE") && TailMatches("FROM")))
    3642           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
    3643             :     /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
    3644          14 :     else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
    3645           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
    3646             :     /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
    3647          14 :     else if (HeadMatches("GRANT") && TailMatches("ON", MatchAny, MatchAny))
    3648           0 :         COMPLETE_WITH("TO");
    3649          14 :     else if (HeadMatches("REVOKE") && TailMatches("ON", MatchAny, MatchAny))
    3650           0 :         COMPLETE_WITH("FROM");
    3651             : 
    3652             :     /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
    3653          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
    3654             :     {
    3655           0 :         if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
    3656           0 :             COMPLETE_WITH("TO");
    3657             :         else
    3658           0 :             COMPLETE_WITH("FROM");
    3659             :     }
    3660             : 
    3661             :     /* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
    3662          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
    3663             :     {
    3664           0 :         if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
    3665           0 :             COMPLETE_WITH("TO");
    3666             :         else
    3667           0 :             COMPLETE_WITH("FROM");
    3668             :     }
    3669             : 
    3670             :     /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
    3671          14 :     else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
    3672             :     {
    3673           0 :         if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
    3674           0 :             COMPLETE_WITH("TO");
    3675             :         else
    3676           0 :             COMPLETE_WITH("FROM");
    3677             :     }
    3678             : 
    3679             : /* GROUP BY */
    3680          14 :     else if (TailMatches("FROM", MatchAny, "GROUP"))
    3681           0 :         COMPLETE_WITH("BY");
    3682             : 
    3683             : /* IMPORT FOREIGN SCHEMA */
    3684          14 :     else if (Matches("IMPORT"))
    3685           0 :         COMPLETE_WITH("FOREIGN SCHEMA");
    3686          14 :     else if (Matches("IMPORT", "FOREIGN"))
    3687           0 :         COMPLETE_WITH("SCHEMA");
    3688          14 :     else if (Matches("IMPORT", "FOREIGN", "SCHEMA", MatchAny))
    3689           0 :         COMPLETE_WITH("EXCEPT (", "FROM SERVER", "LIMIT TO (");
    3690          28 :     else if (TailMatches("LIMIT", "TO", "(*)") ||
    3691          14 :              TailMatches("EXCEPT", "(*)"))
    3692           0 :         COMPLETE_WITH("FROM SERVER");
    3693          14 :     else if (TailMatches("FROM", "SERVER", MatchAny))
    3694           0 :         COMPLETE_WITH("INTO");
    3695          14 :     else if (TailMatches("FROM", "SERVER", MatchAny, "INTO"))
    3696           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
    3697          14 :     else if (TailMatches("FROM", "SERVER", MatchAny, "INTO", MatchAny))
    3698           0 :         COMPLETE_WITH("OPTIONS (");
    3699             : 
    3700             : /* INSERT --- can be inside EXPLAIN, RULE, etc */
    3701             :     /* Complete INSERT with "INTO" */
    3702          14 :     else if (TailMatches("INSERT"))
    3703           0 :         COMPLETE_WITH("INTO");
    3704             :     /* Complete INSERT INTO with table names */
    3705          14 :     else if (TailMatches("INSERT", "INTO"))
    3706           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
    3707             :     /* Complete "INSERT INTO <table> (" with attribute names */
    3708          14 :     else if (TailMatches("INSERT", "INTO", MatchAny, "("))
    3709           0 :         COMPLETE_WITH_ATTR(prev2_wd, "");
    3710             : 
    3711             :     /*
    3712             :      * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
    3713             :      * "TABLE" or "DEFAULT VALUES" or "OVERRIDING"
    3714             :      */
    3715          14 :     else if (TailMatches("INSERT", "INTO", MatchAny))
    3716           0 :         COMPLETE_WITH("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", "OVERRIDING");
    3717             : 
    3718             :     /*
    3719             :      * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
    3720             :      * "TABLE" or "OVERRIDING"
    3721             :      */
    3722          14 :     else if (TailMatches("INSERT", "INTO", MatchAny, MatchAny) &&
    3723           0 :              ends_with(prev_wd, ')'))
    3724           0 :         COMPLETE_WITH("SELECT", "TABLE", "VALUES", "OVERRIDING");
    3725             : 
    3726             :     /* Complete OVERRIDING */
    3727          14 :     else if (TailMatches("OVERRIDING"))
    3728           0 :         COMPLETE_WITH("SYSTEM VALUE", "USER VALUE");
    3729             : 
    3730             :     /* Complete after OVERRIDING clause */
    3731          14 :     else if (TailMatches("OVERRIDING", MatchAny, "VALUE"))
    3732           0 :         COMPLETE_WITH("SELECT", "TABLE", "VALUES");
    3733             : 
    3734             :     /* Insert an open parenthesis after "VALUES" */
    3735          14 :     else if (TailMatches("VALUES") && !TailMatches("DEFAULT", "VALUES"))
    3736           0 :         COMPLETE_WITH("(");
    3737             : 
    3738             : /* LOCK */
    3739             :     /* Complete LOCK [TABLE] [ONLY] with a list of tables */
    3740          14 :     else if (Matches("LOCK"))
    3741           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
    3742             :                                    " UNION SELECT 'TABLE'"
    3743             :                                    " UNION SELECT 'ONLY'");
    3744          14 :     else if (Matches("LOCK", "TABLE"))
    3745           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
    3746             :                                    " UNION SELECT 'ONLY'");
    3747          14 :     else if (Matches("LOCK", "TABLE", "ONLY") || Matches("LOCK", "ONLY"))
    3748           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    3749             :     /* For the following, handle the case of a single table only for now */
    3750             : 
    3751             :     /* Complete LOCK [TABLE] [ONLY] <table> with IN or NOWAIT */
    3752          28 :     else if (Matches("LOCK", MatchAnyExcept("TABLE|ONLY")) ||
    3753          28 :              Matches("LOCK", "TABLE", MatchAnyExcept("ONLY")) ||
    3754          28 :              Matches("LOCK", "ONLY", MatchAny) ||
    3755          14 :              Matches("LOCK", "TABLE", "ONLY", MatchAny))
    3756           0 :         COMPLETE_WITH("IN", "NOWAIT");
    3757             : 
    3758             :     /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
    3759          14 :     else if (HeadMatches("LOCK") && TailMatches("IN"))
    3760           0 :         COMPLETE_WITH("ACCESS SHARE MODE",
    3761             :                       "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
    3762             :                       "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
    3763             :                       "SHARE ROW EXCLUSIVE MODE",
    3764             :                       "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
    3765             : 
    3766             :     /*
    3767             :      * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of lock
    3768             :      * mode
    3769             :      */
    3770          14 :     else if (HeadMatches("LOCK") && TailMatches("IN", "ACCESS|ROW"))
    3771           0 :         COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");
    3772             : 
    3773             :     /* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
    3774          14 :     else if (HeadMatches("LOCK") && TailMatches("IN", "SHARE"))
    3775           0 :         COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
    3776             :                       "UPDATE EXCLUSIVE MODE");
    3777             : 
    3778             :     /* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
    3779          14 :     else if (HeadMatches("LOCK") && TailMatches("MODE"))
    3780           0 :         COMPLETE_WITH("NOWAIT");
    3781             : 
    3782             : /* NOTIFY --- can be inside EXPLAIN, RULE, etc */
    3783          14 :     else if (TailMatches("NOTIFY"))
    3784           0 :         COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'");
    3785             : 
    3786             : /* OPTIONS */
    3787          14 :     else if (TailMatches("OPTIONS"))
    3788           0 :         COMPLETE_WITH("(");
    3789             : 
    3790             : /* OWNER TO  - complete with available roles */
    3791          14 :     else if (TailMatches("OWNER", "TO"))
    3792           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    3793             : 
    3794             : /* ORDER BY */
    3795          14 :     else if (TailMatches("FROM", MatchAny, "ORDER"))
    3796           0 :         COMPLETE_WITH("BY");
    3797          14 :     else if (TailMatches("FROM", MatchAny, "ORDER", "BY"))
    3798           0 :         COMPLETE_WITH_ATTR(prev3_wd, "");
    3799             : 
    3800             : /* PREPARE xx AS */
    3801          14 :     else if (Matches("PREPARE", MatchAny, "AS"))
    3802           0 :         COMPLETE_WITH("SELECT", "UPDATE", "INSERT INTO", "DELETE FROM");
    3803             : 
    3804             : /*
    3805             :  * PREPARE TRANSACTION is missing on purpose. It's intended for transaction
    3806             :  * managers, not for manual use in interactive sessions.
    3807             :  */
    3808             : 
    3809             : /* REASSIGN OWNED BY xxx TO yyy */
    3810          14 :     else if (Matches("REASSIGN"))
    3811           0 :         COMPLETE_WITH("OWNED BY");
    3812          14 :     else if (Matches("REASSIGN", "OWNED"))
    3813           0 :         COMPLETE_WITH("BY");
    3814          14 :     else if (Matches("REASSIGN", "OWNED", "BY"))
    3815           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    3816          14 :     else if (Matches("REASSIGN", "OWNED", "BY", MatchAny))
    3817           0 :         COMPLETE_WITH("TO");
    3818          14 :     else if (Matches("REASSIGN", "OWNED", "BY", MatchAny, "TO"))
    3819           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    3820             : 
    3821             : /* REFRESH MATERIALIZED VIEW */
    3822          14 :     else if (Matches("REFRESH"))
    3823           0 :         COMPLETE_WITH("MATERIALIZED VIEW");
    3824          14 :     else if (Matches("REFRESH", "MATERIALIZED"))
    3825           0 :         COMPLETE_WITH("VIEW");
    3826          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW"))
    3827           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
    3828             :                                    " UNION SELECT 'CONCURRENTLY'");
    3829          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
    3830           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
    3831          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
    3832           0 :         COMPLETE_WITH("WITH");
    3833          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
    3834           0 :         COMPLETE_WITH("WITH");
    3835          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"))
    3836           0 :         COMPLETE_WITH("NO DATA", "DATA");
    3837          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"))
    3838           0 :         COMPLETE_WITH("NO DATA", "DATA");
    3839          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"))
    3840           0 :         COMPLETE_WITH("DATA");
    3841          14 :     else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO"))
    3842           0 :         COMPLETE_WITH("DATA");
    3843             : 
    3844             : /* REINDEX */
    3845          28 :     else if (Matches("REINDEX") ||
    3846          14 :              Matches("REINDEX", "(*)"))
    3847           0 :         COMPLETE_WITH("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
    3848          28 :     else if (Matches("REINDEX", "TABLE") ||
    3849          14 :              Matches("REINDEX", "(*)", "TABLE"))
    3850           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables,
    3851             :                                    " UNION SELECT 'CONCURRENTLY'");
    3852          28 :     else if (Matches("REINDEX", "INDEX") ||
    3853          14 :              Matches("REINDEX", "(*)", "INDEX"))
    3854           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
    3855             :                                    " UNION SELECT 'CONCURRENTLY'");
    3856          28 :     else if (Matches("REINDEX", "SCHEMA") ||
    3857          14 :              Matches("REINDEX", "(*)", "SCHEMA"))
    3858           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_schemas
    3859             :                             " UNION SELECT 'CONCURRENTLY'");
    3860          28 :     else if (Matches("REINDEX", "SYSTEM|DATABASE") ||
    3861          14 :              Matches("REINDEX", "(*)", "SYSTEM|DATABASE"))
    3862           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_databases
    3863             :                             " UNION SELECT 'CONCURRENTLY'");
    3864          28 :     else if (Matches("REINDEX", "TABLE", "CONCURRENTLY") ||
    3865          14 :              Matches("REINDEX", "(*)", "TABLE", "CONCURRENTLY"))
    3866           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables, NULL);
    3867          28 :     else if (Matches("REINDEX", "INDEX", "CONCURRENTLY") ||
    3868          14 :              Matches("REINDEX", "(*)", "INDEX", "CONCURRENTLY"))
    3869           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
    3870          28 :     else if (Matches("REINDEX", "SCHEMA", "CONCURRENTLY") ||
    3871          14 :              Matches("REINDEX", "(*)", "SCHEMA", "CONCURRENTLY"))
    3872           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
    3873          28 :     else if (Matches("REINDEX", "SYSTEM|DATABASE", "CONCURRENTLY") ||
    3874          14 :              Matches("REINDEX", "(*)", "SYSTEM|DATABASE", "CONCURRENTLY"))
    3875           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
    3876          14 :     else if (HeadMatches("REINDEX", "(*") &&
    3877           0 :              !HeadMatches("REINDEX", "(*)"))
    3878             :     {
    3879             :         /*
    3880             :          * This fires if we're in an unfinished parenthesized option list.
    3881             :          * get_previous_words treats a completed parenthesized option list as
    3882             :          * one word, so the above test is correct.
    3883             :          */
    3884           0 :         if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    3885           0 :             COMPLETE_WITH("CONCURRENTLY", "TABLESPACE", "VERBOSE");
    3886           0 :         else if (TailMatches("TABLESPACE"))
    3887           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
    3888             :     }
    3889             : 
    3890             : /* SECURITY LABEL */
    3891          14 :     else if (Matches("SECURITY"))
    3892           0 :         COMPLETE_WITH("LABEL");
    3893          14 :     else if (Matches("SECURITY", "LABEL"))
    3894           0 :         COMPLETE_WITH("ON", "FOR");
    3895          14 :     else if (Matches("SECURITY", "LABEL", "FOR", MatchAny))
    3896           0 :         COMPLETE_WITH("ON");
    3897          28 :     else if (Matches("SECURITY", "LABEL", "ON") ||
    3898          14 :              Matches("SECURITY", "LABEL", "FOR", MatchAny, "ON"))
    3899           0 :         COMPLETE_WITH("TABLE", "COLUMN", "AGGREGATE", "DATABASE", "DOMAIN",
    3900             :                       "EVENT TRIGGER", "FOREIGN TABLE", "FUNCTION",
    3901             :                       "LARGE OBJECT", "MATERIALIZED VIEW", "LANGUAGE",
    3902             :                       "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA",
    3903             :                       "SEQUENCE", "SUBSCRIPTION", "TABLESPACE", "TYPE", "VIEW");
    3904          14 :     else if (Matches("SECURITY", "LABEL", "ON", MatchAny, MatchAny))
    3905           0 :         COMPLETE_WITH("IS");
    3906             : 
    3907             : /* SELECT */
    3908             :     /* naah . . . */
    3909             : 
    3910             : /* SET, RESET, SHOW */
    3911             :     /* Complete with a variable name */
    3912          14 :     else if (TailMatches("SET|RESET") && !TailMatches("UPDATE", MatchAny, "SET"))
    3913           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
    3914          14 :     else if (Matches("SHOW"))
    3915           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
    3916             :     /* Complete "SET TRANSACTION" */
    3917          14 :     else if (Matches("SET", "TRANSACTION"))
    3918           0 :         COMPLETE_WITH("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
    3919          28 :     else if (Matches("BEGIN|START", "TRANSACTION") ||
    3920          28 :              Matches("BEGIN", "WORK") ||
    3921          28 :              Matches("BEGIN") ||
    3922          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION"))
    3923           0 :         COMPLETE_WITH("ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
    3924          28 :     else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "NOT") ||
    3925          28 :              Matches("BEGIN", "NOT") ||
    3926          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "NOT"))
    3927           0 :         COMPLETE_WITH("DEFERRABLE");
    3928          28 :     else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") ||
    3929          28 :              Matches("BEGIN", "ISOLATION") ||
    3930          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION"))
    3931           0 :         COMPLETE_WITH("LEVEL");
    3932          28 :     else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL") ||
    3933          28 :              Matches("BEGIN", "ISOLATION", "LEVEL") ||
    3934          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL"))
    3935           0 :         COMPLETE_WITH("READ", "REPEATABLE READ", "SERIALIZABLE");
    3936          28 :     else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ") ||
    3937          28 :              Matches("BEGIN", "ISOLATION", "LEVEL", "READ") ||
    3938          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "READ"))
    3939           0 :         COMPLETE_WITH("UNCOMMITTED", "COMMITTED");
    3940          28 :     else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE") ||
    3941          28 :              Matches("BEGIN", "ISOLATION", "LEVEL", "REPEATABLE") ||
    3942          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "REPEATABLE"))
    3943           0 :         COMPLETE_WITH("READ");
    3944          28 :     else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "READ") ||
    3945          28 :              Matches("BEGIN", "READ") ||
    3946          14 :              Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "READ"))
    3947           0 :         COMPLETE_WITH("ONLY", "WRITE");
    3948             :     /* SET CONSTRAINTS */
    3949          14 :     else if (Matches("SET", "CONSTRAINTS"))
    3950           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");
    3951             :     /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
    3952          14 :     else if (Matches("SET", "CONSTRAINTS", MatchAny))
    3953           0 :         COMPLETE_WITH("DEFERRED", "IMMEDIATE");
    3954             :     /* Complete SET ROLE */
    3955          14 :     else if (Matches("SET", "ROLE"))
    3956           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    3957             :     /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
    3958          14 :     else if (Matches("SET", "SESSION"))
    3959           0 :         COMPLETE_WITH("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
    3960             :     /* Complete SET SESSION AUTHORIZATION with username */
    3961          14 :     else if (Matches("SET", "SESSION", "AUTHORIZATION"))
    3962           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
    3963             :     /* Complete RESET SESSION with AUTHORIZATION */
    3964          14 :     else if (Matches("RESET", "SESSION"))
    3965           0 :         COMPLETE_WITH("AUTHORIZATION");
    3966             :     /* Complete SET <var> with "TO" */
    3967          14 :     else if (Matches("SET", MatchAny))
    3968           0 :         COMPLETE_WITH("TO");
    3969             : 
    3970             :     /*
    3971             :      * Complete ALTER DATABASE|FUNCTION||PROCEDURE|ROLE|ROUTINE|USER ... SET
    3972             :      * <name>
    3973             :      */
    3974          14 :     else if (HeadMatches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER") &&
    3975           0 :              TailMatches("SET", MatchAny))
    3976           0 :         COMPLETE_WITH("FROM CURRENT", "TO");
    3977             : 
    3978             :     /*
    3979             :      * Suggest possible variable values in SET variable TO|=, along with the
    3980             :      * preceding ALTER syntaxes.
    3981             :      */
    3982          14 :     else if (TailMatches("SET", MatchAny, "TO|=") &&
    3983           0 :              !TailMatches("UPDATE", MatchAny, "SET", MatchAny, "TO|="))
    3984             :     {
    3985             :         /* special cased code for individual GUCs */
    3986           0 :         if (TailMatches("DateStyle", "TO|="))
    3987           0 :             COMPLETE_WITH("ISO", "SQL", "Postgres", "German",
    3988             :                           "YMD", "DMY", "MDY",
    3989             :                           "US", "European", "NonEuropean",
    3990             :                           "DEFAULT");
    3991           0 :         else if (TailMatches("search_path", "TO|="))
    3992           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_schemas
    3993             :                                 " AND nspname not like 'pg\\_toast%%' "
    3994             :                                 " AND nspname not like 'pg\\_temp%%' "
    3995             :                                 " UNION SELECT 'DEFAULT' ");
    3996             :         else
    3997             :         {
    3998             :             /* generic, type based, GUC support */
    3999           0 :             char       *guctype = get_guctype(prev2_wd);
    4000             : 
    4001             :             /*
    4002             :              * Note: if we don't recognize the GUC name, it's important to not
    4003             :              * offer any completions, as most likely we've misinterpreted the
    4004             :              * context and this isn't a GUC-setting command at all.
    4005             :              */
    4006           0 :             if (guctype)
    4007             :             {
    4008           0 :                 if (strcmp(guctype, "enum") == 0)
    4009             :                 {
    4010             :                     char        querybuf[1024];
    4011             : 
    4012           0 :                     snprintf(querybuf, sizeof(querybuf),
    4013           0 :                              Query_for_enum, prev2_wd);
    4014           0 :                     COMPLETE_WITH_QUERY(querybuf);
    4015             :                 }
    4016           0 :                 else if (strcmp(guctype, "bool") == 0)
    4017           0 :                     COMPLETE_WITH("on", "off", "true", "false", "yes", "no",
    4018             :                                   "1", "0", "DEFAULT");
    4019             :                 else
    4020           0 :                     COMPLETE_WITH("DEFAULT");
    4021             : 
    4022           0 :                 free(guctype);
    4023             :             }
    4024             :         }
    4025             :     }
    4026             : 
    4027             : /* START TRANSACTION */
    4028          14 :     else if (Matches("START"))
    4029           0 :         COMPLETE_WITH("TRANSACTION");
    4030             : 
    4031             : /* TABLE, but not TABLE embedded in other commands */
    4032          14 :     else if (Matches("TABLE"))
    4033           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables, NULL);
    4034             : 
    4035             : /* TABLESAMPLE */
    4036          14 :     else if (TailMatches("TABLESAMPLE"))
    4037           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods);
    4038          14 :     else if (TailMatches("TABLESAMPLE", MatchAny))
    4039           0 :         COMPLETE_WITH("(");
    4040             : 
    4041             : /* TRUNCATE */
    4042          14 :     else if (Matches("TRUNCATE"))
    4043           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables,
    4044             :                                    " UNION SELECT 'TABLE'"
    4045             :                                    " UNION SELECT 'ONLY'");
    4046          14 :     else if (Matches("TRUNCATE", "TABLE"))
    4047           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables,
    4048             :                                    " UNION SELECT 'ONLY'");
    4049          14 :     else if (HeadMatches("TRUNCATE") && TailMatches("ONLY"))
    4050           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables, NULL);
    4051          28 :     else if (Matches("TRUNCATE", MatchAny) ||
    4052          28 :              Matches("TRUNCATE", "TABLE|ONLY", MatchAny) ||
    4053          14 :              Matches("TRUNCATE", "TABLE", "ONLY", MatchAny))
    4054           0 :         COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
    4055          14 :     else if (HeadMatches("TRUNCATE") && TailMatches("IDENTITY"))
    4056           0 :         COMPLETE_WITH("CASCADE", "RESTRICT");
    4057             : 
    4058             : /* UNLISTEN */
    4059          14 :     else if (Matches("UNLISTEN"))
    4060           0 :         COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s' UNION SELECT '*'");
    4061             : 
    4062             : /* UPDATE --- can be inside EXPLAIN, RULE, etc */
    4063             :     /* If prev. word is UPDATE suggest a list of tables */
    4064          14 :     else if (TailMatches("UPDATE"))
    4065           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
    4066             :     /* Complete UPDATE <table> with "SET" */
    4067          14 :     else if (TailMatches("UPDATE", MatchAny))
    4068           0 :         COMPLETE_WITH("SET");
    4069             :     /* Complete UPDATE <table> SET with list of attributes */
    4070          14 :     else if (TailMatches("UPDATE", MatchAny, "SET"))
    4071           0 :         COMPLETE_WITH_ATTR(prev2_wd, "");
    4072             :     /* UPDATE <table> SET <attr> = */
    4073          14 :     else if (TailMatches("UPDATE", MatchAny, "SET", MatchAnyExcept("*=")))
    4074           0 :         COMPLETE_WITH("=");
    4075             : 
    4076             : /* USER MAPPING */
    4077          14 :     else if (Matches("ALTER|CREATE|DROP", "USER", "MAPPING"))
    4078           0 :         COMPLETE_WITH("FOR");
    4079          14 :     else if (Matches("CREATE", "USER", "MAPPING", "FOR"))
    4080           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles
    4081             :                             " UNION SELECT 'CURRENT_ROLE'"
    4082             :                             " UNION SELECT 'CURRENT_USER'"
    4083             :                             " UNION SELECT 'PUBLIC'"
    4084             :                             " UNION SELECT 'USER'");
    4085          14 :     else if (Matches("ALTER|DROP", "USER", "MAPPING", "FOR"))
    4086           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
    4087          14 :     else if (Matches("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
    4088           0 :         COMPLETE_WITH("SERVER");
    4089          14 :     else if (Matches("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny))
    4090           0 :         COMPLETE_WITH("OPTIONS");
    4091             : 
    4092             : /*
    4093             :  * VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
    4094             :  * VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]
    4095             :  */
    4096          14 :     else if (Matches("VACUUM"))
    4097           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,
    4098             :                                    " UNION SELECT 'FULL'"
    4099             :                                    " UNION SELECT 'FREEZE'"
    4100             :                                    " UNION SELECT 'ANALYZE'"
    4101             :                                    " UNION SELECT 'VERBOSE'");
    4102          14 :     else if (Matches("VACUUM", "FULL"))
    4103           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,
    4104             :                                    " UNION SELECT 'FREEZE'"
    4105             :                                    " UNION SELECT 'ANALYZE'"
    4106             :                                    " UNION SELECT 'VERBOSE'");
    4107          28 :     else if (Matches("VACUUM", "FREEZE") ||
    4108          14 :              Matches("VACUUM", "FULL", "FREEZE"))
    4109           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,
    4110             :                                    " UNION SELECT 'VERBOSE'"
    4111             :                                    " UNION SELECT 'ANALYZE'");
    4112          28 :     else if (Matches("VACUUM", "VERBOSE") ||
    4113          28 :              Matches("VACUUM", "FULL|FREEZE", "VERBOSE") ||
    4114          14 :              Matches("VACUUM", "FULL", "FREEZE", "VERBOSE"))
    4115           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,
    4116             :                                    " UNION SELECT 'ANALYZE'");
    4117          14 :     else if (HeadMatches("VACUUM", "(*") &&
    4118           0 :              !HeadMatches("VACUUM", "(*)"))
    4119             :     {
    4120             :         /*
    4121             :          * This fires if we're in an unfinished parenthesized option list.
    4122             :          * get_previous_words treats a completed parenthesized option list as
    4123             :          * one word, so the above test is correct.
    4124             :          */
    4125           0 :         if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
    4126           0 :             COMPLETE_WITH("FULL", "FREEZE", "ANALYZE", "VERBOSE",
    4127             :                           "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
    4128             :                           "INDEX_CLEANUP", "PROCESS_TOAST",
    4129             :                           "TRUNCATE", "PARALLEL");
    4130           0 :         else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_TOAST|TRUNCATE"))
    4131           0 :             COMPLETE_WITH("ON", "OFF");
    4132           0 :         else if (TailMatches("INDEX_CLEANUP"))
    4133           0 :             COMPLETE_WITH("AUTO", "ON", "OFF");
    4134             :     }
    4135          14 :     else if (HeadMatches("VACUUM") && TailMatches("("))
    4136             :         /* "VACUUM (" should be caught above, so assume we want columns */
    4137           0 :         COMPLETE_WITH_ATTR(prev2_wd, "");
    4138          14 :     else if (HeadMatches("VACUUM"))
    4139           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables, NULL);
    4140             : 
    4141             : /* WITH [RECURSIVE] */
    4142             : 
    4143             :     /*
    4144             :      * Only match when WITH is the first word, as WITH may appear in many
    4145             :      * other contexts.
    4146             :      */
    4147          14 :     else if (Matches("WITH"))
    4148           0 :         COMPLETE_WITH("RECURSIVE");
    4149             : 
    4150             : /* WHERE */
    4151             :     /* Simple case of the word before the where being the table name */
    4152          14 :     else if (TailMatches(MatchAny, "WHERE"))
    4153           0 :         COMPLETE_WITH_ATTR(prev2_wd, "");
    4154             : 
    4155             : /* ... FROM ... */
    4156             : /* TODO: also include SRF ? */
    4157          14 :     else if (TailMatches("FROM") && !Matches("COPY|\\copy", MatchAny, "FROM"))
    4158          10 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables, NULL);
    4159             : 
    4160             : /* ... JOIN ... */
    4161           4 :     else if (TailMatches("JOIN"))
    4162           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables, NULL);
    4163             : 
    4164             : /* Backslash commands */
    4165             : /* TODO:  \dc \dd \dl */
    4166           4 :     else if (TailMatchesCS("\\?"))
    4167           0 :         COMPLETE_WITH_CS("commands", "options", "variables");
    4168           4 :     else if (TailMatchesCS("\\connect|\\c"))
    4169             :     {
    4170           0 :         if (!recognized_connection_string(text))
    4171           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_databases);
    4172             :     }
    4173           4 :     else if (TailMatchesCS("\\connect|\\c", MatchAny))
    4174             :     {
    4175           0 :         if (!recognized_connection_string(prev_wd))
    4176           0 :             COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    4177             :     }
    4178           4 :     else if (TailMatchesCS("\\da*"))
    4179           0 :         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
    4180           8 :     else if (TailMatchesCS("\\dAc*", MatchAny) ||
    4181           4 :              TailMatchesCS("\\dAf*", MatchAny))
    4182           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    4183           8 :     else if (TailMatchesCS("\\dAo*", MatchAny) ||
    4184           4 :              TailMatchesCS("\\dAp*", MatchAny))
    4185           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families, NULL);
    4186           4 :     else if (TailMatchesCS("\\dA*"))
    4187           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
    4188           4 :     else if (TailMatchesCS("\\db*"))
    4189           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
    4190           4 :     else if (TailMatchesCS("\\dD*"))
    4191           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
    4192           4 :     else if (TailMatchesCS("\\des*"))
    4193           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_servers);
    4194           4 :     else if (TailMatchesCS("\\deu*"))
    4195           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
    4196           4 :     else if (TailMatchesCS("\\dew*"))
    4197           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
    4198           4 :     else if (TailMatchesCS("\\df*"))
    4199           0 :         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
    4200           4 :     else if (HeadMatchesCS("\\df*"))
    4201           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    4202             : 
    4203           4 :     else if (TailMatchesCS("\\dFd*"))
    4204           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
    4205           4 :     else if (TailMatchesCS("\\dFp*"))
    4206           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
    4207           4 :     else if (TailMatchesCS("\\dFt*"))
    4208           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
    4209             :     /* must be at end of \dF alternatives: */
    4210           4 :     else if (TailMatchesCS("\\dF*"))
    4211           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
    4212             : 
    4213           4 :     else if (TailMatchesCS("\\di*"))
    4214           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
    4215           4 :     else if (TailMatchesCS("\\dL*"))
    4216           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
    4217           4 :     else if (TailMatchesCS("\\dn*"))
    4218           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
    4219             :     /* no support for completing operators, but we can complete types: */
    4220           4 :     else if (HeadMatchesCS("\\do*", MatchAny))
    4221           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    4222           4 :     else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
    4223           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);
    4224           4 :     else if (TailMatchesCS("\\dPi*"))
    4225           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);
    4226           4 :     else if (TailMatchesCS("\\dPt*"))
    4227           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, NULL);
    4228           4 :     else if (TailMatchesCS("\\dP*"))
    4229           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);
    4230           4 :     else if (TailMatchesCS("\\ds*"))
    4231           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
    4232           4 :     else if (TailMatchesCS("\\dt*"))
    4233           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
    4234           4 :     else if (TailMatchesCS("\\dT*"))
    4235           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
    4236           4 :     else if (TailMatchesCS("\\du*") || TailMatchesCS("\\dg*"))
    4237           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    4238           4 :     else if (TailMatchesCS("\\dv*"))
    4239           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
    4240           4 :     else if (TailMatchesCS("\\dx*"))
    4241           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
    4242           4 :     else if (TailMatchesCS("\\dX*"))
    4243           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);
    4244           4 :     else if (TailMatchesCS("\\dm*"))
    4245           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
    4246           4 :     else if (TailMatchesCS("\\dE*"))
    4247           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
    4248           4 :     else if (TailMatchesCS("\\dy*"))
    4249           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
    4250             : 
    4251             :     /* must be at end of \d alternatives: */
    4252           4 :     else if (TailMatchesCS("\\d*"))
    4253           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
    4254             : 
    4255           4 :     else if (TailMatchesCS("\\ef"))
    4256           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);
    4257           4 :     else if (TailMatchesCS("\\ev"))
    4258           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
    4259             : 
    4260           4 :     else if (TailMatchesCS("\\encoding"))
    4261           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
    4262           4 :     else if (TailMatchesCS("\\h|\\help"))
    4263           0 :         COMPLETE_WITH_LIST(sql_commands);
    4264           4 :     else if (TailMatchesCS("\\h|\\help", MatchAny))
    4265             :     {
    4266           0 :         if (TailMatches("DROP"))
    4267           0 :             matches = rl_completion_matches(text, drop_command_generator);
    4268           0 :         else if (TailMatches("ALTER"))
    4269           0 :             matches = rl_completion_matches(text, alter_command_generator);
    4270             : 
    4271             :         /*
    4272             :          * CREATE is recognized by tail match elsewhere, so doesn't need to be
    4273             :          * repeated here
    4274             :          */
    4275             :     }
    4276           4 :     else if (TailMatchesCS("\\h|\\help", MatchAny, MatchAny))
    4277             :     {
    4278           0 :         if (TailMatches("CREATE|DROP", "ACCESS"))
    4279           0 :             COMPLETE_WITH("METHOD");
    4280           0 :         else if (TailMatches("ALTER", "DEFAULT"))
    4281           0 :             COMPLETE_WITH("PRIVILEGES");
    4282           0 :         else if (TailMatches("CREATE|ALTER|DROP", "EVENT"))
    4283           0 :             COMPLETE_WITH("TRIGGER");
    4284           0 :         else if (TailMatches("CREATE|ALTER|DROP", "FOREIGN"))
    4285           0 :             COMPLETE_WITH("DATA WRAPPER", "TABLE");
    4286           0 :         else if (TailMatches("ALTER", "LARGE"))
    4287           0 :             COMPLETE_WITH("OBJECT");
    4288           0 :         else if (TailMatches("CREATE|ALTER|DROP", "MATERIALIZED"))
    4289           0 :             COMPLETE_WITH("VIEW");
    4290           0 :         else if (TailMatches("CREATE|ALTER|DROP", "TEXT"))
    4291           0 :             COMPLETE_WITH("SEARCH");
    4292           0 :         else if (TailMatches("CREATE|ALTER|DROP", "USER"))
    4293           0 :             COMPLETE_WITH("MAPPING FOR");
    4294             :     }
    4295           4 :     else if (TailMatchesCS("\\h|\\help", MatchAny, MatchAny, MatchAny))
    4296             :     {
    4297           0 :         if (TailMatches("CREATE|ALTER|DROP", "FOREIGN", "DATA"))
    4298           0 :             COMPLETE_WITH("WRAPPER");
    4299           0 :         else if (TailMatches("CREATE|ALTER|DROP", "TEXT", "SEARCH"))
    4300           0 :             COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
    4301           0 :         else if (TailMatches("CREATE|ALTER|DROP", "USER", "MAPPING"))
    4302           0 :             COMPLETE_WITH("FOR");
    4303             :     }
    4304           4 :     else if (TailMatchesCS("\\l*") && !TailMatchesCS("\\lo*"))
    4305           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
    4306           4 :     else if (TailMatchesCS("\\password"))
    4307           0 :         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
    4308           4 :     else if (TailMatchesCS("\\pset"))
    4309           0 :         COMPLETE_WITH_CS("border", "columns", "csv_fieldsep", "expanded",
    4310             :                          "fieldsep", "fieldsep_zero", "footer", "format",
    4311             :                          "linestyle", "null", "numericlocale",
    4312             :                          "pager", "pager_min_lines",
    4313             :                          "recordsep", "recordsep_zero",
    4314             :                          "tableattr", "title", "tuples_only",
    4315             :                          "unicode_border_linestyle",
    4316             :                          "unicode_column_linestyle",
    4317             :                          "unicode_header_linestyle");
    4318           4 :     else if (TailMatchesCS("\\pset", MatchAny))
    4319             :     {
    4320           0 :         if (TailMatchesCS("format"))
    4321           0 :             COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
    4322             :                              "latex-longtable", "troff-ms", "unaligned",
    4323             :                              "wrapped");
    4324           0 :         else if (TailMatchesCS("linestyle"))
    4325           0 :             COMPLETE_WITH_CS("ascii", "old-ascii", "unicode");
    4326           0 :         else if (TailMatchesCS("pager"))
    4327           0 :             COMPLETE_WITH_CS("on", "off", "always");
    4328           0 :         else if (TailMatchesCS("unicode_border_linestyle|"
    4329             :                                "unicode_column_linestyle|"
    4330             :                                "unicode_header_linestyle"))
    4331           0 :             COMPLETE_WITH_CS("single", "double");
    4332             :     }
    4333           4 :     else if (TailMatchesCS("\\unset"))
    4334           0 :         matches = complete_from_variables(text, "", "", true);
    4335           4 :     else if (TailMatchesCS("\\set"))
    4336           0 :         matches = complete_from_variables(text, "", "", false);
    4337           4 :     else if (TailMatchesCS("\\set", MatchAny))
    4338             :     {
    4339           0 :         if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|"
    4340             :                           "SINGLELINE|SINGLESTEP"))
    4341           0 :             COMPLETE_WITH_CS("on", "off");
    4342           0 :         else if (TailMatchesCS("COMP_KEYWORD_CASE"))
    4343           0 :             COMPLETE_WITH_CS("lower", "upper",
    4344             :                              "preserve-lower", "preserve-upper");
    4345           0 :         else if (TailMatchesCS("ECHO"))
    4346           0 :             COMPLETE_WITH_CS("errors", "queries", "all", "none");
    4347           0 :         else if (TailMatchesCS("ECHO_HIDDEN"))
    4348           0 :             COMPLETE_WITH_CS("noexec", "off", "on");
    4349           0 :         else if (TailMatchesCS("HISTCONTROL"))
    4350           0 :             COMPLETE_WITH_CS("ignorespace", "ignoredups",
    4351             :                              "ignoreboth", "none");
    4352           0 :         else if (TailMatchesCS("ON_ERROR_ROLLBACK"))
    4353           0 :             COMPLETE_WITH_CS("on", "off", "interactive");
    4354           0 :         else if (TailMatchesCS("SHOW_CONTEXT"))
    4355           0 :             COMPLETE_WITH_CS("never", "errors", "always");
    4356           0 :         else if (TailMatchesCS("VERBOSITY"))
    4357           0 :             COMPLETE_WITH_CS("default", "verbose", "terse", "sqlstate");
    4358             :     }
    4359           4 :     else if (TailMatchesCS("\\sf*"))
    4360           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);
    4361           4 :     else if (TailMatchesCS("\\sv*"))
    4362           0 :         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
    4363           4 :     else if (TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|"
    4364             :                            "\\ir|\\include_relative|\\o|\\out|"
    4365             :                            "\\s|\\w|\\write|\\lo_import"))
    4366             :     {
    4367           4 :         completion_charp = "\\";
    4368           4 :         completion_force_quote = false;
    4369           4 :         matches = rl_completion_matches(text, complete_from_files);
    4370             :     }
    4371             : 
    4372             :     /*
    4373             :      * Finally, we look through the list of "things", such as TABLE, INDEX and
    4374             :      * check if that was the previous word. If so, execute the query to get a
    4375             :      * list of them.
    4376             :      */
    4377             :     else
    4378             :     {
    4379             :         int         i;
    4380             : 
    4381           0 :         for (i = 0; words_after_create[i].name; i++)
    4382             :         {
    4383           0 :             if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)
    4384             :             {
    4385           0 :                 if (words_after_create[i].query)
    4386           0 :                     COMPLETE_WITH_QUERY(words_after_create[i].query);
    4387           0 :                 else if (words_after_create[i].vquery)
    4388           0 :                     COMPLETE_WITH_VERSIONED_QUERY(words_after_create[i].vquery);
    4389           0 :                 else if (words_after_create[i].squery)
    4390           0 :                     COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(words_after_create[i].squery,
    4391             :                                                          NULL);
    4392           0 :                 break;
    4393             :             }
    4394             :         }
    4395             :     }
    4396             : 
    4397             :     /*
    4398             :      * If we still don't have anything to match we have to fabricate some sort
    4399             :      * of default list. If we were to just return NULL, readline automatically
    4400             :      * attempts filename completion, and that's usually no good.
    4401             :      */
    4402          28 :     if (matches == NULL)
    4403             :     {
    4404           0 :         COMPLETE_WITH_CONST(true, "");
    4405             : #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
    4406           0 :         rl_completion_append_character = '\0';
    4407             : #endif
    4408             :     }
    4409             : 
    4410             :     /* free storage */
    4411          28 :     free(previous_words);
    4412          28 :     free(words_buffer);
    4413          28 :     free(text_copy);
    4414             : 
    4415             :     /* Return our Grand List O' Matches */
    4416          28 :     return matches;
    4417             : }
    4418             : 
    4419             : 
    4420             : /*
    4421             :  * GENERATOR FUNCTIONS
    4422             :  *
    4423             :  * These functions do all the actual work of completing the input. They get
    4424             :  * passed the text so far and the count how many times they have been called
    4425             :  * so far with the same text.
    4426             :  * If you read the above carefully, you'll see that these don't get called
    4427             :  * directly but through the readline interface.
    4428             :  * The return value is expected to be the full completion of the text, going
    4429             :  * through a list each time, or NULL if there are no more matches. The string
    4430             :  * will be free()'d by readline, so you must run it through strdup() or
    4431             :  * something of that sort.
    4432             :  */
    4433             : 
    4434             : /*
    4435             :  * Common routine for create_command_generator and drop_command_generator.
    4436             :  * Entries that have 'excluded' flags are not returned.
    4437             :  */
    4438             : static char *
    4439           0 : create_or_drop_command_generator(const char *text, int state, bits32 excluded)
    4440             : {
    4441             :     static int  list_index,
    4442             :                 string_length;
    4443             :     const char *name;
    4444             : 
    4445             :     /* If this is the first time for this completion, init some values */
    4446           0 :     if (state == 0)
    4447             :     {
    4448           0 :         list_index = 0;
    4449           0 :         string_length = strlen(text);
    4450             :     }
    4451             : 
    4452             :     /* find something that matches */
    4453           0 :     while ((name = words_after_create[list_index++].name))
    4454             :     {
    4455           0 :         if ((pg_strncasecmp(name, text, string_length) == 0) &&
    4456           0 :             !(words_after_create[list_index - 1].flags & excluded))
    4457           0 :             return pg_strdup_keyword_case(name, text);
    4458             :     }
    4459             :     /* if nothing matches, return NULL */
    4460           0 :     return NULL;
    4461             : }
    4462             : 
    4463             : /*
    4464             :  * This one gives you one from a list of things you can put after CREATE
    4465             :  * as defined above.
    4466             :  */
    4467             : static char *
    4468           0 : create_command_generator(const char *text, int state)
    4469             : {
    4470           0 :     return create_or_drop_command_generator(text, state, THING_NO_CREATE);
    4471             : }
    4472             : 
    4473             : /*
    4474             :  * This function gives you a list of things you can put after a DROP command.
    4475             :  */
    4476             : static char *
    4477           0 : drop_command_generator(const char *text, int state)
    4478             : {
    4479           0 :     return create_or_drop_command_generator(text, state, THING_NO_DROP);
    4480             : }
    4481             : 
    4482             : /*
    4483             :  * This function gives you a list of things you can put after an ALTER command.
    4484             :  */
    4485             : static char *
    4486           0 : alter_command_generator(const char *text, int state)
    4487             : {
    4488           0 :     return create_or_drop_command_generator(text, state, THING_NO_ALTER);
    4489             : }
    4490             : 
    4491             : /*
    4492             :  * These functions generate lists using server queries.
    4493             :  * They are all wrappers for _complete_from_query.
    4494             :  */
    4495             : 
    4496             : static char *
    4497           0 : complete_from_query(const char *text, int state)
    4498             : {
    4499             :     /* query is assumed to work for any server version */
    4500           0 :     return _complete_from_query(completion_charp, NULL, text, state);
    4501             : }
    4502             : 
    4503             : static char *
    4504           0 : complete_from_versioned_query(const char *text, int state)
    4505             : {
    4506           0 :     const VersionedQuery *vquery = completion_vquery;
    4507             : 
    4508             :     /* Find appropriate array element */
    4509           0 :     while (pset.sversion < vquery->min_server_version)
    4510           0 :         vquery++;
    4511             :     /* Fail completion if server is too old */
    4512           0 :     if (vquery->query == NULL)
    4513           0 :         return NULL;
    4514             : 
    4515           0 :     return _complete_from_query(vquery->query, NULL, text, state);
    4516             : }
    4517             : 
    4518             : static char *
    4519          26 : complete_from_schema_query(const char *text, int state)
    4520             : {
    4521             :     /* query is assumed to work for any server version */
    4522          26 :     return _complete_from_query(completion_charp, completion_squery,
    4523             :                                 text, state);
    4524             : }
    4525             : 
    4526             : static char *
    4527           0 : complete_from_versioned_schema_query(const char *text, int state)
    4528             : {
    4529           0 :     const SchemaQuery *squery = completion_squery;
    4530           0 :     const VersionedQuery *vquery = completion_vquery;
    4531             : 
    4532             :     /* Find appropriate array element */
    4533           0 :     while (pset.sversion < squery->min_server_version)
    4534           0 :         squery++;
    4535             :     /* Fail completion if server is too old */
    4536           0 :     if (squery->catname == NULL)
    4537           0 :         return NULL;
    4538             : 
    4539             :     /* Likewise for the add-on text, if any */
    4540           0 :     if (vquery)
    4541             :     {
    4542           0 :         while (pset.sversion < vquery->min_server_version)
    4543           0 :             vquery++;
    4544           0 :         if (vquery->query == NULL)
    4545           0 :             return NULL;
    4546             :     }
    4547             : 
    4548           0 :     return _complete_from_query(vquery ? vquery->query : NULL,
    4549             :                                 squery, text, state);
    4550             : }
    4551             : 
    4552             : 
    4553             : /*
    4554             :  * This creates a list of matching things, according to a query described by
    4555             :  * the initial arguments.  The caller has already done any work needed to
    4556             :  * select the appropriate query for the server's version.
    4557             :  *
    4558             :  * The query can be one of two kinds:
    4559             :  *
    4560             :  * 1. A simple query which must contain a %d and a %s, which will be replaced
    4561             :  * by the string length of the text and the text itself. The query may also
    4562             :  * have up to four more %s in it; the first two such will be replaced by the
    4563             :  * value of completion_info_charp, the next two by the value of
    4564             :  * completion_info_charp2.
    4565             :  *
    4566             :  * 2. A schema query used for completion of both schema and relation names.
    4567             :  * These are more complex and must contain in the following order:
    4568             :  * %d %s %d %s %d %s %s %d %s
    4569             :  * where %d is the string length of the text and %s the text itself.
    4570             :  *
    4571             :  * If both simple_query and schema_query are non-NULL, then we construct
    4572             :  * a schema query and append the (uninterpreted) string simple_query to it.
    4573             :  *
    4574             :  * It is assumed that strings should be escaped to become SQL literals
    4575             :  * (that is, what is in the query is actually ... '%s' ...)
    4576             :  *
    4577             :  * See top of file for examples of both kinds of query.
    4578             :  *
    4579             :  * "text" and "state" are supplied by readline.
    4580             :  */
    4581             : static char *
    4582          26 : _complete_from_query(const char *simple_query,
    4583             :                      const SchemaQuery *schema_query,
    4584             :                      const char *text, int state)
    4585             : {
    4586             :     static int  list_index,
    4587             :                 byte_length;
    4588             :     static PGresult *result = NULL;
    4589             : 
    4590             :     /*
    4591             :      * If this is the first time for this completion, we fetch a list of our
    4592             :      * "things" from the backend.
    4593             :      */
    4594          26 :     if (state == 0)
    4595             :     {
    4596             :         PQExpBufferData query_buffer;
    4597             :         char       *e_text;
    4598             :         char       *e_info_charp;
    4599             :         char       *e_info_charp2;
    4600          10 :         const char *pstr = text;
    4601          10 :         int         char_length = 0;
    4602             : 
    4603          10 :         list_index = 0;
    4604          10 :         byte_length = strlen(text);
    4605             : 
    4606             :         /*
    4607             :          * Count length as number of characters (not bytes), for passing to
    4608             :          * substring
    4609             :          */
    4610          48 :         while (*pstr)
    4611             :         {
    4612          38 :             char_length++;
    4613          38 :             pstr += PQmblenBounded(pstr, pset.encoding);
    4614             :         }
    4615             : 
    4616             :         /* Free any prior result */
    4617          10 :         PQclear(result);
    4618          10 :         result = NULL;
    4619             : 
    4620             :         /* Set up suitably-escaped copies of textual inputs */
    4621          10 :         e_text = escape_string(text);
    4622             : 
    4623          10 :         if (completion_info_charp)
    4624           0 :             e_info_charp = escape_string(completion_info_charp);
    4625             :         else
    4626          10 :             e_info_charp = NULL;
    4627             : 
    4628          10 :         if (completion_info_charp2)
    4629           0 :             e_info_charp2 = escape_string(completion_info_charp2);
    4630             :         else
    4631          10 :             e_info_charp2 = NULL;
    4632             : 
    4633          10 :         initPQExpBuffer(&query_buffer);
    4634             : 
    4635          10 :         if (schema_query)
    4636             :         {
    4637             :             /* schema_query gives us the pieces to assemble */
    4638          10 :             const char *qualresult = schema_query->qualresult;
    4639             : 
    4640          10 :             if (qualresult == NULL)
    4641          10 :                 qualresult = schema_query->result;
    4642             : 
    4643             :             /* Get unqualified names matching the input-so-far */
    4644          10 :             appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
    4645             :                               schema_query->result,
    4646             :                               schema_query->catname);
    4647          10 :             if (schema_query->selcondition)
    4648          10 :                 appendPQExpBuffer(&query_buffer, "%s AND ",
    4649             :                                   schema_query->selcondition);
    4650          10 :             appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
    4651             :                               schema_query->result,
    4652             :                               char_length, e_text);
    4653          10 :             appendPQExpBuffer(&query_buffer, " AND %s",
    4654             :                               schema_query->viscondition);
    4655             : 
    4656             :             /*
    4657             :              * When fetching relation names, suppress system catalogs unless
    4658             :              * the input-so-far begins with "pg_".  This is a compromise
    4659             :              * between not offering system catalogs for completion at all, and
    4660             :              * having them swamp the result when the input is just "p".
    4661             :              */
    4662          10 :             if (strcmp(schema_query->catname,
    4663          10 :                        "pg_catalog.pg_class c") == 0 &&
    4664          10 :                 strncmp(text, "pg_", 3) != 0)
    4665             :             {
    4666          10 :                 appendPQExpBufferStr(&query_buffer,
    4667             :                                      " AND c.relnamespace <> (SELECT oid FROM"
    4668             :                                      " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
    4669             :             }
    4670             : 
    4671             :             /*
    4672             :              * Add in matching schema names, but only if there is more than
    4673             :              * one potential match among schema names.
    4674             :              */
    4675          10 :             appendPQExpBuffer(&query_buffer, "\nUNION\n"
    4676             :                               "SELECT pg_catalog.quote_ident(n.nspname) || '.' "
    4677             :                               "FROM pg_catalog.pg_namespace n "
    4678             :                               "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'",
    4679             :                               char_length, e_text);
    4680          10 :             appendPQExpBuffer(&query_buffer,
    4681             :                               " AND (SELECT pg_catalog.count(*)"
    4682             :                               " FROM pg_catalog.pg_namespace"
    4683             :                               " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
    4684             :                               " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1",
    4685             :                               char_length, e_text);
    4686             : 
    4687             :             /*
    4688             :              * Add in matching qualified names, but only if there is exactly
    4689             :              * one schema matching the input-so-far.
    4690             :              */
    4691          10 :             appendPQExpBuffer(&query_buffer, "\nUNION\n"
    4692             :                               "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
    4693             :                               "FROM %s, pg_catalog.pg_namespace n "
    4694             :                               "WHERE %s = n.oid AND ",
    4695             :                               qualresult,
    4696             :                               schema_query->catname,
    4697             :                               schema_query->namespace);
    4698          10 :             if (schema_query->selcondition)
    4699          10 :                 appendPQExpBuffer(&query_buffer, "%s AND ",
    4700             :                                   schema_query->selcondition);
    4701          10 :             appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
    4702             :                               qualresult,
    4703             :                               char_length, e_text);
    4704             : 
    4705             :             /*
    4706             :              * This condition exploits the single-matching-schema rule to
    4707             :              * speed up the query
    4708             :              */
    4709          10 :             appendPQExpBuffer(&query_buffer,
    4710             :                               " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="
    4711             :                               " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)",
    4712             :                               char_length, e_text);
    4713          10 :             appendPQExpBuffer(&query_buffer,
    4714             :                               " AND (SELECT pg_catalog.count(*)"
    4715             :                               " FROM pg_catalog.pg_namespace"
    4716             :                               " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
    4717             :                               " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1",
    4718             :                               char_length, e_text);
    4719             : 
    4720             :             /* If an addon query was provided, use it */
    4721          10 :             if (simple_query)
    4722           0 :                 appendPQExpBuffer(&query_buffer, "\n%s", simple_query);
    4723             :         }
    4724             :         else
    4725             :         {
    4726             :             Assert(simple_query);
    4727             :             /* simple_query is an sprintf-style format string */
    4728           0 :             appendPQExpBuffer(&query_buffer, simple_query,
    4729             :                               char_length, e_text,
    4730             :                               e_info_charp, e_info_charp,
    4731             :                               e_info_charp2, e_info_charp2);
    4732             :         }
    4733             : 
    4734             :         /* Limit the number of records in the result */
    4735          10 :         appendPQExpBuffer(&query_buffer, "\nLIMIT %d",
    4736             :                           completion_max_records);
    4737             : 
    4738          10 :         result = exec_query(query_buffer.data);
    4739             : 
    4740          10 :         termPQExpBuffer(&query_buffer);
    4741          10 :         free(e_text);
    4742          10 :         if (e_info_charp)
    4743           0 :             free(e_info_charp);
    4744          10 :         if (e_info_charp2)
    4745           0 :             free(e_info_charp2);
    4746             :     }
    4747             : 
    4748             :     /* Find something that matches */
    4749          26 :     if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
    4750             :     {
    4751             :         const char *item;
    4752             : 
    4753          42 :         while (list_index < PQntuples(result) &&
    4754          16 :                (item = PQgetvalue(result, list_index++, 0)))
    4755          16 :             if (pg_strncasecmp(text, item, byte_length) == 0)
    4756          16 :                 return pg_strdup(item);
    4757             :     }
    4758             : 
    4759             :     /* If nothing matches, free the db structure and return null */
    4760          10 :     PQclear(result);
    4761          10 :     result = NULL;
    4762          10 :     return NULL;
    4763             : }
    4764             : 
    4765             : 
    4766             : /*
    4767             :  * This function returns in order one of a fixed, NULL pointer terminated list
    4768             :  * of strings (if matching). This can be used if there are only a fixed number
    4769             :  * SQL words that can appear at certain spot.
    4770             :  */
    4771             : static char *
    4772          14 : complete_from_list(const char *text, int state)
    4773             : {
    4774             :     static int  string_length,
    4775             :                 list_index,
    4776             :                 matches;
    4777             :     static bool casesensitive;
    4778             :     const char *item;
    4779             : 
    4780             :     /* need to have a list */
    4781             :     Assert(completion_charpp != NULL);
    4782             : 
    4783             :     /* Initialization */
    4784          14 :     if (state == 0)
    4785             :     {
    4786           6 :         list_index = 0;
    4787           6 :         string_length = strlen(text);
    4788           6 :         casesensitive = completion_case_sensitive;
    4789           6 :         matches = 0;
    4790             :     }
    4791             : 
    4792         628 :     while ((item = completion_charpp[list_index++]))
    4793             :     {
    4794             :         /* First pass is case sensitive */
    4795         620 :         if (casesensitive && strncmp(text, item, string_length) == 0)
    4796             :         {
    4797           0 :             matches++;
    4798           0 :             return pg_strdup(item);
    4799             :         }
    4800             : 
    4801             :         /* Second pass is case insensitive, don't bother counting matches */
    4802         620 :         if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
    4803             :         {
    4804           6 :             if (completion_case_sensitive)
    4805           2 :                 return pg_strdup(item);
    4806             :             else
    4807             : 
    4808             :                 /*
    4809             :                  * If case insensitive matching was requested initially,
    4810             :                  * adjust the case according to setting.
    4811             :                  */
    4812           4 :                 return pg_strdup_keyword_case(item, text);
    4813             :         }
    4814             :     }
    4815             : 
    4816             :     /*
    4817             :      * No matches found. If we're not case insensitive already, lets switch to
    4818             :      * being case insensitive and try again
    4819             :      */
    4820           8 :     if (casesensitive && matches == 0)
    4821             :     {
    4822           2 :         casesensitive = false;
    4823           2 :         list_index = 0;
    4824           2 :         state++;
    4825           2 :         return complete_from_list(text, state);
    4826             :     }
    4827             : 
    4828             :     /* If no more matches, return null. */
    4829           6 :     return NULL;
    4830             : }
    4831             : 
    4832             : 
    4833             : /*
    4834             :  * This function returns one fixed string the first time even if it doesn't
    4835             :  * match what's there, and nothing the second time.  The string
    4836             :  * to be used must be in completion_charp.
    4837             :  *
    4838             :  * If the given string is "", this has the effect of preventing readline
    4839             :  * from doing any completion.  (Without this, readline tries to do filename
    4840             :  * completion which is seldom the right thing.)
    4841             :  *
    4842             :  * If the given string is not empty, readline will replace whatever the
    4843             :  * user typed with that string.  This behavior might be useful if it's
    4844             :  * completely certain that we know what must appear at a certain spot,
    4845             :  * so that it's okay to overwrite misspellings.  In practice, given the
    4846             :  * relatively lame parsing technology used in this file, the level of
    4847             :  * certainty is seldom that high, so that you probably don't want to
    4848             :  * use this.  Use complete_from_list with a one-element list instead;
    4849             :  * that won't try to auto-correct "misspellings".
    4850             :  */
    4851             : static char *
    4852           0 : complete_from_const(const char *text, int state)
    4853             : {
    4854             :     Assert(completion_charp != NULL);
    4855           0 :     if (state == 0)
    4856             :     {
    4857           0 :         if (completion_case_sensitive)
    4858           0 :             return pg_strdup(completion_charp);
    4859             :         else
    4860             : 
    4861             :             /*
    4862             :              * If case insensitive matching was requested initially, adjust
    4863             :              * the case according to setting.
    4864             :              */
    4865           0 :             return pg_strdup_keyword_case(completion_charp, text);
    4866             :     }
    4867             :     else
    4868           0 :         return NULL;
    4869             : }
    4870             : 
    4871             : 
    4872             : /*
    4873             :  * This function appends the variable name with prefix and suffix to
    4874             :  * the variable names array.
    4875             :  */
    4876             : static void
    4877           0 : append_variable_names(char ***varnames, int *nvars,
    4878             :                       int *maxvars, const char *varname,
    4879             :                       const char *prefix, const char *suffix)
    4880             : {
    4881           0 :     if (*nvars >= *maxvars)
    4882             :     {
    4883           0 :         *maxvars *= 2;
    4884           0 :         *varnames = (char **) pg_realloc(*varnames,
    4885           0 :                                          ((*maxvars) + 1) * sizeof(char *));
    4886             :     }
    4887             : 
    4888           0 :     (*varnames)[(*nvars)++] = psprintf("%s%s%s", prefix, varname, suffix);
    4889           0 : }
    4890             : 
    4891             : 
    4892             : /*
    4893             :  * This function supports completion with the name of a psql variable.
    4894             :  * The variable names can be prefixed and suffixed with additional text
    4895             :  * to support quoting usages. If need_value is true, only variables
    4896             :  * that are currently set are included; otherwise, special variables
    4897             :  * (those that have hooks) are included even if currently unset.
    4898             :  */
    4899             : static char **
    4900           0 : complete_from_variables(const char *text, const char *prefix, const char *suffix,
    4901             :                         bool need_value)
    4902             : {
    4903             :     char      **matches;
    4904             :     char      **varnames;
    4905           0 :     int         nvars = 0;
    4906           0 :     int         maxvars = 100;
    4907             :     int         i;
    4908             :     struct _variable *ptr;
    4909             : 
    4910           0 :     varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *));
    4911             : 
    4912           0 :     for (ptr = pset.vars->next; ptr; ptr = ptr->next)
    4913             :     {
    4914           0 :         if (need_value && !(ptr->value))
    4915           0 :             continue;
    4916           0 :         append_variable_names(&varnames, &nvars, &maxvars, ptr->name,
    4917             :                               prefix, suffix);
    4918             :     }
    4919             : 
    4920           0 :     varnames[nvars] = NULL;
    4921           0 :     COMPLETE_WITH_LIST_CS((const char *const *) varnames);
    4922             : 
    4923           0 :     for (i = 0; i < nvars; i++)
    4924           0 :         free(varnames[i]);
    4925           0 :     free(varnames);
    4926             : 
    4927           0 :     return matches;
    4928             : }
    4929             : 
    4930             : 
    4931             : /*
    4932             :  * This function wraps rl_filename_completion_function() to strip quotes from
    4933             :  * the input before searching for matches and to quote any matches for which
    4934             :  * the consuming command will require it.
    4935             :  *
    4936             :  * Caller must set completion_charp to a zero- or one-character string
    4937             :  * containing the escape character.  This is necessary since \copy has no
    4938             :  * escape character, but every other backslash command recognizes "\" as an
    4939             :  * escape character.
    4940             :  *
    4941             :  * Caller must also set completion_force_quote to indicate whether to force
    4942             :  * quotes around the result.  (The SQL COPY command requires that.)
    4943             :  */
    4944             : static char *
    4945          32 : complete_from_files(const char *text, int state)
    4946             : {
    4947             : #ifdef USE_FILENAME_QUOTING_FUNCTIONS
    4948             : 
    4949             :     /*
    4950             :      * If we're using a version of Readline that supports filename quoting
    4951             :      * hooks, rely on those, and invoke rl_filename_completion_function()
    4952             :      * without messing with its arguments.  Readline does stuff internally
    4953             :      * that does not work well at all if we try to handle dequoting here.
    4954             :      * Instead, Readline will call quote_file_name() and dequote_file_name()
    4955             :      * (see below) at appropriate times.
    4956             :      *
    4957             :      * ... or at least, mostly it will.  There are some paths involving
    4958             :      * unmatched file names in which Readline never calls quote_file_name(),
    4959             :      * and if left to its own devices it will incorrectly append a quote
    4960             :      * anyway.  Set rl_completion_suppress_quote to prevent that.  If we do
    4961             :      * get to quote_file_name(), we'll clear this again.  (Yes, this seems
    4962             :      * like it's working around Readline bugs.)
    4963             :      */
    4964             : #ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
    4965          32 :     rl_completion_suppress_quote = 1;
    4966             : #endif
    4967             : 
    4968             :     /* If user typed a quote, force quoting (never remove user's quote) */
    4969          32 :     if (*text == '\'')
    4970           0 :         completion_force_quote = true;
    4971             : 
    4972          32 :     return rl_filename_completion_function(text, state);
    4973             : #else
    4974             : 
    4975             :     /*
    4976             :      * Otherwise, we have to do the best we can.
    4977             :      */
    4978             :     static const char *unquoted_text;
    4979             :     char       *unquoted_match;
    4980             :     char       *ret = NULL;
    4981             : 
    4982             :     /* If user typed a quote, force quoting (never remove user's quote) */
    4983             :     if (*text == '\'')
    4984             :         completion_force_quote = true;
    4985             : 
    4986             :     if (state == 0)
    4987             :     {
    4988             :         /* Initialization: stash the unquoted input. */
    4989             :         unquoted_text = strtokx(text, "", NULL, "'", *completion_charp,
    4990             :                                 false, true, pset.encoding);
    4991             :         /* expect a NULL return for the empty string only */
    4992             :         if (!unquoted_text)
    4993             :         {
    4994             :             Assert(*text == '\0');
    4995             :             unquoted_text = text;
    4996             :         }
    4997             :     }
    4998             : 
    4999             :     unquoted_match = rl_filename_completion_function(unquoted_text, state);
    5000             :     if (unquoted_match)
    5001             :     {
    5002             :         struct stat statbuf;
    5003             :         bool        is_dir = (stat(unquoted_match, &statbuf) == 0 &&
    5004             :                               S_ISDIR(statbuf.st_mode) != 0);
    5005             : 
    5006             :         /* Re-quote the result, if needed. */
    5007             :         ret = quote_if_needed(unquoted_match, " \t\r\n\"`",
    5008             :                               '\'', *completion_charp,
    5009             :                               completion_force_quote,
    5010             :                               pset.encoding);
    5011             :         if (ret)
    5012             :             free(unquoted_match);
    5013             :         else
    5014             :             ret = unquoted_match;
    5015             : 
    5016             :         /*
    5017             :          * If it's a directory, replace trailing quote with a slash; this is
    5018             :          * usually more convenient.  (If we didn't quote, leave this to
    5019             :          * libedit.)
    5020             :          */
    5021             :         if (*ret == '\'' && is_dir)
    5022             :         {
    5023             :             char       *retend = ret + strlen(ret) - 1;
    5024             : 
    5025             :             Assert(*retend == '\'');
    5026             :             *retend = '/';
    5027             :             /* Try to prevent libedit from adding a space, too */
    5028             : #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
    5029             :             rl_completion_append_character = '\0';
    5030             : #endif
    5031             :         }
    5032             :     }
    5033             : 
    5034             :     return ret;
    5035             : #endif                          /* USE_FILENAME_QUOTING_FUNCTIONS */
    5036             : }
    5037             : 
    5038             : 
    5039             : /* HELPER FUNCTIONS */
    5040             : 
    5041             : 
    5042             : /*
    5043             :  * Make a pg_strdup copy of s and convert the case according to
    5044             :  * COMP_KEYWORD_CASE setting, using ref as the text that was already entered.
    5045             :  */
    5046             : static char *
    5047           4 : pg_strdup_keyword_case(const char *s, const char *ref)
    5048             : {
    5049             :     char       *ret,
    5050             :                *p;
    5051           4 :     unsigned char first = ref[0];
    5052             : 
    5053           4 :     ret = pg_strdup(s);
    5054             : 
    5055           4 :     if (pset.comp_case == PSQL_COMP_CASE_LOWER ||
    5056           4 :         ((pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER ||
    5057           4 :           pset.comp_case == PSQL_COMP_CASE_PRESERVE_UPPER) && islower(first)) ||
    5058           2 :         (pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER && !isalpha(first)))
    5059             :     {
    5060          14 :         for (p = ret; *p; p++)
    5061          12 :             *p = pg_tolower((unsigned char) *p);
    5062             :     }
    5063             :     else
    5064             :     {
    5065          14 :         for (p = ret; *p; p++)
    5066          12 :             *p = pg_toupper((unsigned char) *p);
    5067             :     }
    5068             : 
    5069           4 :     return ret;
    5070             : }
    5071             : 
    5072             : 
    5073             : /*
    5074             :  * escape_string - Escape argument for use as string literal.
    5075             :  *
    5076             :  * The returned value has to be freed.
    5077             :  */
    5078             : static char *
    5079          10 : escape_string(const char *text)
    5080             : {
    5081             :     size_t      text_length;
    5082             :     char       *result;
    5083             : 
    5084          10 :     text_length = strlen(text);
    5085             : 
    5086          10 :     result = pg_malloc(text_length * 2 + 1);
    5087          10 :     PQescapeStringConn(pset.db, result, text, text_length, NULL);
    5088             : 
    5089          10 :     return result;
    5090             : }
    5091             : 
    5092             : 
    5093             : /*
    5094             :  * Execute a query and report any errors. This should be the preferred way of
    5095             :  * talking to the database in this file.
    5096             :  */
    5097             : static PGresult *
    5098          10 : exec_query(const char *query)
    5099             : {
    5100             :     PGresult   *result;
    5101             : 
    5102          10 :     if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK)
    5103           0 :         return NULL;
    5104             : 
    5105          10 :     result = PQexec(pset.db, query);
    5106             : 
    5107          10 :     if (PQresultStatus(result) != PGRES_TUPLES_OK)
    5108             :     {
    5109             : #ifdef NOT_USED
    5110             :         pg_log_error("tab completion query failed: %s\nQuery was:\n%s",
    5111             :                      PQerrorMessage(pset.db), query);
    5112             : #endif
    5113           0 :         PQclear(result);
    5114           0 :         result = NULL;
    5115             :     }
    5116             : 
    5117          10 :     return result;
    5118             : }
    5119             : 
    5120             : 
    5121             : /*
    5122             :  * Parse all the word(s) before point.
    5123             :  *
    5124             :  * Returns a malloc'd array of character pointers that point into the malloc'd
    5125             :  * data array returned to *buffer; caller must free() both of these when done.
    5126             :  * *nwords receives the number of words found, ie, the valid length of the
    5127             :  * return array.
    5128             :  *
    5129             :  * Words are returned right to left, that is, previous_words[0] gets the last
    5130             :  * word before point, previous_words[1] the next-to-last, etc.
    5131             :  */
    5132             : static char **
    5133          28 : get_previous_words(int point, char **buffer, int *nwords)
    5134             : {
    5135             :     char      **previous_words;
    5136             :     char       *buf;
    5137             :     char       *outptr;
    5138          28 :     int         words_found = 0;
    5139             :     int         i;
    5140             : 
    5141             :     /*
    5142             :      * If we have anything in tab_completion_query_buf, paste it together with
    5143             :      * rl_line_buffer to construct the full query.  Otherwise we can just use
    5144             :      * rl_line_buffer as the input string.
    5145             :      */
    5146          28 :     if (tab_completion_query_buf && tab_completion_query_buf->len > 0)
    5147             :     {
    5148           0 :         i = tab_completion_query_buf->len;
    5149           0 :         buf = pg_malloc(point + i + 2);
    5150           0 :         memcpy(buf, tab_completion_query_buf->data, i);
    5151           0 :         buf[i++] = '\n';
    5152           0 :         memcpy(buf + i, rl_line_buffer, point);
    5153           0 :         i += point;
    5154           0 :         buf[i] = '\0';
    5155             :         /* Readjust point to reference appropriate offset in buf */
    5156           0 :         point = i;
    5157             :     }
    5158             :     else
    5159          28 :         buf = rl_line_buffer;
    5160             : 
    5161             :     /*
    5162             :      * Allocate an array of string pointers and a buffer to hold the strings
    5163             :      * themselves.  The worst case is that the line contains only
    5164             :      * non-whitespace WORD_BREAKS characters, making each one a separate word.
    5165             :      * This is usually much more space than we need, but it's cheaper than
    5166             :      * doing a separate malloc() for each word.
    5167             :      */
    5168          28 :     previous_words = (char **) pg_malloc(point * sizeof(char *));
    5169          28 :     *buffer = outptr = (char *) pg_malloc(point * 2);
    5170             : 
    5171             :     /*
    5172             :      * First we look for a non-word char before the current point.  (This is
    5173             :      * probably useless, if readline is on the same page as we are about what
    5174             :      * is a word, but if so it's cheap.)
    5175             :      */
    5176          32 :     for (i = point - 1; i >= 0; i--)
    5177             :     {
    5178          26 :         if (strchr(WORD_BREAKS, buf[i]))
    5179          22 :             break;
    5180             :     }
    5181          28 :     point = i;
    5182             : 
    5183             :     /*
    5184             :      * Now parse words, working backwards, until we hit start of line.  The
    5185             :      * backwards scan has some interesting but intentional properties
    5186             :      * concerning parenthesis handling.
    5187             :      */
    5188          86 :     while (point >= 0)
    5189             :     {
    5190             :         int         start,
    5191             :                     end;
    5192          58 :         bool        inquotes = false;
    5193          58 :         int         parentheses = 0;
    5194             : 
    5195             :         /* now find the first non-space which then constitutes the end */
    5196          58 :         end = -1;
    5197         116 :         for (i = point; i >= 0; i--)
    5198             :         {
    5199         116 :             if (!isspace((unsigned char) buf[i]))
    5200             :             {
    5201          58 :                 end = i;
    5202          58 :                 break;
    5203             :             }
    5204             :         }
    5205             :         /* if no end found, we're done */
    5206          58 :         if (end < 0)
    5207           0 :             break;
    5208             : 
    5209             :         /*
    5210             :          * Otherwise we now look for the start.  The start is either the last
    5211             :          * character before any word-break character going backwards from the
    5212             :          * end, or it's simply character 0.  We also handle open quotes and
    5213             :          * parentheses.
    5214             :          */
    5215         238 :         for (start = end; start > 0; start--)
    5216             :         {
    5217         216 :             if (buf[start] == '"')
    5218           0 :                 inquotes = !inquotes;
    5219         216 :             if (!inquotes)
    5220             :             {
    5221         216 :                 if (buf[start] == ')')
    5222           0 :                     parentheses++;
    5223         216 :                 else if (buf[start] == '(')
    5224             :                 {
    5225           0 :                     if (--parentheses <= 0)
    5226           0 :                         break;
    5227             :                 }
    5228         216 :                 else if (parentheses == 0 &&
    5229         216 :                          strchr(WORD_BREAKS, buf[start - 1]))
    5230          36 :                     break;
    5231             :             }
    5232             :         }
    5233             : 
    5234             :         /* Return the word located at start to end inclusive */
    5235          58 :         previous_words[words_found++] = outptr;
    5236          58 :         i = end - start + 1;
    5237          58 :         memcpy(outptr, &buf[start], i);
    5238          58 :         outptr += i;
    5239          58 :         *outptr++ = '\0';
    5240             : 
    5241             :         /* Continue searching */
    5242          58 :         point = start - 1;
    5243             :     }
    5244             : 
    5245             :     /* Release parsing input workspace, if we made one above */
    5246          28 :     if (buf != rl_line_buffer)
    5247           0 :         free(buf);
    5248             : 
    5249          28 :     *nwords = words_found;
    5250          28 :     return previous_words;
    5251             : }
    5252             : 
    5253             : /*
    5254             :  * Look up the type for the GUC variable with the passed name.
    5255             :  *
    5256             :  * Returns NULL if the variable is unknown. Otherwise the returned string,
    5257             :  * containing the type, has to be freed.
    5258             :  */
    5259             : static char *
    5260           0 : get_guctype(const char *varname)
    5261             : {
    5262             :     PQExpBufferData query_buffer;
    5263             :     char       *e_varname;
    5264             :     PGresult   *result;
    5265           0 :     char       *guctype = NULL;
    5266             : 
    5267           0 :     e_varname = escape_string(varname);
    5268             : 
    5269           0 :     initPQExpBuffer(&query_buffer);
    5270           0 :     appendPQExpBuffer(&query_buffer,
    5271             :                       "SELECT vartype FROM pg_catalog.pg_settings "
    5272             :                       "WHERE pg_catalog.lower(name) = pg_catalog.lower('%s')",
    5273             :                       e_varname);
    5274             : 
    5275           0 :     result = exec_query(query_buffer.data);
    5276           0 :     termPQExpBuffer(&query_buffer);
    5277           0 :     free(e_varname);
    5278             : 
    5279           0 :     if (PQresultStatus(result) == PGRES_TUPLES_OK && PQntuples(result) > 0)
    5280           0 :         guctype = pg_strdup(PQgetvalue(result, 0, 0));
    5281             : 
    5282           0 :     PQclear(result);
    5283             : 
    5284           0 :     return guctype;
    5285             : }
    5286             : 
    5287             : #ifdef USE_FILENAME_QUOTING_FUNCTIONS
    5288             : 
    5289             : /*
    5290             :  * Quote a filename according to SQL rules, returning a malloc'd string.
    5291             :  * completion_charp must point to escape character or '\0', and
    5292             :  * completion_force_quote must be set correctly, as per comments for
    5293             :  * complete_from_files().
    5294             :  */
    5295             : static char *
    5296          10 : quote_file_name(char *fname, int match_type, char *quote_pointer)
    5297             : {
    5298             :     char       *s;
    5299             :     struct stat statbuf;
    5300             : 
    5301             :     /* Quote if needed. */
    5302          10 :     s = quote_if_needed(fname, " \t\r\n\"`",
    5303          10 :                         '\'', *completion_charp,
    5304             :                         completion_force_quote,
    5305             :                         pset.encoding);
    5306          10 :     if (!s)
    5307           4 :         s = pg_strdup(fname);
    5308             : 
    5309             :     /*
    5310             :      * However, some of the time we have to strip the trailing quote from what
    5311             :      * we send back.  Never strip the trailing quote if the user already typed
    5312             :      * one; otherwise, suppress the trailing quote if we have multiple/no
    5313             :      * matches (because we don't want to add a quote if the input is seemingly
    5314             :      * unfinished), or if the input was already quoted (because Readline will
    5315             :      * do arguably-buggy things otherwise), or if the file does not exist, or
    5316             :      * if it's a directory.
    5317             :      */
    5318          10 :     if (*s == '\'' &&
    5319           6 :         completion_last_char != '\'' &&
    5320           2 :         (match_type != SINGLE_MATCH ||
    5321           4 :          (quote_pointer && *quote_pointer == '\'') ||
    5322           2 :          stat(fname, &statbuf) != 0 ||
    5323           2 :          S_ISDIR(statbuf.st_mode)))
    5324             :     {
    5325           4 :         char       *send = s + strlen(s) - 1;
    5326             : 
    5327             :         Assert(*send == '\'');
    5328           4 :         *send = '\0';
    5329             :     }
    5330             : 
    5331             :     /*
    5332             :      * And now we can let Readline do its thing with possibly adding a quote
    5333             :      * on its own accord.  (This covers some additional cases beyond those
    5334             :      * dealt with above.)
    5335             :      */
    5336             : #ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
    5337          10 :     rl_completion_suppress_quote = 0;
    5338             : #endif
    5339             : 
    5340             :     /*
    5341             :      * If user typed a leading quote character other than single quote (i.e.,
    5342             :      * double quote), zap it, so that we replace it with the correct single
    5343             :      * quote.
    5344             :      */
    5345          10 :     if (quote_pointer && *quote_pointer != '\'')
    5346           8 :         *quote_pointer = '\0';
    5347             : 
    5348          10 :     return s;
    5349             : }
    5350             : 
    5351             : /*
    5352             :  * Dequote a filename, if it's quoted.
    5353             :  * completion_charp must point to escape character or '\0', as per
    5354             :  * comments for complete_from_files().
    5355             :  */
    5356             : static char *
    5357          24 : dequote_file_name(char *fname, int quote_char)
    5358             : {
    5359             :     char       *unquoted_fname;
    5360             : 
    5361             :     /*
    5362             :      * If quote_char is set, it's not included in "fname".  We have to add it
    5363             :      * or strtokx will not interpret the string correctly (notably, it won't
    5364             :      * recognize escapes).
    5365             :      */
    5366          24 :     if (quote_char == '\'')
    5367             :     {
    5368          12 :         char       *workspace = (char *) pg_malloc(strlen(fname) + 2);
    5369             : 
    5370          12 :         workspace[0] = quote_char;
    5371          12 :         strcpy(workspace + 1, fname);
    5372          12 :         unquoted_fname = strtokx(workspace, "", NULL, "'", *completion_charp,
    5373             :                                  false, true, pset.encoding);
    5374          12 :         free(workspace);
    5375             :     }
    5376             :     else
    5377          12 :         unquoted_fname = strtokx(fname, "", NULL, "'", *completion_charp,
    5378             :                                  false, true, pset.encoding);
    5379             : 
    5380             :     /* expect a NULL return for the empty string only */
    5381          24 :     if (!unquoted_fname)
    5382             :     {
    5383             :         Assert(*fname == '\0');
    5384           0 :         unquoted_fname = fname;
    5385             :     }
    5386             : 
    5387             :     /* readline expects a malloc'd result that it is to free */
    5388          24 :     return pg_strdup(unquoted_fname);
    5389             : }
    5390             : 
    5391             : #endif                          /* USE_FILENAME_QUOTING_FUNCTIONS */
    5392             : 
    5393             : #endif                          /* USE_READLINE */

Generated by: LCOV version 1.14