Header And Logo

PostgreSQL
| The world's most advanced open source database.

tab-complete.c

Go to the documentation of this file.
00001 /*
00002  * psql - the PostgreSQL interactive terminal
00003  *
00004  * Copyright (c) 2000-2013, PostgreSQL Global Development Group
00005  *
00006  * src/bin/psql/tab-complete.c
00007  */
00008 
00009 /*----------------------------------------------------------------------
00010  * This file implements a somewhat more sophisticated readline "TAB
00011  * completion" in psql. It is not intended to be AI, to replace
00012  * learning SQL, or to relieve you from thinking about what you're
00013  * doing. Also it does not always give you all the syntactically legal
00014  * completions, only those that are the most common or the ones that
00015  * the programmer felt most like implementing.
00016  *
00017  * CAVEAT: Tab completion causes queries to be sent to the backend.
00018  * The number of tuples returned gets limited, in most default
00019  * installations to 1000, but if you still don't like this prospect,
00020  * you can turn off tab completion in your ~/.inputrc (or else
00021  * ${INPUTRC}) file so:
00022  *
00023  *   $if psql
00024  *   set disable-completion on
00025  *   $endif
00026  *
00027  * See `man 3 readline' or `info readline' for the full details. Also,
00028  * hence the
00029  *
00030  * BUGS:
00031  *
00032  * - If you split your queries across lines, this whole thing gets
00033  *   confused. (To fix this, one would have to read psql's query
00034  *   buffer rather than readline's line buffer, which would require
00035  *   some major revisions of things.)
00036  *
00037  * - Table or attribute names with spaces in it may confuse it.
00038  *
00039  * - Quotes, parenthesis, and other funny characters are not handled
00040  *   all that gracefully.
00041  *----------------------------------------------------------------------
00042  */
00043 
00044 #include "postgres_fe.h"
00045 #include "tab-complete.h"
00046 #include "input.h"
00047 
00048 /* If we don't have this, we might as well forget about the whole thing: */
00049 #ifdef USE_READLINE
00050 
00051 #include <ctype.h>
00052 #include "libpq-fe.h"
00053 #include "pqexpbuffer.h"
00054 #include "common.h"
00055 #include "settings.h"
00056 #include "stringutils.h"
00057 
00058 #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
00059 #define filename_completion_function rl_filename_completion_function
00060 #else
00061 /* missing in some header files */
00062 extern char *filename_completion_function();
00063 #endif
00064 
00065 #ifdef HAVE_RL_COMPLETION_MATCHES
00066 #define completion_matches rl_completion_matches
00067 #endif
00068 
00069 /* word break characters */
00070 #define WORD_BREAKS     "\t\n@$><=;|&{() "
00071 
00072 /*
00073  * This struct is used to define "schema queries", which are custom-built
00074  * to obtain possibly-schema-qualified names of database objects.  There is
00075  * enough similarity in the structure that we don't want to repeat it each
00076  * time.  So we put the components of each query into this struct and
00077  * assemble them with the common boilerplate in _complete_from_query().
00078  */
00079 typedef struct SchemaQuery
00080 {
00081     /*
00082      * Name of catalog or catalogs to be queried, with alias, eg.
00083      * "pg_catalog.pg_class c".  Note that "pg_namespace n" will be added.
00084      */
00085     const char *catname;
00086 
00087     /*
00088      * Selection condition --- only rows meeting this condition are candidates
00089      * to display.  If catname mentions multiple tables, include the necessary
00090      * join condition here.  For example, "c.relkind = 'r'". Write NULL (not
00091      * an empty string) if not needed.
00092      */
00093     const char *selcondition;
00094 
00095     /*
00096      * Visibility condition --- which rows are visible without schema
00097      * qualification?  For example, "pg_catalog.pg_table_is_visible(c.oid)".
00098      */
00099     const char *viscondition;
00100 
00101     /*
00102      * Namespace --- name of field to join to pg_namespace.oid. For example,
00103      * "c.relnamespace".
00104      */
00105     const char *namespace;
00106 
00107     /*
00108      * Result --- the appropriately-quoted name to return, in the case of an
00109      * unqualified name.  For example, "pg_catalog.quote_ident(c.relname)".
00110      */
00111     const char *result;
00112 
00113     /*
00114      * In some cases a different result must be used for qualified names.
00115      * Enter that here, or write NULL if result can be used.
00116      */
00117     const char *qualresult;
00118 } SchemaQuery;
00119 
00120 
00121 /* Store maximum number of records we want from database queries
00122  * (implemented via SELECT ... LIMIT xx).
00123  */
00124 static int  completion_max_records;
00125 
00126 /*
00127  * Communication variables set by COMPLETE_WITH_FOO macros and then used by
00128  * the completion callback functions.  Ugly but there is no better way.
00129  */
00130 static const char *completion_charp;    /* to pass a string */
00131 static const char *const * completion_charpp;   /* to pass a list of strings */
00132 static const char *completion_info_charp;       /* to pass a second string */
00133 static const char *completion_info_charp2;      /* to pass a third string */
00134 static const SchemaQuery *completion_squery;    /* to pass a SchemaQuery */
00135 static bool completion_case_sensitive;  /* completion is case sensitive */
00136 
00137 /*
00138  * A few macros to ease typing. You can use these to complete the given
00139  * string with
00140  * 1) The results from a query you pass it. (Perhaps one of those below?)
00141  * 2) The results from a schema query you pass it.
00142  * 3) The items from a null-pointer-terminated list.
00143  * 4) A string constant.
00144  * 5) The list of attributes of the given table (possibly schema-qualified).
00145  * 6/ The list of arguments to the given function (possibly schema-qualified).
00146  */
00147 #define COMPLETE_WITH_QUERY(query) \
00148 do { \
00149     completion_charp = query; \
00150     matches = completion_matches(text, complete_from_query); \
00151 } while (0)
00152 
00153 #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
00154 do { \
00155     completion_squery = &(query); \
00156     completion_charp = addon; \
00157     matches = completion_matches(text, complete_from_schema_query); \
00158 } while (0)
00159 
00160 #define COMPLETE_WITH_LIST_CS(list) \
00161 do { \
00162     completion_charpp = list; \
00163     completion_case_sensitive = true; \
00164     matches = completion_matches(text, complete_from_list); \
00165 } while (0)
00166 
00167 #define COMPLETE_WITH_LIST(list) \
00168 do { \
00169     completion_charpp = list; \
00170     completion_case_sensitive = false; \
00171     matches = completion_matches(text, complete_from_list); \
00172 } while (0)
00173 
00174 #define COMPLETE_WITH_CONST(string) \
00175 do { \
00176     completion_charp = string; \
00177     completion_case_sensitive = false; \
00178     matches = completion_matches(text, complete_from_const); \
00179 } while (0)
00180 
00181 #define COMPLETE_WITH_ATTR(relation, addon) \
00182 do { \
00183     char   *_completion_schema; \
00184     char   *_completion_table; \
00185 \
00186     _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \
00187                                  false, false, pset.encoding); \
00188     (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
00189                    false, false, pset.encoding); \
00190     _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
00191                                 false, false, pset.encoding); \
00192     if (_completion_table == NULL) \
00193     { \
00194         completion_charp = Query_for_list_of_attributes  addon; \
00195         completion_info_charp = relation; \
00196     } \
00197     else \
00198     { \
00199         completion_charp = Query_for_list_of_attributes_with_schema  addon; \
00200         completion_info_charp = _completion_table; \
00201         completion_info_charp2 = _completion_schema; \
00202     } \
00203     matches = completion_matches(text, complete_from_query); \
00204 } while (0)
00205 
00206 #define COMPLETE_WITH_FUNCTION_ARG(function) \
00207 do { \
00208     char   *_completion_schema; \
00209     char   *_completion_function; \
00210 \
00211     _completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \
00212                                  false, false, pset.encoding); \
00213     (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
00214                    false, false, pset.encoding); \
00215     _completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
00216                                    false, false, pset.encoding); \
00217     if (_completion_function == NULL) \
00218     { \
00219         completion_charp = Query_for_list_of_arguments; \
00220         completion_info_charp = function; \
00221     } \
00222     else \
00223     { \
00224         completion_charp = Query_for_list_of_arguments_with_schema; \
00225         completion_info_charp = _completion_function; \
00226         completion_info_charp2 = _completion_schema; \
00227     } \
00228     matches = completion_matches(text, complete_from_query); \
00229 } while (0)
00230 
00231 /*
00232  * Assembly instructions for schema queries
00233  */
00234 
00235 static const SchemaQuery Query_for_list_of_aggregates = {
00236     /* catname */
00237     "pg_catalog.pg_proc p",
00238     /* selcondition */
00239     "p.proisagg",
00240     /* viscondition */
00241     "pg_catalog.pg_function_is_visible(p.oid)",
00242     /* namespace */
00243     "p.pronamespace",
00244     /* result */
00245     "pg_catalog.quote_ident(p.proname)",
00246     /* qualresult */
00247     NULL
00248 };
00249 
00250 static const SchemaQuery Query_for_list_of_datatypes = {
00251     /* catname */
00252     "pg_catalog.pg_type t",
00253     /* selcondition --- ignore table rowtypes and array types */
00254     "(t.typrelid = 0 "
00255     " OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
00256     "AND t.typname !~ '^_'",
00257     /* viscondition */
00258     "pg_catalog.pg_type_is_visible(t.oid)",
00259     /* namespace */
00260     "t.typnamespace",
00261     /* result */
00262     "pg_catalog.format_type(t.oid, NULL)",
00263     /* qualresult */
00264     "pg_catalog.quote_ident(t.typname)"
00265 };
00266 
00267 static const SchemaQuery Query_for_list_of_domains = {
00268     /* catname */
00269     "pg_catalog.pg_type t",
00270     /* selcondition */
00271     "t.typtype = 'd'",
00272     /* viscondition */
00273     "pg_catalog.pg_type_is_visible(t.oid)",
00274     /* namespace */
00275     "t.typnamespace",
00276     /* result */
00277     "pg_catalog.quote_ident(t.typname)",
00278     /* qualresult */
00279     NULL
00280 };
00281 
00282 static const SchemaQuery Query_for_list_of_functions = {
00283     /* catname */
00284     "pg_catalog.pg_proc p",
00285     /* selcondition */
00286     NULL,
00287     /* viscondition */
00288     "pg_catalog.pg_function_is_visible(p.oid)",
00289     /* namespace */
00290     "p.pronamespace",
00291     /* result */
00292     "pg_catalog.quote_ident(p.proname)",
00293     /* qualresult */
00294     NULL
00295 };
00296 
00297 static const SchemaQuery Query_for_list_of_indexes = {
00298     /* catname */
00299     "pg_catalog.pg_class c",
00300     /* selcondition */
00301     "c.relkind IN ('i')",
00302     /* viscondition */
00303     "pg_catalog.pg_table_is_visible(c.oid)",
00304     /* namespace */
00305     "c.relnamespace",
00306     /* result */
00307     "pg_catalog.quote_ident(c.relname)",
00308     /* qualresult */
00309     NULL
00310 };
00311 
00312 static const SchemaQuery Query_for_list_of_sequences = {
00313     /* catname */
00314     "pg_catalog.pg_class c",
00315     /* selcondition */
00316     "c.relkind IN ('S')",
00317     /* viscondition */
00318     "pg_catalog.pg_table_is_visible(c.oid)",
00319     /* namespace */
00320     "c.relnamespace",
00321     /* result */
00322     "pg_catalog.quote_ident(c.relname)",
00323     /* qualresult */
00324     NULL
00325 };
00326 
00327 static const SchemaQuery Query_for_list_of_foreign_tables = {
00328     /* catname */
00329     "pg_catalog.pg_class c",
00330     /* selcondition */
00331     "c.relkind IN ('f')",
00332     /* viscondition */
00333     "pg_catalog.pg_table_is_visible(c.oid)",
00334     /* namespace */
00335     "c.relnamespace",
00336     /* result */
00337     "pg_catalog.quote_ident(c.relname)",
00338     /* qualresult */
00339     NULL
00340 };
00341 
00342 static const SchemaQuery Query_for_list_of_tables = {
00343     /* catname */
00344     "pg_catalog.pg_class c",
00345     /* selcondition */
00346     "c.relkind IN ('r')",
00347     /* viscondition */
00348     "pg_catalog.pg_table_is_visible(c.oid)",
00349     /* namespace */
00350     "c.relnamespace",
00351     /* result */
00352     "pg_catalog.quote_ident(c.relname)",
00353     /* qualresult */
00354     NULL
00355 };
00356 
00357 static const SchemaQuery Query_for_list_of_constraints_with_schema = {
00358     /* catname */
00359     "pg_catalog.pg_constraint c",
00360     /* selcondition */
00361     "c.conrelid <> 0",
00362     /* viscondition */
00363     "true",             /* there is no pg_constraint_is_visible */
00364     /* namespace */
00365     "c.connamespace",
00366     /* result */
00367     "pg_catalog.quote_ident(c.conname)",
00368     /* qualresult */
00369     NULL
00370 };
00371 
00372 /* The bit masks for the following three functions come from
00373  * src/include/catalog/pg_trigger.h.
00374  */
00375 static const SchemaQuery Query_for_list_of_insertables = {
00376     /* catname */
00377     "pg_catalog.pg_class c",
00378     /* selcondition */
00379     "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
00380     "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 2) <> 0)))",
00381     /* viscondition */
00382     "pg_catalog.pg_table_is_visible(c.oid)",
00383     /* namespace */
00384     "c.relnamespace",
00385     /* result */
00386     "pg_catalog.quote_ident(c.relname)",
00387     /* qualresult */
00388     NULL
00389 };
00390 
00391 static const SchemaQuery Query_for_list_of_deletables = {
00392     /* catname */
00393     "pg_catalog.pg_class c",
00394     /* selcondition */
00395     "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
00396     "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 3) <> 0)))",
00397     /* viscondition */
00398     "pg_catalog.pg_table_is_visible(c.oid)",
00399     /* namespace */
00400     "c.relnamespace",
00401     /* result */
00402     "pg_catalog.quote_ident(c.relname)",
00403     /* qualresult */
00404     NULL
00405 };
00406 
00407 static const SchemaQuery Query_for_list_of_updatables = {
00408     /* catname */
00409     "pg_catalog.pg_class c",
00410     /* selcondition */
00411     "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
00412     "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 4) <> 0)))",
00413     /* viscondition */
00414     "pg_catalog.pg_table_is_visible(c.oid)",
00415     /* namespace */
00416     "c.relnamespace",
00417     /* result */
00418     "pg_catalog.quote_ident(c.relname)",
00419     /* qualresult */
00420     NULL
00421 };
00422 
00423 static const SchemaQuery Query_for_list_of_relations = {
00424     /* catname */
00425     "pg_catalog.pg_class c",
00426     /* selcondition */
00427     NULL,
00428     /* viscondition */
00429     "pg_catalog.pg_table_is_visible(c.oid)",
00430     /* namespace */
00431     "c.relnamespace",
00432     /* result */
00433     "pg_catalog.quote_ident(c.relname)",
00434     /* qualresult */
00435     NULL
00436 };
00437 
00438 static const SchemaQuery Query_for_list_of_tsvmf = {
00439     /* catname */
00440     "pg_catalog.pg_class c",
00441     /* selcondition */
00442     "c.relkind IN ('r', 'S', 'v', 'm', 'f')",
00443     /* viscondition */
00444     "pg_catalog.pg_table_is_visible(c.oid)",
00445     /* namespace */
00446     "c.relnamespace",
00447     /* result */
00448     "pg_catalog.quote_ident(c.relname)",
00449     /* qualresult */
00450     NULL
00451 };
00452 
00453 static const SchemaQuery Query_for_list_of_tmf = {
00454     /* catname */
00455     "pg_catalog.pg_class c",
00456     /* selcondition */
00457     "c.relkind IN ('r', 'm', 'f')",
00458     /* viscondition */
00459     "pg_catalog.pg_table_is_visible(c.oid)",
00460     /* namespace */
00461     "c.relnamespace",
00462     /* result */
00463     "pg_catalog.quote_ident(c.relname)",
00464     /* qualresult */
00465     NULL
00466 };
00467 
00468 static const SchemaQuery Query_for_list_of_tm = {
00469     /* catname */
00470     "pg_catalog.pg_class c",
00471     /* selcondition */
00472     "c.relkind IN ('r', 'm')",
00473     /* viscondition */
00474     "pg_catalog.pg_table_is_visible(c.oid)",
00475     /* namespace */
00476     "c.relnamespace",
00477     /* result */
00478     "pg_catalog.quote_ident(c.relname)",
00479     /* qualresult */
00480     NULL
00481 };
00482 
00483 static const SchemaQuery Query_for_list_of_views = {
00484     /* catname */
00485     "pg_catalog.pg_class c",
00486     /* selcondition */
00487     "c.relkind IN ('v')",
00488     /* viscondition */
00489     "pg_catalog.pg_table_is_visible(c.oid)",
00490     /* namespace */
00491     "c.relnamespace",
00492     /* result */
00493     "pg_catalog.quote_ident(c.relname)",
00494     /* qualresult */
00495     NULL
00496 };
00497 
00498 static const SchemaQuery Query_for_list_of_matviews = {
00499     /* catname */
00500     "pg_catalog.pg_class c",
00501     /* selcondition */
00502     "c.relkind IN ('m')",
00503     /* viscondition */
00504     "pg_catalog.pg_table_is_visible(c.oid)",
00505     /* namespace */
00506     "c.relnamespace",
00507     /* result */
00508     "pg_catalog.quote_ident(c.relname)",
00509     /* qualresult */
00510     NULL
00511 };
00512 
00513 
00514 /*
00515  * Queries to get lists of names of various kinds of things, possibly
00516  * restricted to names matching a partially entered name.  In these queries,
00517  * the first %s will be replaced by the text entered so far (suitably escaped
00518  * to become a SQL literal string).  %d will be replaced by the length of the
00519  * string (in unescaped form).  A second and third %s, if present, will be
00520  * replaced by a suitably-escaped version of the string provided in
00521  * completion_info_charp.  A fourth and fifth %s are similarly replaced by
00522  * completion_info_charp2.
00523  *
00524  * Beware that the allowed sequences of %s and %d are determined by
00525  * _complete_from_query().
00526  */
00527 
00528 #define Query_for_list_of_attributes \
00529 "SELECT pg_catalog.quote_ident(attname) "\
00530 "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
00531 " WHERE c.oid = a.attrelid "\
00532 "   AND a.attnum > 0 "\
00533 "   AND NOT a.attisdropped "\
00534 "   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
00535 "   AND (pg_catalog.quote_ident(relname)='%s' "\
00536 "        OR '\"' || relname || '\"'='%s') "\
00537 "   AND pg_catalog.pg_table_is_visible(c.oid)"
00538 
00539 #define Query_for_list_of_attributes_with_schema \
00540 "SELECT pg_catalog.quote_ident(attname) "\
00541 "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
00542 " WHERE c.oid = a.attrelid "\
00543 "   AND n.oid = c.relnamespace "\
00544 "   AND a.attnum > 0 "\
00545 "   AND NOT a.attisdropped "\
00546 "   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
00547 "   AND (pg_catalog.quote_ident(relname)='%s' "\
00548 "        OR '\"' || relname || '\"' ='%s') "\
00549 "   AND (pg_catalog.quote_ident(nspname)='%s' "\
00550 "        OR '\"' || nspname || '\"' ='%s') "
00551 
00552 #define Query_for_list_of_template_databases \
00553 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
00554 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' AND datistemplate"
00555 
00556 #define Query_for_list_of_databases \
00557 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
00558 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"
00559 
00560 #define Query_for_list_of_tablespaces \
00561 "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\
00562 " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"
00563 
00564 #define Query_for_list_of_encodings \
00565 " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
00566 "   FROM pg_catalog.pg_conversion "\
00567 "  WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
00568 
00569 #define Query_for_list_of_languages \
00570 "SELECT pg_catalog.quote_ident(lanname) "\
00571 "  FROM pg_catalog.pg_language "\
00572 " WHERE lanname != 'internal' "\
00573 "   AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'"
00574 
00575 #define Query_for_list_of_schemas \
00576 "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\
00577 " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"
00578 
00579 #define Query_for_list_of_set_vars \
00580 "SELECT name FROM "\
00581 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
00582 "  WHERE context IN ('user', 'superuser') "\
00583 "  UNION ALL SELECT 'constraints' "\
00584 "  UNION ALL SELECT 'transaction' "\
00585 "  UNION ALL SELECT 'session' "\
00586 "  UNION ALL SELECT 'role' "\
00587 "  UNION ALL SELECT 'tablespace' "\
00588 "  UNION ALL SELECT 'all') ss "\
00589 " WHERE substring(name,1,%d)='%s'"
00590 
00591 #define Query_for_list_of_show_vars \
00592 "SELECT name FROM "\
00593 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
00594 "  UNION ALL SELECT 'session authorization' "\
00595 "  UNION ALL SELECT 'all') ss "\
00596 " WHERE substring(name,1,%d)='%s'"
00597 
00598 #define Query_for_list_of_roles \
00599 " SELECT pg_catalog.quote_ident(rolname) "\
00600 "   FROM pg_catalog.pg_roles "\
00601 "  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"
00602 
00603 #define Query_for_list_of_grant_roles \
00604 " SELECT pg_catalog.quote_ident(rolname) "\
00605 "   FROM pg_catalog.pg_roles "\
00606 "  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\
00607 " UNION ALL SELECT 'PUBLIC'"
00608 
00609 /* the silly-looking length condition is just to eat up the current word */
00610 #define Query_for_table_owning_index \
00611 "SELECT pg_catalog.quote_ident(c1.relname) "\
00612 "  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
00613 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
00614 "       and (%d = pg_catalog.length('%s'))"\
00615 "       and pg_catalog.quote_ident(c2.relname)='%s'"\
00616 "       and pg_catalog.pg_table_is_visible(c2.oid)"
00617 
00618 /* the silly-looking length condition is just to eat up the current word */
00619 #define Query_for_index_of_table \
00620 "SELECT pg_catalog.quote_ident(c2.relname) "\
00621 "  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
00622 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
00623 "       and (%d = pg_catalog.length('%s'))"\
00624 "       and pg_catalog.quote_ident(c1.relname)='%s'"\
00625 "       and pg_catalog.pg_table_is_visible(c2.oid)"
00626 
00627 /* the silly-looking length condition is just to eat up the current word */
00628 #define Query_for_constraint_of_table \
00629 "SELECT pg_catalog.quote_ident(conname) "\
00630 "  FROM pg_catalog.pg_class c1, pg_catalog.pg_constraint con "\
00631 " WHERE c1.oid=conrelid and (%d = pg_catalog.length('%s'))"\
00632 "       and pg_catalog.quote_ident(c1.relname)='%s'"\
00633 "       and pg_catalog.pg_table_is_visible(c1.oid)"
00634 
00635 #define Query_for_all_table_constraints \
00636 "SELECT pg_catalog.quote_ident(conname) "\
00637 "  FROM pg_catalog.pg_constraint c "\
00638 " WHERE c.conrelid <> 0 "
00639 
00640 /* the silly-looking length condition is just to eat up the current word */
00641 #define Query_for_constraint_of_type \
00642 "SELECT pg_catalog.quote_ident(conname) "\
00643 "  FROM pg_catalog.pg_type t, pg_catalog.pg_constraint con "\
00644 " WHERE t.oid=contypid and (%d = pg_catalog.length('%s'))"\
00645 "       and pg_catalog.quote_ident(t.typname)='%s'"\
00646 "       and pg_catalog.pg_type_is_visible(t.oid)"
00647 
00648 /* the silly-looking length condition is just to eat up the current word */
00649 #define Query_for_list_of_tables_for_constraint \
00650 "SELECT pg_catalog.quote_ident(relname) "\
00651 "  FROM pg_catalog.pg_class"\
00652 " WHERE (%d = pg_catalog.length('%s'))"\
00653 "   AND oid IN "\
00654 "       (SELECT conrelid FROM pg_catalog.pg_constraint "\
00655 "         WHERE pg_catalog.quote_ident(conname)='%s')"
00656 
00657 /* the silly-looking length condition is just to eat up the current word */
00658 #define Query_for_list_of_tables_for_rule \
00659 "SELECT pg_catalog.quote_ident(relname) "\
00660 "  FROM pg_catalog.pg_class"\
00661 " WHERE (%d = pg_catalog.length('%s'))"\
00662 "   AND oid IN "\
00663 "       (SELECT ev_class FROM pg_catalog.pg_rewrite "\
00664 "         WHERE pg_catalog.quote_ident(rulename)='%s')"
00665 
00666 /* the silly-looking length condition is just to eat up the current word */
00667 #define Query_for_list_of_tables_for_trigger \
00668 "SELECT pg_catalog.quote_ident(relname) "\
00669 "  FROM pg_catalog.pg_class"\
00670 " WHERE (%d = pg_catalog.length('%s'))"\
00671 "   AND oid IN "\
00672 "       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
00673 "         WHERE pg_catalog.quote_ident(tgname)='%s')"
00674 
00675 #define Query_for_list_of_ts_configurations \
00676 "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
00677 " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
00678 
00679 #define Query_for_list_of_ts_dictionaries \
00680 "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\
00681 " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'"
00682 
00683 #define Query_for_list_of_ts_parsers \
00684 "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\
00685 " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'"
00686 
00687 #define Query_for_list_of_ts_templates \
00688 "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\
00689 " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'"
00690 
00691 #define Query_for_list_of_fdws \
00692 " SELECT pg_catalog.quote_ident(fdwname) "\
00693 "   FROM pg_catalog.pg_foreign_data_wrapper "\
00694 "  WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'"
00695 
00696 #define Query_for_list_of_servers \
00697 " SELECT pg_catalog.quote_ident(srvname) "\
00698 "   FROM pg_catalog.pg_foreign_server "\
00699 "  WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'"
00700 
00701 #define Query_for_list_of_user_mappings \
00702 " SELECT pg_catalog.quote_ident(usename) "\
00703 "   FROM pg_catalog.pg_user_mappings "\
00704 "  WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"
00705 
00706 #define Query_for_list_of_access_methods \
00707 " SELECT pg_catalog.quote_ident(amname) "\
00708 "   FROM pg_catalog.pg_am "\
00709 "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
00710 
00711 /* the silly-looking length condition is just to eat up the current word */
00712 #define Query_for_list_of_arguments \
00713 "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
00714 "  FROM pg_catalog.pg_proc "\
00715 " WHERE (%d = pg_catalog.length('%s'))"\
00716 "   AND (pg_catalog.quote_ident(proname)='%s'"\
00717 "        OR '\"' || proname || '\"'='%s') "\
00718 "   AND (pg_catalog.pg_function_is_visible(pg_proc.oid))"
00719 
00720 /* the silly-looking length condition is just to eat up the current word */
00721 #define Query_for_list_of_arguments_with_schema \
00722 "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
00723 "  FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
00724 " WHERE (%d = pg_catalog.length('%s'))"\
00725 "   AND n.oid = p.pronamespace "\
00726 "   AND (pg_catalog.quote_ident(proname)='%s' "\
00727 "        OR '\"' || proname || '\"' ='%s') "\
00728 "   AND (pg_catalog.quote_ident(nspname)='%s' "\
00729 "        OR '\"' || nspname || '\"' ='%s') "
00730 
00731 #define Query_for_list_of_extensions \
00732 " SELECT pg_catalog.quote_ident(extname) "\
00733 "   FROM pg_catalog.pg_extension "\
00734 "  WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'"
00735 
00736 #define Query_for_list_of_available_extensions \
00737 " SELECT pg_catalog.quote_ident(name) "\
00738 "   FROM pg_catalog.pg_available_extensions "\
00739 "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL"
00740 
00741 #define Query_for_list_of_prepared_statements \
00742 " SELECT pg_catalog.quote_ident(name) "\
00743 "   FROM pg_catalog.pg_prepared_statements "\
00744 "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"
00745 
00746 /*
00747  * This is a list of all "things" in Pgsql, which can show up after CREATE or
00748  * DROP; and there is also a query to get a list of them.
00749  */
00750 
00751 typedef struct
00752 {
00753     const char *name;
00754     const char *query;          /* simple query, or NULL */
00755     const SchemaQuery *squery;  /* schema query, or NULL */
00756     const bits32 flags;         /* visibility flags, see below */
00757 } pgsql_thing_t;
00758 
00759 #define THING_NO_CREATE     (1 << 0)    /* should not show up after CREATE */
00760 #define THING_NO_DROP       (1 << 1)    /* should not show up after DROP */
00761 #define THING_NO_SHOW       (THING_NO_CREATE | THING_NO_DROP)
00762 
00763 static const pgsql_thing_t words_after_create[] = {
00764     {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
00765     {"CAST", NULL, NULL},       /* Casts have complex structures for names, so
00766                                  * skip it */
00767     {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding())) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"},
00768 
00769     /*
00770      * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
00771      * to be used only by pg_dump.
00772      */
00773     {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
00774     {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
00775     {"DATABASE", Query_for_list_of_databases},
00776     {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW},
00777     {"DOMAIN", NULL, &Query_for_list_of_domains},
00778     {"EXTENSION", Query_for_list_of_extensions},
00779     {"FOREIGN DATA WRAPPER", NULL, NULL},
00780     {"FOREIGN TABLE", NULL, NULL},
00781     {"FUNCTION", NULL, &Query_for_list_of_functions},
00782     {"GROUP", Query_for_list_of_roles},
00783     {"LANGUAGE", Query_for_list_of_languages},
00784     {"INDEX", NULL, &Query_for_list_of_indexes},
00785     {"MATERIALIZED VIEW", NULL, NULL},
00786     {"OPERATOR", NULL, NULL},   /* Querying for this is probably not such a
00787                                  * good idea. */
00788     {"OWNED", NULL, NULL, THING_NO_CREATE},     /* for DROP OWNED BY ... */
00789     {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
00790     {"ROLE", Query_for_list_of_roles},
00791     {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
00792     {"SCHEMA", Query_for_list_of_schemas},
00793     {"SEQUENCE", NULL, &Query_for_list_of_sequences},
00794     {"SERVER", Query_for_list_of_servers},
00795     {"TABLE", NULL, &Query_for_list_of_tables},
00796     {"TABLESPACE", Query_for_list_of_tablespaces},
00797     {"TEMP", NULL, NULL, THING_NO_DROP},        /* for CREATE TEMP TABLE ... */
00798     {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
00799     {"TEXT SEARCH", NULL, NULL},
00800     {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"},
00801     {"TYPE", NULL, &Query_for_list_of_datatypes},
00802     {"UNIQUE", NULL, NULL, THING_NO_DROP},      /* for CREATE UNIQUE INDEX ... */
00803     {"UNLOGGED", NULL, NULL, THING_NO_DROP},    /* for CREATE UNLOGGED TABLE
00804                                                  * ... */
00805     {"USER", Query_for_list_of_roles},
00806     {"USER MAPPING FOR", NULL, NULL},
00807     {"VIEW", NULL, &Query_for_list_of_views},
00808     {NULL}                      /* end of list */
00809 };
00810 
00811 
00812 /* Forward declaration of functions */
00813 static char **psql_completion(char *text, int start, int end);
00814 static char *create_command_generator(const char *text, int state);
00815 static char *drop_command_generator(const char *text, int state);
00816 static char *complete_from_query(const char *text, int state);
00817 static char *complete_from_schema_query(const char *text, int state);
00818 static char *_complete_from_query(int is_schema_query,
00819                      const char *text, int state);
00820 static char *complete_from_list(const char *text, int state);
00821 static char *complete_from_const(const char *text, int state);
00822 static char **complete_from_variables(char *text,
00823                         const char *prefix, const char *suffix);
00824 static char *complete_from_files(const char *text, int state);
00825 
00826 static char *pg_strdup_keyword_case(const char *s, const char *ref);
00827 static PGresult *exec_query(const char *query);
00828 
00829 static void get_previous_words(int point, char **previous_words, int nwords);
00830 
00831 #ifdef NOT_USED
00832 static char *quote_file_name(char *text, int match_type, char *quote_pointer);
00833 static char *dequote_file_name(char *text, char quote_char);
00834 #endif
00835 
00836 
00837 /*
00838  * Initialize the readline library for our purposes.
00839  */
00840 void
00841 initialize_readline(void)
00842 {
00843     rl_readline_name = (char *) pset.progname;
00844     rl_attempted_completion_function = (void *) psql_completion;
00845 
00846     rl_basic_word_break_characters = WORD_BREAKS;
00847 
00848     completion_max_records = 1000;
00849 
00850     /*
00851      * There is a variable rl_completion_query_items for this but apparently
00852      * it's not defined everywhere.
00853      */
00854 }
00855 
00856 
00857 /*
00858  * The completion function.
00859  *
00860  * According to readline spec this gets passed the text entered so far and its
00861  * start and end positions in the readline buffer. The return value is some
00862  * partially obscure list format that can be generated by readline's
00863  * completion_matches() function, so we don't have to worry about it.
00864  */
00865 static char **
00866 psql_completion(char *text, int start, int end)
00867 {
00868     /* This is the variable we'll return. */
00869     char      **matches = NULL;
00870 
00871     /* This array will contain some scannage of the input line. */
00872     char       *previous_words[6];
00873 
00874     /* For compactness, we use these macros to reference previous_words[]. */
00875 #define prev_wd   (previous_words[0])
00876 #define prev2_wd  (previous_words[1])
00877 #define prev3_wd  (previous_words[2])
00878 #define prev4_wd  (previous_words[3])
00879 #define prev5_wd  (previous_words[4])
00880 #define prev6_wd  (previous_words[5])
00881 
00882     static const char *const sql_commands[] = {
00883         "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
00884         "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
00885         "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
00886         "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
00887         "REASSIGN", "REFRESH", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
00888         "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
00889         "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
00890         NULL
00891     };
00892 
00893     static const char *const backslash_commands[] = {
00894         "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
00895         "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
00896         "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
00897         "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
00898         "\\e", "\\echo", "\\ef", "\\encoding",
00899         "\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
00900         "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
00901         "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
00902         "\\set", "\\sf", "\\t", "\\T",
00903         "\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
00904     };
00905 
00906     (void) end;                 /* not used */
00907 
00908 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
00909     rl_completion_append_character = ' ';
00910 #endif
00911 
00912     /* Clear a few things. */
00913     completion_charp = NULL;
00914     completion_charpp = NULL;
00915     completion_info_charp = NULL;
00916     completion_info_charp2 = NULL;
00917 
00918     /*
00919      * Scan the input line before our current position for the last few words.
00920      * According to those we'll make some smart decisions on what the user is
00921      * probably intending to type.
00922      */
00923     get_previous_words(start, previous_words, lengthof(previous_words));
00924 
00925     /* If a backslash command was started, continue */
00926     if (text[0] == '\\')
00927         COMPLETE_WITH_LIST_CS(backslash_commands);
00928 
00929     /* Variable interpolation */
00930     else if (text[0] == ':' && text[1] != ':')
00931     {
00932         if (text[1] == '\'')
00933             matches = complete_from_variables(text, ":'", "'");
00934         else if (text[1] == '"')
00935             matches = complete_from_variables(text, ":\"", "\"");
00936         else
00937             matches = complete_from_variables(text, ":", "");
00938     }
00939 
00940     /* If no previous word, suggest one of the basic sql commands */
00941     else if (prev_wd[0] == '\0')
00942         COMPLETE_WITH_LIST(sql_commands);
00943 
00944 /* CREATE */
00945     /* complete with something you can create */
00946     else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
00947         matches = completion_matches(text, create_command_generator);
00948 
00949 /* DROP, but not DROP embedded in other commands */
00950     /* complete with something you can drop */
00951     else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
00952              prev2_wd[0] == '\0')
00953         matches = completion_matches(text, drop_command_generator);
00954 
00955 /* ALTER */
00956 
00957     /*
00958      * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're
00959      * in ALTER TABLE sth ALTER
00960      */
00961     else if (pg_strcasecmp(prev_wd, "ALTER") == 0 &&
00962              pg_strcasecmp(prev3_wd, "TABLE") != 0)
00963     {
00964         static const char *const list_ALTER[] =
00965         {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
00966             "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
00967             "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR",
00968             "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
00969             "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
00970         "USER", "USER MAPPING FOR", "VIEW", NULL};
00971 
00972         COMPLETE_WITH_LIST(list_ALTER);
00973     }
00974     /* ALTER AGGREGATE,FUNCTION <name> */
00975     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
00976              (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
00977               pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
00978         COMPLETE_WITH_CONST("(");
00979     /* ALTER AGGREGATE,FUNCTION <name> (...) */
00980     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
00981              (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
00982               pg_strcasecmp(prev3_wd, "FUNCTION") == 0))
00983     {
00984         if (prev_wd[strlen(prev_wd) - 1] == ')')
00985         {
00986             static const char *const list_ALTERAGG[] =
00987             {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
00988 
00989             COMPLETE_WITH_LIST(list_ALTERAGG);
00990         }
00991         else
00992             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
00993     }
00994 
00995     /* ALTER SCHEMA <name> */
00996     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
00997              pg_strcasecmp(prev2_wd, "SCHEMA") == 0)
00998     {
00999         static const char *const list_ALTERGEN[] =
01000         {"OWNER TO", "RENAME TO", NULL};
01001 
01002         COMPLETE_WITH_LIST(list_ALTERGEN);
01003     }
01004 
01005     /* ALTER COLLATION <name> */
01006     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01007              pg_strcasecmp(prev2_wd, "COLLATION") == 0)
01008     {
01009         static const char *const list_ALTERGEN[] =
01010         {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
01011 
01012         COMPLETE_WITH_LIST(list_ALTERGEN);
01013     }
01014 
01015     /* ALTER CONVERSION <name> */
01016     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01017              pg_strcasecmp(prev2_wd, "CONVERSION") == 0)
01018     {
01019         static const char *const list_ALTERGEN[] =
01020         {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
01021 
01022         COMPLETE_WITH_LIST(list_ALTERGEN);
01023     }
01024 
01025     /* ALTER DATABASE <name> */
01026     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01027              pg_strcasecmp(prev2_wd, "DATABASE") == 0)
01028     {
01029         static const char *const list_ALTERDATABASE[] =
01030         {"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};
01031 
01032         COMPLETE_WITH_LIST(list_ALTERDATABASE);
01033     }
01034 
01035     /* ALTER EXTENSION <name> */
01036     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01037              pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
01038     {
01039         static const char *const list_ALTEREXTENSION[] =
01040         {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL};
01041 
01042         COMPLETE_WITH_LIST(list_ALTEREXTENSION);
01043     }
01044 
01045     /* ALTER FOREIGN */
01046     else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
01047              pg_strcasecmp(prev_wd, "FOREIGN") == 0)
01048     {
01049         static const char *const list_ALTER_FOREIGN[] =
01050         {"DATA WRAPPER", "TABLE", NULL};
01051 
01052         COMPLETE_WITH_LIST(list_ALTER_FOREIGN);
01053     }
01054 
01055     /* ALTER FOREIGN DATA WRAPPER <name> */
01056     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01057              pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
01058              pg_strcasecmp(prev3_wd, "DATA") == 0 &&
01059              pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
01060     {
01061         static const char *const list_ALTER_FDW[] =
01062         {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL};
01063 
01064         COMPLETE_WITH_LIST(list_ALTER_FDW);
01065     }
01066 
01067     /* ALTER FOREIGN TABLE <name> */
01068     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01069              pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
01070              pg_strcasecmp(prev2_wd, "TABLE") == 0)
01071     {
01072         static const char *const list_ALTER_FOREIGN_TABLE[] =
01073         {"ALTER", "DROP", "RENAME", "OWNER TO", "SET SCHEMA", NULL};
01074 
01075         COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE);
01076     }
01077 
01078     /* ALTER INDEX <name> */
01079     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01080              pg_strcasecmp(prev2_wd, "INDEX") == 0)
01081     {
01082         static const char *const list_ALTERINDEX[] =
01083         {"OWNER TO", "RENAME TO", "SET", "RESET", NULL};
01084 
01085         COMPLETE_WITH_LIST(list_ALTERINDEX);
01086     }
01087     /* ALTER INDEX <name> SET */
01088     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01089              pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
01090              pg_strcasecmp(prev_wd, "SET") == 0)
01091     {
01092         static const char *const list_ALTERINDEXSET[] =
01093         {"(", "TABLESPACE", NULL};
01094 
01095         COMPLETE_WITH_LIST(list_ALTERINDEXSET);
01096     }
01097     /* ALTER INDEX <name> RESET */
01098     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01099              pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
01100              pg_strcasecmp(prev_wd, "RESET") == 0)
01101         COMPLETE_WITH_CONST("(");
01102     /* ALTER INDEX <foo> SET|RESET ( */
01103     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01104              pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
01105              (pg_strcasecmp(prev2_wd, "SET") == 0 ||
01106               pg_strcasecmp(prev2_wd, "RESET") == 0) &&
01107              pg_strcasecmp(prev_wd, "(") == 0)
01108     {
01109         static const char *const list_INDEXOPTIONS[] =
01110         {"fillfactor", "fastupdate", NULL};
01111 
01112         COMPLETE_WITH_LIST(list_INDEXOPTIONS);
01113     }
01114 
01115     /* ALTER LANGUAGE <name> */
01116     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01117              pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
01118     {
01119         static const char *const list_ALTERLANGUAGE[] =
01120         {"OWNER TO", "RENAME TO", NULL};
01121 
01122         COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
01123     }
01124 
01125     /* ALTER LARGE OBJECT <oid> */
01126     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01127              pg_strcasecmp(prev3_wd, "LARGE") == 0 &&
01128              pg_strcasecmp(prev2_wd, "OBJECT") == 0)
01129     {
01130         static const char *const list_ALTERLARGEOBJECT[] =
01131         {"OWNER TO", NULL};
01132 
01133         COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT);
01134     }
01135 
01136     /* ALTER MATERIALIZED VIEW */
01137     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01138              pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 &&
01139              pg_strcasecmp(prev_wd, "VIEW") == 0)
01140     {
01141         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
01142     }
01143 
01144     /* ALTER USER,ROLE <name> */
01145     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01146              !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
01147              (pg_strcasecmp(prev2_wd, "USER") == 0 ||
01148               pg_strcasecmp(prev2_wd, "ROLE") == 0))
01149     {
01150         static const char *const list_ALTERUSER[] =
01151         {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
01152             "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
01153             "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
01154             "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
01155         "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
01156 
01157         COMPLETE_WITH_LIST(list_ALTERUSER);
01158     }
01159 
01160     /* ALTER USER,ROLE <name> WITH */
01161     else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01162               (pg_strcasecmp(prev3_wd, "USER") == 0 ||
01163                pg_strcasecmp(prev3_wd, "ROLE") == 0) &&
01164               pg_strcasecmp(prev_wd, "WITH") == 0))
01165     {
01166         /* Similar to the above, but don't complete "WITH" again. */
01167         static const char *const list_ALTERUSER_WITH[] =
01168         {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
01169             "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
01170             "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
01171             "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
01172         "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", NULL};
01173 
01174         COMPLETE_WITH_LIST(list_ALTERUSER_WITH);
01175     }
01176 
01177     /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
01178     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01179              (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
01180              (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
01181     {
01182         COMPLETE_WITH_CONST("PASSWORD");
01183     }
01184     /* ALTER DEFAULT PRIVILEGES */
01185     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01186              pg_strcasecmp(prev2_wd, "DEFAULT") == 0 &&
01187              pg_strcasecmp(prev_wd, "PRIVILEGES") == 0)
01188     {
01189         static const char *const list_ALTER_DEFAULT_PRIVILEGES[] =
01190         {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL};
01191 
01192         COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES);
01193     }
01194     /* ALTER DEFAULT PRIVILEGES FOR */
01195     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01196              pg_strcasecmp(prev3_wd, "DEFAULT") == 0 &&
01197              pg_strcasecmp(prev2_wd, "PRIVILEGES") == 0 &&
01198              pg_strcasecmp(prev_wd, "FOR") == 0)
01199     {
01200         static const char *const list_ALTER_DEFAULT_PRIVILEGES_FOR[] =
01201         {"ROLE", "USER", NULL};
01202 
01203         COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_FOR);
01204     }
01205     /* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */
01206     else if (pg_strcasecmp(prev5_wd, "DEFAULT") == 0 &&
01207              pg_strcasecmp(prev4_wd, "PRIVILEGES") == 0 &&
01208              (pg_strcasecmp(prev3_wd, "FOR") == 0 ||
01209               pg_strcasecmp(prev3_wd, "IN") == 0))
01210     {
01211         static const char *const list_ALTER_DEFAULT_PRIVILEGES_REST[] =
01212         {"GRANT", "REVOKE", NULL};
01213 
01214         COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_REST);
01215     }
01216     /* ALTER DOMAIN <name> */
01217     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01218              pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
01219     {
01220         static const char *const list_ALTERDOMAIN[] =
01221         {"ADD", "DROP", "OWNER TO", "RENAME", "SET", "VALIDATE CONSTRAINT", NULL};
01222 
01223         COMPLETE_WITH_LIST(list_ALTERDOMAIN);
01224     }
01225     /* ALTER DOMAIN <sth> DROP */
01226     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01227              pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
01228              pg_strcasecmp(prev_wd, "DROP") == 0)
01229     {
01230         static const char *const list_ALTERDOMAIN2[] =
01231         {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL};
01232 
01233         COMPLETE_WITH_LIST(list_ALTERDOMAIN2);
01234     }
01235     /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
01236     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01237              pg_strcasecmp(prev4_wd, "DOMAIN") == 0 &&
01238              (pg_strcasecmp(prev2_wd, "DROP") == 0 ||
01239               pg_strcasecmp(prev2_wd, "RENAME") == 0 ||
01240               pg_strcasecmp(prev2_wd, "VALIDATE") == 0) &&
01241              pg_strcasecmp(prev_wd, "CONSTRAINT") == 0)
01242     {
01243         completion_info_charp = prev3_wd;
01244         COMPLETE_WITH_QUERY(Query_for_constraint_of_type);
01245     }
01246     /* ALTER DOMAIN <sth> RENAME */
01247     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01248              pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
01249              pg_strcasecmp(prev_wd, "RENAME") == 0)
01250     {
01251         static const char *const list_ALTERDOMAIN[] =
01252         {"CONSTRAINT", "TO", NULL};
01253 
01254         COMPLETE_WITH_LIST(list_ALTERDOMAIN);
01255     }
01256     /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
01257     else if (pg_strcasecmp(prev5_wd, "DOMAIN") == 0 &&
01258              pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
01259              pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0)
01260         COMPLETE_WITH_CONST("TO");
01261 
01262     /* ALTER DOMAIN <sth> SET */
01263     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01264              pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
01265              pg_strcasecmp(prev_wd, "SET") == 0)
01266     {
01267         static const char *const list_ALTERDOMAIN3[] =
01268         {"DEFAULT", "NOT NULL", "SCHEMA", NULL};
01269 
01270         COMPLETE_WITH_LIST(list_ALTERDOMAIN3);
01271     }
01272     /* ALTER SEQUENCE <name> */
01273     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01274              pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)
01275     {
01276         static const char *const list_ALTERSEQUENCE[] =
01277         {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
01278         "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL};
01279 
01280         COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
01281     }
01282     /* ALTER SEQUENCE <name> NO */
01283     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01284              pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 &&
01285              pg_strcasecmp(prev_wd, "NO") == 0)
01286     {
01287         static const char *const list_ALTERSEQUENCE2[] =
01288         {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
01289 
01290         COMPLETE_WITH_LIST(list_ALTERSEQUENCE2);
01291     }
01292     /* ALTER SERVER <name> */
01293     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01294              pg_strcasecmp(prev2_wd, "SERVER") == 0)
01295     {
01296         static const char *const list_ALTER_SERVER[] =
01297         {"VERSION", "OPTIONS", "OWNER TO", NULL};
01298 
01299         COMPLETE_WITH_LIST(list_ALTER_SERVER);
01300     }
01301     /* ALTER VIEW <name> */
01302     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01303              pg_strcasecmp(prev2_wd, "VIEW") == 0)
01304     {
01305         static const char *const list_ALTERVIEW[] =
01306         {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
01307 
01308         COMPLETE_WITH_LIST(list_ALTERVIEW);
01309     }
01310     /* ALTER MATERIALIZED VIEW <name> */
01311     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01312              pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 &&
01313              pg_strcasecmp(prev2_wd, "VIEW") == 0)
01314     {
01315         static const char *const list_ALTERMATVIEW[] =
01316         {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
01317 
01318         COMPLETE_WITH_LIST(list_ALTERMATVIEW);
01319     }
01320 
01321     /* ALTER RULE <name>, add ON */
01322     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01323              pg_strcasecmp(prev2_wd, "RULE") == 0)
01324         COMPLETE_WITH_CONST("ON");
01325 
01326     /* If we have ALTER RULE <name> ON, then add the correct tablename */
01327     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01328              pg_strcasecmp(prev3_wd, "RULE") == 0 &&
01329              pg_strcasecmp(prev_wd, "ON") == 0)
01330     {
01331         completion_info_charp = prev2_wd;
01332         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
01333     }
01334 
01335     /* ALTER RULE <name> ON <name> */
01336     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01337              pg_strcasecmp(prev4_wd, "RULE") == 0)
01338         COMPLETE_WITH_CONST("RENAME TO");
01339 
01340     /* ALTER TRIGGER <name>, add ON */
01341     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01342              pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
01343         COMPLETE_WITH_CONST("ON");
01344 
01345     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01346              pg_strcasecmp(prev3_wd, "TRIGGER") == 0)
01347     {
01348         completion_info_charp = prev2_wd;
01349         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
01350     }
01351 
01352     /*
01353      * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
01354      */
01355     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01356              pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
01357              pg_strcasecmp(prev_wd, "ON") == 0)
01358         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
01359 
01360     /* ALTER TRIGGER <name> ON <name> */
01361     else if (pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
01362              pg_strcasecmp(prev2_wd, "ON") == 0)
01363         COMPLETE_WITH_CONST("RENAME TO");
01364 
01365     /*
01366      * If we detect ALTER TABLE <name>, suggest sub commands
01367      */
01368     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01369              pg_strcasecmp(prev2_wd, "TABLE") == 0)
01370     {
01371         static const char *const list_ALTER2[] =
01372         {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
01373             "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET",
01374         "VALIDATE CONSTRAINT", NULL};
01375 
01376         COMPLETE_WITH_LIST(list_ALTER2);
01377     }
01378     /* ALTER TABLE xxx ENABLE */
01379     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01380              pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01381              pg_strcasecmp(prev_wd, "ENABLE") == 0)
01382     {
01383         static const char *const list_ALTERENABLE[] =
01384         {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL};
01385 
01386         COMPLETE_WITH_LIST(list_ALTERENABLE);
01387     }
01388     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01389              pg_strcasecmp(prev2_wd, "ENABLE") == 0 &&
01390              (pg_strcasecmp(prev_wd, "REPLICA") == 0 ||
01391               pg_strcasecmp(prev_wd, "ALWAYS") == 0))
01392     {
01393         static const char *const list_ALTERENABLE2[] =
01394         {"RULE", "TRIGGER", NULL};
01395 
01396         COMPLETE_WITH_LIST(list_ALTERENABLE2);
01397     }
01398     /* ALTER TABLE xxx INHERIT */
01399     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01400              pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01401              pg_strcasecmp(prev_wd, "INHERIT") == 0)
01402     {
01403         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
01404     }
01405     /* ALTER TABLE xxx NO INHERIT */
01406     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01407              pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01408              pg_strcasecmp(prev2_wd, "NO") == 0 &&
01409              pg_strcasecmp(prev_wd, "INHERIT") == 0)
01410     {
01411         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
01412     }
01413     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01414              pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01415              pg_strcasecmp(prev_wd, "DISABLE") == 0)
01416     {
01417         static const char *const list_ALTERDISABLE[] =
01418         {"RULE", "TRIGGER", NULL};
01419 
01420         COMPLETE_WITH_LIST(list_ALTERDISABLE);
01421     }
01422 
01423     /* ALTER TABLE xxx ALTER */
01424     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01425              pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01426              pg_strcasecmp(prev_wd, "ALTER") == 0)
01427         COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN'");
01428 
01429     /* ALTER TABLE xxx RENAME */
01430     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01431              pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01432              pg_strcasecmp(prev_wd, "RENAME") == 0)
01433         COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'");
01434 
01435     /*
01436      * If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
01437      * columns
01438      */
01439     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01440              (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
01441               pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
01442              pg_strcasecmp(prev_wd, "COLUMN") == 0)
01443         COMPLETE_WITH_ATTR(prev3_wd, "");
01444 
01445     /* ALTER TABLE xxx RENAME yyy */
01446     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01447              pg_strcasecmp(prev2_wd, "RENAME") == 0 &&
01448              pg_strcasecmp(prev_wd, "CONSTRAINT") != 0 &&
01449              pg_strcasecmp(prev_wd, "TO") != 0)
01450         COMPLETE_WITH_CONST("TO");
01451 
01452     /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
01453     else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
01454              pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
01455              (pg_strcasecmp(prev2_wd, "COLUMN") == 0 ||
01456               pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) &&
01457              pg_strcasecmp(prev_wd, "TO") != 0)
01458         COMPLETE_WITH_CONST("TO");
01459 
01460     /* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
01461     else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01462              pg_strcasecmp(prev_wd, "DROP") == 0)
01463     {
01464         static const char *const list_TABLEDROP[] =
01465         {"COLUMN", "CONSTRAINT", NULL};
01466 
01467         COMPLETE_WITH_LIST(list_TABLEDROP);
01468     }
01469     /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
01470     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01471              pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01472              pg_strcasecmp(prev2_wd, "DROP") == 0 &&
01473              pg_strcasecmp(prev_wd, "COLUMN") == 0)
01474         COMPLETE_WITH_ATTR(prev3_wd, "");
01475     /* If we have ALTER TABLE <sth> DROP|RENAME|VALIDATE CONSTRAINT, provide list of constraints */
01476     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01477              pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01478              (pg_strcasecmp(prev2_wd, "DROP") == 0 ||
01479               pg_strcasecmp(prev2_wd, "RENAME") == 0 ||
01480               pg_strcasecmp(prev2_wd, "VALIDATE") == 0) &&
01481              pg_strcasecmp(prev_wd, "CONSTRAINT") == 0)
01482     {
01483         completion_info_charp = prev3_wd;
01484         COMPLETE_WITH_QUERY(Query_for_constraint_of_table);
01485     }
01486     /* ALTER TABLE ALTER [COLUMN] <foo> */
01487     else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01488               pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
01489              (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01490               pg_strcasecmp(prev2_wd, "ALTER") == 0))
01491     {
01492         static const char *const list_COLUMNALTER[] =
01493         {"TYPE", "SET", "RESET", "DROP", NULL};
01494 
01495         COMPLETE_WITH_LIST(list_COLUMNALTER);
01496     }
01497     /* ALTER TABLE ALTER [COLUMN] <foo> SET */
01498     else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01499                pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
01500               (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
01501                pg_strcasecmp(prev3_wd, "ALTER") == 0)) &&
01502              pg_strcasecmp(prev_wd, "SET") == 0)
01503     {
01504         static const char *const list_COLUMNSET[] =
01505         {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
01506 
01507         COMPLETE_WITH_LIST(list_COLUMNSET);
01508     }
01509     /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
01510     else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01511                pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
01512               pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
01513              pg_strcasecmp(prev2_wd, "SET") == 0 &&
01514              pg_strcasecmp(prev_wd, "(") == 0)
01515     {
01516         static const char *const list_COLUMNOPTIONS[] =
01517         {"n_distinct", "n_distinct_inherited", NULL};
01518 
01519         COMPLETE_WITH_LIST(list_COLUMNOPTIONS);
01520     }
01521     /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
01522     else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01523                pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
01524               pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
01525              pg_strcasecmp(prev2_wd, "SET") == 0 &&
01526              pg_strcasecmp(prev_wd, "STORAGE") == 0)
01527     {
01528         static const char *const list_COLUMNSTORAGE[] =
01529         {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL};
01530 
01531         COMPLETE_WITH_LIST(list_COLUMNSTORAGE);
01532     }
01533     /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
01534     else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01535                pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
01536               (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
01537                pg_strcasecmp(prev3_wd, "ALTER") == 0)) &&
01538              pg_strcasecmp(prev_wd, "DROP") == 0)
01539     {
01540         static const char *const list_COLUMNDROP[] =
01541         {"DEFAULT", "NOT NULL", NULL};
01542 
01543         COMPLETE_WITH_LIST(list_COLUMNDROP);
01544     }
01545     else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01546              pg_strcasecmp(prev_wd, "CLUSTER") == 0)
01547         COMPLETE_WITH_CONST("ON");
01548     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01549              pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
01550              pg_strcasecmp(prev_wd, "ON") == 0)
01551     {
01552         completion_info_charp = prev3_wd;
01553         COMPLETE_WITH_QUERY(Query_for_index_of_table);
01554     }
01555     /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
01556     else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01557              pg_strcasecmp(prev_wd, "SET") == 0)
01558     {
01559         static const char *const list_TABLESET[] =
01560         {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
01561 
01562         COMPLETE_WITH_LIST(list_TABLESET);
01563     }
01564     /* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
01565     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01566              pg_strcasecmp(prev2_wd, "SET") == 0 &&
01567              pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
01568         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
01569     /* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
01570     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01571              pg_strcasecmp(prev2_wd, "SET") == 0 &&
01572              pg_strcasecmp(prev_wd, "WITHOUT") == 0)
01573     {
01574         static const char *const list_TABLESET2[] =
01575         {"CLUSTER", "OIDS", NULL};
01576 
01577         COMPLETE_WITH_LIST(list_TABLESET2);
01578     }
01579     /* ALTER TABLE <foo> RESET */
01580     else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
01581              pg_strcasecmp(prev_wd, "RESET") == 0)
01582         COMPLETE_WITH_CONST("(");
01583     /* ALTER TABLE <foo> SET|RESET ( */
01584     else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
01585              (pg_strcasecmp(prev2_wd, "SET") == 0 ||
01586               pg_strcasecmp(prev2_wd, "RESET") == 0) &&
01587              pg_strcasecmp(prev_wd, "(") == 0)
01588     {
01589         static const char *const list_TABLEOPTIONS[] =
01590         {
01591             "autovacuum_analyze_scale_factor",
01592             "autovacuum_analyze_threshold",
01593             "autovacuum_enabled",
01594             "autovacuum_freeze_max_age",
01595             "autovacuum_freeze_min_age",
01596             "autovacuum_freeze_table_age",
01597             "autovacuum_vacuum_cost_delay",
01598             "autovacuum_vacuum_cost_limit",
01599             "autovacuum_vacuum_scale_factor",
01600             "autovacuum_vacuum_threshold",
01601             "fillfactor",
01602             "toast.autovacuum_enabled",
01603             "toast.autovacuum_freeze_max_age",
01604             "toast.autovacuum_freeze_min_age",
01605             "toast.autovacuum_freeze_table_age",
01606             "toast.autovacuum_vacuum_cost_delay",
01607             "toast.autovacuum_vacuum_cost_limit",
01608             "toast.autovacuum_vacuum_scale_factor",
01609             "toast.autovacuum_vacuum_threshold",
01610             NULL
01611         };
01612 
01613         COMPLETE_WITH_LIST(list_TABLEOPTIONS);
01614     }
01615 
01616     /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
01617     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01618              pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
01619     {
01620         static const char *const list_ALTERTSPC[] =
01621         {"RENAME TO", "OWNER TO", "SET", "RESET", NULL};
01622 
01623         COMPLETE_WITH_LIST(list_ALTERTSPC);
01624     }
01625     /* ALTER TABLESPACE <foo> SET|RESET */
01626     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01627              pg_strcasecmp(prev3_wd, "TABLESPACE") == 0 &&
01628              (pg_strcasecmp(prev_wd, "SET") == 0 ||
01629               pg_strcasecmp(prev_wd, "RESET") == 0))
01630         COMPLETE_WITH_CONST("(");
01631     /* ALTER TABLESPACE <foo> SET|RESET ( */
01632     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01633              pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
01634              (pg_strcasecmp(prev2_wd, "SET") == 0 ||
01635               pg_strcasecmp(prev2_wd, "RESET") == 0) &&
01636              pg_strcasecmp(prev_wd, "(") == 0)
01637     {
01638         static const char *const list_TABLESPACEOPTIONS[] =
01639         {"seq_page_cost", "random_page_cost", NULL};
01640 
01641         COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS);
01642     }
01643 
01644     /* ALTER TEXT SEARCH */
01645     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01646              pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
01647              pg_strcasecmp(prev_wd, "SEARCH") == 0)
01648     {
01649         static const char *const list_ALTERTEXTSEARCH[] =
01650         {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
01651 
01652         COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
01653     }
01654     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01655              pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
01656              pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
01657              (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 ||
01658               pg_strcasecmp(prev2_wd, "PARSER") == 0))
01659     {
01660         static const char *const list_ALTERTEXTSEARCH2[] =
01661         {"RENAME TO", "SET SCHEMA", NULL};
01662 
01663         COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2);
01664     }
01665 
01666     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01667              pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
01668              pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
01669              pg_strcasecmp(prev2_wd, "DICTIONARY") == 0)
01670     {
01671         static const char *const list_ALTERTEXTSEARCH3[] =
01672         {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
01673 
01674         COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
01675     }
01676 
01677     else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
01678              pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
01679              pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
01680              pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
01681     {
01682         static const char *const list_ALTERTEXTSEARCH4[] =
01683         {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
01684 
01685         COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH4);
01686     }
01687 
01688     /* complete ALTER TYPE <foo> with actions */
01689     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01690              pg_strcasecmp(prev2_wd, "TYPE") == 0)
01691     {
01692         static const char *const list_ALTERTYPE[] =
01693         {"ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE",
01694         "OWNER TO", "RENAME", "SET SCHEMA", NULL};
01695 
01696         COMPLETE_WITH_LIST(list_ALTERTYPE);
01697     }
01698     /* complete ALTER TYPE <foo> ADD with actions */
01699     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01700              pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
01701              pg_strcasecmp(prev_wd, "ADD") == 0)
01702     {
01703         static const char *const list_ALTERTYPE[] =
01704         {"ATTRIBUTE", "VALUE", NULL};
01705 
01706         COMPLETE_WITH_LIST(list_ALTERTYPE);
01707     }
01708     /* ALTER TYPE <foo> RENAME  */
01709     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01710              pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
01711              pg_strcasecmp(prev_wd, "RENAME") == 0)
01712     {
01713         static const char *const list_ALTERTYPE[] =
01714         {"ATTRIBUTE", "TO", NULL};
01715 
01716         COMPLETE_WITH_LIST(list_ALTERTYPE);
01717     }
01718     /* ALTER TYPE xxx RENAME ATTRIBUTE yyy */
01719     else if (pg_strcasecmp(prev5_wd, "TYPE") == 0 &&
01720              pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
01721              pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0)
01722         COMPLETE_WITH_CONST("TO");
01723 
01724     /*
01725      * If we have TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list of
01726      * attributes
01727      */
01728     else if (pg_strcasecmp(prev4_wd, "TYPE") == 0 &&
01729              (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
01730               pg_strcasecmp(prev2_wd, "DROP") == 0 ||
01731               pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
01732              pg_strcasecmp(prev_wd, "ATTRIBUTE") == 0)
01733         COMPLETE_WITH_ATTR(prev3_wd, "");
01734     /* ALTER TYPE ALTER ATTRIBUTE <foo> */
01735     else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01736               pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0))
01737     {
01738         COMPLETE_WITH_CONST("TYPE");
01739     }
01740     /* complete ALTER GROUP <foo> */
01741     else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
01742              pg_strcasecmp(prev2_wd, "GROUP") == 0)
01743     {
01744         static const char *const list_ALTERGROUP[] =
01745         {"ADD USER", "DROP USER", "RENAME TO", NULL};
01746 
01747         COMPLETE_WITH_LIST(list_ALTERGROUP);
01748     }
01749     /* complete ALTER GROUP <foo> ADD|DROP with USER */
01750     else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
01751              pg_strcasecmp(prev3_wd, "GROUP") == 0 &&
01752              (pg_strcasecmp(prev_wd, "ADD") == 0 ||
01753               pg_strcasecmp(prev_wd, "DROP") == 0))
01754         COMPLETE_WITH_CONST("USER");
01755     /* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
01756     else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 &&
01757              (pg_strcasecmp(prev2_wd, "ADD") == 0 ||
01758               pg_strcasecmp(prev2_wd, "DROP") == 0) &&
01759              pg_strcasecmp(prev_wd, "USER") == 0)
01760         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
01761 
01762 /* BEGIN, END, ABORT */
01763     else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
01764              pg_strcasecmp(prev_wd, "END") == 0 ||
01765              pg_strcasecmp(prev_wd, "ABORT") == 0)
01766     {
01767         static const char *const list_TRANS[] =
01768         {"WORK", "TRANSACTION", NULL};
01769 
01770         COMPLETE_WITH_LIST(list_TRANS);
01771     }
01772 /* COMMIT */
01773     else if (pg_strcasecmp(prev_wd, "COMMIT") == 0)
01774     {
01775         static const char *const list_COMMIT[] =
01776         {"WORK", "TRANSACTION", "PREPARED", NULL};
01777 
01778         COMPLETE_WITH_LIST(list_COMMIT);
01779     }
01780 /* RELEASE SAVEPOINT */
01781     else if (pg_strcasecmp(prev_wd, "RELEASE") == 0)
01782         COMPLETE_WITH_CONST("SAVEPOINT");
01783 /* ROLLBACK*/
01784     else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0)
01785     {
01786         static const char *const list_TRANS[] =
01787         {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL};
01788 
01789         COMPLETE_WITH_LIST(list_TRANS);
01790     }
01791 /* CLUSTER */
01792 
01793     /*
01794      * If the previous word is CLUSTER and not WITHOUT produce list of tables
01795      */
01796     else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 &&
01797              pg_strcasecmp(prev2_wd, "WITHOUT") != 0)
01798         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'");
01799 
01800     /*
01801      * If the previous words are CLUSTER VERBOSE produce list of tables
01802      */
01803     else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
01804              pg_strcasecmp(prev2_wd, "CLUSTER") == 0)
01805         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
01806 
01807     /* If we have CLUSTER <sth>, then add "USING" */
01808     else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
01809              pg_strcasecmp(prev_wd, "ON") != 0 &&
01810              pg_strcasecmp(prev_wd, "VERBOSE") != 0)
01811     {
01812         COMPLETE_WITH_CONST("USING");
01813     }
01814     /* If we have CLUSTER VERBOSE <sth>, then add "USING" */
01815     else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
01816              pg_strcasecmp(prev2_wd, "VERBOSE") == 0)
01817     {
01818         COMPLETE_WITH_CONST("USING");
01819     }
01820 
01821     /*
01822      * If we have CLUSTER <sth> USING, then add the index as well.
01823      */
01824     else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
01825              pg_strcasecmp(prev_wd, "USING") == 0)
01826     {
01827         completion_info_charp = prev2_wd;
01828         COMPLETE_WITH_QUERY(Query_for_index_of_table);
01829     }
01830 
01831     /*
01832      * If we have CLUSTER VERBOSE <sth> USING, then add the index as well.
01833      */
01834     else if (pg_strcasecmp(prev4_wd, "CLUSTER") == 0 &&
01835              pg_strcasecmp(prev3_wd, "VERBOSE") == 0 &&
01836              pg_strcasecmp(prev_wd, "USING") == 0)
01837     {
01838         completion_info_charp = prev2_wd;
01839         COMPLETE_WITH_QUERY(Query_for_index_of_table);
01840     }
01841 
01842 /* COMMENT */
01843     else if (pg_strcasecmp(prev_wd, "COMMENT") == 0)
01844         COMPLETE_WITH_CONST("ON");
01845     else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 &&
01846              pg_strcasecmp(prev_wd, "ON") == 0)
01847     {
01848         static const char *const list_COMMENT[] =
01849         {"CAST", "COLLATION", "CONVERSION", "DATABASE", "EXTENSION",
01850             "FOREIGN DATA WRAPPER", "FOREIGN TABLE",
01851             "SERVER", "INDEX", "LANGUAGE", "RULE", "SCHEMA", "SEQUENCE",
01852             "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
01853             "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
01854         "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
01855 
01856         COMPLETE_WITH_LIST(list_COMMENT);
01857     }
01858     else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 &&
01859              pg_strcasecmp(prev2_wd, "ON") == 0 &&
01860              pg_strcasecmp(prev_wd, "FOREIGN") == 0)
01861     {
01862         static const char *const list_TRANS2[] =
01863         {"DATA WRAPPER", "TABLE", NULL};
01864 
01865         COMPLETE_WITH_LIST(list_TRANS2);
01866     }
01867     else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
01868              pg_strcasecmp(prev3_wd, "ON") == 0 &&
01869              pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
01870              pg_strcasecmp(prev_wd, "SEARCH") == 0)
01871     {
01872         static const char *const list_TRANS2[] =
01873         {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
01874 
01875         COMPLETE_WITH_LIST(list_TRANS2);
01876     }
01877     else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 &&
01878              pg_strcasecmp(prev2_wd, "ON") == 0 &&
01879              pg_strcasecmp(prev_wd, "CONSTRAINT") == 0)
01880     {
01881         COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
01882     }
01883     else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
01884              pg_strcasecmp(prev3_wd, "ON") == 0 &&
01885              pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0)
01886     {
01887         COMPLETE_WITH_CONST("ON");
01888     }
01889     else if (pg_strcasecmp(prev5_wd, "COMMENT") == 0 &&
01890              pg_strcasecmp(prev4_wd, "ON") == 0 &&
01891              pg_strcasecmp(prev3_wd, "CONSTRAINT") == 0 &&
01892              pg_strcasecmp(prev_wd, "ON") == 0)
01893     {
01894         completion_info_charp = prev2_wd;
01895         COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint);
01896     }
01897     else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
01898              pg_strcasecmp(prev3_wd, "ON") == 0 &&
01899              pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 &&
01900              pg_strcasecmp(prev_wd, "VIEW") == 0)
01901     {
01902         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
01903     }
01904     else if ((pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
01905               pg_strcasecmp(prev3_wd, "ON") == 0) ||
01906              (pg_strcasecmp(prev5_wd, "COMMENT") == 0 &&
01907               pg_strcasecmp(prev4_wd, "ON") == 0) ||
01908              (pg_strcasecmp(prev6_wd, "COMMENT") == 0 &&
01909               pg_strcasecmp(prev5_wd, "ON") == 0))
01910         COMPLETE_WITH_CONST("IS");
01911 
01912 /* COPY */
01913 
01914     /*
01915      * If we have COPY [BINARY] (which you'd have to type yourself), offer
01916      * list of tables (Also cover the analogous backslash command)
01917      */
01918     else if (pg_strcasecmp(prev_wd, "COPY") == 0 ||
01919              pg_strcasecmp(prev_wd, "\\copy") == 0 ||
01920              (pg_strcasecmp(prev2_wd, "COPY") == 0 &&
01921               pg_strcasecmp(prev_wd, "BINARY") == 0))
01922         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
01923     /* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
01924     else if (pg_strcasecmp(prev2_wd, "COPY") == 0 ||
01925              pg_strcasecmp(prev2_wd, "\\copy") == 0 ||
01926              pg_strcasecmp(prev2_wd, "BINARY") == 0)
01927     {
01928         static const char *const list_FROMTO[] =
01929         {"FROM", "TO", NULL};
01930 
01931         COMPLETE_WITH_LIST(list_FROMTO);
01932     }
01933     /* If we have COPY|BINARY <sth> FROM|TO, complete with filename */
01934     else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 ||
01935               pg_strcasecmp(prev3_wd, "\\copy") == 0 ||
01936               pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
01937              (pg_strcasecmp(prev_wd, "FROM") == 0 ||
01938               pg_strcasecmp(prev_wd, "TO") == 0))
01939     {
01940         completion_charp = "";
01941         matches = completion_matches(text, complete_from_files);
01942     }
01943 
01944     /* Handle COPY|BINARY <sth> FROM|TO filename */
01945     else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
01946               pg_strcasecmp(prev4_wd, "\\copy") == 0 ||
01947               pg_strcasecmp(prev4_wd, "BINARY") == 0) &&
01948              (pg_strcasecmp(prev2_wd, "FROM") == 0 ||
01949               pg_strcasecmp(prev2_wd, "TO") == 0))
01950     {
01951         static const char *const list_COPY[] =
01952         {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", "ENCODING", NULL};
01953 
01954         COMPLETE_WITH_LIST(list_COPY);
01955     }
01956 
01957     /* Handle COPY|BINARY <sth> FROM|TO filename CSV */
01958     else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
01959              (pg_strcasecmp(prev3_wd, "FROM") == 0 ||
01960               pg_strcasecmp(prev3_wd, "TO") == 0))
01961     {
01962         static const char *const list_CSV[] =
01963         {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "FORCE NOT NULL", NULL};
01964 
01965         COMPLETE_WITH_LIST(list_CSV);
01966     }
01967 
01968     /* CREATE DATABASE */
01969     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
01970              pg_strcasecmp(prev2_wd, "DATABASE") == 0)
01971     {
01972         static const char *const list_DATABASE[] =
01973         {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT",
01974         NULL};
01975 
01976         COMPLETE_WITH_LIST(list_DATABASE);
01977     }
01978 
01979     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
01980              pg_strcasecmp(prev3_wd, "DATABASE") == 0 &&
01981              pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
01982         COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
01983 
01984     /* CREATE EXTENSION */
01985     /* Complete with available extensions rather than installed ones. */
01986     else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
01987              pg_strcasecmp(prev_wd, "EXTENSION") == 0)
01988         COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
01989     /* CREATE EXTENSION <name> */
01990     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
01991              pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
01992         COMPLETE_WITH_CONST("WITH SCHEMA");
01993 
01994     /* CREATE FOREIGN */
01995     else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
01996              pg_strcasecmp(prev_wd, "FOREIGN") == 0)
01997     {
01998         static const char *const list_CREATE_FOREIGN[] =
01999         {"DATA WRAPPER", "TABLE", NULL};
02000 
02001         COMPLETE_WITH_LIST(list_CREATE_FOREIGN);
02002     }
02003 
02004     /* CREATE FOREIGN DATA WRAPPER */
02005     else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
02006              pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
02007              pg_strcasecmp(prev3_wd, "DATA") == 0 &&
02008              pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
02009     {
02010         static const char *const list_CREATE_FOREIGN_DATA_WRAPPER[] =
02011         {"HANDLER", "VALIDATOR", NULL};
02012 
02013         COMPLETE_WITH_LIST(list_CREATE_FOREIGN_DATA_WRAPPER);
02014     }
02015 
02016     /* CREATE INDEX */
02017     /* First off we complete CREATE UNIQUE with "INDEX" */
02018     else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
02019              pg_strcasecmp(prev_wd, "UNIQUE") == 0)
02020         COMPLETE_WITH_CONST("INDEX");
02021     /* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
02022     else if (pg_strcasecmp(prev_wd, "INDEX") == 0 &&
02023              (pg_strcasecmp(prev2_wd, "CREATE") == 0 ||
02024               pg_strcasecmp(prev2_wd, "UNIQUE") == 0))
02025         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
02026                                    " UNION SELECT 'ON'"
02027                                    " UNION SELECT 'CONCURRENTLY'");
02028     /* Complete ... INDEX [<name>] ON with a list of tables  */
02029     else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
02030               pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
02031               pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) &&
02032              pg_strcasecmp(prev_wd, "ON") == 0)
02033         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
02034     /* If we have CREATE|UNIQUE INDEX <sth> CONCURRENTLY, then add "ON" */
02035     else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
02036               pg_strcasecmp(prev2_wd, "INDEX") == 0) &&
02037              pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0)
02038         COMPLETE_WITH_CONST("ON");
02039     /* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" or "CONCURRENTLY" */
02040     else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
02041               pg_strcasecmp(prev3_wd, "UNIQUE") == 0) &&
02042              pg_strcasecmp(prev2_wd, "INDEX") == 0)
02043     {
02044         static const char *const list_CREATE_INDEX[] =
02045         {"CONCURRENTLY", "ON", NULL};
02046 
02047         COMPLETE_WITH_LIST(list_CREATE_INDEX);
02048     }
02049 
02050     /*
02051      * Complete INDEX <name> ON <table> with a list of table columns (which
02052      * should really be in parens)
02053      */
02054     else if ((pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
02055               pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
02056               pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0) &&
02057              pg_strcasecmp(prev2_wd, "ON") == 0)
02058     {
02059         static const char *const list_CREATE_INDEX2[] =
02060         {"(", "USING", NULL};
02061 
02062         COMPLETE_WITH_LIST(list_CREATE_INDEX2);
02063     }
02064     else if ((pg_strcasecmp(prev5_wd, "INDEX") == 0 ||
02065               pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
02066               pg_strcasecmp(prev4_wd, "CONCURRENTLY") == 0) &&
02067              pg_strcasecmp(prev3_wd, "ON") == 0 &&
02068              pg_strcasecmp(prev_wd, "(") == 0)
02069         COMPLETE_WITH_ATTR(prev2_wd, "");
02070     /* same if you put in USING */
02071     else if (pg_strcasecmp(prev5_wd, "ON") == 0 &&
02072              pg_strcasecmp(prev3_wd, "USING") == 0 &&
02073              pg_strcasecmp(prev_wd, "(") == 0)
02074         COMPLETE_WITH_ATTR(prev4_wd, "");
02075     /* Complete USING with an index method */
02076     else if (pg_strcasecmp(prev_wd, "USING") == 0)
02077         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
02078     else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
02079              pg_strcasecmp(prev2_wd, "USING") == 0)
02080         COMPLETE_WITH_CONST("(");
02081 
02082 /* CREATE RULE */
02083     /* Complete "CREATE RULE <sth>" with "AS" */
02084     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02085              pg_strcasecmp(prev2_wd, "RULE") == 0)
02086         COMPLETE_WITH_CONST("AS");
02087     /* Complete "CREATE RULE <sth> AS with "ON" */
02088     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02089              pg_strcasecmp(prev3_wd, "RULE") == 0 &&
02090              pg_strcasecmp(prev_wd, "AS") == 0)
02091         COMPLETE_WITH_CONST("ON");
02092     /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */
02093     else if (pg_strcasecmp(prev4_wd, "RULE") == 0 &&
02094              pg_strcasecmp(prev2_wd, "AS") == 0 &&
02095              pg_strcasecmp(prev_wd, "ON") == 0)
02096     {
02097         static const char *const rule_events[] =
02098         {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
02099 
02100         COMPLETE_WITH_LIST(rule_events);
02101     }
02102     /* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
02103     else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
02104              pg_strcasecmp(prev2_wd, "ON") == 0 &&
02105              (pg_toupper((unsigned char) prev_wd[4]) == 'T' ||
02106               pg_toupper((unsigned char) prev_wd[5]) == 'T'))
02107         COMPLETE_WITH_CONST("TO");
02108     /* Complete "AS ON <sth> TO" with a table name */
02109     else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
02110              pg_strcasecmp(prev3_wd, "ON") == 0 &&
02111              pg_strcasecmp(prev_wd, "TO") == 0)
02112         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
02113 
02114 /* CREATE SERVER <name> */
02115     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02116              pg_strcasecmp(prev2_wd, "SERVER") == 0)
02117     {
02118         static const char *const list_CREATE_SERVER[] =
02119         {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL};
02120 
02121         COMPLETE_WITH_LIST(list_CREATE_SERVER);
02122     }
02123 
02124 /* CREATE TABLE */
02125     /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
02126     else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
02127              (pg_strcasecmp(prev_wd, "TEMP") == 0 ||
02128               pg_strcasecmp(prev_wd, "TEMPORARY") == 0))
02129     {
02130         static const char *const list_TEMP[] =
02131         {"SEQUENCE", "TABLE", "VIEW", NULL};
02132 
02133         COMPLETE_WITH_LIST(list_TEMP);
02134     }
02135     /* Complete "CREATE UNLOGGED" with TABLE */
02136     else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
02137              pg_strcasecmp(prev_wd, "UNLOGGED") == 0)
02138     {
02139         static const char *const list_UNLOGGED[] =
02140         {"TABLE", "MATERIALIZED VIEW", NULL};
02141 
02142         COMPLETE_WITH_LIST(list_UNLOGGED);
02143     }
02144 
02145 /* CREATE TABLESPACE */
02146     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02147              pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
02148     {
02149         static const char *const list_CREATETABLESPACE[] =
02150         {"OWNER", "LOCATION", NULL};
02151 
02152         COMPLETE_WITH_LIST(list_CREATETABLESPACE);
02153     }
02154     /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
02155     else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
02156              pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
02157              pg_strcasecmp(prev2_wd, "OWNER") == 0)
02158     {
02159         COMPLETE_WITH_CONST("LOCATION");
02160     }
02161 
02162 /* CREATE TEXT SEARCH */
02163     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02164              pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
02165              pg_strcasecmp(prev_wd, "SEARCH") == 0)
02166     {
02167         static const char *const list_CREATETEXTSEARCH[] =
02168         {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
02169 
02170         COMPLETE_WITH_LIST(list_CREATETEXTSEARCH);
02171     }
02172     else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
02173              pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
02174              pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
02175         COMPLETE_WITH_CONST("(");
02176 
02177 /* CREATE TRIGGER */
02178     /* complete CREATE TRIGGER <name> with BEFORE,AFTER */
02179     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02180              pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
02181     {
02182         static const char *const list_CREATETRIGGER[] =
02183         {"BEFORE", "AFTER", "INSTEAD OF", NULL};
02184 
02185         COMPLETE_WITH_LIST(list_CREATETRIGGER);
02186     }
02187     /* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */
02188     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02189              pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
02190              (pg_strcasecmp(prev_wd, "BEFORE") == 0 ||
02191               pg_strcasecmp(prev_wd, "AFTER") == 0))
02192     {
02193         static const char *const list_CREATETRIGGER_EVENTS[] =
02194         {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL};
02195 
02196         COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
02197     }
02198     /* complete CREATE TRIGGER <name> INSTEAD OF with an event */
02199     else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
02200              pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
02201              pg_strcasecmp(prev2_wd, "INSTEAD") == 0 &&
02202              pg_strcasecmp(prev_wd, "OF") == 0)
02203     {
02204         static const char *const list_CREATETRIGGER_EVENTS[] =
02205         {"INSERT", "DELETE", "UPDATE", NULL};
02206 
02207         COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
02208     }
02209     /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
02210     else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
02211               pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
02212               (pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
02213                pg_strcasecmp(prev2_wd, "AFTER") == 0)) ||
02214              (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
02215               pg_strcasecmp(prev3_wd, "INSTEAD") == 0 &&
02216               pg_strcasecmp(prev2_wd, "OF") == 0))
02217     {
02218         static const char *const list_CREATETRIGGER2[] =
02219         {"ON", "OR", NULL};
02220 
02221         COMPLETE_WITH_LIST(list_CREATETRIGGER2);
02222     }
02223 
02224     /*
02225      * complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of
02226      * tables
02227      */
02228     else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
02229              (pg_strcasecmp(prev3_wd, "BEFORE") == 0 ||
02230               pg_strcasecmp(prev3_wd, "AFTER") == 0) &&
02231              pg_strcasecmp(prev_wd, "ON") == 0)
02232         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
02233     /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */
02234     else if (pg_strcasecmp(prev4_wd, "INSTEAD") == 0 &&
02235              pg_strcasecmp(prev3_wd, "OF") == 0 &&
02236              pg_strcasecmp(prev_wd, "ON") == 0)
02237         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
02238     /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */
02239     else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 &&
02240              prev2_wd[0] != '\0')
02241         COMPLETE_WITH_CONST("PROCEDURE");
02242 
02243 /* CREATE ROLE,USER,GROUP <name> */
02244     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02245              !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
02246              (pg_strcasecmp(prev2_wd, "ROLE") == 0 ||
02247               pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
02248     {
02249         static const char *const list_CREATEROLE[] =
02250         {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
02251             "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
02252             "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
02253             "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
02254         "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
02255 
02256         COMPLETE_WITH_LIST(list_CREATEROLE);
02257     }
02258 
02259 /* CREATE ROLE,USER,GROUP <name> WITH */
02260     else if ((pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02261               (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
02262                pg_strcasecmp(prev3_wd, "GROUP") == 0 ||
02263                pg_strcasecmp(prev3_wd, "USER") == 0) &&
02264               pg_strcasecmp(prev_wd, "WITH") == 0))
02265     {
02266         /* Similar to the above, but don't complete "WITH" again. */
02267         static const char *const list_CREATEROLE_WITH[] =
02268         {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
02269             "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
02270             "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
02271             "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
02272         "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", NULL};
02273 
02274         COMPLETE_WITH_LIST(list_CREATEROLE_WITH);
02275     }
02276 
02277     /*
02278      * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
02279      * PASSWORD
02280      */
02281     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02282              (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
02283               pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
02284              (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
02285     {
02286         COMPLETE_WITH_CONST("PASSWORD");
02287     }
02288     /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
02289     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02290              (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
02291               pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
02292              pg_strcasecmp(prev_wd, "IN") == 0)
02293     {
02294         static const char *const list_CREATEROLE3[] =
02295         {"GROUP", "ROLE", NULL};
02296 
02297         COMPLETE_WITH_LIST(list_CREATEROLE3);
02298     }
02299 
02300 /* CREATE VIEW */
02301     /* Complete CREATE VIEW <name> with AS */
02302     else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
02303              pg_strcasecmp(prev2_wd, "VIEW") == 0)
02304         COMPLETE_WITH_CONST("AS");
02305     /* Complete "CREATE VIEW <sth> AS with "SELECT" */
02306     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02307              pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
02308              pg_strcasecmp(prev_wd, "AS") == 0)
02309         COMPLETE_WITH_CONST("SELECT");
02310 
02311 /* CREATE MATERIALIZED VIEW */
02312     else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
02313              pg_strcasecmp(prev_wd, "MATERIALIZED") == 0)
02314         COMPLETE_WITH_CONST("VIEW");
02315     /* Complete CREATE MATERIALIZED VIEW <name> with AS */
02316     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
02317              pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 &&
02318              pg_strcasecmp(prev2_wd, "VIEW") == 0)
02319         COMPLETE_WITH_CONST("AS");
02320     /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
02321     else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
02322              pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 &&
02323              pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
02324              pg_strcasecmp(prev_wd, "AS") == 0)
02325         COMPLETE_WITH_CONST("SELECT");
02326 
02327 /* DECLARE */
02328     else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0)
02329     {
02330         static const char *const list_DECLARE[] =
02331         {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL};
02332 
02333         COMPLETE_WITH_LIST(list_DECLARE);
02334     }
02335 
02336 /* CURSOR */
02337     else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
02338     {
02339         static const char *const list_DECLARECURSOR[] =
02340         {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL};
02341 
02342         COMPLETE_WITH_LIST(list_DECLARECURSOR);
02343     }
02344 
02345 
02346 /* DELETE */
02347 
02348     /*
02349      * Complete DELETE with FROM (only if the word before that is not "ON"
02350      * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT)
02351      */
02352     else if (pg_strcasecmp(prev_wd, "DELETE") == 0 &&
02353              !(pg_strcasecmp(prev2_wd, "ON") == 0 ||
02354                pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
02355                pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
02356                pg_strcasecmp(prev2_wd, "AFTER") == 0))
02357         COMPLETE_WITH_CONST("FROM");
02358     /* Complete DELETE FROM with a list of tables */
02359     else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
02360              pg_strcasecmp(prev_wd, "FROM") == 0)
02361         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_deletables, NULL);
02362     /* Complete DELETE FROM <table> */
02363     else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
02364              pg_strcasecmp(prev2_wd, "FROM") == 0)
02365     {
02366         static const char *const list_DELETE[] =
02367         {"USING", "WHERE", "SET", NULL};
02368 
02369         COMPLETE_WITH_LIST(list_DELETE);
02370     }
02371     /* XXX: implement tab completion for DELETE ... USING */
02372 
02373 /* DISCARD */
02374     else if (pg_strcasecmp(prev_wd, "DISCARD") == 0)
02375     {
02376         static const char *const list_DISCARD[] =
02377         {"ALL", "PLANS", "TEMP", NULL};
02378 
02379         COMPLETE_WITH_LIST(list_DISCARD);
02380     }
02381 
02382 /* DO */
02383 
02384     /*
02385      * Complete DO with LANGUAGE.
02386      */
02387     else if (pg_strcasecmp(prev_wd, "DO") == 0)
02388     {
02389         static const char *const list_DO[] =
02390         {"LANGUAGE", NULL};
02391 
02392         COMPLETE_WITH_LIST(list_DO);
02393     }
02394 
02395 /* DROP (when not the previous word) */
02396     /* DROP AGGREGATE */
02397     else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
02398              pg_strcasecmp(prev2_wd, "AGGREGATE") == 0)
02399         COMPLETE_WITH_CONST("(");
02400 
02401     /* DROP object with CASCADE / RESTRICT */
02402     else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
02403               (pg_strcasecmp(prev2_wd, "COLLATION") == 0 ||
02404                pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
02405                pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
02406                pg_strcasecmp(prev2_wd, "EXTENSION") == 0 ||
02407                pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
02408                pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
02409                pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
02410                pg_strcasecmp(prev2_wd, "SCHEMA") == 0 ||
02411                pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 ||
02412                pg_strcasecmp(prev2_wd, "SERVER") == 0 ||
02413                pg_strcasecmp(prev2_wd, "TABLE") == 0 ||
02414                pg_strcasecmp(prev2_wd, "TYPE") == 0 ||
02415                pg_strcasecmp(prev2_wd, "VIEW") == 0)) ||
02416              (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
02417               pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
02418               prev_wd[strlen(prev_wd) - 1] == ')') ||
02419              (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
02420               pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
02421               pg_strcasecmp(prev3_wd, "DATA") == 0 &&
02422               pg_strcasecmp(prev2_wd, "WRAPPER") == 0) ||
02423              (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
02424               pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
02425               pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
02426               (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 ||
02427                pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 ||
02428                pg_strcasecmp(prev2_wd, "PARSER") == 0 ||
02429                pg_strcasecmp(prev2_wd, "TEMPLATE") == 0))
02430         )
02431     {
02432         if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
02433             pg_strcasecmp(prev2_wd, "FUNCTION") == 0)
02434         {
02435             COMPLETE_WITH_CONST("(");
02436         }
02437         else
02438         {
02439             static const char *const list_DROPCR[] =
02440             {"CASCADE", "RESTRICT", NULL};
02441 
02442             COMPLETE_WITH_LIST(list_DROPCR);
02443         }
02444     }
02445     else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
02446              pg_strcasecmp(prev_wd, "FOREIGN") == 0)
02447     {
02448         static const char *const drop_CREATE_FOREIGN[] =
02449         {"DATA WRAPPER", "TABLE", NULL};
02450 
02451         COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
02452     }
02453 
02454     /* DROP MATERIALIZED VIEW */
02455     else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
02456              pg_strcasecmp(prev_wd, "MATERIALIZED") == 0)
02457     {
02458         COMPLETE_WITH_CONST("VIEW");
02459     }
02460     else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
02461              pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 &&
02462              pg_strcasecmp(prev_wd, "VIEW") == 0)
02463     {
02464         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
02465     }
02466 
02467     else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
02468              (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
02469               pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
02470              pg_strcasecmp(prev_wd, "(") == 0)
02471         COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
02472     /* DROP OWNED BY */
02473     else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
02474              pg_strcasecmp(prev_wd, "OWNED") == 0)
02475         COMPLETE_WITH_CONST("BY");
02476     else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
02477              pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
02478              pg_strcasecmp(prev_wd, "BY") == 0)
02479         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
02480     else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
02481              pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
02482              pg_strcasecmp(prev_wd, "SEARCH") == 0)
02483     {
02484 
02485         static const char *const list_ALTERTEXTSEARCH[] =
02486         {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
02487 
02488         COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
02489     }
02490 
02491 /* EXECUTE, but not EXECUTE embedded in other commands */
02492     else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 &&
02493              prev2_wd[0] == '\0')
02494         COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
02495 
02496 /* EXPLAIN */
02497 
02498     /*
02499      * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
02500      */
02501     else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
02502     {
02503         static const char *const list_EXPLAIN[] =
02504         {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL};
02505 
02506         COMPLETE_WITH_LIST(list_EXPLAIN);
02507     }
02508     else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
02509              pg_strcasecmp(prev_wd, "ANALYZE") == 0)
02510     {
02511         static const char *const list_EXPLAIN[] =
02512         {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL};
02513 
02514         COMPLETE_WITH_LIST(list_EXPLAIN);
02515     }
02516     else if ((pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
02517               pg_strcasecmp(prev_wd, "VERBOSE") == 0) ||
02518              (pg_strcasecmp(prev3_wd, "EXPLAIN") == 0 &&
02519               pg_strcasecmp(prev2_wd, "ANALYZE") == 0 &&
02520               pg_strcasecmp(prev_wd, "VERBOSE") == 0))
02521     {
02522         static const char *const list_EXPLAIN[] =
02523         {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
02524 
02525         COMPLETE_WITH_LIST(list_EXPLAIN);
02526     }
02527 
02528 /* FETCH && MOVE */
02529     /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
02530     else if (pg_strcasecmp(prev_wd, "FETCH") == 0 ||
02531              pg_strcasecmp(prev_wd, "MOVE") == 0)
02532     {
02533         static const char *const list_FETCH1[] =
02534         {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL};
02535 
02536         COMPLETE_WITH_LIST(list_FETCH1);
02537     }
02538     /* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
02539     else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 ||
02540              pg_strcasecmp(prev2_wd, "MOVE") == 0)
02541     {
02542         static const char *const list_FETCH2[] =
02543         {"ALL", "NEXT", "PRIOR", NULL};
02544 
02545         COMPLETE_WITH_LIST(list_FETCH2);
02546     }
02547 
02548     /*
02549      * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent,
02550      * but we may as well tab-complete both: perhaps some users prefer one
02551      * variant or the other.
02552      */
02553     else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 ||
02554              pg_strcasecmp(prev3_wd, "MOVE") == 0)
02555     {
02556         static const char *const list_FROMIN[] =
02557         {"FROM", "IN", NULL};
02558 
02559         COMPLETE_WITH_LIST(list_FROMIN);
02560     }
02561 
02562 /* FOREIGN DATA WRAPPER */
02563     /* applies in ALTER/DROP FDW and in CREATE SERVER */
02564     else if (pg_strcasecmp(prev4_wd, "CREATE") != 0 &&
02565              pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
02566              pg_strcasecmp(prev2_wd, "DATA") == 0 &&
02567              pg_strcasecmp(prev_wd, "WRAPPER") == 0)
02568         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
02569 
02570 /* FOREIGN TABLE */
02571     else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 &&
02572              pg_strcasecmp(prev2_wd, "FOREIGN") == 0 &&
02573              pg_strcasecmp(prev_wd, "TABLE") == 0)
02574         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
02575 
02576 /* GRANT && REVOKE */
02577     /* Complete GRANT/REVOKE with a list of roles and privileges */
02578     else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
02579              pg_strcasecmp(prev_wd, "REVOKE") == 0)
02580     {
02581         COMPLETE_WITH_QUERY(Query_for_list_of_roles
02582                             " UNION SELECT 'SELECT'"
02583                             " UNION SELECT 'INSERT'"
02584                             " UNION SELECT 'UPDATE'"
02585                             " UNION SELECT 'DELETE'"
02586                             " UNION SELECT 'TRUNCATE'"
02587                             " UNION SELECT 'REFERENCES'"
02588                             " UNION SELECT 'TRIGGER'"
02589                             " UNION SELECT 'CREATE'"
02590                             " UNION SELECT 'CONNECT'"
02591                             " UNION SELECT 'TEMPORARY'"
02592                             " UNION SELECT 'EXECUTE'"
02593                             " UNION SELECT 'USAGE'"
02594                             " UNION SELECT 'ALL'");
02595     }
02596 
02597     /*
02598      * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
02599      * TO/FROM
02600      */
02601     else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
02602              pg_strcasecmp(prev2_wd, "REVOKE") == 0)
02603     {
02604         if (pg_strcasecmp(prev_wd, "SELECT") == 0
02605             || pg_strcasecmp(prev_wd, "INSERT") == 0
02606             || pg_strcasecmp(prev_wd, "UPDATE") == 0
02607             || pg_strcasecmp(prev_wd, "DELETE") == 0
02608             || pg_strcasecmp(prev_wd, "TRUNCATE") == 0
02609             || pg_strcasecmp(prev_wd, "REFERENCES") == 0
02610             || pg_strcasecmp(prev_wd, "TRIGGER") == 0
02611             || pg_strcasecmp(prev_wd, "CREATE") == 0
02612             || pg_strcasecmp(prev_wd, "CONNECT") == 0
02613             || pg_strcasecmp(prev_wd, "TEMPORARY") == 0
02614             || pg_strcasecmp(prev_wd, "TEMP") == 0
02615             || pg_strcasecmp(prev_wd, "EXECUTE") == 0
02616             || pg_strcasecmp(prev_wd, "USAGE") == 0
02617             || pg_strcasecmp(prev_wd, "ALL") == 0)
02618             COMPLETE_WITH_CONST("ON");
02619         else
02620         {
02621             if (pg_strcasecmp(prev2_wd, "GRANT") == 0)
02622                 COMPLETE_WITH_CONST("TO");
02623             else
02624                 COMPLETE_WITH_CONST("FROM");
02625         }
02626     }
02627 
02628     /*
02629      * Complete GRANT/REVOKE <sth> ON with a list of tables, views, sequences,
02630      * and indexes
02631      *
02632      * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via
02633      * UNION; seems to work intuitively
02634      *
02635      * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
02636      * here will only work if the privilege list contains exactly one
02637      * privilege
02638      */
02639     else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
02640               pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
02641              pg_strcasecmp(prev_wd, "ON") == 0)
02642         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf,
02643                                    " UNION SELECT 'DATABASE'"
02644                                    " UNION SELECT 'DOMAIN'"
02645                                    " UNION SELECT 'FOREIGN DATA WRAPPER'"
02646                                    " UNION SELECT 'FOREIGN SERVER'"
02647                                    " UNION SELECT 'FUNCTION'"
02648                                    " UNION SELECT 'LANGUAGE'"
02649                                    " UNION SELECT 'LARGE OBJECT'"
02650                                    " UNION SELECT 'SCHEMA'"
02651                                    " UNION SELECT 'TABLESPACE'"
02652                                    " UNION SELECT 'TYPE'");
02653     else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
02654               pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
02655              pg_strcasecmp(prev2_wd, "ON") == 0 &&
02656              pg_strcasecmp(prev_wd, "FOREIGN") == 0)
02657     {
02658         static const char *const list_privilege_foreign[] =
02659         {"DATA WRAPPER", "SERVER", NULL};
02660 
02661         COMPLETE_WITH_LIST(list_privilege_foreign);
02662     }
02663 
02664     /* Complete "GRANT/REVOKE * ON * " with "TO/FROM" */
02665     else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
02666               pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
02667              pg_strcasecmp(prev2_wd, "ON") == 0)
02668     {
02669         if (pg_strcasecmp(prev_wd, "DATABASE") == 0)
02670             COMPLETE_WITH_QUERY(Query_for_list_of_databases);
02671         else if (pg_strcasecmp(prev_wd, "DOMAIN") == 0)
02672             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
02673         else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0)
02674             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
02675         else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0)
02676             COMPLETE_WITH_QUERY(Query_for_list_of_languages);
02677         else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0)
02678             COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
02679         else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
02680             COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
02681         else if (pg_strcasecmp(prev_wd, "TYPE") == 0)
02682             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
02683         else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
02684             COMPLETE_WITH_CONST("TO");
02685         else
02686             COMPLETE_WITH_CONST("FROM");
02687     }
02688 
02689     /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
02690     else if (pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
02691              pg_strcasecmp(prev3_wd, "ON") == 0)
02692     {
02693         if (pg_strcasecmp(prev_wd, "TO") == 0)
02694             COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
02695         else
02696             COMPLETE_WITH_CONST("TO");
02697     }
02698     else if (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
02699              pg_strcasecmp(prev3_wd, "ON") == 0)
02700     {
02701         if (pg_strcasecmp(prev_wd, "FROM") == 0)
02702             COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
02703         else
02704             COMPLETE_WITH_CONST("FROM");
02705     }
02706 
02707     /* Complete "GRANT/REVOKE * TO/FROM" with username, GROUP, or PUBLIC */
02708     else if (pg_strcasecmp(prev3_wd, "GRANT") == 0 &&
02709              pg_strcasecmp(prev_wd, "TO") == 0)
02710     {
02711         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
02712     }
02713     else if (pg_strcasecmp(prev3_wd, "REVOKE") == 0 &&
02714              pg_strcasecmp(prev_wd, "FROM") == 0)
02715     {
02716         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
02717     }
02718 
02719 /* GROUP BY */
02720     else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
02721              pg_strcasecmp(prev_wd, "GROUP") == 0)
02722         COMPLETE_WITH_CONST("BY");
02723 
02724 /* INSERT */
02725     /* Complete INSERT with "INTO" */
02726     else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
02727         COMPLETE_WITH_CONST("INTO");
02728     /* Complete INSERT INTO with table names */
02729     else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 &&
02730              pg_strcasecmp(prev_wd, "INTO") == 0)
02731         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_insertables, NULL);
02732     /* Complete "INSERT INTO <table> (" with attribute names */
02733     else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
02734              pg_strcasecmp(prev3_wd, "INTO") == 0 &&
02735              pg_strcasecmp(prev_wd, "(") == 0)
02736         COMPLETE_WITH_ATTR(prev2_wd, "");
02737 
02738     /*
02739      * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
02740      * "TABLE" or "DEFAULT VALUES"
02741      */
02742     else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
02743              pg_strcasecmp(prev2_wd, "INTO") == 0)
02744     {
02745         static const char *const list_INSERT[] =
02746         {"(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL};
02747 
02748         COMPLETE_WITH_LIST(list_INSERT);
02749     }
02750 
02751     /*
02752      * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
02753      * "TABLE"
02754      */
02755     else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
02756              pg_strcasecmp(prev3_wd, "INTO") == 0 &&
02757              prev_wd[strlen(prev_wd) - 1] == ')')
02758     {
02759         static const char *const list_INSERT[] =
02760         {"SELECT", "TABLE", "VALUES", NULL};
02761 
02762         COMPLETE_WITH_LIST(list_INSERT);
02763     }
02764 
02765     /* Insert an open parenthesis after "VALUES" */
02766     else if (pg_strcasecmp(prev_wd, "VALUES") == 0 &&
02767              pg_strcasecmp(prev2_wd, "DEFAULT") != 0)
02768         COMPLETE_WITH_CONST("(");
02769 
02770 /* LOCK */
02771     /* Complete LOCK [TABLE] with a list of tables */
02772     else if (pg_strcasecmp(prev_wd, "LOCK") == 0)
02773         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
02774                                    " UNION SELECT 'TABLE'");
02775     else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
02776              pg_strcasecmp(prev2_wd, "LOCK") == 0)
02777         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
02778 
02779     /* For the following, handle the case of a single table only for now */
02780 
02781     /* Complete LOCK [TABLE] <table> with "IN" */
02782     else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 &&
02783               pg_strcasecmp(prev_wd, "TABLE") != 0) ||
02784              (pg_strcasecmp(prev2_wd, "TABLE") == 0 &&
02785               pg_strcasecmp(prev3_wd, "LOCK") == 0))
02786         COMPLETE_WITH_CONST("IN");
02787 
02788     /* Complete LOCK [TABLE] <table> IN with a lock mode */
02789     else if (pg_strcasecmp(prev_wd, "IN") == 0 &&
02790              (pg_strcasecmp(prev3_wd, "LOCK") == 0 ||
02791               (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
02792                pg_strcasecmp(prev4_wd, "LOCK") == 0)))
02793     {
02794         static const char *const lock_modes[] =
02795         {"ACCESS SHARE MODE",
02796             "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
02797             "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
02798             "SHARE ROW EXCLUSIVE MODE",
02799         "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL};
02800 
02801         COMPLETE_WITH_LIST(lock_modes);
02802     }
02803 
02804 /* NOTIFY */
02805     else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
02806         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'");
02807 
02808 /* OPTIONS */
02809     else if (pg_strcasecmp(prev_wd, "OPTIONS") == 0)
02810         COMPLETE_WITH_CONST("(");
02811 
02812 /* OWNER TO  - complete with available roles */
02813     else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
02814              pg_strcasecmp(prev_wd, "TO") == 0)
02815         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
02816 
02817 /* ORDER BY */
02818     else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
02819              pg_strcasecmp(prev_wd, "ORDER") == 0)
02820         COMPLETE_WITH_CONST("BY");
02821     else if (pg_strcasecmp(prev4_wd, "FROM") == 0 &&
02822              pg_strcasecmp(prev2_wd, "ORDER") == 0 &&
02823              pg_strcasecmp(prev_wd, "BY") == 0)
02824         COMPLETE_WITH_ATTR(prev3_wd, "");
02825 
02826 /* PREPARE xx AS */
02827     else if (pg_strcasecmp(prev_wd, "AS") == 0 &&
02828              pg_strcasecmp(prev3_wd, "PREPARE") == 0)
02829     {
02830         static const char *const list_PREPARE[] =
02831         {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
02832 
02833         COMPLETE_WITH_LIST(list_PREPARE);
02834     }
02835 
02836 /*
02837  * PREPARE TRANSACTION is missing on purpose. It's intended for transaction
02838  * managers, not for manual use in interactive sessions.
02839  */
02840 
02841 /* REASSIGN OWNED BY xxx TO yyy */
02842     else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
02843         COMPLETE_WITH_CONST("OWNED");
02844     else if (pg_strcasecmp(prev_wd, "OWNED") == 0 &&
02845              pg_strcasecmp(prev2_wd, "REASSIGN") == 0)
02846         COMPLETE_WITH_CONST("BY");
02847     else if (pg_strcasecmp(prev_wd, "BY") == 0 &&
02848              pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
02849              pg_strcasecmp(prev3_wd, "REASSIGN") == 0)
02850         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
02851     else if (pg_strcasecmp(prev2_wd, "BY") == 0 &&
02852              pg_strcasecmp(prev3_wd, "OWNED") == 0 &&
02853              pg_strcasecmp(prev4_wd, "REASSIGN") == 0)
02854         COMPLETE_WITH_CONST("TO");
02855     else if (pg_strcasecmp(prev_wd, "TO") == 0 &&
02856              pg_strcasecmp(prev3_wd, "BY") == 0 &&
02857              pg_strcasecmp(prev4_wd, "OWNED") == 0 &&
02858              pg_strcasecmp(prev5_wd, "REASSIGN") == 0)
02859         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
02860 
02861 /* REFRESH MATERIALIZED VIEW */
02862     else if (pg_strcasecmp(prev_wd, "REFRESH") == 0)
02863         COMPLETE_WITH_CONST("MATERIALIZED VIEW");
02864     else if (pg_strcasecmp(prev2_wd, "REFRESH") == 0 &&
02865              pg_strcasecmp(prev_wd, "MATERIALIZED") == 0)
02866         COMPLETE_WITH_CONST("VIEW");
02867     else if (pg_strcasecmp(prev3_wd, "REFRESH") == 0 &&
02868              pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 &&
02869              pg_strcasecmp(prev_wd, "VIEW") == 0)
02870         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
02871     else if (pg_strcasecmp(prev4_wd, "REFRESH") == 0 &&
02872              pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 &&
02873              pg_strcasecmp(prev2_wd, "VIEW") == 0)
02874         COMPLETE_WITH_CONST("WITH");
02875     else if (pg_strcasecmp(prev5_wd, "REFRESH") == 0 &&
02876              pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 &&
02877              pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
02878              pg_strcasecmp(prev_wd, "WITH") == 0)
02879     {
02880         static const char *const list_WITH_DATA[] =
02881         {"NO DATA", "DATA", NULL};
02882 
02883         COMPLETE_WITH_LIST(list_WITH_DATA);
02884     }
02885     else if (pg_strcasecmp(prev6_wd, "REFRESH") == 0 &&
02886              pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 &&
02887              pg_strcasecmp(prev4_wd, "VIEW") == 0 &&
02888              pg_strcasecmp(prev2_wd, "WITH") == 0 &&
02889              pg_strcasecmp(prev_wd, "NO") == 0)
02890         COMPLETE_WITH_CONST("DATA");
02891 
02892 /* REINDEX */
02893     else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
02894     {
02895         static const char *const list_REINDEX[] =
02896         {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL};
02897 
02898         COMPLETE_WITH_LIST(list_REINDEX);
02899     }
02900     else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0)
02901     {
02902         if (pg_strcasecmp(prev_wd, "TABLE") == 0)
02903             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
02904         else if (pg_strcasecmp(prev_wd, "INDEX") == 0)
02905             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
02906         else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 ||
02907                  pg_strcasecmp(prev_wd, "DATABASE") == 0)
02908             COMPLETE_WITH_QUERY(Query_for_list_of_databases);
02909     }
02910 
02911 /* SECURITY LABEL */
02912     else if (pg_strcasecmp(prev_wd, "SECURITY") == 0)
02913         COMPLETE_WITH_CONST("LABEL");
02914     else if (pg_strcasecmp(prev2_wd, "SECURITY") == 0 &&
02915              pg_strcasecmp(prev_wd, "LABEL") == 0)
02916     {
02917         static const char *const list_SECURITY_LABEL_preposition[] =
02918         {"ON", "FOR"};
02919 
02920         COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition);
02921     }
02922     else if (pg_strcasecmp(prev4_wd, "SECURITY") == 0 &&
02923              pg_strcasecmp(prev3_wd, "LABEL") == 0 &&
02924              pg_strcasecmp(prev2_wd, "FOR") == 0)
02925         COMPLETE_WITH_CONST("ON");
02926     else if ((pg_strcasecmp(prev3_wd, "SECURITY") == 0 &&
02927               pg_strcasecmp(prev2_wd, "LABEL") == 0 &&
02928               pg_strcasecmp(prev_wd, "ON") == 0) ||
02929              (pg_strcasecmp(prev5_wd, "SECURITY") == 0 &&
02930               pg_strcasecmp(prev4_wd, "LABEL") == 0 &&
02931               pg_strcasecmp(prev3_wd, "FOR") == 0 &&
02932               pg_strcasecmp(prev_wd, "ON") == 0))
02933     {
02934         static const char *const list_SECURITY_LABEL[] =
02935         {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW",
02936             "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "DOMAIN",
02937             "LARGE OBJECT", NULL};
02938 
02939         COMPLETE_WITH_LIST(list_SECURITY_LABEL);
02940     }
02941     else if (pg_strcasecmp(prev5_wd, "SECURITY") == 0 &&
02942              pg_strcasecmp(prev4_wd, "LABEL") == 0 &&
02943              pg_strcasecmp(prev3_wd, "ON") == 0)
02944         COMPLETE_WITH_CONST("IS");
02945 
02946 /* SELECT */
02947     /* naah . . . */
02948 
02949 /* SET, RESET, SHOW */
02950     /* Complete with a variable name */
02951     else if ((pg_strcasecmp(prev_wd, "SET") == 0 &&
02952               pg_strcasecmp(prev3_wd, "UPDATE") != 0) ||
02953              pg_strcasecmp(prev_wd, "RESET") == 0)
02954         COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
02955     else if (pg_strcasecmp(prev_wd, "SHOW") == 0)
02956         COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
02957     /* Complete "SET TRANSACTION" */
02958     else if ((pg_strcasecmp(prev2_wd, "SET") == 0 &&
02959               pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
02960              || (pg_strcasecmp(prev2_wd, "START") == 0
02961                  && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
02962              || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
02963                  && pg_strcasecmp(prev_wd, "WORK") == 0)
02964              || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
02965                  && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
02966              || (pg_strcasecmp(prev4_wd, "SESSION") == 0
02967                  && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0
02968                  && pg_strcasecmp(prev2_wd, "AS") == 0
02969                  && pg_strcasecmp(prev_wd, "TRANSACTION") == 0))
02970     {
02971         static const char *const my_list[] =
02972         {"ISOLATION LEVEL", "READ", NULL};
02973 
02974         COMPLETE_WITH_LIST(my_list);
02975     }
02976     else if ((pg_strcasecmp(prev3_wd, "SET") == 0
02977               || pg_strcasecmp(prev3_wd, "BEGIN") == 0
02978               || pg_strcasecmp(prev3_wd, "START") == 0
02979               || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0
02980                   && pg_strcasecmp(prev3_wd, "AS") == 0))
02981              && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0
02982                  || pg_strcasecmp(prev2_wd, "WORK") == 0)
02983              && pg_strcasecmp(prev_wd, "ISOLATION") == 0)
02984         COMPLETE_WITH_CONST("LEVEL");
02985     else if ((pg_strcasecmp(prev4_wd, "SET") == 0
02986               || pg_strcasecmp(prev4_wd, "BEGIN") == 0
02987               || pg_strcasecmp(prev4_wd, "START") == 0
02988               || pg_strcasecmp(prev4_wd, "AS") == 0)
02989              && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0
02990                  || pg_strcasecmp(prev3_wd, "WORK") == 0)
02991              && pg_strcasecmp(prev2_wd, "ISOLATION") == 0
02992              && pg_strcasecmp(prev_wd, "LEVEL") == 0)
02993     {
02994         static const char *const my_list[] =
02995         {"READ", "REPEATABLE", "SERIALIZABLE", NULL};
02996 
02997         COMPLETE_WITH_LIST(my_list);
02998     }
02999     else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
03000               pg_strcasecmp(prev4_wd, "WORK") == 0) &&
03001              pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
03002              pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
03003              pg_strcasecmp(prev_wd, "READ") == 0)
03004     {
03005         static const char *const my_list[] =
03006         {"UNCOMMITTED", "COMMITTED", NULL};
03007 
03008         COMPLETE_WITH_LIST(my_list);
03009     }
03010     else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
03011               pg_strcasecmp(prev4_wd, "WORK") == 0) &&
03012              pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
03013              pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
03014              pg_strcasecmp(prev_wd, "REPEATABLE") == 0)
03015         COMPLETE_WITH_CONST("READ");
03016     else if ((pg_strcasecmp(prev3_wd, "SET") == 0 ||
03017               pg_strcasecmp(prev3_wd, "BEGIN") == 0 ||
03018               pg_strcasecmp(prev3_wd, "START") == 0 ||
03019               pg_strcasecmp(prev3_wd, "AS") == 0) &&
03020              (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 ||
03021               pg_strcasecmp(prev2_wd, "WORK") == 0) &&
03022              pg_strcasecmp(prev_wd, "READ") == 0)
03023     {
03024         static const char *const my_list[] =
03025         {"ONLY", "WRITE", NULL};
03026 
03027         COMPLETE_WITH_LIST(my_list);
03028     }
03029     /* SET CONSTRAINTS */
03030     else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
03031              pg_strcasecmp(prev_wd, "CONSTRAINTS") == 0)
03032     {
03033         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");
03034     }
03035     /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
03036     else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
03037              pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
03038     {
03039         static const char *const constraint_list[] =
03040         {"DEFERRED", "IMMEDIATE", NULL};
03041 
03042         COMPLETE_WITH_LIST(constraint_list);
03043     }
03044     /* Complete SET ROLE */
03045     else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
03046              pg_strcasecmp(prev_wd, "ROLE") == 0)
03047         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
03048     /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
03049     else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
03050              pg_strcasecmp(prev_wd, "SESSION") == 0)
03051     {
03052         static const char *const my_list[] =
03053         {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL};
03054 
03055         COMPLETE_WITH_LIST(my_list);
03056     }
03057     /* Complete SET SESSION AUTHORIZATION with username */
03058     else if (pg_strcasecmp(prev3_wd, "SET") == 0
03059              && pg_strcasecmp(prev2_wd, "SESSION") == 0
03060              && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
03061         COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
03062     /* Complete RESET SESSION with AUTHORIZATION */
03063     else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
03064              pg_strcasecmp(prev_wd, "SESSION") == 0)
03065         COMPLETE_WITH_CONST("AUTHORIZATION");
03066     /* Complete SET <var> with "TO" */
03067     else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
03068              pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
03069              pg_strcasecmp(prev_wd, "TABLESPACE") != 0 &&
03070              pg_strcasecmp(prev_wd, "SCHEMA") != 0 &&
03071              prev_wd[strlen(prev_wd) - 1] != ')' &&
03072              prev_wd[strlen(prev_wd) - 1] != '=' &&
03073              pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
03074         COMPLETE_WITH_CONST("TO");
03075     /* Suggest possible variable values */
03076     else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
03077              (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
03078     {
03079         if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
03080         {
03081             static const char *const my_list[] =
03082             {"ISO", "SQL", "Postgres", "German",
03083                 "YMD", "DMY", "MDY",
03084                 "US", "European", "NonEuropean",
03085             "DEFAULT", NULL};
03086 
03087             COMPLETE_WITH_LIST(my_list);
03088         }
03089         else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0)
03090         {
03091             static const char *const my_list[] =
03092             {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL};
03093 
03094             COMPLETE_WITH_LIST(my_list);
03095         }
03096         else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
03097         {
03098             static const char *const my_list[] =
03099             {"ON", "OFF", "DEFAULT", NULL};
03100 
03101             COMPLETE_WITH_LIST(my_list);
03102         }
03103         else
03104         {
03105             static const char *const my_list[] =
03106             {"DEFAULT", NULL};
03107 
03108             COMPLETE_WITH_LIST(my_list);
03109         }
03110     }
03111 
03112 /* START TRANSACTION */
03113     else if (pg_strcasecmp(prev_wd, "START") == 0)
03114         COMPLETE_WITH_CONST("TRANSACTION");
03115 
03116 /* TABLE, but not TABLE embedded in other commands */
03117     else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
03118              prev2_wd[0] == '\0')
03119         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
03120 
03121 /* TRUNCATE */
03122     else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0)
03123         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
03124 
03125 /* UNLISTEN */
03126     else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0)
03127         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 '*'");
03128 
03129 /* UPDATE */
03130     /* If prev. word is UPDATE suggest a list of tables */
03131     else if (pg_strcasecmp(prev_wd, "UPDATE") == 0)
03132         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
03133     /* Complete UPDATE <table> with "SET" */
03134     else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0)
03135         COMPLETE_WITH_CONST("SET");
03136 
03137     /*
03138      * If the previous word is SET (and it wasn't caught above as the _first_
03139      * word) the word before it was (hopefully) a table name and we'll now
03140      * make a list of attributes.
03141      */
03142     else if (pg_strcasecmp(prev_wd, "SET") == 0)
03143         COMPLETE_WITH_ATTR(prev2_wd, "");
03144 
03145 /* UPDATE xx SET yy = */
03146     else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
03147              pg_strcasecmp(prev4_wd, "UPDATE") == 0)
03148         COMPLETE_WITH_CONST("=");
03149 
03150 /* USER MAPPING */
03151     else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 ||
03152               pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
03153               pg_strcasecmp(prev3_wd, "DROP") == 0) &&
03154              pg_strcasecmp(prev2_wd, "USER") == 0 &&
03155              pg_strcasecmp(prev_wd, "MAPPING") == 0)
03156         COMPLETE_WITH_CONST("FOR");
03157     else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
03158              pg_strcasecmp(prev3_wd, "USER") == 0 &&
03159              pg_strcasecmp(prev2_wd, "MAPPING") == 0 &&
03160              pg_strcasecmp(prev_wd, "FOR") == 0)
03161         COMPLETE_WITH_QUERY(Query_for_list_of_roles
03162                             " UNION SELECT 'CURRENT_USER'"
03163                             " UNION SELECT 'PUBLIC'"
03164                             " UNION SELECT 'USER'");
03165     else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 ||
03166               pg_strcasecmp(prev4_wd, "DROP") == 0) &&
03167              pg_strcasecmp(prev3_wd, "USER") == 0 &&
03168              pg_strcasecmp(prev2_wd, "MAPPING") == 0 &&
03169              pg_strcasecmp(prev_wd, "FOR") == 0)
03170         COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
03171     else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 ||
03172               pg_strcasecmp(prev5_wd, "ALTER") == 0 ||
03173               pg_strcasecmp(prev5_wd, "DROP") == 0) &&
03174              pg_strcasecmp(prev4_wd, "USER") == 0 &&
03175              pg_strcasecmp(prev3_wd, "MAPPING") == 0 &&
03176              pg_strcasecmp(prev2_wd, "FOR") == 0)
03177         COMPLETE_WITH_CONST("SERVER");
03178 
03179 /*
03180  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
03181  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
03182  */
03183     else if (pg_strcasecmp(prev_wd, "VACUUM") == 0)
03184         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
03185                                    " UNION SELECT 'FULL'"
03186                                    " UNION SELECT 'FREEZE'"
03187                                    " UNION SELECT 'ANALYZE'"
03188                                    " UNION SELECT 'VERBOSE'");
03189     else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
03190              (pg_strcasecmp(prev_wd, "FULL") == 0 ||
03191               pg_strcasecmp(prev_wd, "FREEZE") == 0))
03192         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
03193                                    " UNION SELECT 'ANALYZE'"
03194                                    " UNION SELECT 'VERBOSE'");
03195     else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
03196              pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
03197              (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
03198               pg_strcasecmp(prev2_wd, "FREEZE") == 0))
03199         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
03200                                    " UNION SELECT 'VERBOSE'");
03201     else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
03202              pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
03203              (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
03204               pg_strcasecmp(prev2_wd, "FREEZE") == 0))
03205         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
03206                                    " UNION SELECT 'ANALYZE'");
03207     else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
03208              pg_strcasecmp(prev_wd, "VERBOSE") == 0)
03209         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
03210                                    " UNION SELECT 'ANALYZE'");
03211     else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
03212              pg_strcasecmp(prev_wd, "ANALYZE") == 0)
03213         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
03214                                    " UNION SELECT 'VERBOSE'");
03215     else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
03216               pg_strcasecmp(prev2_wd, "VERBOSE") == 0) ||
03217              (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
03218               pg_strcasecmp(prev2_wd, "ANALYZE") == 0))
03219         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
03220 
03221 /* WITH [RECURSIVE] */
03222 
03223     /*
03224      * Only match when WITH is the first word, as WITH may appear in many
03225      * other contexts.
03226      */
03227     else if (pg_strcasecmp(prev_wd, "WITH") == 0 &&
03228              prev2_wd[0] == '\0')
03229         COMPLETE_WITH_CONST("RECURSIVE");
03230 
03231 /* ANALYZE */
03232     /* If the previous word is ANALYZE, produce list of tables */
03233     else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0)
03234         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL);
03235 
03236 /* WHERE */
03237     /* Simple case of the word before the where being the table name */
03238     else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
03239         COMPLETE_WITH_ATTR(prev2_wd, "");
03240 
03241 /* ... FROM ... */
03242 /* TODO: also include SRF ? */
03243     else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
03244              pg_strcasecmp(prev3_wd, "COPY") != 0 &&
03245              pg_strcasecmp(prev3_wd, "\\copy") != 0)
03246         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
03247 
03248 /* ... JOIN ... */
03249     else if (pg_strcasecmp(prev_wd, "JOIN") == 0)
03250         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
03251 
03252 /* Backslash commands */
03253 /* TODO:  \dc \dd \dl */
03254     else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
03255         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
03256 
03257     else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0)
03258         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
03259     else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0)
03260         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
03261     else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0)
03262         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
03263     else if (strncmp(prev_wd, "\\des", strlen("\\des")) == 0)
03264         COMPLETE_WITH_QUERY(Query_for_list_of_servers);
03265     else if (strncmp(prev_wd, "\\deu", strlen("\\deu")) == 0)
03266         COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
03267     else if (strncmp(prev_wd, "\\dew", strlen("\\dew")) == 0)
03268         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
03269 
03270     else if (strncmp(prev_wd, "\\df", strlen("\\df")) == 0)
03271         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
03272     else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0)
03273         COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
03274     else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0)
03275         COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
03276     else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0)
03277         COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
03278     /* must be at end of \dF */
03279     else if (strncmp(prev_wd, "\\dF", strlen("\\dF")) == 0)
03280         COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
03281 
03282     else if (strncmp(prev_wd, "\\di", strlen("\\di")) == 0)
03283         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
03284     else if (strncmp(prev_wd, "\\dL", strlen("\\dL")) == 0)
03285         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
03286     else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0)
03287         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
03288     else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
03289              || strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
03290         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
03291     else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
03292         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
03293     else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
03294         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
03295     else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0)
03296         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
03297     else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 0
03298              || (strncmp(prev_wd, "\\dg", strlen("\\dg")) == 0))
03299         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
03300     else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0)
03301         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
03302     else if (strncmp(prev_wd, "\\dm", strlen("\\dm")) == 0)
03303         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
03304 
03305     /* must be at end of \d list */
03306     else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
03307         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
03308 
03309     else if (strcmp(prev_wd, "\\ef") == 0)
03310         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
03311 
03312     else if (strcmp(prev_wd, "\\encoding") == 0)
03313         COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
03314     else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
03315         COMPLETE_WITH_LIST(sql_commands);
03316     else if (strcmp(prev_wd, "\\password") == 0)
03317         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
03318     else if (strcmp(prev_wd, "\\pset") == 0)
03319     {
03320         static const char *const my_list[] =
03321         {"format", "border", "expanded",
03322             "null", "fieldsep", "tuples_only", "title", "tableattr",
03323         "linestyle", "pager", "recordsep", NULL};
03324 
03325         COMPLETE_WITH_LIST_CS(my_list);
03326     }
03327     else if (strcmp(prev2_wd, "\\pset") == 0)
03328     {
03329         if (strcmp(prev_wd, "format") == 0)
03330         {
03331             static const char *const my_list[] =
03332             {"unaligned", "aligned", "wrapped", "html", "latex",
03333             "troff-ms", NULL};
03334 
03335             COMPLETE_WITH_LIST_CS(my_list);
03336         }
03337         else if (strcmp(prev_wd, "linestyle") == 0)
03338         {
03339             static const char *const my_list[] =
03340             {"ascii", "old-ascii", "unicode", NULL};
03341 
03342             COMPLETE_WITH_LIST_CS(my_list);
03343         }
03344     }
03345     else if (strcmp(prev_wd, "\\set") == 0)
03346     {
03347         matches = complete_from_variables(text, "", "");
03348     }
03349     else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
03350         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
03351     else if (strcmp(prev_wd, "\\cd") == 0 ||
03352              strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
03353              strcmp(prev_wd, "\\g") == 0 ||
03354           strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
03355              strcmp(prev_wd, "\\ir") == 0 || strcmp(prev_wd, "\\include_relative") == 0 ||
03356              strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
03357              strcmp(prev_wd, "\\s") == 0 ||
03358              strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
03359         )
03360     {
03361         completion_charp = "\\";
03362         matches = completion_matches(text, complete_from_files);
03363     }
03364 
03365     /*
03366      * Finally, we look through the list of "things", such as TABLE, INDEX and
03367      * check if that was the previous word. If so, execute the query to get a
03368      * list of them.
03369      */
03370     else
03371     {
03372         int         i;
03373 
03374         for (i = 0; words_after_create[i].name; i++)
03375         {
03376             if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)
03377             {
03378                 if (words_after_create[i].query)
03379                     COMPLETE_WITH_QUERY(words_after_create[i].query);
03380                 else if (words_after_create[i].squery)
03381                     COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
03382                                                NULL);
03383                 break;
03384             }
03385         }
03386     }
03387 
03388     /*
03389      * If we still don't have anything to match we have to fabricate some sort
03390      * of default list. If we were to just return NULL, readline automatically
03391      * attempts filename completion, and that's usually no good.
03392      */
03393     if (matches == NULL)
03394     {
03395         COMPLETE_WITH_CONST("");
03396 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
03397         rl_completion_append_character = '\0';
03398 #endif
03399     }
03400 
03401     /* free storage */
03402     {
03403         int         i;
03404 
03405         for (i = 0; i < lengthof(previous_words); i++)
03406             free(previous_words[i]);
03407     }
03408 
03409     /* Return our Grand List O' Matches */
03410     return matches;
03411 }
03412 
03413 
03414 /*
03415  * GENERATOR FUNCTIONS
03416  *
03417  * These functions do all the actual work of completing the input. They get
03418  * passed the text so far and the count how many times they have been called
03419  * so far with the same text.
03420  * If you read the above carefully, you'll see that these don't get called
03421  * directly but through the readline interface.
03422  * The return value is expected to be the full completion of the text, going
03423  * through a list each time, or NULL if there are no more matches. The string
03424  * will be free()'d by readline, so you must run it through strdup() or
03425  * something of that sort.
03426  */
03427 
03428 /*
03429  * Common routine for create_command_generator and drop_command_generator.
03430  * Entries that have 'excluded' flags are not returned.
03431  */
03432 static char *
03433 create_or_drop_command_generator(const char *text, int state, bits32 excluded)
03434 {
03435     static int  list_index,
03436                 string_length;
03437     const char *name;
03438 
03439     /* If this is the first time for this completion, init some values */
03440     if (state == 0)
03441     {
03442         list_index = 0;
03443         string_length = strlen(text);
03444     }
03445 
03446     /* find something that matches */
03447     while ((name = words_after_create[list_index++].name))
03448     {
03449         if ((pg_strncasecmp(name, text, string_length) == 0) &&
03450             !(words_after_create[list_index - 1].flags & excluded))
03451             return pg_strdup_keyword_case(name, text);
03452     }
03453     /* if nothing matches, return NULL */
03454     return NULL;
03455 }
03456 
03457 /*
03458  * This one gives you one from a list of things you can put after CREATE
03459  * as defined above.
03460  */
03461 static char *
03462 create_command_generator(const char *text, int state)
03463 {
03464     return create_or_drop_command_generator(text, state, THING_NO_CREATE);
03465 }
03466 
03467 /*
03468  * This function gives you a list of things you can put after a DROP command.
03469  */
03470 static char *
03471 drop_command_generator(const char *text, int state)
03472 {
03473     return create_or_drop_command_generator(text, state, THING_NO_DROP);
03474 }
03475 
03476 /* The following two functions are wrappers for _complete_from_query */
03477 
03478 static char *
03479 complete_from_query(const char *text, int state)
03480 {
03481     return _complete_from_query(0, text, state);
03482 }
03483 
03484 static char *
03485 complete_from_schema_query(const char *text, int state)
03486 {
03487     return _complete_from_query(1, text, state);
03488 }
03489 
03490 
03491 /*
03492  * This creates a list of matching things, according to a query pointed to
03493  * by completion_charp.
03494  * The query can be one of two kinds:
03495  *
03496  * 1. A simple query which must contain a %d and a %s, which will be replaced
03497  * by the string length of the text and the text itself. The query may also
03498  * have up to four more %s in it; the first two such will be replaced by the
03499  * value of completion_info_charp, the next two by the value of
03500  * completion_info_charp2.
03501  *
03502  * 2. A schema query used for completion of both schema and relation names.
03503  * These are more complex and must contain in the following order:
03504  * %d %s %d %s %d %s %s %d %s
03505  * where %d is the string length of the text and %s the text itself.
03506  *
03507  * It is assumed that strings should be escaped to become SQL literals
03508  * (that is, what is in the query is actually ... '%s' ...)
03509  *
03510  * See top of file for examples of both kinds of query.
03511  */
03512 static char *
03513 _complete_from_query(int is_schema_query, const char *text, int state)
03514 {
03515     static int  list_index,
03516                 string_length;
03517     static PGresult *result = NULL;
03518 
03519     /*
03520      * If this is the first time for this completion, we fetch a list of our
03521      * "things" from the backend.
03522      */
03523     if (state == 0)
03524     {
03525         PQExpBufferData query_buffer;
03526         char       *e_text;
03527         char       *e_info_charp;
03528         char       *e_info_charp2;
03529 
03530         list_index = 0;
03531         string_length = strlen(text);
03532 
03533         /* Free any prior result */
03534         PQclear(result);
03535         result = NULL;
03536 
03537         /* Set up suitably-escaped copies of textual inputs */
03538         e_text = pg_malloc(string_length * 2 + 1);
03539         PQescapeString(e_text, text, string_length);
03540 
03541         if (completion_info_charp)
03542         {
03543             size_t      charp_len;
03544 
03545             charp_len = strlen(completion_info_charp);
03546             e_info_charp = pg_malloc(charp_len * 2 + 1);
03547             PQescapeString(e_info_charp, completion_info_charp,
03548                            charp_len);
03549         }
03550         else
03551             e_info_charp = NULL;
03552 
03553         if (completion_info_charp2)
03554         {
03555             size_t      charp_len;
03556 
03557             charp_len = strlen(completion_info_charp2);
03558             e_info_charp2 = pg_malloc(charp_len * 2 + 1);
03559             PQescapeString(e_info_charp2, completion_info_charp2,
03560                            charp_len);
03561         }
03562         else
03563             e_info_charp2 = NULL;
03564 
03565         initPQExpBuffer(&query_buffer);
03566 
03567         if (is_schema_query)
03568         {
03569             /* completion_squery gives us the pieces to assemble */
03570             const char *qualresult = completion_squery->qualresult;
03571 
03572             if (qualresult == NULL)
03573                 qualresult = completion_squery->result;
03574 
03575             /* Get unqualified names matching the input-so-far */
03576             appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
03577                               completion_squery->result,
03578                               completion_squery->catname);
03579             if (completion_squery->selcondition)
03580                 appendPQExpBuffer(&query_buffer, "%s AND ",
03581                                   completion_squery->selcondition);
03582             appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
03583                               completion_squery->result,
03584                               string_length, e_text);
03585             appendPQExpBuffer(&query_buffer, " AND %s",
03586                               completion_squery->viscondition);
03587 
03588             /*
03589              * When fetching relation names, suppress system catalogs unless
03590              * the input-so-far begins with "pg_".  This is a compromise
03591              * between not offering system catalogs for completion at all, and
03592              * having them swamp the result when the input is just "p".
03593              */
03594             if (strcmp(completion_squery->catname,
03595                        "pg_catalog.pg_class c") == 0 &&
03596                 strncmp(text, "pg_", 3) !=0)
03597             {
03598                 appendPQExpBuffer(&query_buffer,
03599                                   " AND c.relnamespace <> (SELECT oid FROM"
03600                    " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
03601             }
03602 
03603             /*
03604              * Add in matching schema names, but only if there is more than
03605              * one potential match among schema names.
03606              */
03607             appendPQExpBuffer(&query_buffer, "\nUNION\n"
03608                            "SELECT pg_catalog.quote_ident(n.nspname) || '.' "
03609                               "FROM pg_catalog.pg_namespace n "
03610                               "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'",
03611                               string_length, e_text);
03612             appendPQExpBuffer(&query_buffer,
03613                               " AND (SELECT pg_catalog.count(*)"
03614                               " FROM pg_catalog.pg_namespace"
03615             " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
03616                               " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1",
03617                               string_length, e_text);
03618 
03619             /*
03620              * Add in matching qualified names, but only if there is exactly
03621              * one schema matching the input-so-far.
03622              */
03623             appendPQExpBuffer(&query_buffer, "\nUNION\n"
03624                      "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
03625                               "FROM %s, pg_catalog.pg_namespace n "
03626                               "WHERE %s = n.oid AND ",
03627                               qualresult,
03628                               completion_squery->catname,
03629                               completion_squery->namespace);
03630             if (completion_squery->selcondition)
03631                 appendPQExpBuffer(&query_buffer, "%s AND ",
03632                                   completion_squery->selcondition);
03633             appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
03634                               qualresult,
03635                               string_length, e_text);
03636 
03637             /*
03638              * This condition exploits the single-matching-schema rule to
03639              * speed up the query
03640              */
03641             appendPQExpBuffer(&query_buffer,
03642             " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="
03643                               " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)",
03644                               string_length, e_text);
03645             appendPQExpBuffer(&query_buffer,
03646                               " AND (SELECT pg_catalog.count(*)"
03647                               " FROM pg_catalog.pg_namespace"
03648             " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
03649                               " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1",
03650                               string_length, e_text);
03651 
03652             /* If an addon query was provided, use it */
03653             if (completion_charp)
03654                 appendPQExpBuffer(&query_buffer, "\n%s", completion_charp);
03655         }
03656         else
03657         {
03658             /* completion_charp is an sprintf-style format string */
03659             appendPQExpBuffer(&query_buffer, completion_charp,
03660                               string_length, e_text,
03661                               e_info_charp, e_info_charp,
03662                               e_info_charp2, e_info_charp2);
03663         }
03664 
03665         /* Limit the number of records in the result */
03666         appendPQExpBuffer(&query_buffer, "\nLIMIT %d",
03667                           completion_max_records);
03668 
03669         result = exec_query(query_buffer.data);
03670 
03671         termPQExpBuffer(&query_buffer);
03672         free(e_text);
03673         if (e_info_charp)
03674             free(e_info_charp);
03675         if (e_info_charp2)
03676             free(e_info_charp2);
03677     }
03678 
03679     /* Find something that matches */
03680     if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
03681     {
03682         const char *item;
03683 
03684         while (list_index < PQntuples(result) &&
03685                (item = PQgetvalue(result, list_index++, 0)))
03686             if (pg_strncasecmp(text, item, string_length) == 0)
03687                 return pg_strdup(item);
03688     }
03689 
03690     /* If nothing matches, free the db structure and return null */
03691     PQclear(result);
03692     result = NULL;
03693     return NULL;
03694 }
03695 
03696 
03697 /*
03698  * This function returns in order one of a fixed, NULL pointer terminated list
03699  * of strings (if matching). This can be used if there are only a fixed number
03700  * SQL words that can appear at certain spot.
03701  */
03702 static char *
03703 complete_from_list(const char *text, int state)
03704 {
03705     static int  string_length,
03706                 list_index,
03707                 matches;
03708     static bool casesensitive;
03709     const char *item;
03710 
03711     /* need to have a list */
03712     Assert(completion_charpp != NULL);
03713 
03714     /* Initialization */
03715     if (state == 0)
03716     {
03717         list_index = 0;
03718         string_length = strlen(text);
03719         casesensitive = completion_case_sensitive;
03720         matches = 0;
03721     }
03722 
03723     while ((item = completion_charpp[list_index++]))
03724     {
03725         /* First pass is case sensitive */
03726         if (casesensitive && strncmp(text, item, string_length) == 0)
03727         {
03728             matches++;
03729             return pg_strdup(item);
03730         }
03731 
03732         /* Second pass is case insensitive, don't bother counting matches */
03733         if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
03734         {
03735             if (completion_case_sensitive)
03736                 return pg_strdup(item);
03737             else
03738 
03739                 /*
03740                  * If case insensitive matching was requested initially,
03741                  * adjust the case according to setting.
03742                  */
03743                 return pg_strdup_keyword_case(item, text);
03744         }
03745     }
03746 
03747     /*
03748      * No matches found. If we're not case insensitive already, lets switch to
03749      * being case insensitive and try again
03750      */
03751     if (casesensitive && matches == 0)
03752     {
03753         casesensitive = false;
03754         list_index = 0;
03755         state++;
03756         return complete_from_list(text, state);
03757     }
03758 
03759     /* If no more matches, return null. */
03760     return NULL;
03761 }
03762 
03763 
03764 /*
03765  * This function returns one fixed string the first time even if it doesn't
03766  * match what's there, and nothing the second time. This should be used if
03767  * there is only one possibility that can appear at a certain spot, so
03768  * misspellings will be overwritten.  The string to be passed must be in
03769  * completion_charp.
03770  */
03771 static char *
03772 complete_from_const(const char *text, int state)
03773 {
03774     Assert(completion_charp != NULL);
03775     if (state == 0)
03776     {
03777         if (completion_case_sensitive)
03778             return pg_strdup(completion_charp);
03779         else
03780 
03781             /*
03782              * If case insensitive matching was requested initially, adjust
03783              * the case according to setting.
03784              */
03785             return pg_strdup_keyword_case(completion_charp, text);
03786     }
03787     else
03788         return NULL;
03789 }
03790 
03791 
03792 /*
03793  * This function supports completion with the name of a psql variable.
03794  * The variable names can be prefixed and suffixed with additional text
03795  * to support quoting usages.
03796  */
03797 static char **
03798 complete_from_variables(char *text, const char *prefix, const char *suffix)
03799 {
03800     char      **matches;
03801     int         overhead = strlen(prefix) + strlen(suffix) + 1;
03802     char      **varnames;
03803     int         nvars = 0;
03804     int         maxvars = 100;
03805     int         i;
03806     struct _variable *ptr;
03807 
03808     varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *));
03809 
03810     for (ptr = pset.vars->next; ptr; ptr = ptr->next)
03811     {
03812         char       *buffer;
03813 
03814         if (nvars >= maxvars)
03815         {
03816             maxvars *= 2;
03817             varnames = (char **) realloc(varnames,
03818                                          (maxvars + 1) * sizeof(char *));
03819             if (!varnames)
03820             {
03821                 psql_error("out of memory\n");
03822                 exit(EXIT_FAILURE);
03823             }
03824         }
03825 
03826         buffer = (char *) pg_malloc(strlen(ptr->name) + overhead);
03827         sprintf(buffer, "%s%s%s", prefix, ptr->name, suffix);
03828         varnames[nvars++] = buffer;
03829     }
03830 
03831     varnames[nvars] = NULL;
03832     COMPLETE_WITH_LIST_CS((const char *const *) varnames);
03833 
03834     for (i = 0; i < nvars; i++)
03835         free(varnames[i]);
03836     free(varnames);
03837 
03838     return matches;
03839 }
03840 
03841 
03842 /*
03843  * This function wraps rl_filename_completion_function() to strip quotes from
03844  * the input before searching for matches and to quote any matches for which
03845  * the consuming command will require it.
03846  */
03847 static char *
03848 complete_from_files(const char *text, int state)
03849 {
03850     static const char *unquoted_text;
03851     char       *unquoted_match;
03852     char       *ret = NULL;
03853 
03854     if (state == 0)
03855     {
03856         /* Initialization: stash the unquoted input. */
03857         unquoted_text = strtokx(text, "", NULL, "'", *completion_charp,
03858                                 false, true, pset.encoding);
03859         /* expect a NULL return for the empty string only */
03860         if (!unquoted_text)
03861         {
03862             Assert(*text == '\0');
03863             unquoted_text = text;
03864         }
03865     }
03866 
03867     unquoted_match = filename_completion_function(unquoted_text, state);
03868     if (unquoted_match)
03869     {
03870         /*
03871          * Caller sets completion_charp to a zero- or one-character string
03872          * containing the escape character.  This is necessary since \copy has
03873          * no escape character, but every other backslash command recognizes
03874          * "\" as an escape character.  Since we have only two callers, don't
03875          * bother providing a macro to simplify this.
03876          */
03877         ret = quote_if_needed(unquoted_match, " \t\r\n\"`",
03878                               '\'', *completion_charp, pset.encoding);
03879         if (ret)
03880             free(unquoted_match);
03881         else
03882             ret = unquoted_match;
03883     }
03884 
03885     return ret;
03886 }
03887 
03888 
03889 /* HELPER FUNCTIONS */
03890 
03891 
03892 /*
03893  * Make a pg_strdup copy of s and convert the case according to
03894  * COMP_KEYWORD_CASE variable, using ref as the text that was already entered.
03895  */
03896 static char *
03897 pg_strdup_keyword_case(const char *s, const char *ref)
03898 {
03899     char       *ret,
03900                *p;
03901     unsigned char first = ref[0];
03902     int         tocase;
03903     const char *varval;
03904 
03905     varval = GetVariable(pset.vars, "COMP_KEYWORD_CASE");
03906     if (!varval)
03907         tocase = 0;
03908     else if (strcmp(varval, "lower") == 0)
03909         tocase = -2;
03910     else if (strcmp(varval, "preserve-lower") == 0)
03911         tocase = -1;
03912     else if (strcmp(varval, "preserve-upper") == 0)
03913         tocase = +1;
03914     else if (strcmp(varval, "upper") == 0)
03915         tocase = +2;
03916     else
03917         tocase = 0;
03918 
03919     /* default */
03920     if (tocase == 0)
03921         tocase = +1;
03922 
03923     ret = pg_strdup(s);
03924 
03925     if (tocase == -2
03926         || ((tocase == -1 || tocase == +1) && islower(first))
03927         || (tocase == -1 && !isalpha(first))
03928         )
03929         for (p = ret; *p; p++)
03930             *p = pg_tolower((unsigned char) *p);
03931     else
03932         for (p = ret; *p; p++)
03933             *p = pg_toupper((unsigned char) *p);
03934 
03935     return ret;
03936 }
03937 
03938 
03939 /*
03940  * Execute a query and report any errors. This should be the preferred way of
03941  * talking to the database in this file.
03942  */
03943 static PGresult *
03944 exec_query(const char *query)
03945 {
03946     PGresult   *result;
03947 
03948     if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK)
03949         return NULL;
03950 
03951     result = PQexec(pset.db, query);
03952 
03953     if (PQresultStatus(result) != PGRES_TUPLES_OK)
03954     {
03955 #ifdef NOT_USED
03956         psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
03957                    PQerrorMessage(pset.db), query);
03958 #endif
03959         PQclear(result);
03960         result = NULL;
03961     }
03962 
03963     return result;
03964 }
03965 
03966 
03967 /*
03968  * Return the nwords word(s) before point.  Words are returned right to left,
03969  * that is, previous_words[0] gets the last word before point.
03970  * If we run out of words, remaining array elements are set to empty strings.
03971  * Each array element is filled with a malloc'd string.
03972  */
03973 static void
03974 get_previous_words(int point, char **previous_words, int nwords)
03975 {
03976     const char *buf = rl_line_buffer;   /* alias */
03977     int         i;
03978 
03979     /* first we look for a non-word char before the current point */
03980     for (i = point - 1; i >= 0; i--)
03981         if (strchr(WORD_BREAKS, buf[i]))
03982             break;
03983     point = i;
03984 
03985     while (nwords-- > 0)
03986     {
03987         int         start,
03988                     end;
03989         char       *s;
03990 
03991         /* now find the first non-space which then constitutes the end */
03992         end = -1;
03993         for (i = point; i >= 0; i--)
03994         {
03995             if (!isspace((unsigned char) buf[i]))
03996             {
03997                 end = i;
03998                 break;
03999             }
04000         }
04001 
04002         /*
04003          * If no end found we return an empty string, because there is no word
04004          * before the point
04005          */
04006         if (end < 0)
04007         {
04008             point = end;
04009             s = pg_strdup("");
04010         }
04011         else
04012         {
04013             /*
04014              * Otherwise we now look for the start. The start is either the
04015              * last character before any word-break character going backwards
04016              * from the end, or it's simply character 0. We also handle open
04017              * quotes and parentheses.
04018              */
04019             bool        inquotes = false;
04020             int         parentheses = 0;
04021 
04022             for (start = end; start > 0; start--)
04023             {
04024                 if (buf[start] == '"')
04025                     inquotes = !inquotes;
04026                 if (!inquotes)
04027                 {
04028                     if (buf[start] == ')')
04029                         parentheses++;
04030                     else if (buf[start] == '(')
04031                     {
04032                         if (--parentheses <= 0)
04033                             break;
04034                     }
04035                     else if (parentheses == 0 &&
04036                              strchr(WORD_BREAKS, buf[start - 1]))
04037                         break;
04038                 }
04039             }
04040 
04041             point = start - 1;
04042 
04043             /* make a copy of chars from start to end inclusive */
04044             s = pg_malloc(end - start + 2);
04045             strlcpy(s, &buf[start], end - start + 2);
04046         }
04047 
04048         *previous_words++ = s;
04049     }
04050 }
04051 
04052 #ifdef NOT_USED
04053 
04054 /*
04055  * Surround a string with single quotes. This works for both SQL and
04056  * psql internal. Currently disabled because it is reported not to
04057  * cooperate with certain versions of readline.
04058  */
04059 static char *
04060 quote_file_name(char *text, int match_type, char *quote_pointer)
04061 {
04062     char       *s;
04063     size_t      length;
04064 
04065     (void) quote_pointer;       /* not used */
04066 
04067     length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2);
04068     s = pg_malloc(length);
04069     s[0] = '\'';
04070     strcpy(s + 1, text);
04071     if (match_type == SINGLE_MATCH)
04072         s[length - 2] = '\'';
04073     s[length - 1] = '\0';
04074     return s;
04075 }
04076 
04077 static char *
04078 dequote_file_name(char *text, char quote_char)
04079 {
04080     char       *s;
04081     size_t      length;
04082 
04083     if (!quote_char)
04084         return pg_strdup(text);
04085 
04086     length = strlen(text);
04087     s = pg_malloc(length - 2 + 1);
04088     strlcpy(s, text +1, length - 2 + 1);
04089 
04090     return s;
04091 }
04092 #endif   /* NOT_USED */
04093 
04094 #endif   /* USE_READLINE */