]> granicus.if.org Git - postgresql/blob - src/bin/psql/tab-complete.c
More psql tab-completion for new commands.
[postgresql] / src / bin / psql / tab-complete.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2011, PostgreSQL Global Development Group
5  *
6  * src/bin/psql/tab-complete.c
7  */
8
9 /*----------------------------------------------------------------------
10  * This file implements a somewhat more sophisticated readline "TAB
11  * completion" in psql. It is not intended to be AI, to replace
12  * learning SQL, or to relieve you from thinking about what you're
13  * doing. Also it does not always give you all the syntactically legal
14  * completions, only those that are the most common or the ones that
15  * the programmer felt most like implementing.
16  *
17  * CAVEAT: Tab completion causes queries to be sent to the backend.
18  * The number of tuples returned gets limited, in most default
19  * installations to 1000, but if you still don't like this prospect,
20  * you can turn off tab completion in your ~/.inputrc (or else
21  * ${INPUTRC}) file so:
22  *
23  *       $if psql
24  *       set disable-completion on
25  *       $endif
26  *
27  * See `man 3 readline' or `info readline' for the full details. Also,
28  * hence the
29  *
30  * BUGS:
31  *
32  * - If you split your queries across lines, this whole thing gets
33  *       confused. (To fix this, one would have to read psql's query
34  *       buffer rather than readline's line buffer, which would require
35  *       some major revisions of things.)
36  *
37  * - Table or attribute names with spaces in it may confuse it.
38  *
39  * - Quotes, parenthesis, and other funny characters are not handled
40  *       all that gracefully.
41  *----------------------------------------------------------------------
42  */
43
44 #include "postgres_fe.h"
45 #include "tab-complete.h"
46 #include "input.h"
47
48 /* If we don't have this, we might as well forget about the whole thing: */
49 #ifdef USE_READLINE
50
51 #include <ctype.h>
52 #include "libpq-fe.h"
53 #include "pqexpbuffer.h"
54 #include "common.h"
55 #include "settings.h"
56 #include "stringutils.h"
57
58 #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
59 #define filename_completion_function rl_filename_completion_function
60 #else
61 /* missing in some header files */
62 extern char *filename_completion_function();
63 #endif
64
65 #ifdef HAVE_RL_COMPLETION_MATCHES
66 #define completion_matches rl_completion_matches
67 #endif
68
69 /* word break characters */
70 #define WORD_BREAKS             "\t\n@$><=;|&{() "
71
72 /*
73  * This struct is used to define "schema queries", which are custom-built
74  * to obtain possibly-schema-qualified names of database objects.  There is
75  * enough similarity in the structure that we don't want to repeat it each
76  * time.  So we put the components of each query into this struct and
77  * assemble them with the common boilerplate in _complete_from_query().
78  */
79 typedef struct SchemaQuery
80 {
81         /*
82          * Name of catalog or catalogs to be queried, with alias, eg.
83          * "pg_catalog.pg_class c".  Note that "pg_namespace n" will be added.
84          */
85         const char *catname;
86
87         /*
88          * Selection condition --- only rows meeting this condition are candidates
89          * to display.  If catname mentions multiple tables, include the necessary
90          * join condition here.  For example, "c.relkind = 'r'". Write NULL (not
91          * an empty string) if not needed.
92          */
93         const char *selcondition;
94
95         /*
96          * Visibility condition --- which rows are visible without schema
97          * qualification?  For example, "pg_catalog.pg_table_is_visible(c.oid)".
98          */
99         const char *viscondition;
100
101         /*
102          * Namespace --- name of field to join to pg_namespace.oid. For example,
103          * "c.relnamespace".
104          */
105         const char *namespace;
106
107         /*
108          * Result --- the appropriately-quoted name to return, in the case of an
109          * unqualified name.  For example, "pg_catalog.quote_ident(c.relname)".
110          */
111         const char *result;
112
113         /*
114          * In some cases a different result must be used for qualified names.
115          * Enter that here, or write NULL if result can be used.
116          */
117         const char *qualresult;
118 } SchemaQuery;
119
120
121 /* Store maximum number of records we want from database queries
122  * (implemented via SELECT ... LIMIT xx).
123  */
124 static int      completion_max_records;
125
126 /*
127  * Communication variables set by COMPLETE_WITH_FOO macros and then used by
128  * the completion callback functions.  Ugly but there is no better way.
129  */
130 static const char *completion_charp;    /* to pass a string */
131 static const char *const * completion_charpp;   /* to pass a list of strings */
132 static const char *completion_info_charp;               /* to pass a second string */
133 static const char *completion_info_charp2;              /* to pass a third string */
134 static const SchemaQuery *completion_squery;    /* to pass a SchemaQuery */
135
136 /*
137  * A few macros to ease typing. You can use these to complete the given
138  * string with
139  * 1) The results from a query you pass it. (Perhaps one of those below?)
140  * 2) The results from a schema query you pass it.
141  * 3) The items from a null-pointer-terminated list.
142  * 4) A string constant.
143  * 5) The list of attributes of the given table (possibly schema-qualified).
144  */
145 #define COMPLETE_WITH_QUERY(query) \
146 do { \
147         completion_charp = query; \
148         matches = completion_matches(text, complete_from_query); \
149 } while (0)
150
151 #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
152 do { \
153         completion_squery = &(query); \
154         completion_charp = addon; \
155         matches = completion_matches(text, complete_from_schema_query); \
156 } while (0)
157
158 #define COMPLETE_WITH_LIST(list) \
159 do { \
160         completion_charpp = list; \
161         matches = completion_matches(text, complete_from_list); \
162 } while (0)
163
164 #define COMPLETE_WITH_CONST(string) \
165 do { \
166         completion_charp = string; \
167         matches = completion_matches(text, complete_from_const); \
168 } while (0)
169
170 #define COMPLETE_WITH_ATTR(relation, addon) \
171 do { \
172         char   *_completion_schema; \
173         char   *_completion_table; \
174 \
175         _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \
176                                                                  false, false, pset.encoding); \
177         (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
178                                    false, false, pset.encoding); \
179         _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
180                                                                 false, false, pset.encoding); \
181         if (_completion_table == NULL) \
182         { \
183                 completion_charp = Query_for_list_of_attributes  addon; \
184                 completion_info_charp = relation; \
185         } \
186         else \
187         { \
188                 completion_charp = Query_for_list_of_attributes_with_schema  addon; \
189                 completion_info_charp = _completion_table; \
190                 completion_info_charp2 = _completion_schema; \
191         } \
192         matches = completion_matches(text, complete_from_query); \
193 } while (0)
194
195 /*
196  * Assembly instructions for schema queries
197  */
198
199 static const SchemaQuery Query_for_list_of_aggregates = {
200         /* catname */
201         "pg_catalog.pg_proc p",
202         /* selcondition */
203         "p.proisagg",
204         /* viscondition */
205         "pg_catalog.pg_function_is_visible(p.oid)",
206         /* namespace */
207         "p.pronamespace",
208         /* result */
209         "pg_catalog.quote_ident(p.proname)",
210         /* qualresult */
211         NULL
212 };
213
214 static const SchemaQuery Query_for_list_of_datatypes = {
215         /* catname */
216         "pg_catalog.pg_type t",
217         /* selcondition --- ignore table rowtypes and array types */
218         "(t.typrelid = 0 "
219         " OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
220         "AND t.typname !~ '^_'",
221         /* viscondition */
222         "pg_catalog.pg_type_is_visible(t.oid)",
223         /* namespace */
224         "t.typnamespace",
225         /* result */
226         "pg_catalog.format_type(t.oid, NULL)",
227         /* qualresult */
228         "pg_catalog.quote_ident(t.typname)"
229 };
230
231 static const SchemaQuery Query_for_list_of_domains = {
232         /* catname */
233         "pg_catalog.pg_type t",
234         /* selcondition */
235         "t.typtype = 'd'",
236         /* viscondition */
237         "pg_catalog.pg_type_is_visible(t.oid)",
238         /* namespace */
239         "t.typnamespace",
240         /* result */
241         "pg_catalog.quote_ident(t.typname)",
242         /* qualresult */
243         NULL
244 };
245
246 static const SchemaQuery Query_for_list_of_functions = {
247         /* catname */
248         "pg_catalog.pg_proc p",
249         /* selcondition */
250         NULL,
251         /* viscondition */
252         "pg_catalog.pg_function_is_visible(p.oid)",
253         /* namespace */
254         "p.pronamespace",
255         /* result */
256         "pg_catalog.quote_ident(p.proname)",
257         /* qualresult */
258         NULL
259 };
260
261 static const SchemaQuery Query_for_list_of_indexes = {
262         /* catname */
263         "pg_catalog.pg_class c",
264         /* selcondition */
265         "c.relkind IN ('i')",
266         /* viscondition */
267         "pg_catalog.pg_table_is_visible(c.oid)",
268         /* namespace */
269         "c.relnamespace",
270         /* result */
271         "pg_catalog.quote_ident(c.relname)",
272         /* qualresult */
273         NULL
274 };
275
276 static const SchemaQuery Query_for_list_of_sequences = {
277         /* catname */
278         "pg_catalog.pg_class c",
279         /* selcondition */
280         "c.relkind IN ('S')",
281         /* viscondition */
282         "pg_catalog.pg_table_is_visible(c.oid)",
283         /* namespace */
284         "c.relnamespace",
285         /* result */
286         "pg_catalog.quote_ident(c.relname)",
287         /* qualresult */
288         NULL
289 };
290
291 static const SchemaQuery Query_for_list_of_foreign_tables = {
292         /* catname */
293         "pg_catalog.pg_class c",
294         /* selcondition */
295         "c.relkind IN ('f')",
296         /* viscondition */
297         "pg_catalog.pg_table_is_visible(c.oid)",
298         /* namespace */
299         "c.relnamespace",
300         /* result */
301         "pg_catalog.quote_ident(c.relname)",
302         /* qualresult */
303         NULL
304 };
305
306 static const SchemaQuery Query_for_list_of_tables = {
307         /* catname */
308         "pg_catalog.pg_class c",
309         /* selcondition */
310         "c.relkind IN ('r')",
311         /* viscondition */
312         "pg_catalog.pg_table_is_visible(c.oid)",
313         /* namespace */
314         "c.relnamespace",
315         /* result */
316         "pg_catalog.quote_ident(c.relname)",
317         /* qualresult */
318         NULL
319 };
320
321 /* The bit masks for the following three functions come from
322  * src/include/catalog/pg_trigger.h.
323  */
324 static const SchemaQuery Query_for_list_of_insertables = {
325         /* catname */
326         "pg_catalog.pg_class c",
327         /* selcondition */
328         "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
329         "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 2) <> 0)))",
330         /* viscondition */
331         "pg_catalog.pg_table_is_visible(c.oid)",
332         /* namespace */
333         "c.relnamespace",
334         /* result */
335         "pg_catalog.quote_ident(c.relname)",
336         /* qualresult */
337         NULL
338 };
339
340 static const SchemaQuery Query_for_list_of_deletables = {
341         /* catname */
342         "pg_catalog.pg_class c",
343         /* selcondition */
344         "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
345         "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 3) <> 0)))",
346         /* viscondition */
347         "pg_catalog.pg_table_is_visible(c.oid)",
348         /* namespace */
349         "c.relnamespace",
350         /* result */
351         "pg_catalog.quote_ident(c.relname)",
352         /* qualresult */
353         NULL
354 };
355
356 static const SchemaQuery Query_for_list_of_updatables = {
357         /* catname */
358         "pg_catalog.pg_class c",
359         /* selcondition */
360         "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
361         "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 4) <> 0)))",
362         /* viscondition */
363         "pg_catalog.pg_table_is_visible(c.oid)",
364         /* namespace */
365         "c.relnamespace",
366         /* result */
367         "pg_catalog.quote_ident(c.relname)",
368         /* qualresult */
369         NULL
370 };
371
372 static const SchemaQuery Query_for_list_of_tisvf = {
373         /* catname */
374         "pg_catalog.pg_class c",
375         /* selcondition */
376         "c.relkind IN ('r', 'i', 'S', 'v', 'f')",
377         /* viscondition */
378         "pg_catalog.pg_table_is_visible(c.oid)",
379         /* namespace */
380         "c.relnamespace",
381         /* result */
382         "pg_catalog.quote_ident(c.relname)",
383         /* qualresult */
384         NULL
385 };
386
387 static const SchemaQuery Query_for_list_of_tsvf = {
388         /* catname */
389         "pg_catalog.pg_class c",
390         /* selcondition */
391         "c.relkind IN ('r', 'S', 'v', 'f')",
392         /* viscondition */
393         "pg_catalog.pg_table_is_visible(c.oid)",
394         /* namespace */
395         "c.relnamespace",
396         /* result */
397         "pg_catalog.quote_ident(c.relname)",
398         /* qualresult */
399         NULL
400 };
401
402 static const SchemaQuery Query_for_list_of_views = {
403         /* catname */
404         "pg_catalog.pg_class c",
405         /* selcondition */
406         "c.relkind IN ('v')",
407         /* viscondition */
408         "pg_catalog.pg_table_is_visible(c.oid)",
409         /* namespace */
410         "c.relnamespace",
411         /* result */
412         "pg_catalog.quote_ident(c.relname)",
413         /* qualresult */
414         NULL
415 };
416
417
418 /*
419  * Queries to get lists of names of various kinds of things, possibly
420  * restricted to names matching a partially entered name.  In these queries,
421  * the first %s will be replaced by the text entered so far (suitably escaped
422  * to become a SQL literal string).  %d will be replaced by the length of the
423  * string (in unescaped form).  A second and third %s, if present, will be
424  * replaced by a suitably-escaped version of the string provided in
425  * completion_info_charp.  A fourth and fifth %s are similarly replaced by
426  * completion_info_charp2.
427  *
428  * Beware that the allowed sequences of %s and %d are determined by
429  * _complete_from_query().
430  */
431
432 #define Query_for_list_of_attributes \
433 "SELECT pg_catalog.quote_ident(attname) "\
434 "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
435 " WHERE c.oid = a.attrelid "\
436 "   AND a.attnum > 0 "\
437 "   AND NOT a.attisdropped "\
438 "   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
439 "   AND (pg_catalog.quote_ident(relname)='%s' "\
440 "        OR '\"' || relname || '\"'='%s') "\
441 "   AND pg_catalog.pg_table_is_visible(c.oid)"
442
443 #define Query_for_list_of_attributes_with_schema \
444 "SELECT pg_catalog.quote_ident(attname) "\
445 "  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
446 " WHERE c.oid = a.attrelid "\
447 "   AND n.oid = c.relnamespace "\
448 "   AND a.attnum > 0 "\
449 "   AND NOT a.attisdropped "\
450 "   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
451 "   AND (pg_catalog.quote_ident(relname)='%s' "\
452 "        OR '\"' || relname || '\"' ='%s') "\
453 "   AND (pg_catalog.quote_ident(nspname)='%s' "\
454 "        OR '\"' || nspname || '\"' ='%s') "
455
456 #define Query_for_list_of_template_databases \
457 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
458 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' AND datistemplate"
459
460 #define Query_for_list_of_databases \
461 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
462 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"
463
464 #define Query_for_list_of_tablespaces \
465 "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\
466 " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"
467
468 #define Query_for_list_of_encodings \
469 " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
470 "   FROM pg_catalog.pg_conversion "\
471 "  WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
472
473 #define Query_for_list_of_languages \
474 "SELECT pg_catalog.quote_ident(lanname) "\
475 "  FROM pg_catalog.pg_language "\
476 " WHERE lanname != 'internal' "\
477 "   AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'"
478
479 #define Query_for_list_of_schemas \
480 "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\
481 " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"
482
483 #define Query_for_list_of_set_vars \
484 "SELECT name FROM "\
485 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
486 "  WHERE context IN ('user', 'superuser') "\
487 "  UNION ALL SELECT 'constraints' "\
488 "  UNION ALL SELECT 'transaction' "\
489 "  UNION ALL SELECT 'session' "\
490 "  UNION ALL SELECT 'role' "\
491 "  UNION ALL SELECT 'tablespace' "\
492 "  UNION ALL SELECT 'all') ss "\
493 " WHERE substring(name,1,%d)='%s'"
494
495 #define Query_for_list_of_show_vars \
496 "SELECT name FROM "\
497 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
498 "  UNION ALL SELECT 'session authorization' "\
499 "  UNION ALL SELECT 'all') ss "\
500 " WHERE substring(name,1,%d)='%s'"
501
502 #define Query_for_list_of_roles \
503 " SELECT pg_catalog.quote_ident(rolname) "\
504 "   FROM pg_catalog.pg_roles "\
505 "  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"
506
507 #define Query_for_list_of_grant_roles \
508 " SELECT pg_catalog.quote_ident(rolname) "\
509 "   FROM pg_catalog.pg_roles "\
510 "  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\
511 " UNION ALL SELECT 'PUBLIC'"
512
513 /* the silly-looking length condition is just to eat up the current word */
514 #define Query_for_table_owning_index \
515 "SELECT pg_catalog.quote_ident(c1.relname) "\
516 "  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
517 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
518 "       and (%d = pg_catalog.length('%s'))"\
519 "       and pg_catalog.quote_ident(c2.relname)='%s'"\
520 "       and pg_catalog.pg_table_is_visible(c2.oid)"
521
522 /* the silly-looking length condition is just to eat up the current word */
523 #define Query_for_index_of_table \
524 "SELECT pg_catalog.quote_ident(c2.relname) "\
525 "  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
526 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
527 "       and (%d = pg_catalog.length('%s'))"\
528 "       and pg_catalog.quote_ident(c1.relname)='%s'"\
529 "       and pg_catalog.pg_table_is_visible(c2.oid)"
530
531 /* the silly-looking length condition is just to eat up the current word */
532 #define Query_for_list_of_tables_for_trigger \
533 "SELECT pg_catalog.quote_ident(relname) "\
534 "  FROM pg_catalog.pg_class"\
535 " WHERE (%d = pg_catalog.length('%s'))"\
536 "   AND oid IN "\
537 "       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
538 "         WHERE pg_catalog.quote_ident(tgname)='%s')"
539
540 #define Query_for_list_of_ts_configurations \
541 "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
542 " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
543
544 #define Query_for_list_of_ts_dictionaries \
545 "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\
546 " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'"
547
548 #define Query_for_list_of_ts_parsers \
549 "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\
550 " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'"
551
552 #define Query_for_list_of_ts_templates \
553 "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\
554 " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'"
555
556 #define Query_for_list_of_fdws \
557 " SELECT pg_catalog.quote_ident(fdwname) "\
558 "   FROM pg_catalog.pg_foreign_data_wrapper "\
559 "  WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'"
560
561 #define Query_for_list_of_servers \
562 " SELECT pg_catalog.quote_ident(srvname) "\
563 "   FROM pg_catalog.pg_foreign_server "\
564 "  WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'"
565
566 #define Query_for_list_of_user_mappings \
567 " SELECT pg_catalog.quote_ident(usename) "\
568 "   FROM pg_catalog.pg_user_mappings "\
569 "  WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"
570
571 #define Query_for_list_of_access_methods \
572 " SELECT pg_catalog.quote_ident(amname) "\
573 "   FROM pg_catalog.pg_am "\
574 "  WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
575
576 #define Query_for_list_of_arguments \
577 " SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
578 "   FROM pg_catalog.pg_proc "\
579 "  WHERE proname='%s'"
580
581 #define Query_for_list_of_extensions \
582 " SELECT pg_catalog.quote_ident(extname) "\
583 "   FROM pg_catalog.pg_extension "\
584 "  WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'"
585
586 #define Query_for_list_of_available_extensions \
587 " SELECT pg_catalog.quote_ident(name) "\
588 "   FROM pg_catalog.pg_available_extensions "\
589 "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL"
590
591 /*
592  * This is a list of all "things" in Pgsql, which can show up after CREATE or
593  * DROP; and there is also a query to get a list of them.
594  */
595
596 typedef struct
597 {
598         const char *name;
599         const char *query;                      /* simple query, or NULL */
600         const SchemaQuery *squery;      /* schema query, or NULL */
601         const bits32 flags;                     /* visibility flags, see below */
602 } pgsql_thing_t;
603
604 #define THING_NO_CREATE         (1 << 0)        /* should not show up after CREATE */
605 #define THING_NO_DROP           (1 << 1)        /* should not show up after DROP */
606 #define THING_NO_SHOW           (THING_NO_CREATE | THING_NO_DROP)
607
608 static const pgsql_thing_t words_after_create[] = {
609         {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
610         {"CAST", NULL, NULL},           /* Casts have complex structures for names, so
611                                                                  * skip it */
612         {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding = pg_char_to_encoding(getdatabaseencoding()) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"},
613
614         /*
615          * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
616          * to be used only by pg_dump.
617          */
618         {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
619         {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
620         {"DATABASE", Query_for_list_of_databases},
621         {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW},
622         {"DOMAIN", NULL, &Query_for_list_of_domains},
623         {"EXTENSION", Query_for_list_of_extensions},
624         {"FOREIGN DATA WRAPPER", NULL, NULL},
625         {"FOREIGN TABLE", NULL, NULL},
626         {"FUNCTION", NULL, &Query_for_list_of_functions},
627         {"GROUP", Query_for_list_of_roles},
628         {"LANGUAGE", Query_for_list_of_languages},
629         {"INDEX", NULL, &Query_for_list_of_indexes},
630         {"OPERATOR", NULL, NULL},       /* Querying for this is probably not such a
631                                                                  * good idea. */
632         {"OWNED", NULL, NULL, THING_NO_CREATE}, /* for DROP OWNED BY ... */
633         {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
634         {"ROLE", Query_for_list_of_roles},
635         {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
636         {"SCHEMA", Query_for_list_of_schemas},
637         {"SEQUENCE", NULL, &Query_for_list_of_sequences},
638         {"SERVER", Query_for_list_of_servers},
639         {"TABLE", NULL, &Query_for_list_of_tables},
640         {"TABLESPACE", Query_for_list_of_tablespaces},
641         {"TEMP", NULL, NULL, THING_NO_DROP},    /* for CREATE TEMP TABLE ... */
642         {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
643         {"TEXT SEARCH", NULL, NULL},
644         {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"},
645         {"TYPE", NULL, &Query_for_list_of_datatypes},
646         {"UNIQUE", NULL, NULL, THING_NO_DROP},  /* for CREATE UNIQUE INDEX ... */
647         {"UNLOGGED", NULL, NULL, THING_NO_DROP},/* for CREATE UNLOGGED TABLE ... */
648         {"USER", Query_for_list_of_roles},
649         {"USER MAPPING FOR", NULL, NULL},
650         {"VIEW", NULL, &Query_for_list_of_views},
651         {NULL}  /* end of list */
652 };
653
654
655 /* Forward declaration of functions */
656 static char **psql_completion(char *text, int start, int end);
657 static char *create_command_generator(const char *text, int state);
658 static char *drop_command_generator(const char *text, int state);
659 static char *complete_from_query(const char *text, int state);
660 static char *complete_from_schema_query(const char *text, int state);
661 static char *_complete_from_query(int is_schema_query,
662                                          const char *text, int state);
663 static char *complete_from_list(const char *text, int state);
664 static char *complete_from_const(const char *text, int state);
665 static char **complete_from_variables(char *text,
666                                                                           const char *prefix, const char *suffix);
667
668 static PGresult *exec_query(const char *query);
669
670 static char *previous_word(int point, int skip);
671
672 #ifdef NOT_USED
673 static char *quote_file_name(char *text, int match_type, char *quote_pointer);
674 static char *dequote_file_name(char *text, char quote_char);
675 #endif
676
677
678 /*
679  * Initialize the readline library for our purposes.
680  */
681 void
682 initialize_readline(void)
683 {
684         rl_readline_name = (char *) pset.progname;
685         rl_attempted_completion_function = (void *) psql_completion;
686
687         rl_basic_word_break_characters = WORD_BREAKS;
688
689         completion_max_records = 1000;
690
691         /*
692          * There is a variable rl_completion_query_items for this but apparently
693          * it's not defined everywhere.
694          */
695 }
696
697
698 /*
699  * The completion function.
700  *
701  * According to readline spec this gets passed the text entered so far and its
702  * start and end positions in the readline buffer. The return value is some
703  * partially obscure list format that can be generated by readline's
704  * completion_matches() function, so we don't have to worry about it.
705  */
706 static char **
707 psql_completion(char *text, int start, int end)
708 {
709         /* This is the variable we'll return. */
710         char      **matches = NULL;
711
712         /* These are going to contain some scannage of the input line. */
713         char       *prev_wd,
714                            *prev2_wd,
715                            *prev3_wd,
716                            *prev4_wd,
717                            *prev5_wd;
718
719         static const char *const sql_commands[] = {
720                 "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
721                 "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
722                 "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
723                 "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
724                 "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
725                 "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
726                 "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
727                 NULL
728         };
729
730         static const char *const backslash_commands[] = {
731                 "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
732                 "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
733                 "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
734                 "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
735                 "\\e", "\\echo", "\\ef", "\\encoding",
736                 "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
737                 "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
738                 "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
739                 "\\set", "\\sf", "\\t", "\\T",
740                 "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
741         };
742
743         (void) end;                                     /* not used */
744
745 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
746         rl_completion_append_character = ' ';
747 #endif
748
749         /* Clear a few things. */
750         completion_charp = NULL;
751         completion_charpp = NULL;
752         completion_info_charp = NULL;
753         completion_info_charp2 = NULL;
754
755         /*
756          * Scan the input line before our current position for the last five
757          * words. According to those we'll make some smart decisions on what the
758          * user is probably intending to type. TODO: Use strtokx() to do this.
759          */
760         prev_wd = previous_word(start, 0);
761         prev2_wd = previous_word(start, 1);
762         prev3_wd = previous_word(start, 2);
763         prev4_wd = previous_word(start, 3);
764         prev5_wd = previous_word(start, 4);
765
766         /* If a backslash command was started, continue */
767         if (text[0] == '\\')
768                 COMPLETE_WITH_LIST(backslash_commands);
769
770         /* Variable interpolation */
771         else if (text[0] == ':' && text[1] != ':')
772         {
773                 if (text[1] == '\'')
774                         matches = complete_from_variables(text, ":'", "'");
775                 else if (text[1] == '"')
776                         matches = complete_from_variables(text, ":\"", "\"");
777                 else
778                         matches = complete_from_variables(text, ":", "");
779         }
780
781         /* If no previous word, suggest one of the basic sql commands */
782         else if (!prev_wd)
783                 COMPLETE_WITH_LIST(sql_commands);
784
785 /* CREATE */
786         /* complete with something you can create */
787         else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
788                 matches = completion_matches(text, create_command_generator);
789
790 /* DROP, but watch out for DROP embedded in other commands */
791         /* complete with something you can drop */
792         else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
793                          pg_strcasecmp(prev2_wd, "DROP") == 0)
794                 matches = completion_matches(text, drop_command_generator);
795
796 /* ALTER */
797
798         /*
799          * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're
800          * in ALTER TABLE sth ALTER
801          */
802         else if (pg_strcasecmp(prev_wd, "ALTER") == 0 &&
803                          pg_strcasecmp(prev3_wd, "TABLE") != 0)
804         {
805                 static const char *const list_ALTER[] =
806                 {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
807                  "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
808                  "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
809                  "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
810                  "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
811                  "USER", "USER MAPPING FOR", "VIEW", NULL};
812
813                 COMPLETE_WITH_LIST(list_ALTER);
814         }
815         /* ALTER AGGREGATE,FUNCTION <name> */
816         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
817                          (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
818                           pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
819                 COMPLETE_WITH_CONST("(");
820         /* ALTER AGGREGATE,FUNCTION <name> (...) */
821         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
822                          (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
823                           pg_strcasecmp(prev3_wd, "FUNCTION") == 0))
824         {
825                 if (prev_wd[strlen(prev_wd) - 1] == ')')
826                 {
827                         static const char *const list_ALTERAGG[] =
828                         {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
829
830                         COMPLETE_WITH_LIST(list_ALTERAGG);
831                 }
832                 else
833                 {
834                         char       *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
835
836                         sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
837                         COMPLETE_WITH_QUERY(tmp_buf);
838                         free(tmp_buf);
839                 }
840         }
841
842         /* ALTER SCHEMA <name> */
843         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
844                          pg_strcasecmp(prev2_wd, "SCHEMA") == 0)
845         {
846                 static const char *const list_ALTERGEN[] =
847                 {"OWNER TO", "RENAME TO", NULL};
848
849                 COMPLETE_WITH_LIST(list_ALTERGEN);
850         }
851
852         /* ALTER COLLATION <name> */
853         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
854                          pg_strcasecmp(prev2_wd, "COLLATION") == 0)
855         {
856                 static const char *const list_ALTERGEN[] =
857                 {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
858
859                 COMPLETE_WITH_LIST(list_ALTERGEN);
860         }
861
862         /* ALTER CONVERSION <name> */
863         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
864                          pg_strcasecmp(prev2_wd, "CONVERSION") == 0)
865         {
866                 static const char *const list_ALTERGEN[] =
867                 {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
868
869                 COMPLETE_WITH_LIST(list_ALTERGEN);
870         }
871
872         /* ALTER DATABASE <name> */
873         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
874                          pg_strcasecmp(prev2_wd, "DATABASE") == 0)
875         {
876                 static const char *const list_ALTERDATABASE[] =
877                 {"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};
878
879                 COMPLETE_WITH_LIST(list_ALTERDATABASE);
880         }
881
882         /* ALTER EXTENSION <name> */
883         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
884                          pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
885         {
886                 static const char *const list_ALTEREXTENSION[] =
887                 {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL};
888
889                 COMPLETE_WITH_LIST(list_ALTEREXTENSION);
890         }
891
892         /* ALTER FOREIGN */
893         else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
894                          pg_strcasecmp(prev_wd, "FOREIGN") == 0)
895         {
896                 static const char *const list_ALTER_FOREIGN[] =
897                 {"DATA WRAPPER", "TABLE", NULL};
898
899                 COMPLETE_WITH_LIST(list_ALTER_FOREIGN);
900         }
901
902         /* ALTER FOREIGN DATA WRAPPER <name> */
903         else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
904                          pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
905                          pg_strcasecmp(prev3_wd, "DATA") == 0 &&
906                          pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
907         {
908                 static const char *const list_ALTER_FDW[] =
909                 {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL};
910
911                 COMPLETE_WITH_LIST(list_ALTER_FDW);
912         }
913
914         /* ALTER FOREIGN TABLE <name> */
915         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
916                          pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
917                          pg_strcasecmp(prev2_wd, "TABLE") == 0)
918         {
919                 static const char *const list_ALTER_FOREIGN_TABLE[] =
920                 {"ALTER", "DROP", "RENAME", "OWNER TO", "SET SCHEMA", NULL};
921
922                 COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE);
923         }
924
925         /* ALTER INDEX <name> */
926         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
927                          pg_strcasecmp(prev2_wd, "INDEX") == 0)
928         {
929                 static const char *const list_ALTERINDEX[] =
930                 {"OWNER TO", "RENAME TO", "SET", "RESET", NULL};
931
932                 COMPLETE_WITH_LIST(list_ALTERINDEX);
933         }
934         /* ALTER INDEX <name> SET */
935         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
936                          pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
937                          pg_strcasecmp(prev_wd, "SET") == 0)
938         {
939                 static const char *const list_ALTERINDEXSET[] =
940                 {"(", "TABLESPACE", NULL};
941
942                 COMPLETE_WITH_LIST(list_ALTERINDEXSET);
943         }
944         /* ALTER INDEX <name> RESET */
945         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
946                          pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
947                          pg_strcasecmp(prev_wd, "RESET") == 0)
948                 COMPLETE_WITH_CONST("(");
949         /* ALTER INDEX <foo> SET|RESET ( */
950         else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
951                          pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
952                          (pg_strcasecmp(prev2_wd, "SET") == 0 ||
953                           pg_strcasecmp(prev2_wd, "RESET") == 0) &&
954                          pg_strcasecmp(prev_wd, "(") == 0)
955         {
956                 static const char *const list_INDEXOPTIONS[] =
957                 {"fillfactor", "fastupdate", NULL};
958
959                 COMPLETE_WITH_LIST(list_INDEXOPTIONS);
960         }
961
962         /* ALTER LANGUAGE <name> */
963         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
964                          pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
965         {
966                 static const char *const list_ALTERLANGUAGE[] =
967                 {"OWNER TO", "RENAME TO", NULL};
968
969                 COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
970         }
971
972         /* ALTER LARGE OBJECT <oid> */
973         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
974                          pg_strcasecmp(prev3_wd, "LARGE") == 0 &&
975                          pg_strcasecmp(prev2_wd, "OBJECT") == 0)
976         {
977                 static const char *const list_ALTERLARGEOBJECT[] =
978                 {"OWNER TO", NULL};
979
980                 COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT);
981         }
982
983         /* ALTER USER,ROLE <name> */
984         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
985                          !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
986                          (pg_strcasecmp(prev2_wd, "USER") == 0 ||
987                           pg_strcasecmp(prev2_wd, "ROLE") == 0))
988         {
989                 static const char *const list_ALTERUSER[] =
990                 {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
991                         "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
992                         "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
993                         "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
994                 "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", NULL};
995
996                 COMPLETE_WITH_LIST(list_ALTERUSER);
997         }
998
999         /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
1000         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1001                          (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1002                          (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
1003         {
1004                 COMPLETE_WITH_CONST("PASSWORD");
1005         }
1006         /* ALTER DEFAULT PRIVILEGES */
1007         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1008                          pg_strcasecmp(prev2_wd, "DEFAULT") == 0 &&
1009                          pg_strcasecmp(prev_wd, "PRIVILEGES") == 0)
1010         {
1011                 static const char *const list_ALTER_DEFAULT_PRIVILEGES[] =
1012                 {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL};
1013
1014                 COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES);
1015         }
1016         /* ALTER DEFAULT PRIVILEGES FOR */
1017         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1018                          pg_strcasecmp(prev3_wd, "DEFAULT") == 0 &&
1019                          pg_strcasecmp(prev2_wd, "PRIVILEGES") == 0 &&
1020                          pg_strcasecmp(prev_wd, "FOR") == 0)
1021         {
1022                 static const char *const list_ALTER_DEFAULT_PRIVILEGES_FOR[] =
1023                 {"ROLE", "USER", NULL};
1024
1025                 COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_FOR);
1026         }
1027         /* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */
1028         else if (pg_strcasecmp(prev5_wd, "DEFAULT") == 0 &&
1029                          pg_strcasecmp(prev4_wd, "PRIVILEGES") == 0 &&
1030                          (pg_strcasecmp(prev3_wd, "FOR") == 0 ||
1031                           pg_strcasecmp(prev3_wd, "IN") == 0))
1032         {
1033                 static const char *const list_ALTER_DEFAULT_PRIVILEGES_REST[] =
1034                 {"GRANT", "REVOKE", NULL};
1035
1036                 COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_REST);
1037         }
1038         /* ALTER DOMAIN <name> */
1039         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1040                          pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
1041         {
1042                 static const char *const list_ALTERDOMAIN[] =
1043                 {"ADD", "DROP", "OWNER TO", "SET", NULL};
1044
1045                 COMPLETE_WITH_LIST(list_ALTERDOMAIN);
1046         }
1047         /* ALTER DOMAIN <sth> DROP */
1048         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1049                          pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
1050                          pg_strcasecmp(prev_wd, "DROP") == 0)
1051         {
1052                 static const char *const list_ALTERDOMAIN2[] =
1053                 {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL};
1054
1055                 COMPLETE_WITH_LIST(list_ALTERDOMAIN2);
1056         }
1057         /* ALTER DOMAIN <sth> SET */
1058         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1059                          pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
1060                          pg_strcasecmp(prev_wd, "SET") == 0)
1061         {
1062                 static const char *const list_ALTERDOMAIN3[] =
1063                 {"DEFAULT", "NOT NULL", "SCHEMA", NULL};
1064
1065                 COMPLETE_WITH_LIST(list_ALTERDOMAIN3);
1066         }
1067         /* ALTER SEQUENCE <name> */
1068         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1069                          pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)
1070         {
1071                 static const char *const list_ALTERSEQUENCE[] =
1072                 {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
1073                 "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL};
1074
1075                 COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
1076         }
1077         /* ALTER SEQUENCE <name> NO */
1078         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1079                          pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 &&
1080                          pg_strcasecmp(prev_wd, "NO") == 0)
1081         {
1082                 static const char *const list_ALTERSEQUENCE2[] =
1083                 {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
1084
1085                 COMPLETE_WITH_LIST(list_ALTERSEQUENCE2);
1086         }
1087         /* ALTER SERVER <name> */
1088         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1089                          pg_strcasecmp(prev2_wd, "SERVER") == 0)
1090         {
1091                 static const char *const list_ALTER_SERVER[] =
1092                 {"VERSION", "OPTIONS", "OWNER TO", NULL};
1093
1094                 COMPLETE_WITH_LIST(list_ALTER_SERVER);
1095         }
1096         /* ALTER VIEW <name> */
1097         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1098                          pg_strcasecmp(prev2_wd, "VIEW") == 0)
1099         {
1100                 static const char *const list_ALTERVIEW[] =
1101                 {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
1102
1103                 COMPLETE_WITH_LIST(list_ALTERVIEW);
1104         }
1105         /* ALTER TRIGGER <name>, add ON */
1106         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1107                          pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
1108                 COMPLETE_WITH_CONST("ON");
1109
1110         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1111                          pg_strcasecmp(prev3_wd, "TRIGGER") == 0)
1112         {
1113                 completion_info_charp = prev2_wd;
1114                 COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
1115         }
1116
1117         /*
1118          * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
1119          */
1120         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1121                          pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
1122                          pg_strcasecmp(prev_wd, "ON") == 0)
1123                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1124
1125         /* ALTER TRIGGER <name> ON <name> */
1126         else if (pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
1127                          pg_strcasecmp(prev2_wd, "ON") == 0)
1128                 COMPLETE_WITH_CONST("RENAME TO");
1129
1130         /*
1131          * If we detect ALTER TABLE <name>, suggest sub commands
1132          */
1133         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1134                          pg_strcasecmp(prev2_wd, "TABLE") == 0)
1135         {
1136                 static const char *const list_ALTER2[] =
1137                 {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
1138                 "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET",
1139                 "VALIDATE CONSTRAINT", NULL};
1140
1141                 COMPLETE_WITH_LIST(list_ALTER2);
1142         }
1143         /* ALTER TABLE xxx ENABLE */
1144         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1145                          pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1146                          pg_strcasecmp(prev_wd, "ENABLE") == 0)
1147         {
1148                 static const char *const list_ALTERENABLE[] =
1149                 {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL};
1150
1151                 COMPLETE_WITH_LIST(list_ALTERENABLE);
1152         }
1153         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1154                          pg_strcasecmp(prev2_wd, "ENABLE") == 0 &&
1155                          (pg_strcasecmp(prev_wd, "REPLICA") == 0 ||
1156                           pg_strcasecmp(prev_wd, "ALWAYS") == 0))
1157         {
1158                 static const char *const list_ALTERENABLE2[] =
1159                 {"RULE", "TRIGGER", NULL};
1160
1161                 COMPLETE_WITH_LIST(list_ALTERENABLE2);
1162         }
1163         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1164                          pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1165                          pg_strcasecmp(prev_wd, "DISABLE") == 0)
1166         {
1167                 static const char *const list_ALTERDISABLE[] =
1168                 {"RULE", "TRIGGER", NULL};
1169
1170                 COMPLETE_WITH_LIST(list_ALTERDISABLE);
1171         }
1172
1173         /* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
1174         else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1175                          (pg_strcasecmp(prev_wd, "ALTER") == 0 ||
1176                           pg_strcasecmp(prev_wd, "RENAME") == 0))
1177                 COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN'");
1178
1179         /*
1180          * If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
1181          * columns
1182          */
1183         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1184                          (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
1185                           pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
1186                          pg_strcasecmp(prev_wd, "COLUMN") == 0)
1187                 COMPLETE_WITH_ATTR(prev3_wd, "");
1188
1189         /* ALTER TABLE xxx RENAME yyy */
1190         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1191                          pg_strcasecmp(prev2_wd, "RENAME") == 0 &&
1192                          pg_strcasecmp(prev_wd, "TO") != 0)
1193                 COMPLETE_WITH_CONST("TO");
1194
1195         /* ALTER TABLE xxx RENAME COLUMN yyy */
1196         else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
1197                          pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
1198                          pg_strcasecmp(prev2_wd, "COLUMN") == 0 &&
1199                          pg_strcasecmp(prev_wd, "TO") != 0)
1200                 COMPLETE_WITH_CONST("TO");
1201
1202         /* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
1203         else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1204                          pg_strcasecmp(prev_wd, "DROP") == 0)
1205         {
1206                 static const char *const list_TABLEDROP[] =
1207                 {"COLUMN", "CONSTRAINT", NULL};
1208
1209                 COMPLETE_WITH_LIST(list_TABLEDROP);
1210         }
1211         /* If we have TABLE <sth> DROP COLUMN, provide list of columns */
1212         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1213                          pg_strcasecmp(prev2_wd, "DROP") == 0 &&
1214                          pg_strcasecmp(prev_wd, "COLUMN") == 0)
1215                 COMPLETE_WITH_ATTR(prev3_wd, "");
1216         /* ALTER TABLE ALTER [COLUMN] <foo> */
1217         else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1218                           pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
1219                          (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1220                           pg_strcasecmp(prev2_wd, "ALTER") == 0))
1221         {
1222                 static const char *const list_COLUMNALTER[] =
1223                 {"TYPE", "SET", "RESET", "DROP", NULL};
1224
1225                 COMPLETE_WITH_LIST(list_COLUMNALTER);
1226         }
1227         /* ALTER TABLE ALTER [COLUMN] <foo> SET */
1228         else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1229                            pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
1230                           (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
1231                            pg_strcasecmp(prev3_wd, "ALTER") == 0)) &&
1232                          pg_strcasecmp(prev_wd, "SET") == 0)
1233         {
1234                 static const char *const list_COLUMNSET[] =
1235                 {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
1236
1237                 COMPLETE_WITH_LIST(list_COLUMNSET);
1238         }
1239         /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
1240         else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1241                            pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
1242                           pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
1243                          pg_strcasecmp(prev2_wd, "SET") == 0 &&
1244                          pg_strcasecmp(prev_wd, "(") == 0)
1245         {
1246                 static const char *const list_COLUMNOPTIONS[] =
1247                 {"n_distinct", "n_distinct_inherited", NULL};
1248
1249                 COMPLETE_WITH_LIST(list_COLUMNOPTIONS);
1250         }
1251         /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
1252         else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1253                            pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
1254                           pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
1255                          pg_strcasecmp(prev2_wd, "SET") == 0 &&
1256                          pg_strcasecmp(prev_wd, "STORAGE") == 0)
1257         {
1258                 static const char *const list_COLUMNSTORAGE[] =
1259                 {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL};
1260
1261                 COMPLETE_WITH_LIST(list_COLUMNSTORAGE);
1262         }
1263         /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
1264         else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1265                            pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
1266                           (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
1267                            pg_strcasecmp(prev3_wd, "ALTER") == 0)) &&
1268                          pg_strcasecmp(prev_wd, "DROP") == 0)
1269         {
1270                 static const char *const list_COLUMNDROP[] =
1271                 {"DEFAULT", "NOT NULL", NULL};
1272
1273                 COMPLETE_WITH_LIST(list_COLUMNDROP);
1274         }
1275         else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1276                          pg_strcasecmp(prev_wd, "CLUSTER") == 0)
1277                 COMPLETE_WITH_CONST("ON");
1278         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1279                          pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
1280                          pg_strcasecmp(prev_wd, "ON") == 0)
1281         {
1282                 completion_info_charp = prev3_wd;
1283                 COMPLETE_WITH_QUERY(Query_for_index_of_table);
1284         }
1285         /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
1286         else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1287                          pg_strcasecmp(prev_wd, "SET") == 0)
1288         {
1289                 static const char *const list_TABLESET[] =
1290                 {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
1291
1292                 COMPLETE_WITH_LIST(list_TABLESET);
1293         }
1294         /* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
1295         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1296                          pg_strcasecmp(prev2_wd, "SET") == 0 &&
1297                          pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
1298                 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
1299         /* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
1300         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1301                          pg_strcasecmp(prev2_wd, "SET") == 0 &&
1302                          pg_strcasecmp(prev_wd, "WITHOUT") == 0)
1303         {
1304                 static const char *const list_TABLESET2[] =
1305                 {"CLUSTER", "OIDS", NULL};
1306
1307                 COMPLETE_WITH_LIST(list_TABLESET2);
1308         }
1309         /* ALTER TABLE <foo> RESET */
1310         else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1311                          pg_strcasecmp(prev_wd, "RESET") == 0)
1312                 COMPLETE_WITH_CONST("(");
1313         /* ALTER TABLE <foo> SET|RESET ( */
1314         else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1315                          (pg_strcasecmp(prev2_wd, "SET") == 0 ||
1316                           pg_strcasecmp(prev2_wd, "RESET") == 0) &&
1317                          pg_strcasecmp(prev_wd, "(") == 0)
1318         {
1319                 static const char *const list_TABLEOPTIONS[] =
1320                 {
1321                         "autovacuum_analyze_scale_factor",
1322                         "autovacuum_analyze_threshold",
1323                         "autovacuum_enabled",
1324                         "autovacuum_freeze_max_age",
1325                         "autovacuum_freeze_min_age",
1326                         "autovacuum_freeze_table_age",
1327                         "autovacuum_vacuum_cost_delay",
1328                         "autovacuum_vacuum_cost_limit",
1329                         "autovacuum_vacuum_scale_factor",
1330                         "autovacuum_vacuum_threshold",
1331                         "fillfactor",
1332                         "toast.autovacuum_enabled",
1333                         "toast.autovacuum_freeze_max_age",
1334                         "toast.autovacuum_freeze_min_age",
1335                         "toast.autovacuum_freeze_table_age",
1336                         "toast.autovacuum_vacuum_cost_delay",
1337                         "toast.autovacuum_vacuum_cost_limit",
1338                         "toast.autovacuum_vacuum_scale_factor",
1339                         "toast.autovacuum_vacuum_threshold",
1340                         NULL
1341                 };
1342
1343                 COMPLETE_WITH_LIST(list_TABLEOPTIONS);
1344         }
1345
1346         /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
1347         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1348                          pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
1349         {
1350                 static const char *const list_ALTERTSPC[] =
1351                 {"RENAME TO", "OWNER TO", "SET", "RESET", NULL};
1352
1353                 COMPLETE_WITH_LIST(list_ALTERTSPC);
1354         }
1355         /* ALTER TABLESPACE <foo> SET|RESET */
1356         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1357                          pg_strcasecmp(prev3_wd, "TABLESPACE") == 0 &&
1358                          (pg_strcasecmp(prev_wd, "SET") == 0 ||
1359                           pg_strcasecmp(prev_wd, "RESET") == 0))
1360                 COMPLETE_WITH_CONST("(");
1361         /* ALTER TABLESPACE <foo> SET|RESET ( */
1362         else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1363                          pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
1364                          (pg_strcasecmp(prev2_wd, "SET") == 0 ||
1365                           pg_strcasecmp(prev2_wd, "RESET") == 0) &&
1366                          pg_strcasecmp(prev_wd, "(") == 0)
1367         {
1368                 static const char *const list_TABLESPACEOPTIONS[] =
1369                 {"seq_page_cost", "random_page_cost", NULL};
1370
1371                 COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS);
1372         }
1373
1374         /* ALTER TEXT SEARCH */
1375         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1376                          pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1377                          pg_strcasecmp(prev_wd, "SEARCH") == 0)
1378         {
1379                 static const char *const list_ALTERTEXTSEARCH[] =
1380                 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1381
1382                 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
1383         }
1384         else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1385                          pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1386                          pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1387                          (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 ||
1388                           pg_strcasecmp(prev2_wd, "PARSER") == 0))
1389         {
1390                 static const char *const list_ALTERTEXTSEARCH2[] =
1391                 {"RENAME TO", "SET SCHEMA", NULL};
1392
1393                 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2);
1394         }
1395
1396         else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1397                          pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1398                          pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1399                          pg_strcasecmp(prev2_wd, "DICTIONARY") == 0)
1400         {
1401                 static const char *const list_ALTERTEXTSEARCH3[] =
1402                 {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
1403
1404                 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
1405         }
1406
1407         else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1408                          pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1409                          pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1410                          pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
1411         {
1412                 static const char *const list_ALTERTEXTSEARCH4[] =
1413                 {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
1414
1415                 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH4);
1416         }
1417
1418         /* complete ALTER TYPE <foo> with actions */
1419         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1420                          pg_strcasecmp(prev2_wd, "TYPE") == 0)
1421         {
1422                 static const char *const list_ALTERTYPE[] =
1423                 {"ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE",
1424                  "OWNER TO", "RENAME", "SET SCHEMA", NULL};
1425
1426                 COMPLETE_WITH_LIST(list_ALTERTYPE);
1427         }
1428         /* complete ALTER TYPE <foo> ADD with actions */
1429         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1430                          pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
1431                          pg_strcasecmp(prev_wd, "ADD") == 0)
1432         {
1433                 static const char *const list_ALTERTYPE[] =
1434                 {"ATTRIBUTE", "VALUE", NULL};
1435
1436                 COMPLETE_WITH_LIST(list_ALTERTYPE);
1437         }
1438         /* ALTER TYPE <foo> RENAME  */
1439         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1440                          pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
1441                          pg_strcasecmp(prev_wd, "RENAME") == 0)
1442         {
1443                 static const char *const list_ALTERTYPE[] =
1444                 {"ATTRIBUTE", "TO", NULL};
1445
1446                 COMPLETE_WITH_LIST(list_ALTERTYPE);
1447         }
1448         /* ALTER TYPE xxx RENAME ATTRIBUTE yyy */
1449         else if (pg_strcasecmp(prev5_wd, "TYPE") == 0 &&
1450                          pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
1451                          pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0)
1452                 COMPLETE_WITH_CONST("TO");
1453
1454         /* If we have TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list of attributes */
1455         else if (pg_strcasecmp(prev4_wd, "TYPE") == 0 &&
1456                          (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
1457                           pg_strcasecmp(prev2_wd, "DROP") == 0 ||
1458                           pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
1459                          pg_strcasecmp(prev_wd, "ATTRIBUTE") == 0)
1460                 COMPLETE_WITH_ATTR(prev3_wd, "");
1461         /* ALTER TYPE ALTER ATTRIBUTE <foo> */
1462         else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1463                           pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0))
1464         {
1465                 COMPLETE_WITH_CONST("TYPE");
1466         }
1467         /* complete ALTER GROUP <foo> */
1468         else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1469                          pg_strcasecmp(prev2_wd, "GROUP") == 0)
1470         {
1471                 static const char *const list_ALTERGROUP[] =
1472                 {"ADD USER", "DROP USER", "RENAME TO", NULL};
1473
1474                 COMPLETE_WITH_LIST(list_ALTERGROUP);
1475         }
1476         /* complete ALTER GROUP <foo> ADD|DROP with USER */
1477         else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1478                          pg_strcasecmp(prev3_wd, "GROUP") == 0 &&
1479                          (pg_strcasecmp(prev_wd, "ADD") == 0 ||
1480                           pg_strcasecmp(prev_wd, "DROP") == 0))
1481                 COMPLETE_WITH_CONST("USER");
1482         /* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
1483         else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 &&
1484                          (pg_strcasecmp(prev2_wd, "ADD") == 0 ||
1485                           pg_strcasecmp(prev2_wd, "DROP") == 0) &&
1486                          pg_strcasecmp(prev_wd, "USER") == 0)
1487                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1488
1489 /* BEGIN, END, ABORT */
1490         else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
1491                          pg_strcasecmp(prev_wd, "END") == 0 ||
1492                          pg_strcasecmp(prev_wd, "ABORT") == 0)
1493         {
1494                 static const char *const list_TRANS[] =
1495                 {"WORK", "TRANSACTION", NULL};
1496
1497                 COMPLETE_WITH_LIST(list_TRANS);
1498         }
1499 /* COMMIT */
1500         else if (pg_strcasecmp(prev_wd, "COMMIT") == 0)
1501         {
1502                 static const char *const list_COMMIT[] =
1503                 {"WORK", "TRANSACTION", "PREPARED", NULL};
1504
1505                 COMPLETE_WITH_LIST(list_COMMIT);
1506         }
1507 /* RELEASE SAVEPOINT */
1508         else if (pg_strcasecmp(prev_wd, "RELEASE") == 0)
1509                 COMPLETE_WITH_CONST("SAVEPOINT");
1510 /* ROLLBACK*/
1511         else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0)
1512         {
1513                 static const char *const list_TRANS[] =
1514                 {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL};
1515
1516                 COMPLETE_WITH_LIST(list_TRANS);
1517         }
1518 /* CLUSTER */
1519
1520         /*
1521          * If the previous word is CLUSTER and not without produce list of tables
1522          */
1523         else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 &&
1524                          pg_strcasecmp(prev2_wd, "WITHOUT") != 0)
1525                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1526         /* If we have CLUSTER <sth>, then add "USING" */
1527         else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
1528                          pg_strcasecmp(prev_wd, "ON") != 0)
1529         {
1530                 COMPLETE_WITH_CONST("USING");
1531         }
1532
1533         /*
1534          * If we have CLUSTER <sth> USING, then add the index as well.
1535          */
1536         else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
1537                          pg_strcasecmp(prev_wd, "USING") == 0)
1538         {
1539                 completion_info_charp = prev2_wd;
1540                 COMPLETE_WITH_QUERY(Query_for_index_of_table);
1541         }
1542
1543 /* COMMENT */
1544         else if (pg_strcasecmp(prev_wd, "COMMENT") == 0)
1545                 COMPLETE_WITH_CONST("ON");
1546         else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 &&
1547                          pg_strcasecmp(prev_wd, "ON") == 0)
1548         {
1549                 static const char *const list_COMMENT[] =
1550                 {"CAST", "COLLATION", "CONVERSION", "DATABASE", "FOREIGN TABLE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
1551                         "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
1552                         "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
1553                 "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
1554
1555                 COMPLETE_WITH_LIST(list_COMMENT);
1556         }
1557         else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
1558                          pg_strcasecmp(prev3_wd, "ON") == 0 &&
1559                          pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1560                          pg_strcasecmp(prev_wd, "SEARCH") == 0)
1561         {
1562                 static const char *const list_TRANS2[] =
1563                 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1564
1565                 COMPLETE_WITH_LIST(list_TRANS2);
1566         }
1567         else if ((pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
1568                           pg_strcasecmp(prev3_wd, "ON") == 0) ||
1569                          (pg_strcasecmp(prev5_wd, "ON") == 0 &&
1570                           pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1571                           pg_strcasecmp(prev3_wd, "SEARCH") == 0))
1572                 COMPLETE_WITH_CONST("IS");
1573
1574 /* COPY */
1575
1576         /*
1577          * If we have COPY [BINARY] (which you'd have to type yourself), offer
1578          * list of tables (Also cover the analogous backslash command)
1579          */
1580         else if (pg_strcasecmp(prev_wd, "COPY") == 0 ||
1581                          pg_strcasecmp(prev_wd, "\\copy") == 0 ||
1582                          (pg_strcasecmp(prev2_wd, "COPY") == 0 &&
1583                           pg_strcasecmp(prev_wd, "BINARY") == 0))
1584                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1585         /* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
1586         else if (pg_strcasecmp(prev2_wd, "COPY") == 0 ||
1587                          pg_strcasecmp(prev2_wd, "\\copy") == 0 ||
1588                          pg_strcasecmp(prev2_wd, "BINARY") == 0)
1589         {
1590                 static const char *const list_FROMTO[] =
1591                 {"FROM", "TO", NULL};
1592
1593                 COMPLETE_WITH_LIST(list_FROMTO);
1594         }
1595         /* If we have COPY|BINARY <sth> FROM|TO, complete with filename */
1596         else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 ||
1597                           pg_strcasecmp(prev3_wd, "\\copy") == 0 ||
1598                           pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
1599                          (pg_strcasecmp(prev_wd, "FROM") == 0 ||
1600                           pg_strcasecmp(prev_wd, "TO") == 0))
1601                 matches = completion_matches(text, filename_completion_function);
1602
1603         /* Handle COPY|BINARY <sth> FROM|TO filename */
1604         else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
1605                           pg_strcasecmp(prev4_wd, "\\copy") == 0 ||
1606                           pg_strcasecmp(prev4_wd, "BINARY") == 0) &&
1607                          (pg_strcasecmp(prev2_wd, "FROM") == 0 ||
1608                           pg_strcasecmp(prev2_wd, "TO") == 0))
1609         {
1610                 static const char *const list_COPY[] =
1611                 {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", "ENCODING", NULL};
1612
1613                 COMPLETE_WITH_LIST(list_COPY);
1614         }
1615
1616         /* Handle COPY|BINARY <sth> FROM|TO filename CSV */
1617         else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
1618                          (pg_strcasecmp(prev3_wd, "FROM") == 0 ||
1619                           pg_strcasecmp(prev3_wd, "TO") == 0))
1620         {
1621                 static const char *const list_CSV[] =
1622                 {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "FORCE NOT NULL", NULL};
1623
1624                 COMPLETE_WITH_LIST(list_CSV);
1625         }
1626
1627         /* CREATE DATABASE */
1628         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1629                          pg_strcasecmp(prev2_wd, "DATABASE") == 0)
1630         {
1631                 static const char *const list_DATABASE[] =
1632                 {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT",
1633                 NULL};
1634
1635                 COMPLETE_WITH_LIST(list_DATABASE);
1636         }
1637
1638         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1639                          pg_strcasecmp(prev3_wd, "DATABASE") == 0 &&
1640                          pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
1641                 COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
1642
1643         /* CREATE EXTENSION */
1644         /* Complete with available extensions rather than installed ones. */
1645         else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1646                          pg_strcasecmp(prev_wd, "EXTENSION") == 0)
1647                 COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
1648         /* CREATE EXTENSION <name> */
1649         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1650                          pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
1651                 COMPLETE_WITH_CONST("WITH SCHEMA");
1652
1653         /* CREATE FOREIGN */
1654         else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1655                          pg_strcasecmp(prev_wd, "FOREIGN") == 0)
1656         {
1657                 static const char *const list_CREATE_FOREIGN[] =
1658                 {"DATA WRAPPER", "TABLE", NULL};
1659
1660                 COMPLETE_WITH_LIST(list_CREATE_FOREIGN);
1661         }
1662
1663         /* CREATE FOREIGN DATA WRAPPER */
1664         else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1665                          pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
1666                          pg_strcasecmp(prev3_wd, "DATA") == 0 &&
1667                          pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
1668         {
1669                 static const char *const list_CREATE_FOREIGN_DATA_WRAPPER[] =
1670                 {"HANDLER", "VALIDATOR", NULL};
1671
1672                 COMPLETE_WITH_LIST(list_CREATE_FOREIGN_DATA_WRAPPER);
1673         }
1674
1675         /* CREATE INDEX */
1676         /* First off we complete CREATE UNIQUE with "INDEX" */
1677         else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1678                          pg_strcasecmp(prev_wd, "UNIQUE") == 0)
1679                 COMPLETE_WITH_CONST("INDEX");
1680         /* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
1681         else if (pg_strcasecmp(prev_wd, "INDEX") == 0 &&
1682                          (pg_strcasecmp(prev2_wd, "CREATE") == 0 ||
1683                           pg_strcasecmp(prev2_wd, "UNIQUE") == 0))
1684                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
1685                                                                    " UNION SELECT 'ON'"
1686                                                                    " UNION SELECT 'CONCURRENTLY'");
1687         /* Complete ... INDEX [<name>] ON with a list of tables  */
1688         else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
1689                           pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
1690                           pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) &&
1691                          pg_strcasecmp(prev_wd, "ON") == 0)
1692                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1693         /* If we have CREATE|UNIQUE INDEX <sth> CONCURRENTLY, then add "ON" */
1694         else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
1695                           pg_strcasecmp(prev2_wd, "INDEX") == 0) &&
1696                          pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0)
1697                 COMPLETE_WITH_CONST("ON");
1698         /* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" or "CONCURRENTLY" */
1699         else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
1700                           pg_strcasecmp(prev3_wd, "UNIQUE") == 0) &&
1701                          pg_strcasecmp(prev2_wd, "INDEX") == 0)
1702         {
1703                 static const char *const list_CREATE_INDEX[] =
1704                 {"CONCURRENTLY", "ON", NULL};
1705
1706                 COMPLETE_WITH_LIST(list_CREATE_INDEX);
1707         }
1708
1709         /*
1710          * Complete INDEX <name> ON <table> with a list of table columns (which
1711          * should really be in parens)
1712          */
1713         else if ((pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
1714                           pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
1715                           pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0) &&
1716                          pg_strcasecmp(prev2_wd, "ON") == 0)
1717         {
1718                 static const char *const list_CREATE_INDEX2[] =
1719                 {"(", "USING", NULL};
1720
1721                 COMPLETE_WITH_LIST(list_CREATE_INDEX2);
1722         }
1723         else if ((pg_strcasecmp(prev5_wd, "INDEX") == 0 ||
1724                           pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
1725                           pg_strcasecmp(prev4_wd, "CONCURRENTLY") == 0) &&
1726                          pg_strcasecmp(prev3_wd, "ON") == 0 &&
1727                          pg_strcasecmp(prev_wd, "(") == 0)
1728                 COMPLETE_WITH_ATTR(prev2_wd, "");
1729         /* same if you put in USING */
1730         else if (pg_strcasecmp(prev5_wd, "ON") == 0 &&
1731                          pg_strcasecmp(prev3_wd, "USING") == 0 &&
1732                          pg_strcasecmp(prev_wd, "(") == 0)
1733                 COMPLETE_WITH_ATTR(prev4_wd, "");
1734         /* Complete USING with an index method */
1735         else if (pg_strcasecmp(prev_wd, "USING") == 0)
1736                 COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
1737         else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
1738                          pg_strcasecmp(prev2_wd, "USING") == 0)
1739                 COMPLETE_WITH_CONST("(");
1740
1741 /* CREATE RULE */
1742         /* Complete "CREATE RULE <sth>" with "AS" */
1743         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1744                          pg_strcasecmp(prev2_wd, "RULE") == 0)
1745                 COMPLETE_WITH_CONST("AS");
1746         /* Complete "CREATE RULE <sth> AS with "ON" */
1747         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1748                          pg_strcasecmp(prev3_wd, "RULE") == 0 &&
1749                          pg_strcasecmp(prev_wd, "AS") == 0)
1750                 COMPLETE_WITH_CONST("ON");
1751         /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */
1752         else if (pg_strcasecmp(prev4_wd, "RULE") == 0 &&
1753                          pg_strcasecmp(prev2_wd, "AS") == 0 &&
1754                          pg_strcasecmp(prev_wd, "ON") == 0)
1755         {
1756                 static const char *const rule_events[] =
1757                 {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
1758
1759                 COMPLETE_WITH_LIST(rule_events);
1760         }
1761         /* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
1762         else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
1763                          pg_strcasecmp(prev2_wd, "ON") == 0 &&
1764                          (pg_toupper((unsigned char) prev_wd[4]) == 'T' ||
1765                           pg_toupper((unsigned char) prev_wd[5]) == 'T'))
1766                 COMPLETE_WITH_CONST("TO");
1767         /* Complete "AS ON <sth> TO" with a table name */
1768         else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
1769                          pg_strcasecmp(prev3_wd, "ON") == 0 &&
1770                          pg_strcasecmp(prev_wd, "TO") == 0)
1771                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1772
1773 /* CREATE SERVER <name> */
1774         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1775                          pg_strcasecmp(prev2_wd, "SERVER") == 0)
1776         {
1777                 static const char *const list_CREATE_SERVER[] =
1778                 {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL};
1779
1780                 COMPLETE_WITH_LIST(list_CREATE_SERVER);
1781         }
1782
1783 /* CREATE TABLE */
1784         /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
1785         else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1786                          (pg_strcasecmp(prev_wd, "TEMP") == 0 ||
1787                           pg_strcasecmp(prev_wd, "TEMPORARY") == 0))
1788         {
1789                 static const char *const list_TEMP[] =
1790                 {"SEQUENCE", "TABLE", "VIEW", NULL};
1791
1792                 COMPLETE_WITH_LIST(list_TEMP);
1793         }
1794         /* Complete "CREATE UNLOGGED" with TABLE */
1795         else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1796                          pg_strcasecmp(prev_wd, "UNLOGGED") == 0)
1797         {
1798                 COMPLETE_WITH_CONST("TABLE");
1799         }
1800
1801 /* CREATE TABLESPACE */
1802         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1803                          pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
1804         {
1805                 static const char *const list_CREATETABLESPACE[] =
1806                 {"OWNER", "LOCATION", NULL};
1807
1808                 COMPLETE_WITH_LIST(list_CREATETABLESPACE);
1809         }
1810         /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
1811         else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1812                          pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
1813                          pg_strcasecmp(prev2_wd, "OWNER") == 0)
1814         {
1815                 COMPLETE_WITH_CONST("LOCATION");
1816         }
1817
1818 /* CREATE TEXT SEARCH */
1819         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1820                          pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1821                          pg_strcasecmp(prev_wd, "SEARCH") == 0)
1822         {
1823                 static const char *const list_CREATETEXTSEARCH[] =
1824                 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1825
1826                 COMPLETE_WITH_LIST(list_CREATETEXTSEARCH);
1827         }
1828         else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1829                          pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1830                          pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
1831                 COMPLETE_WITH_CONST("(");
1832
1833 /* CREATE TRIGGER */
1834         /* complete CREATE TRIGGER <name> with BEFORE,AFTER */
1835         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1836                          pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
1837         {
1838                 static const char *const list_CREATETRIGGER[] =
1839                 {"BEFORE", "AFTER", "INSTEAD OF", NULL};
1840
1841                 COMPLETE_WITH_LIST(list_CREATETRIGGER);
1842         }
1843         /* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */
1844         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1845                          pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
1846                          (pg_strcasecmp(prev_wd, "BEFORE") == 0 ||
1847                           pg_strcasecmp(prev_wd, "AFTER") == 0))
1848         {
1849                 static const char *const list_CREATETRIGGER_EVENTS[] =
1850                 {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL};
1851
1852                 COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
1853         }
1854         /* complete CREATE TRIGGER <name> INSTEAD OF with an event */
1855         else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1856                          pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
1857                          pg_strcasecmp(prev2_wd, "INSTEAD") == 0 &&
1858                          pg_strcasecmp(prev_wd, "OF") == 0)
1859         {
1860                 static const char *const list_CREATETRIGGER_EVENTS[] =
1861                 {"INSERT", "DELETE", "UPDATE", NULL};
1862
1863                 COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
1864         }
1865         /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
1866         else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1867                           pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
1868                           (pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
1869                            pg_strcasecmp(prev2_wd, "AFTER") == 0)) ||
1870                          (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
1871                           pg_strcasecmp(prev3_wd, "INSTEAD") == 0 &&
1872                           pg_strcasecmp(prev2_wd, "OF") == 0))
1873         {
1874                 static const char *const list_CREATETRIGGER2[] =
1875                 {"ON", "OR", NULL};
1876
1877                 COMPLETE_WITH_LIST(list_CREATETRIGGER2);
1878         }
1879
1880         /*
1881          * complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of
1882          * tables
1883          */
1884         else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
1885                          (pg_strcasecmp(prev3_wd, "BEFORE") == 0 ||
1886                           pg_strcasecmp(prev3_wd, "AFTER") == 0) &&
1887                          pg_strcasecmp(prev_wd, "ON") == 0)
1888                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1889         /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */
1890         else if (pg_strcasecmp(prev4_wd, "INSTEAD") == 0 &&
1891                          pg_strcasecmp(prev3_wd, "OF") == 0 &&
1892                          pg_strcasecmp(prev_wd, "ON") == 0)
1893                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
1894         /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */
1895         else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0)
1896                 COMPLETE_WITH_CONST("PROCEDURE");
1897
1898 /* CREATE ROLE,USER,GROUP */
1899         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1900                          !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
1901                          (pg_strcasecmp(prev2_wd, "ROLE") == 0 ||
1902                           pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
1903         {
1904                 static const char *const list_CREATEROLE[] =
1905                 {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
1906                         "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
1907                         "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
1908                         "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
1909                 "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", NULL};
1910
1911                 COMPLETE_WITH_LIST(list_CREATEROLE);
1912         }
1913
1914         /*
1915          * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
1916          * PASSWORD
1917          */
1918         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1919                          (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
1920                           pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1921                          (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
1922         {
1923                 COMPLETE_WITH_CONST("PASSWORD");
1924         }
1925         /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
1926         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1927                          (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
1928                           pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1929                          pg_strcasecmp(prev_wd, "IN") == 0)
1930         {
1931                 static const char *const list_CREATEROLE3[] =
1932                 {"GROUP", "ROLE", NULL};
1933
1934                 COMPLETE_WITH_LIST(list_CREATEROLE3);
1935         }
1936
1937 /* CREATE VIEW */
1938         /* Complete CREATE VIEW <name> with AS */
1939         else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1940                          pg_strcasecmp(prev2_wd, "VIEW") == 0)
1941                 COMPLETE_WITH_CONST("AS");
1942         /* Complete "CREATE VIEW <sth> AS with "SELECT" */
1943         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1944                          pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
1945                          pg_strcasecmp(prev_wd, "AS") == 0)
1946                 COMPLETE_WITH_CONST("SELECT");
1947
1948 /* DECLARE */
1949         else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0)
1950         {
1951                 static const char *const list_DECLARE[] =
1952                 {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL};
1953
1954                 COMPLETE_WITH_LIST(list_DECLARE);
1955         }
1956
1957 /* CURSOR */
1958         else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
1959         {
1960                 static const char *const list_DECLARECURSOR[] =
1961                 {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL};
1962
1963                 COMPLETE_WITH_LIST(list_DECLARECURSOR);
1964         }
1965
1966
1967 /* DELETE */
1968
1969         /*
1970          * Complete DELETE with FROM (only if the word before that is not "ON"
1971          * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT)
1972          */
1973         else if (pg_strcasecmp(prev_wd, "DELETE") == 0 &&
1974                          !(pg_strcasecmp(prev2_wd, "ON") == 0 ||
1975                            pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
1976                            pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
1977                            pg_strcasecmp(prev2_wd, "AFTER") == 0))
1978                 COMPLETE_WITH_CONST("FROM");
1979         /* Complete DELETE FROM with a list of tables */
1980         else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
1981                          pg_strcasecmp(prev_wd, "FROM") == 0)
1982                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_deletables, NULL);
1983         /* Complete DELETE FROM <table> */
1984         else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
1985                          pg_strcasecmp(prev2_wd, "FROM") == 0)
1986         {
1987                 static const char *const list_DELETE[] =
1988                 {"USING", "WHERE", "SET", NULL};
1989
1990                 COMPLETE_WITH_LIST(list_DELETE);
1991         }
1992         /* XXX: implement tab completion for DELETE ... USING */
1993
1994 /* DISCARD */
1995         else if (pg_strcasecmp(prev_wd, "DISCARD") == 0)
1996         {
1997                 static const char *const list_DISCARD[] =
1998                 {"ALL", "PLANS", "TEMP", NULL};
1999
2000                 COMPLETE_WITH_LIST(list_DISCARD);
2001         }
2002
2003 /* DO */
2004
2005         /*
2006          * Complete DO with LANGUAGE.
2007          */
2008         else if (pg_strcasecmp(prev_wd, "DO") == 0)
2009         {
2010                 static const char *const list_DO[] =
2011                 {"LANGUAGE", NULL};
2012
2013                 COMPLETE_WITH_LIST(list_DO);
2014         }
2015
2016 /* DROP (when not the previous word) */
2017         /* DROP AGGREGATE */
2018         else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
2019                          pg_strcasecmp(prev2_wd, "AGGREGATE") == 0)
2020                 COMPLETE_WITH_CONST("(");
2021
2022         /* DROP object with CASCADE / RESTRICT */
2023         else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
2024                           (pg_strcasecmp(prev2_wd, "COLLATION") == 0 ||
2025                            pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
2026                            pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
2027                            pg_strcasecmp(prev2_wd, "EXTENSION") == 0 ||
2028                            pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
2029                            pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
2030                            pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
2031                            pg_strcasecmp(prev2_wd, "SCHEMA") == 0 ||
2032                            pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 ||
2033                            pg_strcasecmp(prev2_wd, "SERVER") == 0 ||
2034                            pg_strcasecmp(prev2_wd, "TABLE") == 0 ||
2035                            pg_strcasecmp(prev2_wd, "TYPE") == 0 ||
2036                            pg_strcasecmp(prev2_wd, "VIEW") == 0)) ||
2037                          (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
2038                           pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
2039                           prev_wd[strlen(prev_wd) - 1] == ')') ||
2040                          (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
2041                           pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
2042                           pg_strcasecmp(prev3_wd, "DATA") == 0 &&
2043                           pg_strcasecmp(prev2_wd, "WRAPPER") == 0) ||
2044                          (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
2045                           pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
2046                           pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
2047                           (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 ||
2048                            pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 ||
2049                            pg_strcasecmp(prev2_wd, "PARSER") == 0 ||
2050                            pg_strcasecmp(prev2_wd, "TEMPLATE") == 0))
2051                 )
2052         {
2053                 if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
2054                         pg_strcasecmp(prev2_wd, "FUNCTION") == 0)
2055                 {
2056                         COMPLETE_WITH_CONST("(");
2057                 }
2058                 else
2059                 {
2060                         static const char *const list_DROPCR[] =
2061                         {"CASCADE", "RESTRICT", NULL};
2062
2063                         COMPLETE_WITH_LIST(list_DROPCR);
2064                 }
2065         }
2066         else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
2067                          pg_strcasecmp(prev_wd, "FOREIGN") == 0)
2068         {
2069                 static const char *const drop_CREATE_FOREIGN[] =
2070                 {"DATA WRAPPER", "TABLE", NULL};
2071
2072                 COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
2073         }
2074         else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
2075                          (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
2076                           pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
2077                          pg_strcasecmp(prev_wd, "(") == 0)
2078         {
2079                 char       *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
2080
2081                 sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
2082                 COMPLETE_WITH_QUERY(tmp_buf);
2083                 free(tmp_buf);
2084         }
2085         /* DROP OWNED BY */
2086         else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
2087                          pg_strcasecmp(prev_wd, "OWNED") == 0)
2088                 COMPLETE_WITH_CONST("BY");
2089         else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
2090                          pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
2091                          pg_strcasecmp(prev_wd, "BY") == 0)
2092                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2093         else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
2094                          pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
2095                          pg_strcasecmp(prev_wd, "SEARCH") == 0)
2096         {
2097
2098                 static const char *const list_ALTERTEXTSEARCH[] =
2099                 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
2100
2101                 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
2102         }
2103
2104 /* EXPLAIN */
2105
2106         /*
2107          * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
2108          */
2109         else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
2110         {
2111                 static const char *const list_EXPLAIN[] =
2112                 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL};
2113
2114                 COMPLETE_WITH_LIST(list_EXPLAIN);
2115         }
2116         else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
2117                          pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2118         {
2119                 static const char *const list_EXPLAIN[] =
2120                 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL};
2121
2122                 COMPLETE_WITH_LIST(list_EXPLAIN);
2123         }
2124         else if ((pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
2125                           pg_strcasecmp(prev_wd, "VERBOSE") == 0) ||
2126                          (pg_strcasecmp(prev3_wd, "EXPLAIN") == 0 &&
2127                           pg_strcasecmp(prev2_wd, "ANALYZE") == 0 &&
2128                           pg_strcasecmp(prev_wd, "VERBOSE") == 0))
2129         {
2130                 static const char *const list_EXPLAIN[] =
2131                 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
2132
2133                 COMPLETE_WITH_LIST(list_EXPLAIN);
2134         }
2135
2136 /* FETCH && MOVE */
2137         /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
2138         else if (pg_strcasecmp(prev_wd, "FETCH") == 0 ||
2139                          pg_strcasecmp(prev_wd, "MOVE") == 0)
2140         {
2141                 static const char *const list_FETCH1[] =
2142                 {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL};
2143
2144                 COMPLETE_WITH_LIST(list_FETCH1);
2145         }
2146         /* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
2147         else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 ||
2148                          pg_strcasecmp(prev2_wd, "MOVE") == 0)
2149         {
2150                 static const char *const list_FETCH2[] =
2151                 {"ALL", "NEXT", "PRIOR", NULL};
2152
2153                 COMPLETE_WITH_LIST(list_FETCH2);
2154         }
2155
2156         /*
2157          * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent,
2158          * but we may as well tab-complete both: perhaps some users prefer one
2159          * variant or the other.
2160          */
2161         else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 ||
2162                          pg_strcasecmp(prev3_wd, "MOVE") == 0)
2163         {
2164                 static const char *const list_FROMIN[] =
2165                 {"FROM", "IN", NULL};
2166
2167                 COMPLETE_WITH_LIST(list_FROMIN);
2168         }
2169
2170 /* FOREIGN DATA WRAPPER */
2171         /* applies in ALTER/DROP FDW and in CREATE SERVER */
2172         else if (pg_strcasecmp(prev4_wd, "CREATE") != 0 &&
2173                          pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
2174                          pg_strcasecmp(prev2_wd, "DATA") == 0 &&
2175                          pg_strcasecmp(prev_wd, "WRAPPER") == 0)
2176                 COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
2177
2178 /* FOREIGN TABLE */
2179         else if (pg_strcasecmp(prev3_wd, "CREATE") != 0 &&
2180                          pg_strcasecmp(prev2_wd, "FOREIGN") == 0 &&
2181                          pg_strcasecmp(prev_wd, "TABLE") == 0)
2182                  COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
2183
2184 /* GRANT && REVOKE */
2185         /* Complete GRANT/REVOKE with a list of privileges */
2186         else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
2187                          pg_strcasecmp(prev_wd, "REVOKE") == 0)
2188         {
2189                 static const char *const list_privilege[] =
2190                 {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
2191                         "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
2192                 "ALL", NULL};
2193
2194                 COMPLETE_WITH_LIST(list_privilege);
2195         }
2196         /* Complete GRANT/REVOKE <sth> with "ON" */
2197         else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
2198                          pg_strcasecmp(prev2_wd, "REVOKE") == 0)
2199                 COMPLETE_WITH_CONST("ON");
2200
2201         /*
2202          * Complete GRANT/REVOKE <sth> ON with a list of tables, views, sequences,
2203          * and indexes
2204          *
2205          * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via
2206          * UNION; seems to work intuitively
2207          *
2208          * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
2209          * here will only work if the privilege list contains exactly one
2210          * privilege
2211          */
2212         else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
2213                           pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
2214                          pg_strcasecmp(prev_wd, "ON") == 0)
2215                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf,
2216                                                                    " UNION SELECT 'DATABASE'"
2217                                                                    " UNION SELECT 'FOREIGN DATA WRAPPER'"
2218                                                                    " UNION SELECT 'FOREIGN SERVER'"
2219                                                                    " UNION SELECT 'FOREIGN TABLE'"
2220                                                                    " UNION SELECT 'FUNCTION'"
2221                                                                    " UNION SELECT 'LANGUAGE'"
2222                                                                    " UNION SELECT 'LARGE OBJECT'"
2223                                                                    " UNION SELECT 'SCHEMA'"
2224                                                                    " UNION SELECT 'TABLESPACE'");
2225         else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
2226                           pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
2227                          pg_strcasecmp(prev2_wd, "ON") == 0 &&
2228                          pg_strcasecmp(prev_wd, "FOREIGN") == 0)
2229         {
2230                 static const char *const list_privilege_foreign[] =
2231                 {"DATA WRAPPER", "SERVER", "TABLE", NULL};
2232
2233                 COMPLETE_WITH_LIST(list_privilege_foreign);
2234         }
2235
2236         /* Complete "GRANT/REVOKE * ON * " with "TO/FROM" */
2237         else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
2238                           pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
2239                          pg_strcasecmp(prev2_wd, "ON") == 0)
2240         {
2241                 if (pg_strcasecmp(prev_wd, "DATABASE") == 0)
2242                         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
2243                 else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0)
2244                         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2245                 else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0)
2246                         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
2247                 else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0)
2248                         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
2249                 else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
2250                         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
2251                 else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
2252                         COMPLETE_WITH_CONST("TO");
2253                 else
2254                         COMPLETE_WITH_CONST("FROM");
2255         }
2256
2257         /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
2258         else if (pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
2259                          pg_strcasecmp(prev3_wd, "ON") == 0)
2260         {
2261                 if (pg_strcasecmp(prev_wd, "TO") == 0)
2262                         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
2263                 else
2264                         COMPLETE_WITH_CONST("TO");
2265         }
2266         else if (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
2267                          pg_strcasecmp(prev3_wd, "ON") == 0)
2268         {
2269                 if (pg_strcasecmp(prev_wd, "FROM") == 0)
2270                         COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
2271                 else
2272                         COMPLETE_WITH_CONST("FROM");
2273         }
2274
2275 /* GROUP BY */
2276         else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
2277                          pg_strcasecmp(prev_wd, "GROUP") == 0)
2278                 COMPLETE_WITH_CONST("BY");
2279
2280 /* INSERT */
2281         /* Complete INSERT with "INTO" */
2282         else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
2283                 COMPLETE_WITH_CONST("INTO");
2284         /* Complete INSERT INTO with table names */
2285         else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 &&
2286                          pg_strcasecmp(prev_wd, "INTO") == 0)
2287                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_insertables, NULL);
2288         /* Complete "INSERT INTO <table> (" with attribute names */
2289         else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
2290                          pg_strcasecmp(prev3_wd, "INTO") == 0 &&
2291                          pg_strcasecmp(prev_wd, "(") == 0)
2292                 COMPLETE_WITH_ATTR(prev2_wd, "");
2293
2294         /*
2295          * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
2296          * "TABLE" or "DEFAULT VALUES"
2297          */
2298         else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
2299                          pg_strcasecmp(prev2_wd, "INTO") == 0)
2300         {
2301                 static const char *const list_INSERT[] =
2302                 {"(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL};
2303
2304                 COMPLETE_WITH_LIST(list_INSERT);
2305         }
2306
2307         /*
2308          * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
2309          * "TABLE"
2310          */
2311         else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
2312                          pg_strcasecmp(prev3_wd, "INTO") == 0 &&
2313                          prev_wd[strlen(prev_wd) - 1] == ')')
2314         {
2315                 static const char *const list_INSERT[] =
2316                 {"SELECT", "TABLE", "VALUES", NULL};
2317
2318                 COMPLETE_WITH_LIST(list_INSERT);
2319         }
2320
2321         /* Insert an open parenthesis after "VALUES" */
2322         else if (pg_strcasecmp(prev_wd, "VALUES") == 0 &&
2323                          pg_strcasecmp(prev2_wd, "DEFAULT") != 0)
2324                 COMPLETE_WITH_CONST("(");
2325
2326 /* LOCK */
2327         /* Complete LOCK [TABLE] with a list of tables */
2328         else if (pg_strcasecmp(prev_wd, "LOCK") == 0)
2329                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2330                                                                    " UNION SELECT 'TABLE'");
2331         else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
2332                          pg_strcasecmp(prev2_wd, "LOCK") == 0)
2333                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
2334
2335         /* For the following, handle the case of a single table only for now */
2336
2337         /* Complete LOCK [TABLE] <table> with "IN" */
2338         else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 &&
2339                           pg_strcasecmp(prev_wd, "TABLE")) ||
2340                          (pg_strcasecmp(prev2_wd, "TABLE") == 0 &&
2341                           pg_strcasecmp(prev3_wd, "LOCK") == 0))
2342                 COMPLETE_WITH_CONST("IN");
2343
2344         /* Complete LOCK [TABLE] <table> IN with a lock mode */
2345         else if (pg_strcasecmp(prev_wd, "IN") == 0 &&
2346                          (pg_strcasecmp(prev3_wd, "LOCK") == 0 ||
2347                           (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
2348                            pg_strcasecmp(prev4_wd, "LOCK") == 0)))
2349         {
2350                 static const char *const lock_modes[] =
2351                 {"ACCESS SHARE MODE",
2352                         "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
2353                         "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
2354                         "SHARE ROW EXCLUSIVE MODE",
2355                 "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL};
2356
2357                 COMPLETE_WITH_LIST(lock_modes);
2358         }
2359
2360 /* NOTIFY */
2361         else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
2362                 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'");
2363
2364 /* OPTIONS */
2365         else if (pg_strcasecmp(prev_wd, "OPTIONS") == 0)
2366                 COMPLETE_WITH_CONST("(");
2367
2368 /* OWNER TO  - complete with available roles */
2369         else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
2370                          pg_strcasecmp(prev_wd, "TO") == 0)
2371                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2372
2373 /* ORDER BY */
2374         else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
2375                          pg_strcasecmp(prev_wd, "ORDER") == 0)
2376                 COMPLETE_WITH_CONST("BY");
2377         else if (pg_strcasecmp(prev4_wd, "FROM") == 0 &&
2378                          pg_strcasecmp(prev2_wd, "ORDER") == 0 &&
2379                          pg_strcasecmp(prev_wd, "BY") == 0)
2380                 COMPLETE_WITH_ATTR(prev3_wd, "");
2381
2382 /* PREPARE xx AS */
2383         else if (pg_strcasecmp(prev_wd, "AS") == 0 &&
2384                          pg_strcasecmp(prev3_wd, "PREPARE") == 0)
2385         {
2386                 static const char *const list_PREPARE[] =
2387                 {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
2388
2389                 COMPLETE_WITH_LIST(list_PREPARE);
2390         }
2391
2392 /*
2393  * PREPARE TRANSACTION is missing on purpose. It's intended for transaction
2394  * managers, not for manual use in interactive sessions.
2395  */
2396
2397 /* REASSIGN OWNED BY xxx TO yyy */
2398         else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
2399                 COMPLETE_WITH_CONST("OWNED");
2400         else if (pg_strcasecmp(prev_wd, "OWNED") == 0 &&
2401                          pg_strcasecmp(prev2_wd, "REASSIGN") == 0)
2402                 COMPLETE_WITH_CONST("BY");
2403         else if (pg_strcasecmp(prev_wd, "BY") == 0 &&
2404                          pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
2405                          pg_strcasecmp(prev3_wd, "REASSIGN") == 0)
2406                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2407         else if (pg_strcasecmp(prev2_wd, "BY") == 0 &&
2408                          pg_strcasecmp(prev3_wd, "OWNED") == 0 &&
2409                          pg_strcasecmp(prev4_wd, "REASSIGN") == 0)
2410                 COMPLETE_WITH_CONST("TO");
2411         else if (pg_strcasecmp(prev_wd, "TO") == 0 &&
2412                          pg_strcasecmp(prev3_wd, "BY") == 0 &&
2413                          pg_strcasecmp(prev4_wd, "OWNED") == 0 &&
2414                          pg_strcasecmp(prev5_wd, "REASSIGN") == 0)
2415                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2416
2417 /* REINDEX */
2418         else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
2419         {
2420                 static const char *const list_REINDEX[] =
2421                 {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL};
2422
2423                 COMPLETE_WITH_LIST(list_REINDEX);
2424         }
2425         else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0)
2426         {
2427                 if (pg_strcasecmp(prev_wd, "TABLE") == 0)
2428                         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2429                 else if (pg_strcasecmp(prev_wd, "INDEX") == 0)
2430                         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
2431                 else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 ||
2432                                  pg_strcasecmp(prev_wd, "DATABASE") == 0)
2433                         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
2434         }
2435
2436 /* SECURITY LABEL */
2437         else if (pg_strcasecmp(prev_wd, "SECURITY") == 0)
2438                 COMPLETE_WITH_CONST("LABEL");
2439         else if (pg_strcasecmp(prev2_wd, "SECURITY") == 0 &&
2440                          pg_strcasecmp(prev_wd, "LABEL") == 0)
2441         {
2442                 static const char *const list_SECURITY_LABEL_preposition[] =
2443                         {"ON", "FOR"};
2444                 COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition);
2445         }
2446         else if (pg_strcasecmp(prev4_wd, "SECURITY") == 0 &&
2447                          pg_strcasecmp(prev3_wd, "LABEL") == 0 &&
2448                          pg_strcasecmp(prev2_wd, "FOR") == 0)
2449                 COMPLETE_WITH_CONST("ON");
2450         else if ((pg_strcasecmp(prev3_wd, "SECURITY") == 0 &&
2451                           pg_strcasecmp(prev2_wd, "LABEL") == 0 &&
2452                           pg_strcasecmp(prev_wd, "ON") == 0) ||
2453                  (pg_strcasecmp(prev5_wd, "SECURITY") == 0 &&
2454                           pg_strcasecmp(prev4_wd, "LABEL") == 0 &&
2455                           pg_strcasecmp(prev3_wd, "FOR") == 0 &&
2456                           pg_strcasecmp(prev_wd, "ON") == 0))
2457         {
2458                 static const char *const list_SECURITY_LABEL[] =
2459                 {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN",
2460                  "AGGREGATE", "FUNCTION", "DOMAIN", "LARGE OBJECT",
2461                 NULL};
2462
2463                 COMPLETE_WITH_LIST(list_SECURITY_LABEL);
2464         }
2465         else if (pg_strcasecmp(prev5_wd, "SECURITY") == 0 &&
2466                          pg_strcasecmp(prev4_wd, "LABEL") == 0 &&
2467                          pg_strcasecmp(prev3_wd, "ON") == 0)
2468                 COMPLETE_WITH_CONST("IS");
2469
2470 /* SELECT */
2471         /* naah . . . */
2472
2473 /* SET, RESET, SHOW */
2474         /* Complete with a variable name */
2475         else if ((pg_strcasecmp(prev_wd, "SET") == 0 &&
2476                           pg_strcasecmp(prev3_wd, "UPDATE") != 0) ||
2477                          pg_strcasecmp(prev_wd, "RESET") == 0)
2478                 COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
2479         else if (pg_strcasecmp(prev_wd, "SHOW") == 0)
2480                 COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
2481         /* Complete "SET TRANSACTION" */
2482         else if ((pg_strcasecmp(prev2_wd, "SET") == 0 &&
2483                           pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
2484                          || (pg_strcasecmp(prev2_wd, "START") == 0
2485                                  && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
2486                          || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
2487                                  && pg_strcasecmp(prev_wd, "WORK") == 0)
2488                          || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
2489                                  && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
2490                          || (pg_strcasecmp(prev4_wd, "SESSION") == 0
2491                                  && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0
2492                                  && pg_strcasecmp(prev2_wd, "AS") == 0
2493                                  && pg_strcasecmp(prev_wd, "TRANSACTION") == 0))
2494         {
2495                 static const char *const my_list[] =
2496                 {"ISOLATION LEVEL", "READ", NULL};
2497
2498                 COMPLETE_WITH_LIST(my_list);
2499         }
2500         else if ((pg_strcasecmp(prev3_wd, "SET") == 0
2501                           || pg_strcasecmp(prev3_wd, "BEGIN") == 0
2502                           || pg_strcasecmp(prev3_wd, "START") == 0
2503                           || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0
2504                                   && pg_strcasecmp(prev3_wd, "AS") == 0))
2505                          && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0
2506                                  || pg_strcasecmp(prev2_wd, "WORK") == 0)
2507                          && pg_strcasecmp(prev_wd, "ISOLATION") == 0)
2508                 COMPLETE_WITH_CONST("LEVEL");
2509         else if ((pg_strcasecmp(prev4_wd, "SET") == 0
2510                           || pg_strcasecmp(prev4_wd, "BEGIN") == 0
2511                           || pg_strcasecmp(prev4_wd, "START") == 0
2512                           || pg_strcasecmp(prev4_wd, "AS") == 0)
2513                          && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0
2514                                  || pg_strcasecmp(prev3_wd, "WORK") == 0)
2515                          && pg_strcasecmp(prev2_wd, "ISOLATION") == 0
2516                          && pg_strcasecmp(prev_wd, "LEVEL") == 0)
2517         {
2518                 static const char *const my_list[] =
2519                 {"READ", "REPEATABLE", "SERIALIZABLE", NULL};
2520
2521                 COMPLETE_WITH_LIST(my_list);
2522         }
2523         else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
2524                           pg_strcasecmp(prev4_wd, "WORK") == 0) &&
2525                          pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
2526                          pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
2527                          pg_strcasecmp(prev_wd, "READ") == 0)
2528         {
2529                 static const char *const my_list[] =
2530                 {"UNCOMMITTED", "COMMITTED", NULL};
2531
2532                 COMPLETE_WITH_LIST(my_list);
2533         }
2534         else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
2535                           pg_strcasecmp(prev4_wd, "WORK") == 0) &&
2536                          pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
2537                          pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
2538                          pg_strcasecmp(prev_wd, "REPEATABLE") == 0)
2539                 COMPLETE_WITH_CONST("READ");
2540         else if ((pg_strcasecmp(prev3_wd, "SET") == 0 ||
2541                           pg_strcasecmp(prev3_wd, "BEGIN") == 0 ||
2542                           pg_strcasecmp(prev3_wd, "START") == 0 ||
2543                           pg_strcasecmp(prev3_wd, "AS") == 0) &&
2544                          (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 ||
2545                           pg_strcasecmp(prev2_wd, "WORK") == 0) &&
2546                          pg_strcasecmp(prev_wd, "READ") == 0)
2547         {
2548                 static const char *const my_list[] =
2549                 {"ONLY", "WRITE", NULL};
2550
2551                 COMPLETE_WITH_LIST(my_list);
2552         }
2553         /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
2554         else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
2555                          pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
2556         {
2557                 static const char *const constraint_list[] =
2558                 {"DEFERRED", "IMMEDIATE", NULL};
2559
2560                 COMPLETE_WITH_LIST(constraint_list);
2561         }
2562         /* Complete SET ROLE */
2563         else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2564                          pg_strcasecmp(prev_wd, "ROLE") == 0)
2565                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2566         /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
2567         else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2568                          pg_strcasecmp(prev_wd, "SESSION") == 0)
2569         {
2570                 static const char *const my_list[] =
2571                 {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL};
2572
2573                 COMPLETE_WITH_LIST(my_list);
2574         }
2575         /* Complete SET SESSION AUTHORIZATION with username */
2576         else if (pg_strcasecmp(prev3_wd, "SET") == 0
2577                          && pg_strcasecmp(prev2_wd, "SESSION") == 0
2578                          && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
2579                 COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
2580         /* Complete RESET SESSION with AUTHORIZATION */
2581         else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
2582                          pg_strcasecmp(prev_wd, "SESSION") == 0)
2583                 COMPLETE_WITH_CONST("AUTHORIZATION");
2584         /* Complete SET <var> with "TO" */
2585         else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2586                          pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
2587                          pg_strcasecmp(prev_wd, "TABLESPACE") != 0 &&
2588                          pg_strcasecmp(prev_wd, "SCHEMA") != 0 &&
2589                          prev_wd[strlen(prev_wd) - 1] != ')' &&
2590                          pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
2591                 COMPLETE_WITH_CONST("TO");
2592         /* Suggest possible variable values */
2593         else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
2594                          (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
2595         {
2596                 if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
2597                 {
2598                         static const char *const my_list[] =
2599                         {"ISO", "SQL", "Postgres", "German",
2600                                 "YMD", "DMY", "MDY",
2601                                 "US", "European", "NonEuropean",
2602                         "DEFAULT", NULL};
2603
2604                         COMPLETE_WITH_LIST(my_list);
2605                 }
2606                 else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0)
2607                 {
2608                         static const char *const my_list[] =
2609                         {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL};
2610
2611                         COMPLETE_WITH_LIST(my_list);
2612                 }
2613                 else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
2614                 {
2615                         static const char *const my_list[] =
2616                         {"ON", "OFF", "DEFAULT", NULL};
2617
2618                         COMPLETE_WITH_LIST(my_list);
2619                 }
2620                 else
2621                 {
2622                         static const char *const my_list[] =
2623                         {"DEFAULT", NULL};
2624
2625                         COMPLETE_WITH_LIST(my_list);
2626                 }
2627         }
2628
2629 /* START TRANSACTION */
2630         else if (pg_strcasecmp(prev_wd, "START") == 0)
2631                 COMPLETE_WITH_CONST("TRANSACTION");
2632
2633 /* TRUNCATE */
2634         else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0)
2635                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2636
2637 /* UNLISTEN */
2638         else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0)
2639                 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 '*'");
2640
2641 /* UPDATE */
2642         /* If prev. word is UPDATE suggest a list of tables */
2643         else if (pg_strcasecmp(prev_wd, "UPDATE") == 0)
2644                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
2645         /* Complete UPDATE <table> with "SET" */
2646         else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0)
2647                 COMPLETE_WITH_CONST("SET");
2648
2649         /*
2650          * If the previous word is SET (and it wasn't caught above as the _first_
2651          * word) the word before it was (hopefully) a table name and we'll now
2652          * make a list of attributes.
2653          */
2654         else if (pg_strcasecmp(prev_wd, "SET") == 0)
2655                 COMPLETE_WITH_ATTR(prev2_wd, "");
2656
2657 /* UPDATE xx SET yy = */
2658         else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2659                          pg_strcasecmp(prev4_wd, "UPDATE") == 0)
2660                 COMPLETE_WITH_CONST("=");
2661
2662 /* USER MAPPING */
2663         else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 ||
2664                           pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
2665                           pg_strcasecmp(prev3_wd, "DROP") == 0) &&
2666                          pg_strcasecmp(prev2_wd, "USER") == 0 &&
2667                          pg_strcasecmp(prev_wd, "MAPPING") == 0)
2668                 COMPLETE_WITH_CONST("FOR");
2669         else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
2670                          pg_strcasecmp(prev3_wd, "USER") == 0 &&
2671                          pg_strcasecmp(prev2_wd, "MAPPING") == 0 &&
2672                          pg_strcasecmp(prev_wd, "FOR") == 0)
2673                 COMPLETE_WITH_QUERY(Query_for_list_of_roles
2674                                                         " UNION SELECT 'CURRENT_USER'"
2675                                                         " UNION SELECT 'PUBLIC'"
2676                                                         " UNION SELECT 'USER'");
2677         else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 ||
2678                           pg_strcasecmp(prev4_wd, "DROP") == 0) &&
2679                          pg_strcasecmp(prev3_wd, "USER") == 0 &&
2680                          pg_strcasecmp(prev2_wd, "MAPPING") == 0 &&
2681                          pg_strcasecmp(prev_wd, "FOR") == 0)
2682                 COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
2683         else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 ||
2684                           pg_strcasecmp(prev5_wd, "ALTER") == 0 ||
2685                           pg_strcasecmp(prev5_wd, "DROP") == 0) &&
2686                          pg_strcasecmp(prev4_wd, "USER") == 0 &&
2687                          pg_strcasecmp(prev3_wd, "MAPPING") == 0 &&
2688                          pg_strcasecmp(prev2_wd, "FOR") == 0)
2689                 COMPLETE_WITH_CONST("SERVER");
2690
2691 /*
2692  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
2693  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
2694  */
2695         else if (pg_strcasecmp(prev_wd, "VACUUM") == 0)
2696                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2697                                                                    " UNION SELECT 'FULL'"
2698                                                                    " UNION SELECT 'FREEZE'"
2699                                                                    " UNION SELECT 'ANALYZE'"
2700                                                                    " UNION SELECT 'VERBOSE'");
2701         else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2702                          (pg_strcasecmp(prev_wd, "FULL") == 0 ||
2703                           pg_strcasecmp(prev_wd, "FREEZE") == 0))
2704                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2705                                                                    " UNION SELECT 'ANALYZE'"
2706                                                                    " UNION SELECT 'VERBOSE'");
2707         else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
2708                          pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
2709                          (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
2710                           pg_strcasecmp(prev2_wd, "FREEZE") == 0))
2711                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2712                                                                    " UNION SELECT 'VERBOSE'");
2713         else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
2714                          pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
2715                          (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
2716                           pg_strcasecmp(prev2_wd, "FREEZE") == 0))
2717                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2718                                                                    " UNION SELECT 'ANALYZE'");
2719         else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2720                          pg_strcasecmp(prev_wd, "VERBOSE") == 0)
2721                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2722                                                                    " UNION SELECT 'ANALYZE'");
2723         else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2724                          pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2725                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2726                                                                    " UNION SELECT 'VERBOSE'");
2727         else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
2728                           pg_strcasecmp(prev2_wd, "VERBOSE") == 0) ||
2729                          (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
2730                           pg_strcasecmp(prev2_wd, "ANALYZE") == 0))
2731                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2732
2733 /* WITH [RECURSIVE] */
2734         else if (pg_strcasecmp(prev_wd, "WITH") == 0)
2735                 COMPLETE_WITH_CONST("RECURSIVE");
2736
2737 /* ANALYZE */
2738         /* If the previous word is ANALYZE, produce list of tables */
2739         else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2740                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2741
2742 /* WHERE */
2743         /* Simple case of the word before the where being the table name */
2744         else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
2745                 COMPLETE_WITH_ATTR(prev2_wd, "");
2746
2747 /* ... FROM ... */
2748 /* TODO: also include SRF ? */
2749         else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
2750                          pg_strcasecmp(prev3_wd, "COPY") != 0 &&
2751                          pg_strcasecmp(prev3_wd, "\\copy") != 0)
2752                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf, NULL);
2753
2754 /* Backslash commands */
2755 /* TODO:  \dc \dd \dl */
2756         else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
2757                 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
2758
2759         else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0)
2760                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
2761         else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0)
2762                 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
2763         else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0)
2764                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
2765         else if (strncmp(prev_wd, "\\des", strlen("\\des")) == 0)
2766                 COMPLETE_WITH_QUERY(Query_for_list_of_servers);
2767         else if (strncmp(prev_wd, "\\deu", strlen("\\deu")) == 0)
2768                 COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
2769         else if (strncmp(prev_wd, "\\dew", strlen("\\dew")) == 0)
2770                 COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
2771
2772         else if (strncmp(prev_wd, "\\df", strlen("\\df")) == 0)
2773                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2774         else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0)
2775                 COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
2776         else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0)
2777                 COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
2778         else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0)
2779                 COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
2780         /* must be at end of \dF */
2781         else if (strncmp(prev_wd, "\\dF", strlen("\\dF")) == 0)
2782                 COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
2783
2784         else if (strncmp(prev_wd, "\\di", strlen("\\di")) == 0)
2785                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
2786         else if (strncmp(prev_wd, "\\dL", strlen("\\dL")) == 0)
2787                 COMPLETE_WITH_QUERY(Query_for_list_of_languages);
2788         else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0)
2789                 COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
2790         else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
2791                          || strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
2792                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvf, NULL);
2793         else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
2794                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
2795         else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
2796                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2797         else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0)
2798                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
2799         else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 0
2800                          || (strncmp(prev_wd, "\\dg", strlen("\\dg")) == 0))
2801                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2802         else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0)
2803                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
2804
2805         /* must be at end of \d list */
2806         else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
2807                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisvf, NULL);
2808
2809         else if (strcmp(prev_wd, "\\ef") == 0)
2810                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2811
2812         else if (strcmp(prev_wd, "\\encoding") == 0)
2813                 COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
2814         else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
2815                 COMPLETE_WITH_LIST(sql_commands);
2816         else if (strcmp(prev_wd, "\\password") == 0)
2817                 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2818         else if (strcmp(prev_wd, "\\pset") == 0)
2819         {
2820                 static const char *const my_list[] =
2821                 {"format", "border", "expanded",
2822                         "null", "fieldsep", "tuples_only", "title", "tableattr",
2823                 "linestyle", "pager", "recordsep", NULL};
2824
2825                 COMPLETE_WITH_LIST(my_list);
2826         }
2827         else if (strcmp(prev_wd, "\\set") == 0)
2828         {
2829                 matches = complete_from_variables(text, "", "");
2830         }
2831         else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
2832                 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2833         else if (strcmp(prev_wd, "\\cd") == 0 ||
2834                          strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
2835                          strcmp(prev_wd, "\\g") == 0 ||
2836                   strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
2837                          strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
2838                          strcmp(prev_wd, "\\s") == 0 ||
2839                          strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
2840                 )
2841                 matches = completion_matches(text, filename_completion_function);
2842
2843         /*
2844          * Finally, we look through the list of "things", such as TABLE, INDEX and
2845          * check if that was the previous word. If so, execute the query to get a
2846          * list of them.
2847          */
2848         else
2849         {
2850                 int                     i;
2851
2852                 for (i = 0; words_after_create[i].name; i++)
2853                 {
2854                         if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)
2855                         {
2856                                 if (words_after_create[i].query)
2857                                         COMPLETE_WITH_QUERY(words_after_create[i].query);
2858                                 else if (words_after_create[i].squery)
2859                                         COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
2860                                                                                            NULL);
2861                                 break;
2862                         }
2863                 }
2864         }
2865
2866         /*
2867          * If we still don't have anything to match we have to fabricate some sort
2868          * of default list. If we were to just return NULL, readline automatically
2869          * attempts filename completion, and that's usually no good.
2870          */
2871         if (matches == NULL)
2872         {
2873                 COMPLETE_WITH_CONST("");
2874 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
2875                 rl_completion_append_character = '\0';
2876 #endif
2877         }
2878
2879         /* free storage */
2880         free(prev_wd);
2881         free(prev2_wd);
2882         free(prev3_wd);
2883         free(prev4_wd);
2884         free(prev5_wd);
2885
2886         /* Return our Grand List O' Matches */
2887         return matches;
2888 }
2889
2890
2891 /*
2892  * GENERATOR FUNCTIONS
2893  *
2894  * These functions do all the actual work of completing the input. They get
2895  * passed the text so far and the count how many times they have been called
2896  * so far with the same text.
2897  * If you read the above carefully, you'll see that these don't get called
2898  * directly but through the readline interface.
2899  * The return value is expected to be the full completion of the text, going
2900  * through a list each time, or NULL if there are no more matches. The string
2901  * will be free()'d by readline, so you must run it through strdup() or
2902  * something of that sort.
2903  */
2904
2905 /*
2906  * Common routine for create_command_generator and drop_command_generator.
2907  * Entries that have 'excluded' flags are not returned.
2908  */
2909 static char *
2910 create_or_drop_command_generator(const char *text, int state, bits32 excluded)
2911 {
2912         static int      list_index,
2913                                 string_length;
2914         const char *name;
2915
2916         /* If this is the first time for this completion, init some values */
2917         if (state == 0)
2918         {
2919                 list_index = 0;
2920                 string_length = strlen(text);
2921         }
2922
2923         /* find something that matches */
2924         while ((name = words_after_create[list_index++].name))
2925         {
2926                 if ((pg_strncasecmp(name, text, string_length) == 0) &&
2927                         !(words_after_create[list_index - 1].flags & excluded))
2928                         return pg_strdup(name);
2929         }
2930         /* if nothing matches, return NULL */
2931         return NULL;
2932 }
2933
2934 /*
2935  * This one gives you one from a list of things you can put after CREATE
2936  * as defined above.
2937  */
2938 static char *
2939 create_command_generator(const char *text, int state)
2940 {
2941         return create_or_drop_command_generator(text, state, THING_NO_CREATE);
2942 }
2943
2944 /*
2945  * This function gives you a list of things you can put after a DROP command.
2946  */
2947 static char *
2948 drop_command_generator(const char *text, int state)
2949 {
2950         return create_or_drop_command_generator(text, state, THING_NO_DROP);
2951 }
2952
2953 /* The following two functions are wrappers for _complete_from_query */
2954
2955 static char *
2956 complete_from_query(const char *text, int state)
2957 {
2958         return _complete_from_query(0, text, state);
2959 }
2960
2961 static char *
2962 complete_from_schema_query(const char *text, int state)
2963 {
2964         return _complete_from_query(1, text, state);
2965 }
2966
2967
2968 /*
2969  * This creates a list of matching things, according to a query pointed to
2970  * by completion_charp.
2971  * The query can be one of two kinds:
2972  *
2973  * 1. A simple query which must contain a %d and a %s, which will be replaced
2974  * by the string length of the text and the text itself. The query may also
2975  * have up to four more %s in it; the first two such will be replaced by the
2976  * value of completion_info_charp, the next two by the value of
2977  * completion_info_charp2.
2978  *
2979  * 2. A schema query used for completion of both schema and relation names.
2980  * These are more complex and must contain in the following order:
2981  * %d %s %d %s %d %s %s %d %s
2982  * where %d is the string length of the text and %s the text itself.
2983  *
2984  * It is assumed that strings should be escaped to become SQL literals
2985  * (that is, what is in the query is actually ... '%s' ...)
2986  *
2987  * See top of file for examples of both kinds of query.
2988  */
2989 static char *
2990 _complete_from_query(int is_schema_query, const char *text, int state)
2991 {
2992         static int      list_index,
2993                                 string_length;
2994         static PGresult *result = NULL;
2995
2996         /*
2997          * If this is the first time for this completion, we fetch a list of our
2998          * "things" from the backend.
2999          */
3000         if (state == 0)
3001         {
3002                 PQExpBufferData query_buffer;
3003                 char       *e_text;
3004                 char       *e_info_charp;
3005                 char       *e_info_charp2;
3006
3007                 list_index = 0;
3008                 string_length = strlen(text);
3009
3010                 /* Free any prior result */
3011                 PQclear(result);
3012                 result = NULL;
3013
3014                 /* Set up suitably-escaped copies of textual inputs */
3015                 e_text = pg_malloc(string_length * 2 + 1);
3016                 PQescapeString(e_text, text, string_length);
3017
3018                 if (completion_info_charp)
3019                 {
3020                         size_t          charp_len;
3021
3022                         charp_len = strlen(completion_info_charp);
3023                         e_info_charp = pg_malloc(charp_len * 2 + 1);
3024                         PQescapeString(e_info_charp, completion_info_charp,
3025                                                    charp_len);
3026                 }
3027                 else
3028                         e_info_charp = NULL;
3029
3030                 if (completion_info_charp2)
3031                 {
3032                         size_t          charp_len;
3033
3034                         charp_len = strlen(completion_info_charp2);
3035                         e_info_charp2 = pg_malloc(charp_len * 2 + 1);
3036                         PQescapeString(e_info_charp2, completion_info_charp2,
3037                                                    charp_len);
3038                 }
3039                 else
3040                         e_info_charp2 = NULL;
3041
3042                 initPQExpBuffer(&query_buffer);
3043
3044                 if (is_schema_query)
3045                 {
3046                         /* completion_squery gives us the pieces to assemble */
3047                         const char *qualresult = completion_squery->qualresult;
3048
3049                         if (qualresult == NULL)
3050                                 qualresult = completion_squery->result;
3051
3052                         /* Get unqualified names matching the input-so-far */
3053                         appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
3054                                                           completion_squery->result,
3055                                                           completion_squery->catname);
3056                         if (completion_squery->selcondition)
3057                                 appendPQExpBuffer(&query_buffer, "%s AND ",
3058                                                                   completion_squery->selcondition);
3059                         appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
3060                                                           completion_squery->result,
3061                                                           string_length, e_text);
3062                         appendPQExpBuffer(&query_buffer, " AND %s",
3063                                                           completion_squery->viscondition);
3064
3065                         /*
3066                          * When fetching relation names, suppress system catalogs unless
3067                          * the input-so-far begins with "pg_".  This is a compromise
3068                          * between not offering system catalogs for completion at all, and
3069                          * having them swamp the result when the input is just "p".
3070                          */
3071                         if (strcmp(completion_squery->catname,
3072                                            "pg_catalog.pg_class c") == 0 &&
3073                                 strncmp(text, "pg_", 3) !=0)
3074                         {
3075                                 appendPQExpBuffer(&query_buffer,
3076                                                                   " AND c.relnamespace <> (SELECT oid FROM"
3077                                    " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
3078                         }
3079
3080                         /*
3081                          * Add in matching schema names, but only if there is more than
3082                          * one potential match among schema names.
3083                          */
3084                         appendPQExpBuffer(&query_buffer, "\nUNION\n"
3085                                                    "SELECT pg_catalog.quote_ident(n.nspname) || '.' "
3086                                                           "FROM pg_catalog.pg_namespace n "
3087                                                           "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'",
3088                                                           string_length, e_text);
3089                         appendPQExpBuffer(&query_buffer,
3090                                                           " AND (SELECT pg_catalog.count(*)"
3091                                                           " FROM pg_catalog.pg_namespace"
3092                         " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
3093                                                           " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1",
3094                                                           string_length, e_text);
3095
3096                         /*
3097                          * Add in matching qualified names, but only if there is exactly
3098                          * one schema matching the input-so-far.
3099                          */
3100                         appendPQExpBuffer(&query_buffer, "\nUNION\n"
3101                                          "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
3102                                                           "FROM %s, pg_catalog.pg_namespace n "
3103                                                           "WHERE %s = n.oid AND ",
3104                                                           qualresult,
3105                                                           completion_squery->catname,
3106                                                           completion_squery->namespace);
3107                         if (completion_squery->selcondition)
3108                                 appendPQExpBuffer(&query_buffer, "%s AND ",
3109                                                                   completion_squery->selcondition);
3110                         appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
3111                                                           qualresult,
3112                                                           string_length, e_text);
3113
3114                         /*
3115                          * This condition exploits the single-matching-schema rule to
3116                          * speed up the query
3117                          */
3118                         appendPQExpBuffer(&query_buffer,
3119                         " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="
3120                                                           " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)",
3121                                                           string_length, e_text);
3122                         appendPQExpBuffer(&query_buffer,
3123                                                           " AND (SELECT pg_catalog.count(*)"
3124                                                           " FROM pg_catalog.pg_namespace"
3125                         " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
3126                                                           " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1",
3127                                                           string_length, e_text);
3128
3129                         /* If an addon query was provided, use it */
3130                         if (completion_charp)
3131                                 appendPQExpBuffer(&query_buffer, "\n%s", completion_charp);
3132                 }
3133                 else
3134                 {
3135                         /* completion_charp is an sprintf-style format string */
3136                         appendPQExpBuffer(&query_buffer, completion_charp,
3137                                                           string_length, e_text,
3138                                                           e_info_charp, e_info_charp,
3139                                                           e_info_charp2, e_info_charp2);
3140                 }
3141
3142                 /* Limit the number of records in the result */
3143                 appendPQExpBuffer(&query_buffer, "\nLIMIT %d",
3144                                                   completion_max_records);
3145
3146                 result = exec_query(query_buffer.data);
3147
3148                 termPQExpBuffer(&query_buffer);
3149                 free(e_text);
3150                 if (e_info_charp)
3151                         free(e_info_charp);
3152                 if (e_info_charp2)
3153                         free(e_info_charp2);
3154         }
3155
3156         /* Find something that matches */
3157         if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
3158         {
3159                 const char *item;
3160
3161                 while (list_index < PQntuples(result) &&
3162                            (item = PQgetvalue(result, list_index++, 0)))
3163                         if (pg_strncasecmp(text, item, string_length) == 0)
3164                                 return pg_strdup(item);
3165         }
3166
3167         /* If nothing matches, free the db structure and return null */
3168         PQclear(result);
3169         result = NULL;
3170         return NULL;
3171 }
3172
3173
3174 /*
3175  * This function returns in order one of a fixed, NULL pointer terminated list
3176  * of strings (if matching). This can be used if there are only a fixed number
3177  * SQL words that can appear at certain spot.
3178  */
3179 static char *
3180 complete_from_list(const char *text, int state)
3181 {
3182         static int      string_length,
3183                                 list_index,
3184                                 matches;
3185         static bool casesensitive;
3186         const char *item;
3187
3188         /* need to have a list */
3189         psql_assert(completion_charpp);
3190
3191         /* Initialization */
3192         if (state == 0)
3193         {
3194                 list_index = 0;
3195                 string_length = strlen(text);
3196                 casesensitive = true;
3197                 matches = 0;
3198         }
3199
3200         while ((item = completion_charpp[list_index++]))
3201         {
3202                 /* First pass is case sensitive */
3203                 if (casesensitive && strncmp(text, item, string_length) == 0)
3204                 {
3205                         matches++;
3206                         return pg_strdup(item);
3207                 }
3208
3209                 /* Second pass is case insensitive, don't bother counting matches */
3210                 if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
3211                         return pg_strdup(item);
3212         }
3213
3214         /*
3215          * No matches found. If we're not case insensitive already, lets switch to
3216          * being case insensitive and try again
3217          */
3218         if (casesensitive && matches == 0)
3219         {
3220                 casesensitive = false;
3221                 list_index = 0;
3222                 state++;
3223                 return complete_from_list(text, state);
3224         }
3225
3226         /* If no more matches, return null. */
3227         return NULL;
3228 }
3229
3230
3231 /*
3232  * This function returns one fixed string the first time even if it doesn't
3233  * match what's there, and nothing the second time. This should be used if
3234  * there is only one possibility that can appear at a certain spot, so
3235  * misspellings will be overwritten.  The string to be passed must be in
3236  * completion_charp.
3237  */
3238 static char *
3239 complete_from_const(const char *text, int state)
3240 {
3241         (void) text;                            /* We don't care about what was entered
3242                                                                  * already. */
3243
3244         psql_assert(completion_charp);
3245         if (state == 0)
3246                 return pg_strdup(completion_charp);
3247         else
3248                 return NULL;
3249 }
3250
3251
3252 /*
3253  * This function supports completion with the name of a psql variable.
3254  * The variable names can be prefixed and suffixed with additional text
3255  * to support quoting usages.
3256  */
3257 static char **
3258 complete_from_variables(char *text, const char *prefix, const char *suffix)
3259 {
3260         char      **matches;
3261         int                     overhead = strlen(prefix) + strlen(suffix) + 1;
3262         const char **varnames;
3263         int                     nvars = 0;
3264         int                     maxvars = 100;
3265         int                     i;
3266         struct _variable *ptr;
3267
3268         varnames = (const char **) pg_malloc((maxvars + 1) * sizeof(char *));
3269
3270         for (ptr = pset.vars->next; ptr; ptr = ptr->next)
3271         {
3272                 char   *buffer;
3273
3274                 if (nvars >= maxvars)
3275                 {
3276                         maxvars *= 2;
3277                         varnames = (const char **) realloc(varnames,
3278                                                                                            (maxvars + 1) * sizeof(char *));
3279                         if (!varnames)
3280                         {
3281                                 psql_error("out of memory\n");
3282                                 exit(EXIT_FAILURE);
3283                         }
3284                 }
3285
3286                 buffer = (char *) pg_malloc(strlen(ptr->name) + overhead);
3287                 sprintf(buffer, "%s%s%s", prefix, ptr->name, suffix);
3288                 varnames[nvars++] = buffer;
3289         }
3290
3291         varnames[nvars] = NULL;
3292         COMPLETE_WITH_LIST(varnames);
3293
3294         for (i = 0; i < nvars; i++)
3295                 free((void *) varnames[i]);
3296         free(varnames);
3297
3298         return matches;
3299 }
3300
3301
3302 /* HELPER FUNCTIONS */
3303
3304
3305 /*
3306  * Execute a query and report any errors. This should be the preferred way of
3307  * talking to the database in this file.
3308  */
3309 static PGresult *
3310 exec_query(const char *query)
3311 {
3312         PGresult   *result;
3313
3314         if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK)
3315                 return NULL;
3316
3317         result = PQexec(pset.db, query);
3318
3319         if (PQresultStatus(result) != PGRES_TUPLES_OK)
3320         {
3321 #ifdef NOT_USED
3322                 psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
3323                                    PQerrorMessage(pset.db), query);
3324 #endif
3325                 PQclear(result);
3326                 result = NULL;
3327         }
3328
3329         return result;
3330 }
3331
3332
3333 /*
3334  * Return the word (space delimited) before point. Set skip > 0 to
3335  * skip that many words; e.g. skip=1 finds the word before the
3336  * previous one. Return value is NULL or a malloc'ed string.
3337  */
3338 static char *
3339 previous_word(int point, int skip)
3340 {
3341         int                     i,
3342                                 start = 0,
3343                                 end = -1,
3344                                 inquotes = 0;
3345         char       *s;
3346         const char *buf = rl_line_buffer;       /* alias */
3347
3348         /* first we look for a space or a parenthesis before the current word */
3349         for (i = point - 1; i >= 0; i--)
3350                 if (strchr(WORD_BREAKS, buf[i]))
3351                         break;
3352         point = i;
3353
3354         while (skip-- >= 0)
3355         {
3356                 int                     parentheses = 0;
3357
3358                 /* now find the first non-space which then constitutes the end */
3359                 for (i = point; i >= 0; i--)
3360                         if (buf[i] != ' ')
3361                         {
3362                                 end = i;
3363                                 break;
3364                         }
3365
3366                 /*
3367                  * If no end found we return null, because there is no word before the
3368                  * point
3369                  */
3370                 if (end == -1)
3371                         return NULL;
3372
3373                 /*
3374                  * Otherwise we now look for the start. The start is either the last
3375                  * character before any space going backwards from the end, or it's
3376                  * simply character 0. We also handle open quotes and parentheses.
3377                  */
3378                 for (start = end; start > 0; start--)
3379                 {
3380                         if (buf[start] == '"')
3381                                 inquotes = !inquotes;
3382                         if (inquotes == 0)
3383                         {
3384                                 if (buf[start] == ')')
3385                                         parentheses++;
3386                                 else if (buf[start] == '(')
3387                                 {
3388                                         if (--parentheses <= 0)
3389                                                 break;
3390                                 }
3391                                 else if (parentheses == 0 &&
3392                                                  strchr(WORD_BREAKS, buf[start - 1]))
3393                                         break;
3394                         }
3395                 }
3396
3397                 point = start - 1;
3398         }
3399
3400         /* make a copy */
3401         s = pg_malloc(end - start + 2);
3402         strlcpy(s, &buf[start], end - start + 2);
3403
3404         return s;
3405 }
3406
3407 #ifdef NOT_USED
3408
3409 /*
3410  * Surround a string with single quotes. This works for both SQL and
3411  * psql internal. Currently disabled because it is reported not to
3412  * cooperate with certain versions of readline.
3413  */
3414 static char *
3415 quote_file_name(char *text, int match_type, char *quote_pointer)
3416 {
3417         char       *s;
3418         size_t          length;
3419
3420         (void) quote_pointer;           /* not used */
3421
3422         length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2);
3423         s = pg_malloc(length);
3424         s[0] = '\'';
3425         strcpy(s + 1, text);
3426         if (match_type == SINGLE_MATCH)
3427                 s[length - 2] = '\'';
3428         s[length - 1] = '\0';
3429         return s;
3430 }
3431
3432 static char *
3433 dequote_file_name(char *text, char quote_char)
3434 {
3435         char       *s;
3436         size_t          length;
3437
3438         if (!quote_char)
3439                 return pg_strdup(text);
3440
3441         length = strlen(text);
3442         s = pg_malloc(length - 2 + 1);
3443         strlcpy(s, text +1, length - 2 + 1);
3444
3445         return s;
3446 }
3447 #endif   /* NOT_USED */
3448
3449 #endif   /* USE_READLINE */