2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2010, PostgreSQL Global Development Group
6 * src/bin/psql/tab-complete.c
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.
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:
24 * set disable-completion on
27 * See `man 3 readline' or `info readline' for the full details. Also,
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.)
37 * - Table or attribute names with spaces in it may confuse it.
39 * - Quotes, parenthesis, and other funny characters are not handled
40 * all that gracefully.
41 *----------------------------------------------------------------------
44 #include "postgres_fe.h"
45 #include "tab-complete.h"
48 /* If we don't have this, we might as well forget about the whole thing: */
53 #include "pqexpbuffer.h"
56 #include "stringutils.h"
58 #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
59 #define filename_completion_function rl_filename_completion_function
61 /* missing in some header files */
62 extern char *filename_completion_function();
65 #ifdef HAVE_RL_COMPLETION_MATCHES
66 #define completion_matches rl_completion_matches
69 /* word break characters */
70 #define WORD_BREAKS "\t\n@$><=;|&{() "
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().
79 typedef struct SchemaQuery
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.
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.
93 const char *selcondition;
96 * Visibility condition --- which rows are visible without schema
97 * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)".
99 const char *viscondition;
102 * Namespace --- name of field to join to pg_namespace.oid. For example,
105 const char *namespace;
108 * Result --- the appropriately-quoted name to return, in the case of an
109 * unqualified name. For example, "pg_catalog.quote_ident(c.relname)".
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.
117 const char *qualresult;
121 /* Store maximum number of records we want from database queries
122 * (implemented via SELECT ... LIMIT xx).
124 static int completion_max_records;
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.
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 */
137 * A few macros to ease typing. You can use these to complete the given
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).
145 #define COMPLETE_WITH_QUERY(query) \
147 completion_charp = query; \
148 matches = completion_matches(text, complete_from_query); \
151 #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
153 completion_squery = &(query); \
154 completion_charp = addon; \
155 matches = completion_matches(text, complete_from_schema_query); \
158 #define COMPLETE_WITH_LIST(list) \
160 completion_charpp = list; \
161 matches = completion_matches(text, complete_from_list); \
164 #define COMPLETE_WITH_CONST(string) \
166 completion_charp = string; \
167 matches = completion_matches(text, complete_from_const); \
170 #define COMPLETE_WITH_ATTR(relation, addon) \
172 char *_completion_schema; \
173 char *_completion_table; \
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) \
183 completion_charp = Query_for_list_of_attributes addon; \
184 completion_info_charp = relation; \
188 completion_charp = Query_for_list_of_attributes_with_schema addon; \
189 completion_info_charp = _completion_table; \
190 completion_info_charp2 = _completion_schema; \
192 matches = completion_matches(text, complete_from_query); \
196 * Assembly instructions for schema queries
199 static const SchemaQuery Query_for_list_of_aggregates = {
201 "pg_catalog.pg_proc p",
205 "pg_catalog.pg_function_is_visible(p.oid)",
209 "pg_catalog.quote_ident(p.proname)",
214 static const SchemaQuery Query_for_list_of_datatypes = {
216 "pg_catalog.pg_type t",
217 /* selcondition --- ignore table rowtypes and array types */
219 " OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
220 "AND t.typname !~ '^_'",
222 "pg_catalog.pg_type_is_visible(t.oid)",
226 "pg_catalog.format_type(t.oid, NULL)",
228 "pg_catalog.quote_ident(t.typname)"
231 static const SchemaQuery Query_for_list_of_domains = {
233 "pg_catalog.pg_type t",
237 "pg_catalog.pg_type_is_visible(t.oid)",
241 "pg_catalog.quote_ident(t.typname)",
246 static const SchemaQuery Query_for_list_of_functions = {
248 "pg_catalog.pg_proc p",
252 "pg_catalog.pg_function_is_visible(p.oid)",
256 "pg_catalog.quote_ident(p.proname)",
261 static const SchemaQuery Query_for_list_of_indexes = {
263 "pg_catalog.pg_class c",
265 "c.relkind IN ('i')",
267 "pg_catalog.pg_table_is_visible(c.oid)",
271 "pg_catalog.quote_ident(c.relname)",
276 static const SchemaQuery Query_for_list_of_sequences = {
278 "pg_catalog.pg_class c",
280 "c.relkind IN ('S')",
282 "pg_catalog.pg_table_is_visible(c.oid)",
286 "pg_catalog.quote_ident(c.relname)",
291 static const SchemaQuery Query_for_list_of_tables = {
293 "pg_catalog.pg_class c",
295 "c.relkind IN ('r')",
297 "pg_catalog.pg_table_is_visible(c.oid)",
301 "pg_catalog.quote_ident(c.relname)",
306 static const SchemaQuery Query_for_list_of_tisv = {
308 "pg_catalog.pg_class c",
310 "c.relkind IN ('r', 'i', 'S', 'v')",
312 "pg_catalog.pg_table_is_visible(c.oid)",
316 "pg_catalog.quote_ident(c.relname)",
321 static const SchemaQuery Query_for_list_of_tsv = {
323 "pg_catalog.pg_class c",
325 "c.relkind IN ('r', 'S', 'v')",
327 "pg_catalog.pg_table_is_visible(c.oid)",
331 "pg_catalog.quote_ident(c.relname)",
336 static const SchemaQuery Query_for_list_of_views = {
338 "pg_catalog.pg_class c",
340 "c.relkind IN ('v')",
342 "pg_catalog.pg_table_is_visible(c.oid)",
346 "pg_catalog.quote_ident(c.relname)",
353 * Queries to get lists of names of various kinds of things, possibly
354 * restricted to names matching a partially entered name. In these queries,
355 * the first %s will be replaced by the text entered so far (suitably escaped
356 * to become a SQL literal string). %d will be replaced by the length of the
357 * string (in unescaped form). A second and third %s, if present, will be
358 * replaced by a suitably-escaped version of the string provided in
359 * completion_info_charp. A fourth and fifth %s are similarly replaced by
360 * completion_info_charp2.
362 * Beware that the allowed sequences of %s and %d are determined by
363 * _complete_from_query().
366 #define Query_for_list_of_attributes \
367 "SELECT pg_catalog.quote_ident(attname) "\
368 " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
369 " WHERE c.oid = a.attrelid "\
370 " AND a.attnum > 0 "\
371 " AND NOT a.attisdropped "\
372 " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
373 " AND (pg_catalog.quote_ident(relname)='%s' "\
374 " OR '\"' || relname || '\"'='%s') "\
375 " AND pg_catalog.pg_table_is_visible(c.oid)"
377 #define Query_for_list_of_attributes_with_schema \
378 "SELECT pg_catalog.quote_ident(attname) "\
379 " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
380 " WHERE c.oid = a.attrelid "\
381 " AND n.oid = c.relnamespace "\
382 " AND a.attnum > 0 "\
383 " AND NOT a.attisdropped "\
384 " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
385 " AND (pg_catalog.quote_ident(relname)='%s' "\
386 " OR '\"' || relname || '\"' ='%s') "\
387 " AND (pg_catalog.quote_ident(nspname)='%s' "\
388 " OR '\"' || nspname || '\"' ='%s') "
390 #define Query_for_list_of_template_databases \
391 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
392 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' AND datistemplate"
394 #define Query_for_list_of_databases \
395 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
396 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"
398 #define Query_for_list_of_tablespaces \
399 "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\
400 " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"
402 #define Query_for_list_of_encodings \
403 " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
404 " FROM pg_catalog.pg_conversion "\
405 " WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
407 #define Query_for_list_of_languages \
408 "SELECT pg_catalog.quote_ident(lanname) "\
409 " FROM pg_catalog.pg_language "\
410 " WHERE lanname != 'internal' "\
411 " AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'"
413 #define Query_for_list_of_schemas \
414 "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\
415 " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"
417 #define Query_for_list_of_set_vars \
419 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
420 " WHERE context IN ('user', 'superuser') "\
421 " UNION ALL SELECT 'constraints' "\
422 " UNION ALL SELECT 'transaction' "\
423 " UNION ALL SELECT 'session' "\
424 " UNION ALL SELECT 'role' "\
425 " UNION ALL SELECT 'tablespace' "\
426 " UNION ALL SELECT 'all') ss "\
427 " WHERE substring(name,1,%d)='%s'"
429 #define Query_for_list_of_show_vars \
431 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
432 " UNION ALL SELECT 'session authorization' "\
433 " UNION ALL SELECT 'all') ss "\
434 " WHERE substring(name,1,%d)='%s'"
436 #define Query_for_list_of_roles \
437 " SELECT pg_catalog.quote_ident(rolname) "\
438 " FROM pg_catalog.pg_roles "\
439 " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"
441 #define Query_for_list_of_grant_roles \
442 " SELECT pg_catalog.quote_ident(rolname) "\
443 " FROM pg_catalog.pg_roles "\
444 " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\
445 " UNION ALL SELECT 'PUBLIC'"
447 /* the silly-looking length condition is just to eat up the current word */
448 #define Query_for_table_owning_index \
449 "SELECT pg_catalog.quote_ident(c1.relname) "\
450 " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
451 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
452 " and (%d = pg_catalog.length('%s'))"\
453 " and pg_catalog.quote_ident(c2.relname)='%s'"\
454 " and pg_catalog.pg_table_is_visible(c2.oid)"
456 /* the silly-looking length condition is just to eat up the current word */
457 #define Query_for_index_of_table \
458 "SELECT pg_catalog.quote_ident(c2.relname) "\
459 " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
460 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
461 " and (%d = pg_catalog.length('%s'))"\
462 " and pg_catalog.quote_ident(c1.relname)='%s'"\
463 " and pg_catalog.pg_table_is_visible(c2.oid)"
465 /* the silly-looking length condition is just to eat up the current word */
466 #define Query_for_list_of_tables_for_trigger \
467 "SELECT pg_catalog.quote_ident(relname) "\
468 " FROM pg_catalog.pg_class"\
469 " WHERE (%d = pg_catalog.length('%s'))"\
471 " (SELECT tgrelid FROM pg_catalog.pg_trigger "\
472 " WHERE pg_catalog.quote_ident(tgname)='%s')"
474 #define Query_for_list_of_ts_configurations \
475 "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
476 " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
478 #define Query_for_list_of_ts_dictionaries \
479 "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\
480 " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'"
482 #define Query_for_list_of_ts_parsers \
483 "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\
484 " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'"
486 #define Query_for_list_of_ts_templates \
487 "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\
488 " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'"
490 #define Query_for_list_of_fdws \
491 " SELECT pg_catalog.quote_ident(fdwname) "\
492 " FROM pg_catalog.pg_foreign_data_wrapper "\
493 " WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'"
495 #define Query_for_list_of_servers \
496 " SELECT pg_catalog.quote_ident(srvname) "\
497 " FROM pg_catalog.pg_foreign_server "\
498 " WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'"
500 #define Query_for_list_of_user_mappings \
501 " SELECT pg_catalog.quote_ident(usename) "\
502 " FROM pg_catalog.pg_user_mappings "\
503 " WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"
505 #define Query_for_list_of_access_methods \
506 " SELECT pg_catalog.quote_ident(amname) "\
507 " FROM pg_catalog.pg_am "\
508 " WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
510 #define Query_for_list_of_arguments \
511 " SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
512 " FROM pg_catalog.pg_proc "\
513 " WHERE proname='%s'"
516 * This is a list of all "things" in Pgsql, which can show up after CREATE or
517 * DROP; and there is also a query to get a list of them.
523 const char *query; /* simple query, or NULL */
524 const SchemaQuery *squery; /* schema query, or NULL */
525 const bool noshow; /* NULL or true if this word should not show
526 * up after CREATE or DROP */
529 static const pgsql_thing_t words_after_create[] = {
530 {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
531 {"CAST", NULL, NULL}, /* Casts have complex structures for names, so
535 * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
536 * to be used only by pg_dump.
538 {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, true},
539 {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
540 {"DATABASE", Query_for_list_of_databases},
541 {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
542 {"DOMAIN", NULL, &Query_for_list_of_domains},
543 {"FOREIGN DATA WRAPPER", NULL, NULL},
544 {"FUNCTION", NULL, &Query_for_list_of_functions},
545 {"GROUP", Query_for_list_of_roles},
546 {"LANGUAGE", Query_for_list_of_languages},
547 {"INDEX", NULL, &Query_for_list_of_indexes},
548 {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a
550 {"PARSER", Query_for_list_of_ts_parsers, NULL, true},
551 {"ROLE", Query_for_list_of_roles},
552 {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
553 {"SCHEMA", Query_for_list_of_schemas},
554 {"SEQUENCE", NULL, &Query_for_list_of_sequences},
555 {"SERVER", Query_for_list_of_servers},
556 {"TABLE", NULL, &Query_for_list_of_tables},
557 {"TABLESPACE", Query_for_list_of_tablespaces},
558 {"TEMP", NULL, NULL}, /* for CREATE TEMP TABLE ... */
559 {"TEMPLATE", Query_for_list_of_ts_templates, NULL, true},
560 {"TEXT SEARCH", NULL, NULL},
561 {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"},
562 {"TYPE", NULL, &Query_for_list_of_datatypes},
563 {"UNIQUE", NULL, NULL}, /* for CREATE UNIQUE INDEX ... */
564 {"USER", Query_for_list_of_roles},
565 {"USER MAPPING FOR", NULL, NULL},
566 {"VIEW", NULL, &Query_for_list_of_views},
567 {NULL, NULL, NULL, false} /* end of list */
571 /* Forward declaration of functions */
572 static char **psql_completion(char *text, int start, int end);
573 static char *create_command_generator(const char *text, int state);
574 static char *drop_command_generator(const char *text, int state);
575 static char *complete_from_query(const char *text, int state);
576 static char *complete_from_schema_query(const char *text, int state);
577 static char *_complete_from_query(int is_schema_query,
578 const char *text, int state);
579 static char *complete_from_const(const char *text, int state);
580 static char *complete_from_list(const char *text, int state);
582 static PGresult *exec_query(const char *query);
584 static char *previous_word(int point, int skip);
587 static char *quote_file_name(char *text, int match_type, char *quote_pointer);
588 static char *dequote_file_name(char *text, char quote_char);
592 /* Initialize the readline library for our purposes. */
594 initialize_readline(void)
596 rl_readline_name = (char *) pset.progname;
597 rl_attempted_completion_function = (void *) psql_completion;
599 rl_basic_word_break_characters = WORD_BREAKS;
601 completion_max_records = 1000;
604 * There is a variable rl_completion_query_items for this but apparently
605 * it's not defined everywhere.
610 /* The completion function. Acc. to readline spec this gets passed the text
611 entered to far and its start and end in the readline buffer. The return value
612 is some partially obscure list format that can be generated by the readline
613 libraries completion_matches() function, so we don't have to worry about it.
616 psql_completion(char *text, int start, int end)
618 /* This is the variable we'll return. */
619 char **matches = NULL;
621 /* These are going to contain some scannage of the input line. */
628 static const char *const sql_commands[] = {
629 "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
630 "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
631 "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
632 "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
633 "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
634 "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TABLE", "TRUNCATE", "UNLISTEN",
635 "UPDATE", "VACUUM", "VALUES", "WITH", NULL
638 static const char *const backslash_commands[] = {
639 "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
640 "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
641 "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
642 "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
643 "\\e", "\\echo", "\\ef", "\\encoding",
644 "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
645 "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
646 "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
647 "\\set", "\\sf", "\\t", "\\T",
648 "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
651 (void) end; /* not used */
653 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
654 rl_completion_append_character = ' ';
657 /* Clear a few things. */
658 completion_charp = NULL;
659 completion_charpp = NULL;
660 completion_info_charp = NULL;
661 completion_info_charp2 = NULL;
664 * Scan the input line before our current position for the last five
665 * words. According to those we'll make some smart decisions on what the
666 * user is probably intending to type. TODO: Use strtokx() to do this.
668 prev_wd = previous_word(start, 0);
669 prev2_wd = previous_word(start, 1);
670 prev3_wd = previous_word(start, 2);
671 prev4_wd = previous_word(start, 3);
672 prev5_wd = previous_word(start, 4);
674 /* If a backslash command was started, continue */
676 COMPLETE_WITH_LIST(backslash_commands);
678 /* If no previous word, suggest one of the basic sql commands */
680 COMPLETE_WITH_LIST(sql_commands);
683 /* complete with something you can create */
684 else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
685 matches = completion_matches(text, create_command_generator);
687 /* DROP, but watch out for DROP embedded in other commands */
688 /* complete with something you can drop */
689 else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
690 pg_strcasecmp(prev2_wd, "DROP") == 0)
691 matches = completion_matches(text, drop_command_generator);
696 * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're
697 * in ALTER TABLE sth ALTER
699 else if (pg_strcasecmp(prev_wd, "ALTER") == 0 &&
700 pg_strcasecmp(prev3_wd, "TABLE") != 0)
702 static const char *const list_ALTER[] =
703 {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
704 "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
705 "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
707 COMPLETE_WITH_LIST(list_ALTER);
709 /* ALTER AGGREGATE,FUNCTION <name> */
710 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
711 (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
712 pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
713 COMPLETE_WITH_CONST("(");
714 /* ALTER AGGREGATE,FUNCTION <name> (...) */
715 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
716 (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
717 pg_strcasecmp(prev3_wd, "FUNCTION") == 0))
719 if (prev_wd[strlen(prev_wd) - 1] == ')')
721 static const char *const list_ALTERAGG[] =
722 {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
724 COMPLETE_WITH_LIST(list_ALTERAGG);
728 char *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
730 sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
731 COMPLETE_WITH_QUERY(tmp_buf);
736 /* ALTER CONVERSION,SCHEMA <name> */
737 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
738 (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
739 pg_strcasecmp(prev2_wd, "SCHEMA") == 0))
741 static const char *const list_ALTERGEN[] =
742 {"OWNER TO", "RENAME TO", NULL};
744 COMPLETE_WITH_LIST(list_ALTERGEN);
747 /* ALTER DATABASE <name> */
748 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
749 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
751 static const char *const list_ALTERDATABASE[] =
752 {"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};
754 COMPLETE_WITH_LIST(list_ALTERDATABASE);
757 /* ALTER FOREIGN DATA WRAPPER <name> */
758 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
759 pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
760 pg_strcasecmp(prev3_wd, "DATA") == 0 &&
761 pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
763 static const char *const list_ALTER_FDW[] =
764 {"VALIDATOR", "OPTIONS", "OWNER TO", NULL};
766 COMPLETE_WITH_LIST(list_ALTER_FDW);
769 /* ALTER INDEX <name> */
770 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
771 pg_strcasecmp(prev2_wd, "INDEX") == 0)
773 static const char *const list_ALTERINDEX[] =
774 {"OWNER TO", "RENAME TO", "SET", "RESET", NULL};
776 COMPLETE_WITH_LIST(list_ALTERINDEX);
778 /* ALTER INDEX <name> SET */
779 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
780 pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
781 pg_strcasecmp(prev_wd, "SET") == 0)
783 static const char *const list_ALTERINDEXSET[] =
784 {"(", "TABLESPACE", NULL};
786 COMPLETE_WITH_LIST(list_ALTERINDEXSET);
788 /* ALTER INDEX <name> RESET */
789 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
790 pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
791 pg_strcasecmp(prev_wd, "RESET") == 0)
792 COMPLETE_WITH_CONST("(");
793 /* ALTER INDEX <foo> SET|RESET ( */
794 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
795 pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
796 (pg_strcasecmp(prev2_wd, "SET") == 0 ||
797 pg_strcasecmp(prev2_wd, "RESET") == 0) &&
798 pg_strcasecmp(prev_wd, "(") == 0)
800 static const char *const list_INDEXOPTIONS[] =
801 {"fillfactor", "fastupdate", NULL};
803 COMPLETE_WITH_LIST(list_INDEXOPTIONS);
806 /* ALTER LANGUAGE <name> */
807 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
808 pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
810 static const char *const list_ALTERLANGUAGE[] =
811 {"OWNER TO", "RENAME TO", NULL};
813 COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
816 /* ALTER LARGE OBJECT <oid> */
817 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
818 pg_strcasecmp(prev3_wd, "LARGE") == 0 &&
819 pg_strcasecmp(prev2_wd, "OBJECT") == 0)
821 static const char *const list_ALTERLARGEOBJECT[] =
824 COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT);
827 /* ALTER USER,ROLE <name> */
828 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
829 !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
830 (pg_strcasecmp(prev2_wd, "USER") == 0 ||
831 pg_strcasecmp(prev2_wd, "ROLE") == 0))
833 static const char *const list_ALTERUSER[] =
834 {"ENCRYPTED", "UNENCRYPTED", "CREATEDB", "NOCREATEDB", "CREATEUSER",
835 "NOCREATEUSER", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT",
836 "LOGIN", "NOLOGIN", "CONNECTION LIMIT", "VALID UNTIL", "RENAME TO",
837 "SUPERUSER", "NOSUPERUSER", "SET", "RESET", NULL};
839 COMPLETE_WITH_LIST(list_ALTERUSER);
842 /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
843 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
844 (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
845 (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
847 COMPLETE_WITH_CONST("PASSWORD");
849 /* ALTER DEFAULT PRIVILEGES */
850 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
851 pg_strcasecmp(prev2_wd, "DEFAULT") == 0 &&
852 pg_strcasecmp(prev_wd, "PRIVILEGES") == 0)
854 static const char *const list_ALTER_DEFAULT_PRIVILEGES[] =
855 {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL};
857 COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES);
859 /* ALTER DEFAULT PRIVILEGES FOR */
860 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
861 pg_strcasecmp(prev3_wd, "DEFAULT") == 0 &&
862 pg_strcasecmp(prev2_wd, "PRIVILEGES") == 0 &&
863 pg_strcasecmp(prev_wd, "FOR") == 0)
865 static const char *const list_ALTER_DEFAULT_PRIVILEGES_FOR[] =
866 {"ROLE", "USER", NULL};
868 COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_FOR);
870 /* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */
871 else if (pg_strcasecmp(prev5_wd, "DEFAULT") == 0 &&
872 pg_strcasecmp(prev4_wd, "PRIVILEGES") == 0 &&
873 (pg_strcasecmp(prev3_wd, "FOR") == 0 ||
874 pg_strcasecmp(prev3_wd, "IN") == 0))
876 static const char *const list_ALTER_DEFAULT_PRIVILEGES_REST[] =
877 {"GRANT", "REVOKE", NULL};
879 COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_REST);
881 /* ALTER DOMAIN <name> */
882 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
883 pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
885 static const char *const list_ALTERDOMAIN[] =
886 {"ADD", "DROP", "OWNER TO", "SET", NULL};
888 COMPLETE_WITH_LIST(list_ALTERDOMAIN);
890 /* ALTER DOMAIN <sth> DROP */
891 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
892 pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
893 pg_strcasecmp(prev_wd, "DROP") == 0)
895 static const char *const list_ALTERDOMAIN2[] =
896 {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL};
898 COMPLETE_WITH_LIST(list_ALTERDOMAIN2);
900 /* ALTER DOMAIN <sth> SET */
901 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
902 pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
903 pg_strcasecmp(prev_wd, "SET") == 0)
905 static const char *const list_ALTERDOMAIN3[] =
906 {"DEFAULT", "NOT NULL", "SCHEMA", NULL};
908 COMPLETE_WITH_LIST(list_ALTERDOMAIN3);
910 /* ALTER SEQUENCE <name> */
911 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
912 pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)
914 static const char *const list_ALTERSEQUENCE[] =
915 {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
916 "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL};
918 COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
920 /* ALTER SEQUENCE <name> NO */
921 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
922 pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 &&
923 pg_strcasecmp(prev_wd, "NO") == 0)
925 static const char *const list_ALTERSEQUENCE2[] =
926 {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
928 COMPLETE_WITH_LIST(list_ALTERSEQUENCE2);
930 /* ALTER SERVER <name> */
931 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
932 pg_strcasecmp(prev2_wd, "SERVER") == 0)
934 static const char *const list_ALTER_SERVER[] =
935 {"VERSION", "OPTIONS", "OWNER TO", NULL};
937 COMPLETE_WITH_LIST(list_ALTER_SERVER);
939 /* ALTER VIEW <name> */
940 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
941 pg_strcasecmp(prev2_wd, "VIEW") == 0)
943 static const char *const list_ALTERVIEW[] =
944 {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
946 COMPLETE_WITH_LIST(list_ALTERVIEW);
948 /* ALTER TRIGGER <name>, add ON */
949 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
950 pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
951 COMPLETE_WITH_CONST("ON");
953 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
954 pg_strcasecmp(prev3_wd, "TRIGGER") == 0)
956 completion_info_charp = prev2_wd;
957 COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
961 * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
963 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
964 pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
965 pg_strcasecmp(prev_wd, "ON") == 0)
966 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
968 /* ALTER TRIGGER <name> ON <name> */
969 else if (pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
970 pg_strcasecmp(prev2_wd, "ON") == 0)
971 COMPLETE_WITH_CONST("RENAME TO");
974 * If we detect ALTER TABLE <name>, suggest either ADD, DROP, ALTER,
975 * RENAME, CLUSTER ON or OWNER
977 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
978 pg_strcasecmp(prev2_wd, "TABLE") == 0)
980 static const char *const list_ALTER2[] =
981 {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
982 "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", NULL};
984 COMPLETE_WITH_LIST(list_ALTER2);
986 /* ALTER TABLE xxx ENABLE */
987 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
988 pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
989 pg_strcasecmp(prev_wd, "ENABLE") == 0)
991 static const char *const list_ALTERENABLE[] =
992 {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL};
994 COMPLETE_WITH_LIST(list_ALTERENABLE);
996 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
997 pg_strcasecmp(prev2_wd, "ENABLE") == 0 &&
998 (pg_strcasecmp(prev_wd, "REPLICA") == 0 ||
999 pg_strcasecmp(prev_wd, "ALWAYS") == 0))
1001 static const char *const list_ALTERENABLE2[] =
1002 {"RULE", "TRIGGER", NULL};
1004 COMPLETE_WITH_LIST(list_ALTERENABLE2);
1006 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1007 pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1008 pg_strcasecmp(prev_wd, "DISABLE") == 0)
1010 static const char *const list_ALTERDISABLE[] =
1011 {"RULE", "TRIGGER", NULL};
1013 COMPLETE_WITH_LIST(list_ALTERDISABLE);
1016 /* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
1017 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1018 (pg_strcasecmp(prev_wd, "ALTER") == 0 ||
1019 pg_strcasecmp(prev_wd, "RENAME") == 0))
1020 COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN'");
1023 * If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
1026 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1027 (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
1028 pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
1029 pg_strcasecmp(prev_wd, "COLUMN") == 0)
1030 COMPLETE_WITH_ATTR(prev3_wd, "");
1032 /* ALTER TABLE xxx RENAME yyy */
1033 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1034 pg_strcasecmp(prev2_wd, "RENAME") == 0 &&
1035 pg_strcasecmp(prev_wd, "TO") != 0)
1036 COMPLETE_WITH_CONST("TO");
1038 /* ALTER TABLE xxx RENAME COLUMN yyy */
1039 else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
1040 pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
1041 pg_strcasecmp(prev2_wd, "COLUMN") == 0 &&
1042 pg_strcasecmp(prev_wd, "TO") != 0)
1043 COMPLETE_WITH_CONST("TO");
1045 /* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
1046 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1047 pg_strcasecmp(prev_wd, "DROP") == 0)
1049 static const char *const list_TABLEDROP[] =
1050 {"COLUMN", "CONSTRAINT", NULL};
1052 COMPLETE_WITH_LIST(list_TABLEDROP);
1054 /* If we have TABLE <sth> DROP COLUMN, provide list of columns */
1055 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1056 pg_strcasecmp(prev2_wd, "DROP") == 0 &&
1057 pg_strcasecmp(prev_wd, "COLUMN") == 0)
1058 COMPLETE_WITH_ATTR(prev3_wd, "");
1059 /* ALTER TABLE ALTER [COLUMN] <foo> */
1060 else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1061 pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
1062 (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1063 pg_strcasecmp(prev2_wd, "ALTER") == 0))
1065 static const char *const list_COLUMNALTER[] =
1066 {"TYPE", "SET", "RESET", "DROP", NULL};
1068 COMPLETE_WITH_LIST(list_COLUMNALTER);
1070 /* ALTER TABLE ALTER [COLUMN] <foo> SET */
1071 else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1072 pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
1073 (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
1074 pg_strcasecmp(prev3_wd, "ALTER") == 0)) &&
1075 pg_strcasecmp(prev_wd, "SET") == 0)
1077 static const char *const list_COLUMNSET[] =
1078 {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
1080 COMPLETE_WITH_LIST(list_COLUMNSET);
1082 /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
1083 else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1084 pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
1085 pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
1086 pg_strcasecmp(prev2_wd, "SET") == 0 &&
1087 pg_strcasecmp(prev_wd, "(") == 0)
1089 static const char *const list_COLUMNOPTIONS[] =
1090 {"n_distinct", "n_distinct_inherited", NULL};
1092 COMPLETE_WITH_LIST(list_COLUMNOPTIONS);
1094 /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
1095 else if (((pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1096 pg_strcasecmp(prev4_wd, "COLUMN") == 0) ||
1097 pg_strcasecmp(prev4_wd, "ALTER") == 0) &&
1098 pg_strcasecmp(prev2_wd, "SET") == 0 &&
1099 pg_strcasecmp(prev_wd, "STORAGE") == 0)
1101 static const char *const list_COLUMNSTORAGE[] =
1102 {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL};
1104 COMPLETE_WITH_LIST(list_COLUMNSTORAGE);
1106 /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
1107 else if (((pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1108 pg_strcasecmp(prev3_wd, "COLUMN") == 0) ||
1109 (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
1110 pg_strcasecmp(prev3_wd, "ALTER") == 0)) &&
1111 pg_strcasecmp(prev_wd, "DROP") == 0)
1113 static const char *const list_COLUMNDROP[] =
1114 {"DEFAULT", "NOT NULL", NULL};
1116 COMPLETE_WITH_LIST(list_COLUMNDROP);
1118 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1119 pg_strcasecmp(prev_wd, "CLUSTER") == 0)
1120 COMPLETE_WITH_CONST("ON");
1121 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1122 pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
1123 pg_strcasecmp(prev_wd, "ON") == 0)
1125 completion_info_charp = prev3_wd;
1126 COMPLETE_WITH_QUERY(Query_for_index_of_table);
1128 /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
1129 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1130 pg_strcasecmp(prev_wd, "SET") == 0)
1132 static const char *const list_TABLESET[] =
1133 {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
1135 COMPLETE_WITH_LIST(list_TABLESET);
1137 /* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
1138 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1139 pg_strcasecmp(prev2_wd, "SET") == 0 &&
1140 pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
1141 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
1142 /* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
1143 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1144 pg_strcasecmp(prev2_wd, "SET") == 0 &&
1145 pg_strcasecmp(prev_wd, "WITHOUT") == 0)
1147 static const char *const list_TABLESET2[] =
1148 {"CLUSTER", "OIDS", NULL};
1150 COMPLETE_WITH_LIST(list_TABLESET2);
1152 /* ALTER TABLE <foo> RESET */
1153 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1154 pg_strcasecmp(prev_wd, "RESET") == 0)
1155 COMPLETE_WITH_CONST("(");
1156 /* ALTER TABLE <foo> SET|RESET ( */
1157 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
1158 (pg_strcasecmp(prev2_wd, "SET") == 0 ||
1159 pg_strcasecmp(prev2_wd, "RESET") == 0) &&
1160 pg_strcasecmp(prev_wd, "(") == 0)
1162 static const char *const list_TABLEOPTIONS[] =
1164 "autovacuum_analyze_scale_factor",
1165 "autovacuum_analyze_threshold",
1166 "autovacuum_enabled",
1167 "autovacuum_freeze_max_age",
1168 "autovacuum_freeze_min_age",
1169 "autovacuum_freeze_table_age",
1170 "autovacuum_vacuum_cost_delay",
1171 "autovacuum_vacuum_cost_limit",
1172 "autovacuum_vacuum_scale_factor",
1173 "autovacuum_vacuum_threshold",
1175 "toast.autovacuum_enabled",
1176 "toast.autovacuum_freeze_max_age",
1177 "toast.autovacuum_freeze_min_age",
1178 "toast.autovacuum_freeze_table_age",
1179 "toast.autovacuum_vacuum_cost_delay",
1180 "toast.autovacuum_vacuum_cost_limit",
1181 "toast.autovacuum_vacuum_scale_factor",
1182 "toast.autovacuum_vacuum_threshold",
1186 COMPLETE_WITH_LIST(list_TABLEOPTIONS);
1189 /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
1190 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1191 pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
1193 static const char *const list_ALTERTSPC[] =
1194 {"RENAME TO", "OWNER TO", "SET", "RESET", NULL};
1196 COMPLETE_WITH_LIST(list_ALTERTSPC);
1198 /* ALTER TABLESPACE <foo> SET|RESET */
1199 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1200 pg_strcasecmp(prev3_wd, "TABLESPACE") == 0 &&
1201 (pg_strcasecmp(prev_wd, "SET") == 0 ||
1202 pg_strcasecmp(prev_wd, "RESET") == 0))
1203 COMPLETE_WITH_CONST("(");
1204 /* ALTER TABLESPACE <foo> SET|RESET ( */
1205 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1206 pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
1207 (pg_strcasecmp(prev2_wd, "SET") == 0 ||
1208 pg_strcasecmp(prev2_wd, "RESET") == 0) &&
1209 pg_strcasecmp(prev_wd, "(") == 0)
1211 static const char *const list_TABLESPACEOPTIONS[] =
1212 {"seq_page_cost", "random_page_cost", NULL};
1214 COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS);
1217 /* ALTER TEXT SEARCH */
1218 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1219 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1220 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1222 static const char *const list_ALTERTEXTSEARCH[] =
1223 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1225 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
1227 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1228 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1229 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1230 (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 ||
1231 pg_strcasecmp(prev2_wd, "PARSER") == 0))
1232 COMPLETE_WITH_CONST("RENAME TO");
1234 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1235 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1236 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1237 pg_strcasecmp(prev2_wd, "DICTIONARY") == 0)
1239 static const char *const list_ALTERTEXTSEARCH2[] =
1240 {"OWNER TO", "RENAME TO", NULL};
1242 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2);
1245 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1246 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1247 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1248 pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
1250 static const char *const list_ALTERTEXTSEARCH3[] =
1251 {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", NULL};
1253 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
1256 /* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */
1257 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1258 pg_strcasecmp(prev2_wd, "TYPE") == 0)
1260 static const char *const list_ALTERTYPE[] =
1261 {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
1263 COMPLETE_WITH_LIST(list_ALTERTYPE);
1265 /* complete ALTER GROUP <foo> */
1266 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1267 pg_strcasecmp(prev2_wd, "GROUP") == 0)
1269 static const char *const list_ALTERGROUP[] =
1270 {"ADD USER", "DROP USER", "RENAME TO", NULL};
1272 COMPLETE_WITH_LIST(list_ALTERGROUP);
1274 /* complete ALTER GROUP <foo> ADD|DROP with USER */
1275 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1276 pg_strcasecmp(prev3_wd, "GROUP") == 0 &&
1277 (pg_strcasecmp(prev_wd, "ADD") == 0 ||
1278 pg_strcasecmp(prev_wd, "DROP") == 0))
1279 COMPLETE_WITH_CONST("USER");
1280 /* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
1281 else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 &&
1282 (pg_strcasecmp(prev2_wd, "ADD") == 0 ||
1283 pg_strcasecmp(prev2_wd, "DROP") == 0) &&
1284 pg_strcasecmp(prev_wd, "USER") == 0)
1285 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1287 /* BEGIN, END, ABORT */
1288 else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
1289 pg_strcasecmp(prev_wd, "END") == 0 ||
1290 pg_strcasecmp(prev_wd, "ABORT") == 0)
1292 static const char *const list_TRANS[] =
1293 {"WORK", "TRANSACTION", NULL};
1295 COMPLETE_WITH_LIST(list_TRANS);
1298 else if (pg_strcasecmp(prev_wd, "COMMIT") == 0)
1300 static const char *const list_COMMIT[] =
1301 {"WORK", "TRANSACTION", "PREPARED", NULL};
1303 COMPLETE_WITH_LIST(list_COMMIT);
1305 /* RELEASE SAVEPOINT */
1306 else if (pg_strcasecmp(prev_wd, "RELEASE") == 0)
1307 COMPLETE_WITH_CONST("SAVEPOINT");
1309 else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0)
1311 static const char *const list_TRANS[] =
1312 {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL};
1314 COMPLETE_WITH_LIST(list_TRANS);
1319 * If the previous word is CLUSTER and not without produce list of tables
1321 else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 &&
1322 pg_strcasecmp(prev2_wd, "WITHOUT") != 0)
1323 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1324 /* If we have CLUSTER <sth>, then add "USING" */
1325 else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
1326 pg_strcasecmp(prev_wd, "ON") != 0)
1328 COMPLETE_WITH_CONST("USING");
1332 * If we have CLUSTER <sth> USING, then add the index as well.
1334 else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
1335 pg_strcasecmp(prev_wd, "USING") == 0)
1337 completion_info_charp = prev2_wd;
1338 COMPLETE_WITH_QUERY(Query_for_index_of_table);
1342 else if (pg_strcasecmp(prev_wd, "COMMENT") == 0)
1343 COMPLETE_WITH_CONST("ON");
1344 else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 &&
1345 pg_strcasecmp(prev_wd, "ON") == 0)
1347 static const char *const list_COMMENT[] =
1348 {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
1349 "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
1350 "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
1351 "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
1353 COMPLETE_WITH_LIST(list_COMMENT);
1355 else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
1356 pg_strcasecmp(prev3_wd, "ON") == 0 &&
1357 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1358 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1360 static const char *const list_TRANS2[] =
1361 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1363 COMPLETE_WITH_LIST(list_TRANS2);
1365 else if ((pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
1366 pg_strcasecmp(prev3_wd, "ON") == 0) ||
1367 (pg_strcasecmp(prev5_wd, "ON") == 0 &&
1368 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1369 pg_strcasecmp(prev3_wd, "SEARCH") == 0))
1370 COMPLETE_WITH_CONST("IS");
1375 * If we have COPY [BINARY] (which you'd have to type yourself), offer
1376 * list of tables (Also cover the analogous backslash command)
1378 else if (pg_strcasecmp(prev_wd, "COPY") == 0 ||
1379 pg_strcasecmp(prev_wd, "\\copy") == 0 ||
1380 (pg_strcasecmp(prev2_wd, "COPY") == 0 &&
1381 pg_strcasecmp(prev_wd, "BINARY") == 0))
1382 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1383 /* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
1384 else if (pg_strcasecmp(prev2_wd, "COPY") == 0 ||
1385 pg_strcasecmp(prev2_wd, "\\copy") == 0 ||
1386 pg_strcasecmp(prev2_wd, "BINARY") == 0)
1388 static const char *const list_FROMTO[] =
1389 {"FROM", "TO", NULL};
1391 COMPLETE_WITH_LIST(list_FROMTO);
1393 /* If we have COPY|BINARY <sth> FROM|TO, complete with filename */
1394 else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 ||
1395 pg_strcasecmp(prev3_wd, "\\copy") == 0 ||
1396 pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
1397 (pg_strcasecmp(prev_wd, "FROM") == 0 ||
1398 pg_strcasecmp(prev_wd, "TO") == 0))
1399 matches = completion_matches(text, filename_completion_function);
1401 /* Handle COPY|BINARY <sth> FROM|TO filename */
1402 else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
1403 pg_strcasecmp(prev4_wd, "\\copy") == 0 ||
1404 pg_strcasecmp(prev4_wd, "BINARY") == 0) &&
1405 (pg_strcasecmp(prev2_wd, "FROM") == 0 ||
1406 pg_strcasecmp(prev2_wd, "TO") == 0))
1408 static const char *const list_COPY[] =
1409 {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL};
1411 COMPLETE_WITH_LIST(list_COPY);
1414 /* Handle COPY|BINARY <sth> FROM|TO filename CSV */
1415 else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
1416 (pg_strcasecmp(prev3_wd, "FROM") == 0 ||
1417 pg_strcasecmp(prev3_wd, "TO") == 0))
1419 static const char *const list_CSV[] =
1420 {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
1422 COMPLETE_WITH_LIST(list_CSV);
1425 /* CREATE DATABASE */
1426 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1427 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
1429 static const char *const list_DATABASE[] =
1430 {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT",
1433 COMPLETE_WITH_LIST(list_DATABASE);
1436 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1437 pg_strcasecmp(prev3_wd, "DATABASE") == 0 &&
1438 pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
1439 COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
1441 /* CREATE FOREIGN DATA WRAPPER */
1442 else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1443 pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
1444 pg_strcasecmp(prev3_wd, "DATA") == 0 &&
1445 pg_strcasecmp(prev2_wd, "WRAPPER") == 0)
1446 COMPLETE_WITH_CONST("VALIDATOR");
1449 /* First off we complete CREATE UNIQUE with "INDEX" */
1450 else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1451 pg_strcasecmp(prev_wd, "UNIQUE") == 0)
1452 COMPLETE_WITH_CONST("INDEX");
1453 /* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
1454 else if (pg_strcasecmp(prev_wd, "INDEX") == 0 &&
1455 (pg_strcasecmp(prev2_wd, "CREATE") == 0 ||
1456 pg_strcasecmp(prev2_wd, "UNIQUE") == 0))
1457 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
1458 " UNION SELECT 'ON'"
1459 " UNION SELECT 'CONCURRENTLY'");
1460 /* Complete ... INDEX [<name>] ON with a list of tables */
1461 else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
1462 pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
1463 pg_strcasecmp(prev2_wd, "CONCURRENTLY") == 0) &&
1464 pg_strcasecmp(prev_wd, "ON") == 0)
1465 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1466 /* If we have CREATE|UNIQUE INDEX <sth> CONCURRENTLY, then add "ON" */
1467 else if ((pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
1468 pg_strcasecmp(prev2_wd, "INDEX") == 0) &&
1469 pg_strcasecmp(prev_wd, "CONCURRENTLY") == 0)
1470 COMPLETE_WITH_CONST("ON");
1471 /* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" or "CONCURRENTLY" */
1472 else if ((pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
1473 pg_strcasecmp(prev3_wd, "UNIQUE") == 0) &&
1474 pg_strcasecmp(prev2_wd, "INDEX") == 0)
1476 static const char *const list_CREATE_INDEX[] =
1477 {"CONCURRENTLY", "ON", NULL};
1479 COMPLETE_WITH_LIST(list_CREATE_INDEX);
1483 * Complete INDEX <name> ON <table> with a list of table columns (which
1484 * should really be in parens)
1486 else if ((pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
1487 pg_strcasecmp(prev3_wd, "INDEX") == 0 ||
1488 pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0) &&
1489 pg_strcasecmp(prev2_wd, "ON") == 0)
1491 static const char *const list_CREATE_INDEX2[] =
1492 {"(", "USING", NULL};
1494 COMPLETE_WITH_LIST(list_CREATE_INDEX2);
1496 else if ((pg_strcasecmp(prev5_wd, "INDEX") == 0 ||
1497 pg_strcasecmp(prev4_wd, "INDEX") == 0 ||
1498 pg_strcasecmp(prev4_wd, "CONCURRENTLY") == 0) &&
1499 pg_strcasecmp(prev3_wd, "ON") == 0 &&
1500 pg_strcasecmp(prev_wd, "(") == 0)
1501 COMPLETE_WITH_ATTR(prev2_wd, "");
1502 /* same if you put in USING */
1503 else if (pg_strcasecmp(prev5_wd, "ON") == 0 &&
1504 pg_strcasecmp(prev3_wd, "USING") == 0 &&
1505 pg_strcasecmp(prev_wd, "(") == 0)
1506 COMPLETE_WITH_ATTR(prev4_wd, "");
1507 /* Complete USING with an index method */
1508 else if (pg_strcasecmp(prev_wd, "USING") == 0)
1509 COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
1510 else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
1511 pg_strcasecmp(prev2_wd, "USING") == 0)
1512 COMPLETE_WITH_CONST("(");
1515 /* Complete "CREATE RULE <sth>" with "AS" */
1516 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1517 pg_strcasecmp(prev2_wd, "RULE") == 0)
1518 COMPLETE_WITH_CONST("AS");
1519 /* Complete "CREATE RULE <sth> AS with "ON" */
1520 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1521 pg_strcasecmp(prev3_wd, "RULE") == 0 &&
1522 pg_strcasecmp(prev_wd, "AS") == 0)
1523 COMPLETE_WITH_CONST("ON");
1524 /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */
1525 else if (pg_strcasecmp(prev4_wd, "RULE") == 0 &&
1526 pg_strcasecmp(prev2_wd, "AS") == 0 &&
1527 pg_strcasecmp(prev_wd, "ON") == 0)
1529 static const char *const rule_events[] =
1530 {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
1532 COMPLETE_WITH_LIST(rule_events);
1534 /* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
1535 else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
1536 pg_strcasecmp(prev2_wd, "ON") == 0 &&
1537 (pg_toupper((unsigned char) prev_wd[4]) == 'T' ||
1538 pg_toupper((unsigned char) prev_wd[5]) == 'T'))
1539 COMPLETE_WITH_CONST("TO");
1540 /* Complete "AS ON <sth> TO" with a table name */
1541 else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
1542 pg_strcasecmp(prev3_wd, "ON") == 0 &&
1543 pg_strcasecmp(prev_wd, "TO") == 0)
1544 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1546 /* CREATE SERVER <name> */
1547 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1548 pg_strcasecmp(prev2_wd, "SERVER") == 0)
1550 static const char *const list_CREATE_SERVER[] =
1551 {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL};
1553 COMPLETE_WITH_LIST(list_CREATE_SERVER);
1557 /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
1558 else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1559 (pg_strcasecmp(prev_wd, "TEMP") == 0 ||
1560 pg_strcasecmp(prev_wd, "TEMPORARY") == 0))
1562 static const char *const list_TEMP[] =
1563 {"SEQUENCE", "TABLE", "VIEW", NULL};
1565 COMPLETE_WITH_LIST(list_TEMP);
1568 /* CREATE TABLESPACE */
1569 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1570 pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
1572 static const char *const list_CREATETABLESPACE[] =
1573 {"OWNER", "LOCATION", NULL};
1575 COMPLETE_WITH_LIST(list_CREATETABLESPACE);
1577 /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
1578 else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1579 pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
1580 pg_strcasecmp(prev2_wd, "OWNER") == 0)
1582 COMPLETE_WITH_CONST("LOCATION");
1585 /* CREATE TEXT SEARCH */
1586 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1587 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1588 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1590 static const char *const list_CREATETEXTSEARCH[] =
1591 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1593 COMPLETE_WITH_LIST(list_CREATETEXTSEARCH);
1595 else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1596 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1597 pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
1598 COMPLETE_WITH_CONST("(");
1600 /* CREATE TRIGGER */
1601 /* complete CREATE TRIGGER <name> with BEFORE,AFTER */
1602 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1603 pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
1605 static const char *const list_CREATETRIGGER[] =
1606 {"BEFORE", "AFTER", NULL};
1608 COMPLETE_WITH_LIST(list_CREATETRIGGER);
1610 /* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */
1611 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1612 pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
1613 (pg_strcasecmp(prev_wd, "BEFORE") == 0 ||
1614 pg_strcasecmp(prev_wd, "AFTER") == 0))
1616 static const char *const list_CREATETRIGGER_EVENTS[] =
1617 {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL};
1619 COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
1621 /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
1622 else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1623 pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
1624 (pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
1625 pg_strcasecmp(prev2_wd, "AFTER") == 0))
1627 static const char *const list_CREATETRIGGER2[] =
1630 COMPLETE_WITH_LIST(list_CREATETRIGGER2);
1634 * complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of
1637 else if (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
1638 (pg_strcasecmp(prev3_wd, "BEFORE") == 0 ||
1639 pg_strcasecmp(prev3_wd, "AFTER") == 0) &&
1640 pg_strcasecmp(prev_wd, "ON") == 0)
1641 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1642 /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */
1643 else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0)
1644 COMPLETE_WITH_CONST("PROCEDURE");
1646 /* CREATE ROLE,USER,GROUP */
1647 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1648 !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) &&
1649 (pg_strcasecmp(prev2_wd, "ROLE") == 0 ||
1650 pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
1652 static const char *const list_CREATEROLE[] =
1653 {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
1654 "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOINHERIT", "NOLOGIN", "NOCREATEDB",
1655 "NOCREATEROLE", "NOCREATEUSER", "NOSUPERUSER", "ROLE", "SUPERUSER", "SYSID",
1656 "UNENCRYPTED", NULL};
1658 COMPLETE_WITH_LIST(list_CREATEROLE);
1662 * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
1665 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1666 (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
1667 pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1668 (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
1670 COMPLETE_WITH_CONST("PASSWORD");
1672 /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
1673 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1674 (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
1675 pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1676 pg_strcasecmp(prev_wd, "IN") == 0)
1678 static const char *const list_CREATEROLE3[] =
1679 {"GROUP", "ROLE", NULL};
1681 COMPLETE_WITH_LIST(list_CREATEROLE3);
1685 /* Complete CREATE VIEW <name> with AS */
1686 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1687 pg_strcasecmp(prev2_wd, "VIEW") == 0)
1688 COMPLETE_WITH_CONST("AS");
1689 /* Complete "CREATE VIEW <sth> AS with "SELECT" */
1690 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1691 pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
1692 pg_strcasecmp(prev_wd, "AS") == 0)
1693 COMPLETE_WITH_CONST("SELECT");
1696 else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0)
1698 static const char *const list_DECLARE[] =
1699 {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL};
1701 COMPLETE_WITH_LIST(list_DECLARE);
1705 else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
1707 static const char *const list_DECLARECURSOR[] =
1708 {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL};
1710 COMPLETE_WITH_LIST(list_DECLARECURSOR);
1717 * Complete DELETE with FROM (only if the word before that is not "ON"
1718 * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT)
1720 else if (pg_strcasecmp(prev_wd, "DELETE") == 0 &&
1721 !(pg_strcasecmp(prev2_wd, "ON") == 0 ||
1722 pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
1723 pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
1724 pg_strcasecmp(prev2_wd, "AFTER") == 0))
1725 COMPLETE_WITH_CONST("FROM");
1726 /* Complete DELETE FROM with a list of tables */
1727 else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
1728 pg_strcasecmp(prev_wd, "FROM") == 0)
1729 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1730 /* Complete DELETE FROM <table> */
1731 else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
1732 pg_strcasecmp(prev2_wd, "FROM") == 0)
1734 static const char *const list_DELETE[] =
1735 {"USING", "WHERE", "SET", NULL};
1737 COMPLETE_WITH_LIST(list_DELETE);
1739 /* XXX: implement tab completion for DELETE ... USING */
1742 else if (pg_strcasecmp(prev_wd, "DISCARD") == 0)
1744 static const char *const list_DISCARD[] =
1745 {"ALL", "PLANS", "TEMP", NULL};
1747 COMPLETE_WITH_LIST(list_DISCARD);
1753 * Complete DO with LANGUAGE.
1755 else if (pg_strcasecmp(prev_wd, "DO") == 0)
1757 static const char *const list_DO[] =
1760 COMPLETE_WITH_LIST(list_DO);
1763 /* DROP (when not the previous word) */
1764 /* DROP AGGREGATE */
1765 else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1766 pg_strcasecmp(prev2_wd, "AGGREGATE") == 0)
1767 COMPLETE_WITH_CONST("(");
1769 /* DROP object with CASCADE / RESTRICT */
1770 else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1771 (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
1772 pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
1773 pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
1774 pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
1775 pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
1776 pg_strcasecmp(prev2_wd, "SCHEMA") == 0 ||
1777 pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 ||
1778 pg_strcasecmp(prev2_wd, "SERVER") == 0 ||
1779 pg_strcasecmp(prev2_wd, "TABLE") == 0 ||
1780 pg_strcasecmp(prev2_wd, "TYPE") == 0 ||
1781 pg_strcasecmp(prev2_wd, "VIEW") == 0)) ||
1782 (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
1783 pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
1784 prev_wd[strlen(prev_wd) - 1] == ')') ||
1785 (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
1786 pg_strcasecmp(prev4_wd, "FOREIGN") == 0 &&
1787 pg_strcasecmp(prev3_wd, "DATA") == 0 &&
1788 pg_strcasecmp(prev2_wd, "WRAPPER") == 0) ||
1789 (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
1790 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1791 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1792 (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 ||
1793 pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 ||
1794 pg_strcasecmp(prev2_wd, "PARSER") == 0 ||
1795 pg_strcasecmp(prev2_wd, "TEMPLATE") == 0))
1798 if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1799 pg_strcasecmp(prev2_wd, "FUNCTION") == 0)
1801 COMPLETE_WITH_CONST("(");
1805 static const char *const list_DROPCR[] =
1806 {"CASCADE", "RESTRICT", NULL};
1808 COMPLETE_WITH_LIST(list_DROPCR);
1811 else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
1812 (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
1813 pg_strcasecmp(prev3_wd, "FUNCTION") == 0) &&
1814 pg_strcasecmp(prev_wd, "(") == 0)
1816 char *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
1818 sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
1819 COMPLETE_WITH_QUERY(tmp_buf);
1823 else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
1824 pg_strcasecmp(prev_wd, "OWNED") == 0)
1825 COMPLETE_WITH_CONST("BY");
1826 else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1827 pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
1828 pg_strcasecmp(prev_wd, "BY") == 0)
1829 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1830 else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1831 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1832 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1835 static const char *const list_ALTERTEXTSEARCH[] =
1836 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1838 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
1844 * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
1846 else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
1848 static const char *const list_EXPLAIN[] =
1849 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL};
1851 COMPLETE_WITH_LIST(list_EXPLAIN);
1853 else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
1854 pg_strcasecmp(prev_wd, "ANALYZE") == 0)
1856 static const char *const list_EXPLAIN[] =
1857 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL};
1859 COMPLETE_WITH_LIST(list_EXPLAIN);
1861 else if ((pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
1862 pg_strcasecmp(prev_wd, "VERBOSE") == 0) ||
1863 (pg_strcasecmp(prev3_wd, "EXPLAIN") == 0 &&
1864 pg_strcasecmp(prev2_wd, "ANALYZE") == 0 &&
1865 pg_strcasecmp(prev_wd, "VERBOSE") == 0))
1867 static const char *const list_EXPLAIN[] =
1868 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
1870 COMPLETE_WITH_LIST(list_EXPLAIN);
1874 /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
1875 else if (pg_strcasecmp(prev_wd, "FETCH") == 0 ||
1876 pg_strcasecmp(prev_wd, "MOVE") == 0)
1878 static const char *const list_FETCH1[] =
1879 {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL};
1881 COMPLETE_WITH_LIST(list_FETCH1);
1883 /* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
1884 else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 ||
1885 pg_strcasecmp(prev2_wd, "MOVE") == 0)
1887 static const char *const list_FETCH2[] =
1888 {"ALL", "NEXT", "PRIOR", NULL};
1890 COMPLETE_WITH_LIST(list_FETCH2);
1894 * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent,
1895 * but we may as well tab-complete both: perhaps some users prefer one
1896 * variant or the other.
1898 else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 ||
1899 pg_strcasecmp(prev3_wd, "MOVE") == 0)
1901 static const char *const list_FROMIN[] =
1902 {"FROM", "IN", NULL};
1904 COMPLETE_WITH_LIST(list_FROMIN);
1907 /* FOREIGN DATA WRAPPER */
1908 /* applies in ALTER/DROP FDW and in CREATE SERVER */
1909 else if (pg_strcasecmp(prev4_wd, "CREATE") != 0 &&
1910 pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
1911 pg_strcasecmp(prev2_wd, "DATA") == 0 &&
1912 pg_strcasecmp(prev_wd, "WRAPPER") == 0)
1913 COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
1915 /* GRANT && REVOKE*/
1916 /* Complete GRANT/REVOKE with a list of privileges */
1917 else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
1918 pg_strcasecmp(prev_wd, "REVOKE") == 0)
1920 static const char *const list_privilege[] =
1921 {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
1922 "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
1925 COMPLETE_WITH_LIST(list_privilege);
1927 /* Complete GRANT/REVOKE <sth> with "ON" */
1928 else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
1929 pg_strcasecmp(prev2_wd, "REVOKE") == 0)
1930 COMPLETE_WITH_CONST("ON");
1933 * Complete GRANT/REVOKE <sth> ON with a list of tables, views, sequences,
1936 * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via
1937 * UNION; seems to work intuitively
1939 * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
1940 * here will only work if the privilege list contains exactly one
1943 else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
1944 pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
1945 pg_strcasecmp(prev_wd, "ON") == 0)
1946 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv,
1947 " UNION SELECT 'DATABASE'"
1948 " UNION SELECT 'FOREIGN DATA WRAPPER'"
1949 " UNION SELECT 'FOREIGN SERVER'"
1950 " UNION SELECT 'FUNCTION'"
1951 " UNION SELECT 'LANGUAGE'"
1952 " UNION SELECT 'LARGE OBJECT'"
1953 " UNION SELECT 'SCHEMA'"
1954 " UNION SELECT 'TABLESPACE'");
1955 else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
1956 pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
1957 pg_strcasecmp(prev2_wd, "ON") == 0 &&
1958 pg_strcasecmp(prev_wd, "FOREIGN") == 0)
1960 static const char *const list_privilege_foreign[] =
1961 {"DATA WRAPPER", "SERVER", NULL};
1963 COMPLETE_WITH_LIST(list_privilege_foreign);
1966 /* Complete "GRANT/REVOKE * ON * " with "TO/FROM" */
1967 else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
1968 pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
1969 pg_strcasecmp(prev2_wd, "ON") == 0)
1971 if (pg_strcasecmp(prev_wd, "DATABASE") == 0)
1972 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
1973 else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0)
1974 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
1975 else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0)
1976 COMPLETE_WITH_QUERY(Query_for_list_of_languages);
1977 else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0)
1978 COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
1979 else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
1980 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
1981 else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
1982 COMPLETE_WITH_CONST("TO");
1984 COMPLETE_WITH_CONST("FROM");
1987 /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
1988 else if (pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
1989 pg_strcasecmp(prev3_wd, "ON") == 0)
1991 if (pg_strcasecmp(prev_wd, "TO") == 0)
1992 COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
1994 COMPLETE_WITH_CONST("TO");
1996 else if (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
1997 pg_strcasecmp(prev3_wd, "ON") == 0)
1999 if (pg_strcasecmp(prev_wd, "FROM") == 0)
2000 COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
2002 COMPLETE_WITH_CONST("FROM");
2006 else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
2007 pg_strcasecmp(prev_wd, "GROUP") == 0)
2008 COMPLETE_WITH_CONST("BY");
2011 /* Complete INSERT with "INTO" */
2012 else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
2013 COMPLETE_WITH_CONST("INTO");
2014 /* Complete INSERT INTO with table names */
2015 else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 &&
2016 pg_strcasecmp(prev_wd, "INTO") == 0)
2017 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2018 /* Complete "INSERT INTO <table> (" with attribute names */
2019 else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
2020 pg_strcasecmp(prev3_wd, "INTO") == 0 &&
2021 pg_strcasecmp(prev_wd, "(") == 0)
2022 COMPLETE_WITH_ATTR(prev2_wd, "");
2025 * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
2026 * "TABLE" or "DEFAULT VALUES"
2028 else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
2029 pg_strcasecmp(prev2_wd, "INTO") == 0)
2031 static const char *const list_INSERT[] =
2032 {"(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL};
2034 COMPLETE_WITH_LIST(list_INSERT);
2038 * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
2041 else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
2042 pg_strcasecmp(prev3_wd, "INTO") == 0 &&
2043 prev_wd[strlen(prev_wd) - 1] == ')')
2045 static const char *const list_INSERT[] =
2046 {"SELECT", "TABLE", "VALUES", NULL};
2048 COMPLETE_WITH_LIST(list_INSERT);
2051 /* Insert an open parenthesis after "VALUES" */
2052 else if (pg_strcasecmp(prev_wd, "VALUES") == 0 &&
2053 pg_strcasecmp(prev2_wd, "DEFAULT") != 0)
2054 COMPLETE_WITH_CONST("(");
2057 /* Complete LOCK [TABLE] with a list of tables */
2058 else if (pg_strcasecmp(prev_wd, "LOCK") == 0)
2059 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2060 " UNION SELECT 'TABLE'");
2061 else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
2062 pg_strcasecmp(prev2_wd, "LOCK") == 0)
2063 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
2065 /* For the following, handle the case of a single table only for now */
2067 /* Complete LOCK [TABLE] <table> with "IN" */
2068 else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 &&
2069 pg_strcasecmp(prev_wd, "TABLE")) ||
2070 (pg_strcasecmp(prev2_wd, "TABLE") == 0 &&
2071 pg_strcasecmp(prev3_wd, "LOCK") == 0))
2072 COMPLETE_WITH_CONST("IN");
2074 /* Complete LOCK [TABLE] <table> IN with a lock mode */
2075 else if (pg_strcasecmp(prev_wd, "IN") == 0 &&
2076 (pg_strcasecmp(prev3_wd, "LOCK") == 0 ||
2077 (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
2078 pg_strcasecmp(prev4_wd, "LOCK") == 0)))
2080 static const char *const lock_modes[] =
2081 {"ACCESS SHARE MODE",
2082 "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
2083 "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
2084 "SHARE ROW EXCLUSIVE MODE",
2085 "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL};
2087 COMPLETE_WITH_LIST(lock_modes);
2091 else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
2092 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'");
2095 else if (pg_strcasecmp(prev_wd, "OPTIONS") == 0)
2096 COMPLETE_WITH_CONST("(");
2098 /* OWNER TO - complete with available roles */
2099 else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
2100 pg_strcasecmp(prev_wd, "TO") == 0)
2101 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2104 else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
2105 pg_strcasecmp(prev_wd, "ORDER") == 0)
2106 COMPLETE_WITH_CONST("BY");
2107 else if (pg_strcasecmp(prev4_wd, "FROM") == 0 &&
2108 pg_strcasecmp(prev2_wd, "ORDER") == 0 &&
2109 pg_strcasecmp(prev_wd, "BY") == 0)
2110 COMPLETE_WITH_ATTR(prev3_wd, "");
2113 else if (pg_strcasecmp(prev_wd, "AS") == 0 &&
2114 pg_strcasecmp(prev3_wd, "PREPARE") == 0)
2116 static const char *const list_PREPARE[] =
2117 {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
2119 COMPLETE_WITH_LIST(list_PREPARE);
2123 * PREPARE TRANSACTION is missing on purpose. It's intended for transaction
2124 * managers, not for manual use in interactive sessions.
2127 /* REASSIGN OWNED BY xxx TO yyy */
2128 else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
2129 COMPLETE_WITH_CONST("OWNED");
2130 else if (pg_strcasecmp(prev_wd, "OWNED") == 0 &&
2131 pg_strcasecmp(prev2_wd, "REASSIGN") == 0)
2132 COMPLETE_WITH_CONST("BY");
2133 else if (pg_strcasecmp(prev_wd, "BY") == 0 &&
2134 pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
2135 pg_strcasecmp(prev3_wd, "REASSIGN") == 0)
2136 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2137 else if (pg_strcasecmp(prev2_wd, "BY") == 0 &&
2138 pg_strcasecmp(prev3_wd, "OWNED") == 0 &&
2139 pg_strcasecmp(prev4_wd, "REASSIGN") == 0)
2140 COMPLETE_WITH_CONST("TO");
2141 else if (pg_strcasecmp(prev_wd, "TO") == 0 &&
2142 pg_strcasecmp(prev3_wd, "BY") == 0 &&
2143 pg_strcasecmp(prev4_wd, "OWNED") == 0 &&
2144 pg_strcasecmp(prev5_wd, "REASSIGN") == 0)
2145 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2148 else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
2150 static const char *const list_REINDEX[] =
2151 {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL};
2153 COMPLETE_WITH_LIST(list_REINDEX);
2155 else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0)
2157 if (pg_strcasecmp(prev_wd, "TABLE") == 0)
2158 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2159 else if (pg_strcasecmp(prev_wd, "INDEX") == 0)
2160 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
2161 else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 ||
2162 pg_strcasecmp(prev_wd, "DATABASE") == 0)
2163 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
2169 /* SET, RESET, SHOW */
2170 /* Complete with a variable name */
2171 else if ((pg_strcasecmp(prev_wd, "SET") == 0 &&
2172 pg_strcasecmp(prev3_wd, "UPDATE") != 0) ||
2173 pg_strcasecmp(prev_wd, "RESET") == 0)
2174 COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
2175 else if (pg_strcasecmp(prev_wd, "SHOW") == 0)
2176 COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
2177 /* Complete "SET TRANSACTION" */
2178 else if ((pg_strcasecmp(prev2_wd, "SET") == 0 &&
2179 pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
2180 || (pg_strcasecmp(prev2_wd, "START") == 0
2181 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
2182 || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
2183 && pg_strcasecmp(prev_wd, "WORK") == 0)
2184 || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
2185 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
2186 || (pg_strcasecmp(prev4_wd, "SESSION") == 0
2187 && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0
2188 && pg_strcasecmp(prev2_wd, "AS") == 0
2189 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0))
2191 static const char *const my_list[] =
2192 {"ISOLATION LEVEL", "READ", NULL};
2194 COMPLETE_WITH_LIST(my_list);
2196 else if ((pg_strcasecmp(prev3_wd, "SET") == 0
2197 || pg_strcasecmp(prev3_wd, "BEGIN") == 0
2198 || pg_strcasecmp(prev3_wd, "START") == 0
2199 || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0
2200 && pg_strcasecmp(prev3_wd, "AS") == 0))
2201 && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0
2202 || pg_strcasecmp(prev2_wd, "WORK") == 0)
2203 && pg_strcasecmp(prev_wd, "ISOLATION") == 0)
2204 COMPLETE_WITH_CONST("LEVEL");
2205 else if ((pg_strcasecmp(prev4_wd, "SET") == 0
2206 || pg_strcasecmp(prev4_wd, "BEGIN") == 0
2207 || pg_strcasecmp(prev4_wd, "START") == 0
2208 || pg_strcasecmp(prev4_wd, "AS") == 0)
2209 && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0
2210 || pg_strcasecmp(prev3_wd, "WORK") == 0)
2211 && pg_strcasecmp(prev2_wd, "ISOLATION") == 0
2212 && pg_strcasecmp(prev_wd, "LEVEL") == 0)
2214 static const char *const my_list[] =
2215 {"READ", "REPEATABLE", "SERIALIZABLE", NULL};
2217 COMPLETE_WITH_LIST(my_list);
2219 else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
2220 pg_strcasecmp(prev4_wd, "WORK") == 0) &&
2221 pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
2222 pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
2223 pg_strcasecmp(prev_wd, "READ") == 0)
2225 static const char *const my_list[] =
2226 {"UNCOMMITTED", "COMMITTED", NULL};
2228 COMPLETE_WITH_LIST(my_list);
2230 else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
2231 pg_strcasecmp(prev4_wd, "WORK") == 0) &&
2232 pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
2233 pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
2234 pg_strcasecmp(prev_wd, "REPEATABLE") == 0)
2235 COMPLETE_WITH_CONST("READ");
2236 else if ((pg_strcasecmp(prev3_wd, "SET") == 0 ||
2237 pg_strcasecmp(prev3_wd, "BEGIN") == 0 ||
2238 pg_strcasecmp(prev3_wd, "START") == 0 ||
2239 pg_strcasecmp(prev3_wd, "AS") == 0) &&
2240 (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 ||
2241 pg_strcasecmp(prev2_wd, "WORK") == 0) &&
2242 pg_strcasecmp(prev_wd, "READ") == 0)
2244 static const char *const my_list[] =
2245 {"ONLY", "WRITE", NULL};
2247 COMPLETE_WITH_LIST(my_list);
2249 /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
2250 else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
2251 pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
2253 static const char *const constraint_list[] =
2254 {"DEFERRED", "IMMEDIATE", NULL};
2256 COMPLETE_WITH_LIST(constraint_list);
2258 /* Complete SET ROLE */
2259 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2260 pg_strcasecmp(prev_wd, "ROLE") == 0)
2261 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2262 /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
2263 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2264 pg_strcasecmp(prev_wd, "SESSION") == 0)
2266 static const char *const my_list[] =
2267 {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL};
2269 COMPLETE_WITH_LIST(my_list);
2271 /* Complete SET SESSION AUTHORIZATION with username */
2272 else if (pg_strcasecmp(prev3_wd, "SET") == 0
2273 && pg_strcasecmp(prev2_wd, "SESSION") == 0
2274 && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
2275 COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
2276 /* Complete RESET SESSION with AUTHORIZATION */
2277 else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
2278 pg_strcasecmp(prev_wd, "SESSION") == 0)
2279 COMPLETE_WITH_CONST("AUTHORIZATION");
2280 /* Complete SET <var> with "TO" */
2281 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2282 pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
2283 pg_strcasecmp(prev_wd, "TABLESPACE") != 0 &&
2284 pg_strcasecmp(prev_wd, "SCHEMA") != 0 &&
2285 prev_wd[strlen(prev_wd) - 1] != ')' &&
2286 pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
2287 COMPLETE_WITH_CONST("TO");
2288 /* Suggest possible variable values */
2289 else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
2290 (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
2292 if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
2294 static const char *const my_list[] =
2295 {"ISO", "SQL", "Postgres", "German",
2296 "YMD", "DMY", "MDY",
2297 "US", "European", "NonEuropean",
2300 COMPLETE_WITH_LIST(my_list);
2302 else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0)
2304 static const char *const my_list[] =
2305 {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL};
2307 COMPLETE_WITH_LIST(my_list);
2309 else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
2311 static const char *const my_list[] =
2312 {"ON", "OFF", "DEFAULT", NULL};
2314 COMPLETE_WITH_LIST(my_list);
2318 static const char *const my_list[] =
2321 COMPLETE_WITH_LIST(my_list);
2325 /* START TRANSACTION */
2326 else if (pg_strcasecmp(prev_wd, "START") == 0)
2327 COMPLETE_WITH_CONST("TRANSACTION");
2330 else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0)
2331 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2334 else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0)
2335 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 '*'");
2338 /* If prev. word is UPDATE suggest a list of tables */
2339 else if (pg_strcasecmp(prev_wd, "UPDATE") == 0)
2340 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2341 /* Complete UPDATE <table> with "SET" */
2342 else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0)
2343 COMPLETE_WITH_CONST("SET");
2346 * If the previous word is SET (and it wasn't caught above as the _first_
2347 * word) the word before it was (hopefully) a table name and we'll now
2348 * make a list of attributes.
2350 else if (pg_strcasecmp(prev_wd, "SET") == 0)
2351 COMPLETE_WITH_ATTR(prev2_wd, "");
2353 /* UPDATE xx SET yy = */
2354 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2355 pg_strcasecmp(prev4_wd, "UPDATE") == 0)
2356 COMPLETE_WITH_CONST("=");
2359 else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 ||
2360 pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
2361 pg_strcasecmp(prev3_wd, "DROP") == 0) &&
2362 pg_strcasecmp(prev2_wd, "USER") == 0 &&
2363 pg_strcasecmp(prev_wd, "MAPPING") == 0)
2364 COMPLETE_WITH_CONST("FOR");
2365 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
2366 pg_strcasecmp(prev3_wd, "USER") == 0 &&
2367 pg_strcasecmp(prev2_wd, "MAPPING") == 0 &&
2368 pg_strcasecmp(prev_wd, "FOR") == 0)
2369 COMPLETE_WITH_QUERY(Query_for_list_of_roles
2370 " UNION SELECT 'CURRENT_USER'"
2371 " UNION SELECT 'PUBLIC'"
2372 " UNION SELECT 'USER'");
2373 else if ((pg_strcasecmp(prev4_wd, "ALTER") == 0 ||
2374 pg_strcasecmp(prev4_wd, "DROP") == 0) &&
2375 pg_strcasecmp(prev3_wd, "USER") == 0 &&
2376 pg_strcasecmp(prev2_wd, "MAPPING") == 0 &&
2377 pg_strcasecmp(prev_wd, "FOR") == 0)
2378 COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
2379 else if ((pg_strcasecmp(prev5_wd, "CREATE") == 0 ||
2380 pg_strcasecmp(prev5_wd, "ALTER") == 0 ||
2381 pg_strcasecmp(prev5_wd, "DROP") == 0) &&
2382 pg_strcasecmp(prev4_wd, "USER") == 0 &&
2383 pg_strcasecmp(prev3_wd, "MAPPING") == 0 &&
2384 pg_strcasecmp(prev2_wd, "FOR") == 0)
2385 COMPLETE_WITH_CONST("SERVER");
2388 * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
2389 * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
2391 else if (pg_strcasecmp(prev_wd, "VACUUM") == 0)
2392 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2393 " UNION SELECT 'FULL'"
2394 " UNION SELECT 'FREEZE'"
2395 " UNION SELECT 'ANALYZE'"
2396 " UNION SELECT 'VERBOSE'");
2397 else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2398 (pg_strcasecmp(prev_wd, "FULL") == 0 ||
2399 pg_strcasecmp(prev_wd, "FREEZE") == 0))
2400 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2401 " UNION SELECT 'ANALYZE'"
2402 " UNION SELECT 'VERBOSE'");
2403 else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
2404 pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
2405 (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
2406 pg_strcasecmp(prev2_wd, "FREEZE") == 0))
2407 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2408 " UNION SELECT 'VERBOSE'");
2409 else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
2410 pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
2411 (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
2412 pg_strcasecmp(prev2_wd, "FREEZE") == 0))
2413 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2414 " UNION SELECT 'ANALYZE'");
2415 else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2416 pg_strcasecmp(prev_wd, "VERBOSE") == 0)
2417 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2418 " UNION SELECT 'ANALYZE'");
2419 else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2420 pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2421 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2422 " UNION SELECT 'VERBOSE'");
2423 else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
2424 pg_strcasecmp(prev2_wd, "VERBOSE") == 0) ||
2425 (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
2426 pg_strcasecmp(prev2_wd, "ANALYZE") == 0))
2427 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2429 /* WITH [RECURSIVE] */
2430 else if (pg_strcasecmp(prev_wd, "WITH") == 0)
2431 COMPLETE_WITH_CONST("RECURSIVE");
2434 /* If the previous word is ANALYZE, produce list of tables */
2435 else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2436 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2439 /* Simple case of the word before the where being the table name */
2440 else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
2441 COMPLETE_WITH_ATTR(prev2_wd, "");
2444 /* TODO: also include SRF ? */
2445 else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
2446 pg_strcasecmp(prev3_wd, "COPY") != 0 &&
2447 pg_strcasecmp(prev3_wd, "\\copy") != 0)
2448 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
2451 /* Backslash commands */
2452 /* TODO: \dc \dd \dl */
2453 else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
2454 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
2456 else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0)
2457 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
2458 else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0)
2459 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
2460 else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0)
2461 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
2462 else if (strncmp(prev_wd, "\\des", strlen("\\des")) == 0)
2463 COMPLETE_WITH_QUERY(Query_for_list_of_servers);
2464 else if (strncmp(prev_wd, "\\deu", strlen("\\deu")) == 0)
2465 COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
2466 else if (strncmp(prev_wd, "\\dew", strlen("\\dew")) == 0)
2467 COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
2469 else if (strncmp(prev_wd, "\\df", strlen("\\df")) == 0)
2470 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2471 else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0)
2472 COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
2473 else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0)
2474 COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
2475 else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0)
2476 COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
2477 /* must be at end of \dF */
2478 else if (strncmp(prev_wd, "\\dF", strlen("\\dF")) == 0)
2479 COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
2481 else if (strncmp(prev_wd, "\\di", strlen("\\di")) == 0)
2482 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
2483 else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0)
2484 COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
2485 else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0)
2486 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
2487 else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
2488 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
2489 else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
2490 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2491 else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0)
2492 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
2493 else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 0)
2494 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2495 else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0)
2496 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
2498 /* must be at end of \d list */
2499 else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
2500 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
2502 else if (strcmp(prev_wd, "\\ef") == 0)
2503 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2505 else if (strcmp(prev_wd, "\\encoding") == 0)
2506 COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
2507 else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
2508 COMPLETE_WITH_LIST(sql_commands);
2509 else if (strcmp(prev_wd, "\\password") == 0)
2510 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2511 else if (strcmp(prev_wd, "\\pset") == 0)
2513 static const char *const my_list[] =
2514 {"format", "border", "expanded",
2515 "null", "fieldsep", "tuples_only", "title", "tableattr",
2516 "linestyle", "pager", "recordsep", NULL};
2518 COMPLETE_WITH_LIST(my_list);
2520 else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
2521 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2522 else if (strcmp(prev_wd, "\\cd") == 0 ||
2523 strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
2524 strcmp(prev_wd, "\\g") == 0 ||
2525 strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
2526 strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
2527 strcmp(prev_wd, "\\s") == 0 ||
2528 strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
2530 matches = completion_matches(text, filename_completion_function);
2534 * Finally, we look through the list of "things", such as TABLE, INDEX and
2535 * check if that was the previous word. If so, execute the query to get a
2542 for (i = 0; words_after_create[i].name; i++)
2544 if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)
2546 if (words_after_create[i].query)
2547 COMPLETE_WITH_QUERY(words_after_create[i].query);
2548 else if (words_after_create[i].squery)
2549 COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
2557 * If we still don't have anything to match we have to fabricate some sort
2558 * of default list. If we were to just return NULL, readline automatically
2559 * attempts filename completion, and that's usually no good.
2561 if (matches == NULL)
2563 COMPLETE_WITH_CONST("");
2564 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
2565 rl_completion_append_character = '\0';
2576 /* Return our Grand List O' Matches */
2582 /* GENERATOR FUNCTIONS
2584 These functions do all the actual work of completing the input. They get
2585 passed the text so far and the count how many times they have been called so
2586 far with the same text.
2587 If you read the above carefully, you'll see that these don't get called
2588 directly but through the readline interface.
2589 The return value is expected to be the full completion of the text, going
2590 through a list each time, or NULL if there are no more matches. The string
2591 will be free()'d by readline, so you must run it through strdup() or
2592 something of that sort.
2595 /* This one gives you one from a list of things you can put after CREATE
2599 create_command_generator(const char *text, int state)
2601 static int list_index,
2605 /* If this is the first time for this completion, init some values */
2609 string_length = strlen(text);
2612 /* find something that matches */
2613 while ((name = words_after_create[list_index++].name))
2615 if ((pg_strncasecmp(name, text, string_length) == 0) && !words_after_create[list_index - 1].noshow)
2616 return pg_strdup(name);
2618 /* if nothing matches, return NULL */
2623 * This function gives you a list of things you can put after a DROP command.
2624 * Very similar to create_command_generator, but has an additional entry for
2625 * OWNED BY. (We do it this way in order not to duplicate the
2626 * words_after_create list.)
2629 drop_command_generator(const char *text, int state)
2631 static int list_index,
2637 /* If this is the first time for this completion, init some values */
2639 string_length = strlen(text);
2642 * DROP can be followed by "OWNED BY", which is not found in the list
2643 * for CREATE matches, so make it the first state. (We do not make it
2644 * the last state because it would be more difficult to detect when we
2645 * have to return NULL instead.)
2647 * Make sure we advance to the next state.
2650 if (pg_strncasecmp("OWNED", text, string_length) == 0)
2651 return pg_strdup("OWNED");
2655 * In subsequent attempts, try to complete with the same items we use for
2658 while ((name = words_after_create[list_index++ - 1].name))
2660 if ((pg_strncasecmp(name, text, string_length) == 0) && (!words_after_create[list_index - 2].noshow))
2661 return pg_strdup(name);
2664 /* if nothing matches, return NULL */
2668 /* The following two functions are wrappers for _complete_from_query */
2671 complete_from_query(const char *text, int state)
2673 return _complete_from_query(0, text, state);
2677 complete_from_schema_query(const char *text, int state)
2679 return _complete_from_query(1, text, state);
2683 /* This creates a list of matching things, according to a query pointed to
2684 by completion_charp.
2685 The query can be one of two kinds:
2686 - A simple query which must contain a %d and a %s, which will be replaced
2687 by the string length of the text and the text itself. The query may also
2688 have up to four more %s in it; the first two such will be replaced by the
2689 value of completion_info_charp, the next two by the value of
2690 completion_info_charp2.
2692 - A schema query used for completion of both schema and relation names;
2693 these are more complex and must contain in the following order:
2694 %d %s %d %s %d %s %s %d %s
2695 where %d is the string length of the text and %s the text itself.
2697 It is assumed that strings should be escaped to become SQL literals
2698 (that is, what is in the query is actually ... '%s' ...)
2700 See top of file for examples of both kinds of query.
2704 _complete_from_query(int is_schema_query, const char *text, int state)
2706 static int list_index,
2708 static PGresult *result = NULL;
2711 * If this is the first time for this completion, we fetch a list of our
2712 * "things" from the backend.
2716 PQExpBufferData query_buffer;
2719 char *e_info_charp2;
2722 string_length = strlen(text);
2724 /* Free any prior result */
2728 /* Set up suitably-escaped copies of textual inputs */
2729 e_text = pg_malloc(string_length * 2 + 1);
2730 PQescapeString(e_text, text, string_length);
2732 if (completion_info_charp)
2736 charp_len = strlen(completion_info_charp);
2737 e_info_charp = pg_malloc(charp_len * 2 + 1);
2738 PQescapeString(e_info_charp, completion_info_charp,
2742 e_info_charp = NULL;
2744 if (completion_info_charp2)
2748 charp_len = strlen(completion_info_charp2);
2749 e_info_charp2 = pg_malloc(charp_len * 2 + 1);
2750 PQescapeString(e_info_charp2, completion_info_charp2,
2754 e_info_charp2 = NULL;
2756 initPQExpBuffer(&query_buffer);
2758 if (is_schema_query)
2760 /* completion_squery gives us the pieces to assemble */
2761 const char *qualresult = completion_squery->qualresult;
2763 if (qualresult == NULL)
2764 qualresult = completion_squery->result;
2766 /* Get unqualified names matching the input-so-far */
2767 appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
2768 completion_squery->result,
2769 completion_squery->catname);
2770 if (completion_squery->selcondition)
2771 appendPQExpBuffer(&query_buffer, "%s AND ",
2772 completion_squery->selcondition);
2773 appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
2774 completion_squery->result,
2775 string_length, e_text);
2776 appendPQExpBuffer(&query_buffer, " AND %s",
2777 completion_squery->viscondition);
2780 * When fetching relation names, suppress system catalogs unless
2781 * the input-so-far begins with "pg_". This is a compromise
2782 * between not offering system catalogs for completion at all, and
2783 * having them swamp the result when the input is just "p".
2785 if (strcmp(completion_squery->catname,
2786 "pg_catalog.pg_class c") == 0 &&
2787 strncmp(text, "pg_", 3) !=0)
2789 appendPQExpBuffer(&query_buffer,
2790 " AND c.relnamespace <> (SELECT oid FROM"
2791 " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
2795 * Add in matching schema names, but only if there is more than
2796 * one potential match among schema names.
2798 appendPQExpBuffer(&query_buffer, "\nUNION\n"
2799 "SELECT pg_catalog.quote_ident(n.nspname) || '.' "
2800 "FROM pg_catalog.pg_namespace n "
2801 "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'",
2802 string_length, e_text);
2803 appendPQExpBuffer(&query_buffer,
2804 " AND (SELECT pg_catalog.count(*)"
2805 " FROM pg_catalog.pg_namespace"
2806 " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
2807 " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1",
2808 string_length, e_text);
2811 * Add in matching qualified names, but only if there is exactly
2812 * one schema matching the input-so-far.
2814 appendPQExpBuffer(&query_buffer, "\nUNION\n"
2815 "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
2816 "FROM %s, pg_catalog.pg_namespace n "
2817 "WHERE %s = n.oid AND ",
2819 completion_squery->catname,
2820 completion_squery->namespace);
2821 if (completion_squery->selcondition)
2822 appendPQExpBuffer(&query_buffer, "%s AND ",
2823 completion_squery->selcondition);
2824 appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
2826 string_length, e_text);
2829 * This condition exploits the single-matching-schema rule to
2830 * speed up the query
2832 appendPQExpBuffer(&query_buffer,
2833 " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="
2834 " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)",
2835 string_length, e_text);
2836 appendPQExpBuffer(&query_buffer,
2837 " AND (SELECT pg_catalog.count(*)"
2838 " FROM pg_catalog.pg_namespace"
2839 " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
2840 " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1",
2841 string_length, e_text);
2843 /* If an addon query was provided, use it */
2844 if (completion_charp)
2845 appendPQExpBuffer(&query_buffer, "\n%s", completion_charp);
2849 /* completion_charp is an sprintf-style format string */
2850 appendPQExpBuffer(&query_buffer, completion_charp,
2851 string_length, e_text,
2852 e_info_charp, e_info_charp,
2853 e_info_charp2, e_info_charp2);
2856 /* Limit the number of records in the result */
2857 appendPQExpBuffer(&query_buffer, "\nLIMIT %d",
2858 completion_max_records);
2860 result = exec_query(query_buffer.data);
2862 termPQExpBuffer(&query_buffer);
2867 free(e_info_charp2);
2870 /* Find something that matches */
2871 if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
2875 while (list_index < PQntuples(result) &&
2876 (item = PQgetvalue(result, list_index++, 0)))
2877 if (pg_strncasecmp(text, item, string_length) == 0)
2878 return pg_strdup(item);
2881 /* If nothing matches, free the db structure and return null */
2888 /* This function returns in order one of a fixed, NULL pointer terminated list
2889 of strings (if matching). This can be used if there are only a fixed number
2890 SQL words that can appear at certain spot.
2893 complete_from_list(const char *text, int state)
2895 static int string_length,
2898 static bool casesensitive;
2901 /* need to have a list */
2902 psql_assert(completion_charpp);
2904 /* Initialization */
2908 string_length = strlen(text);
2909 casesensitive = true;
2913 while ((item = completion_charpp[list_index++]))
2915 /* First pass is case sensitive */
2916 if (casesensitive && strncmp(text, item, string_length) == 0)
2919 return pg_strdup(item);
2922 /* Second pass is case insensitive, don't bother counting matches */
2923 if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
2924 return pg_strdup(item);
2928 * No matches found. If we're not case insensitive already, lets switch to
2929 * being case insensitive and try again
2931 if (casesensitive && matches == 0)
2933 casesensitive = false;
2936 return complete_from_list(text, state);
2939 /* If no more matches, return null. */
2944 /* This function returns one fixed string the first time even if it doesn't
2945 match what's there, and nothing the second time. This should be used if there
2946 is only one possibility that can appear at a certain spot, so misspellings
2947 will be overwritten.
2948 The string to be passed must be in completion_charp.
2951 complete_from_const(const char *text, int state)
2953 (void) text; /* We don't care about what was entered
2956 psql_assert(completion_charp);
2958 return pg_strdup(completion_charp);
2965 /* HELPER FUNCTIONS */
2969 * Execute a query and report any errors. This should be the preferred way of
2970 * talking to the database in this file.
2973 exec_query(const char *query)
2977 if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK)
2980 result = PQexec(pset.db, query);
2982 if (PQresultStatus(result) != PGRES_TUPLES_OK)
2985 psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
2986 PQerrorMessage(pset.db), query);
2998 * Return the word (space delimited) before point. Set skip > 0 to
2999 * skip that many words; e.g. skip=1 finds the word before the
3000 * previous one. Return value is NULL or a malloc'ed string.
3003 previous_word(int point, int skip)
3010 const char *buf = rl_line_buffer; /* alias */
3012 /* first we look for a space or a parenthesis before the current word */
3013 for (i = point - 1; i >= 0; i--)
3014 if (strchr(WORD_BREAKS, buf[i]))
3020 int parentheses = 0;
3022 /* now find the first non-space which then constitutes the end */
3023 for (i = point; i >= 0; i--)
3031 * If no end found we return null, because there is no word before the
3038 * Otherwise we now look for the start. The start is either the last
3039 * character before any space going backwards from the end, or it's
3040 * simply character 0. We also handle open quotes and parentheses.
3042 for (start = end; start > 0; start--)
3044 if (buf[start] == '"')
3045 inquotes = !inquotes;
3048 if (buf[start] == ')')
3050 else if (buf[start] == '(')
3052 if (--parentheses <= 0)
3055 else if (parentheses == 0 &&
3056 strchr(WORD_BREAKS, buf[start - 1]))
3065 s = pg_malloc(end - start + 2);
3066 strlcpy(s, &buf[start], end - start + 2);
3074 * Surround a string with single quotes. This works for both SQL and
3075 * psql internal. Currently disabled because it is reported not to
3076 * cooperate with certain versions of readline.
3079 quote_file_name(char *text, int match_type, char *quote_pointer)
3084 (void) quote_pointer; /* not used */
3086 length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2);
3087 s = pg_malloc(length);
3089 strcpy(s + 1, text);
3090 if (match_type == SINGLE_MATCH)
3091 s[length - 2] = '\'';
3092 s[length - 1] = '\0';
3099 dequote_file_name(char *text, char quote_char)
3105 return pg_strdup(text);
3107 length = strlen(text);
3108 s = pg_malloc(length - 2 + 1);
3109 strlcpy(s, text +1, length - 2 + 1);
3115 #endif /* USE_READLINE */