Fix obsolete comment regarding FSM truncation.
[PostgreSQL.git] / src / bin / psql / tab-complete.c
blob784413b167546e131164f39451b8831a9f749616
1 /*
2 * psql - the PostgreSQL interactive terminal
4 * Copyright (c) 2000-2008, PostgreSQL Global Development Group
6 * $PostgreSQL$
7 */
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:
23 * $if psql
24 * set disable-completion on
25 * $endif
27 * See `man 3 readline' or `info readline' for the full details. Also,
28 * hence the
30 * BUGS:
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"
46 #include "input.h"
48 /* If we don't have this, we might as well forget about the whole thing: */
49 #ifdef USE_READLINE
51 #include <ctype.h>
52 #include "libpq-fe.h"
53 #include "pqexpbuffer.h"
54 #include "common.h"
55 #include "settings.h"
56 #include "stringutils.h"
58 #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
59 #define filename_completion_function rl_filename_completion_function
60 #else
61 /* missing in some header files */
62 extern char *filename_completion_function();
63 #endif
65 #ifdef HAVE_RL_COMPLETION_MATCHES
66 #define completion_matches rl_completion_matches
67 #endif
71 * This struct is used to define "schema queries", which are custom-built
72 * to obtain possibly-schema-qualified names of database objects. There is
73 * enough similarity in the structure that we don't want to repeat it each
74 * time. So we put the components of each query into this struct and
75 * assemble them with the common boilerplate in _complete_from_query().
77 typedef struct SchemaQuery
80 * Name of catalog or catalogs to be queried, with alias, eg.
81 * "pg_catalog.pg_class c". Note that "pg_namespace n" will be added.
83 const char *catname;
86 * Selection condition --- only rows meeting this condition are candidates
87 * to display. If catname mentions multiple tables, include the necessary
88 * join condition here. For example, "c.relkind = 'r'". Write NULL (not
89 * an empty string) if not needed.
91 const char *selcondition;
94 * Visibility condition --- which rows are visible without schema
95 * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)".
97 const char *viscondition;
100 * Namespace --- name of field to join to pg_namespace.oid. For example,
101 * "c.relnamespace".
103 const char *namespace;
106 * Result --- the appropriately-quoted name to return, in the case of an
107 * unqualified name. For example, "pg_catalog.quote_ident(c.relname)".
109 const char *result;
112 * In some cases a different result must be used for qualified names.
113 * Enter that here, or write NULL if result can be used.
115 const char *qualresult;
116 } SchemaQuery;
119 /* Store maximum number of records we want from database queries
120 * (implemented via SELECT ... LIMIT xx).
122 static int completion_max_records;
125 * Communication variables set by COMPLETE_WITH_FOO macros and then used by
126 * the completion callback functions. Ugly but there is no better way.
128 static const char *completion_charp; /* to pass a string */
129 static const char *const * completion_charpp; /* to pass a list of strings */
130 static const char *completion_info_charp; /* to pass a second string */
131 static const char *completion_info_charp2; /* to pass a third string */
132 static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */
135 * A few macros to ease typing. You can use these to complete the given
136 * string with
137 * 1) The results from a query you pass it. (Perhaps one of those below?)
138 * 2) The results from a schema query you pass it.
139 * 3) The items from a null-pointer-terminated list.
140 * 4) A string constant.
141 * 5) The list of attributes of the given table (possibly schema-qualified).
143 #define COMPLETE_WITH_QUERY(query) \
144 do { \
145 completion_charp = query; \
146 matches = completion_matches(text, complete_from_query); \
147 } while (0)
149 #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
150 do { \
151 completion_squery = &(query); \
152 completion_charp = addon; \
153 matches = completion_matches(text, complete_from_schema_query); \
154 } while (0)
156 #define COMPLETE_WITH_LIST(list) \
157 do { \
158 completion_charpp = list; \
159 matches = completion_matches(text, complete_from_list); \
160 } while (0)
162 #define COMPLETE_WITH_CONST(string) \
163 do { \
164 completion_charp = string; \
165 matches = completion_matches(text, complete_from_const); \
166 } while (0)
168 #define COMPLETE_WITH_ATTR(relation, addon) \
169 do { \
170 char *_completion_schema; \
171 char *_completion_table; \
173 _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \
174 false, false, pset.encoding); \
175 (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
176 false, false, pset.encoding); \
177 _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
178 false, false, pset.encoding); \
179 if (_completion_table == NULL) \
181 completion_charp = Query_for_list_of_attributes addon; \
182 completion_info_charp = relation; \
184 else \
186 completion_charp = Query_for_list_of_attributes_with_schema addon; \
187 completion_info_charp = _completion_table; \
188 completion_info_charp2 = _completion_schema; \
190 matches = completion_matches(text, complete_from_query); \
191 } while (0)
194 * Assembly instructions for schema queries
197 static const SchemaQuery Query_for_list_of_aggregates = {
198 /* catname */
199 "pg_catalog.pg_proc p",
200 /* selcondition */
201 "p.proisagg",
202 /* viscondition */
203 "pg_catalog.pg_function_is_visible(p.oid)",
204 /* namespace */
205 "p.pronamespace",
206 /* result */
207 "pg_catalog.quote_ident(p.proname)",
208 /* qualresult */
209 NULL
212 static const SchemaQuery Query_for_list_of_datatypes = {
213 /* catname */
214 "pg_catalog.pg_type t",
215 /* selcondition --- ignore table rowtypes and array types */
216 "(t.typrelid = 0 "
217 " OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
218 "AND t.typname !~ '^_'",
219 /* viscondition */
220 "pg_catalog.pg_type_is_visible(t.oid)",
221 /* namespace */
222 "t.typnamespace",
223 /* result */
224 "pg_catalog.format_type(t.oid, NULL)",
225 /* qualresult */
226 "pg_catalog.quote_ident(t.typname)"
229 static const SchemaQuery Query_for_list_of_domains = {
230 /* catname */
231 "pg_catalog.pg_type t",
232 /* selcondition */
233 "t.typtype = 'd'",
234 /* viscondition */
235 "pg_catalog.pg_type_is_visible(t.oid)",
236 /* namespace */
237 "t.typnamespace",
238 /* result */
239 "pg_catalog.quote_ident(t.typname)",
240 /* qualresult */
241 NULL
244 static const SchemaQuery Query_for_list_of_functions = {
245 /* catname */
246 "pg_catalog.pg_proc p",
247 /* selcondition */
248 NULL,
249 /* viscondition */
250 "pg_catalog.pg_function_is_visible(p.oid)",
251 /* namespace */
252 "p.pronamespace",
253 /* result */
254 "pg_catalog.quote_ident(p.proname)",
255 /* qualresult */
256 NULL
259 static const SchemaQuery Query_for_list_of_indexes = {
260 /* catname */
261 "pg_catalog.pg_class c",
262 /* selcondition */
263 "c.relkind IN ('i')",
264 /* viscondition */
265 "pg_catalog.pg_table_is_visible(c.oid)",
266 /* namespace */
267 "c.relnamespace",
268 /* result */
269 "pg_catalog.quote_ident(c.relname)",
270 /* qualresult */
271 NULL
274 static const SchemaQuery Query_for_list_of_sequences = {
275 /* catname */
276 "pg_catalog.pg_class c",
277 /* selcondition */
278 "c.relkind IN ('S')",
279 /* viscondition */
280 "pg_catalog.pg_table_is_visible(c.oid)",
281 /* namespace */
282 "c.relnamespace",
283 /* result */
284 "pg_catalog.quote_ident(c.relname)",
285 /* qualresult */
286 NULL
289 static const SchemaQuery Query_for_list_of_tables = {
290 /* catname */
291 "pg_catalog.pg_class c",
292 /* selcondition */
293 "c.relkind IN ('r')",
294 /* viscondition */
295 "pg_catalog.pg_table_is_visible(c.oid)",
296 /* namespace */
297 "c.relnamespace",
298 /* result */
299 "pg_catalog.quote_ident(c.relname)",
300 /* qualresult */
301 NULL
304 static const SchemaQuery Query_for_list_of_tisv = {
305 /* catname */
306 "pg_catalog.pg_class c",
307 /* selcondition */
308 "c.relkind IN ('r', 'i', 'S', 'v')",
309 /* viscondition */
310 "pg_catalog.pg_table_is_visible(c.oid)",
311 /* namespace */
312 "c.relnamespace",
313 /* result */
314 "pg_catalog.quote_ident(c.relname)",
315 /* qualresult */
316 NULL
319 static const SchemaQuery Query_for_list_of_tsv = {
320 /* catname */
321 "pg_catalog.pg_class c",
322 /* selcondition */
323 "c.relkind IN ('r', 'S', 'v')",
324 /* viscondition */
325 "pg_catalog.pg_table_is_visible(c.oid)",
326 /* namespace */
327 "c.relnamespace",
328 /* result */
329 "pg_catalog.quote_ident(c.relname)",
330 /* qualresult */
331 NULL
334 static const SchemaQuery Query_for_list_of_views = {
335 /* catname */
336 "pg_catalog.pg_class c",
337 /* selcondition */
338 "c.relkind IN ('v')",
339 /* viscondition */
340 "pg_catalog.pg_table_is_visible(c.oid)",
341 /* namespace */
342 "c.relnamespace",
343 /* result */
344 "pg_catalog.quote_ident(c.relname)",
345 /* qualresult */
346 NULL
351 * Queries to get lists of names of various kinds of things, possibly
352 * restricted to names matching a partially entered name. In these queries,
353 * the first %s will be replaced by the text entered so far (suitably escaped
354 * to become a SQL literal string). %d will be replaced by the length of the
355 * string (in unescaped form). A second and third %s, if present, will be
356 * replaced by a suitably-escaped version of the string provided in
357 * completion_info_charp. A fourth and fifth %s are similarly replaced by
358 * completion_info_charp2.
360 * Beware that the allowed sequences of %s and %d are determined by
361 * _complete_from_query().
364 #define Query_for_list_of_attributes \
365 "SELECT pg_catalog.quote_ident(attname) "\
366 " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
367 " WHERE c.oid = a.attrelid "\
368 " AND a.attnum > 0 "\
369 " AND NOT a.attisdropped "\
370 " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
371 " AND (pg_catalog.quote_ident(relname)='%s' "\
372 " OR '\"' || relname || '\"'='%s') "\
373 " AND pg_catalog.pg_table_is_visible(c.oid)"
375 #define Query_for_list_of_attributes_with_schema \
376 "SELECT pg_catalog.quote_ident(attname) "\
377 " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
378 " WHERE c.oid = a.attrelid "\
379 " AND n.oid = c.relnamespace "\
380 " AND a.attnum > 0 "\
381 " AND NOT a.attisdropped "\
382 " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
383 " AND (pg_catalog.quote_ident(relname)='%s' "\
384 " OR '\"' || relname || '\"' ='%s') "\
385 " AND (pg_catalog.quote_ident(nspname)='%s' "\
386 " OR '\"' || nspname || '\"' ='%s') "
388 #define Query_for_list_of_template_databases \
389 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
390 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' and datistemplate IS TRUE"
392 #define Query_for_list_of_databases \
393 "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
394 " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"
396 #define Query_for_list_of_tablespaces \
397 "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\
398 " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"
400 #define Query_for_list_of_encodings \
401 " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
402 " FROM pg_catalog.pg_conversion "\
403 " WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
405 #define Query_for_list_of_languages \
406 "SELECT pg_catalog.quote_ident(lanname) "\
407 " FROM pg_language "\
408 " WHERE lanname != 'internal' "\
409 " AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s' "
411 #define Query_for_list_of_schemas \
412 "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\
413 " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"
415 #define Query_for_list_of_set_vars \
416 "SELECT name FROM "\
417 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
418 " WHERE context IN ('user', 'superuser') "\
419 " UNION ALL SELECT 'constraints' "\
420 " UNION ALL SELECT 'transaction' "\
421 " UNION ALL SELECT 'session' "\
422 " UNION ALL SELECT 'role' "\
423 " UNION ALL SELECT 'tablespace' "\
424 " UNION ALL SELECT 'all') ss "\
425 " WHERE substring(name,1,%d)='%s'"
427 #define Query_for_list_of_show_vars \
428 "SELECT name FROM "\
429 " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
430 " UNION ALL SELECT 'session authorization' "\
431 " UNION ALL SELECT 'all') ss "\
432 " WHERE substring(name,1,%d)='%s'"
435 * Note: As of Pg 8.2, we no longer use relkind 's', but we keep it here
436 * for compatibility with older servers
438 #define Query_for_list_of_system_relations \
439 "SELECT pg_catalog.quote_ident(relname) "\
440 " FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
441 " WHERE c.relkind IN ('r', 'v', 's', 'S') "\
442 " AND substring(pg_catalog.quote_ident(relname),1,%d)='%s' "\
443 " AND c.relnamespace = n.oid "\
444 " AND n.nspname = 'pg_catalog'"
446 #define Query_for_list_of_roles \
447 " SELECT pg_catalog.quote_ident(rolname) "\
448 " FROM pg_catalog.pg_roles "\
449 " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"
451 #define Query_for_list_of_grant_roles \
452 " SELECT pg_catalog.quote_ident(rolname) "\
453 " FROM pg_catalog.pg_roles "\
454 " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\
455 " UNION ALL SELECT 'PUBLIC'"
457 /* the silly-looking length condition is just to eat up the current word */
458 #define Query_for_table_owning_index \
459 "SELECT pg_catalog.quote_ident(c1.relname) "\
460 " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
461 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
462 " and (%d = pg_catalog.length('%s'))"\
463 " and pg_catalog.quote_ident(c2.relname)='%s'"\
464 " and pg_catalog.pg_table_is_visible(c2.oid)"
466 /* the silly-looking length condition is just to eat up the current word */
467 #define Query_for_index_of_table \
468 "SELECT pg_catalog.quote_ident(c2.relname) "\
469 " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
470 " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
471 " and (%d = pg_catalog.length('%s'))"\
472 " and pg_catalog.quote_ident(c1.relname)='%s'"\
473 " and pg_catalog.pg_table_is_visible(c2.oid)"
475 /* the silly-looking length condition is just to eat up the current word */
476 #define Query_for_list_of_tables_for_trigger \
477 "SELECT pg_catalog.quote_ident(relname) "\
478 " FROM pg_catalog.pg_class"\
479 " WHERE (%d = pg_catalog.length('%s'))"\
480 " AND oid IN "\
481 " (SELECT tgrelid FROM pg_catalog.pg_trigger "\
482 " WHERE pg_catalog.quote_ident(tgname)='%s')"
484 #define Query_for_list_of_ts_configurations \
485 "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
486 " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
488 #define Query_for_list_of_ts_dictionaries \
489 "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\
490 " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'"
492 #define Query_for_list_of_ts_parsers \
493 "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\
494 " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'"
496 #define Query_for_list_of_ts_templates \
497 "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\
498 " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'"
501 * This is a list of all "things" in Pgsql, which can show up after CREATE or
502 * DROP; and there is also a query to get a list of them.
505 typedef struct
507 const char *name;
508 const char *query; /* simple query, or NULL */
509 const SchemaQuery *squery; /* schema query, or NULL */
510 const bool noshow; /* NULL or true if this word should not show
511 * up after CREATE or DROP */
512 } pgsql_thing_t;
514 static const pgsql_thing_t words_after_create[] = {
515 {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
516 {"CAST", NULL, NULL}, /* Casts have complex structures for names, so
517 * skip it */
520 * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
521 * to be used only by pg_dump.
523 {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, true},
524 {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
525 {"DATABASE", Query_for_list_of_databases},
526 {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
527 {"DOMAIN", NULL, &Query_for_list_of_domains},
528 {"FUNCTION", NULL, &Query_for_list_of_functions},
529 {"GROUP", Query_for_list_of_roles},
530 {"LANGUAGE", Query_for_list_of_languages},
531 {"INDEX", NULL, &Query_for_list_of_indexes},
532 {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a
533 * good idea. */
534 {"PARSER", Query_for_list_of_ts_parsers, NULL, true},
535 {"ROLE", Query_for_list_of_roles},
536 {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
537 {"SCHEMA", Query_for_list_of_schemas},
538 {"SEQUENCE", NULL, &Query_for_list_of_sequences},
539 {"TABLE", NULL, &Query_for_list_of_tables},
540 {"TABLESPACE", Query_for_list_of_tablespaces},
541 {"TEMP", NULL, NULL}, /* for CREATE TEMP TABLE ... */
542 {"TEMPLATE", Query_for_list_of_ts_templates, NULL, true},
543 {"TEXT SEARCH", NULL, NULL},
544 {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"},
545 {"TYPE", NULL, &Query_for_list_of_datatypes},
546 {"UNIQUE", NULL, NULL}, /* for CREATE UNIQUE INDEX ... */
547 {"USER", Query_for_list_of_roles},
548 {"VIEW", NULL, &Query_for_list_of_views},
549 {NULL, NULL, NULL, false} /* end of list */
553 /* Forward declaration of functions */
554 static char **psql_completion(char *text, int start, int end);
555 static char *create_command_generator(const char *text, int state);
556 static char *drop_command_generator(const char *text, int state);
557 static char *complete_from_query(const char *text, int state);
558 static char *complete_from_schema_query(const char *text, int state);
559 static char *_complete_from_query(int is_schema_query,
560 const char *text, int state);
561 static char *complete_from_const(const char *text, int state);
562 static char *complete_from_list(const char *text, int state);
564 static PGresult *exec_query(const char *query);
566 static char *previous_word(int point, int skip);
568 static int find_open_parenthesis(int end);
570 #if 0
571 static char *quote_file_name(char *text, int match_type, char *quote_pointer);
572 static char *dequote_file_name(char *text, char quote_char);
573 #endif
576 /* Initialize the readline library for our purposes. */
577 void
578 initialize_readline(void)
580 rl_readline_name = (char *) pset.progname;
581 rl_attempted_completion_function = (void *) psql_completion;
583 rl_basic_word_break_characters = "\t\n@$><=;|&{( ";
585 completion_max_records = 1000;
588 * There is a variable rl_completion_query_items for this but apparently
589 * it's not defined everywhere.
594 /* The completion function. Acc. to readline spec this gets passed the text
595 entered to far and its start and end in the readline buffer. The return value
596 is some partially obscure list format that can be generated by the readline
597 libraries completion_matches() function, so we don't have to worry about it.
599 static char **
600 psql_completion(char *text, int start, int end)
602 /* This is the variable we'll return. */
603 char **matches = NULL;
605 /* These are going to contain some scannage of the input line. */
606 char *prev_wd,
607 *prev2_wd,
608 *prev3_wd,
609 *prev4_wd,
610 *prev5_wd;
612 static const char *const sql_commands[] = {
613 "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
614 "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
615 "DELETE FROM", "DISCARD", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
616 "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
617 "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
618 "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TABLE", "TRUNCATE", "UNLISTEN",
619 "UPDATE", "VACUUM", "VALUES", "WITH", NULL
622 static const char *const backslash_commands[] = {
623 "\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright",
624 "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\df",
625 "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
626 "\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
627 "\\e", "\\echo", "\\encoding",
628 "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
629 "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
630 "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
631 "\\set", "\\t", "\\T",
632 "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
635 (void) end; /* not used */
637 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
638 rl_completion_append_character = ' ';
639 #endif
641 /* Clear a few things. */
642 completion_charp = NULL;
643 completion_charpp = NULL;
644 completion_info_charp = NULL;
645 completion_info_charp2 = NULL;
648 * Scan the input line before our current position for the last five
649 * words. According to those we'll make some smart decisions on what the
650 * user is probably intending to type. TODO: Use strtokx() to do this.
652 prev_wd = previous_word(start, 0);
653 prev2_wd = previous_word(start, 1);
654 prev3_wd = previous_word(start, 2);
655 prev4_wd = previous_word(start, 3);
656 prev5_wd = previous_word(start, 4);
658 /* If a backslash command was started, continue */
659 if (text[0] == '\\')
660 COMPLETE_WITH_LIST(backslash_commands);
662 /* If no previous word, suggest one of the basic sql commands */
663 else if (!prev_wd)
664 COMPLETE_WITH_LIST(sql_commands);
666 /* CREATE */
667 /* complete with something you can create */
668 else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
669 matches = completion_matches(text, create_command_generator);
671 /* DROP, except ALTER (TABLE|DOMAIN|GROUP) sth DROP */
672 /* complete with something you can drop */
673 else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
674 pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
675 pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
676 pg_strcasecmp(prev3_wd, "GROUP") != 0)
677 matches = completion_matches(text, drop_command_generator);
679 /* ALTER */
682 * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're
683 * in ALTER TABLE sth ALTER
685 else if (pg_strcasecmp(prev_wd, "ALTER") == 0 &&
686 pg_strcasecmp(prev3_wd, "TABLE") != 0)
688 static const char *const list_ALTER[] =
689 {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION",
690 "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE",
691 "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "VIEW", NULL};
693 COMPLETE_WITH_LIST(list_ALTER);
695 /* ALTER AGGREGATE,FUNCTION <name> */
696 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
697 (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
698 pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
700 static const char *const list_ALTERAGG[] =
701 {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
703 COMPLETE_WITH_LIST(list_ALTERAGG);
706 /* ALTER CONVERSION,SCHEMA <name> */
707 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
708 (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
709 pg_strcasecmp(prev2_wd, "SCHEMA") == 0))
711 static const char *const list_ALTERGEN[] =
712 {"OWNER TO", "RENAME TO", NULL};
714 COMPLETE_WITH_LIST(list_ALTERGEN);
717 /* ALTER DATABASE <name> */
718 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
719 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
721 static const char *const list_ALTERDATABASE[] =
722 {"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};
724 COMPLETE_WITH_LIST(list_ALTERDATABASE);
727 /* ALTER INDEX <name> */
728 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
729 pg_strcasecmp(prev2_wd, "INDEX") == 0)
731 static const char *const list_ALTERINDEX[] =
732 {"OWNER TO", "RENAME TO", "SET", "RESET", NULL};
734 COMPLETE_WITH_LIST(list_ALTERINDEX);
737 /* ALTER LANGUAGE <name> */
738 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
739 pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
741 static const char *const list_ALTERLANGUAGE[] =
742 {"OWNER TO", "RENAME TO", NULL};
744 COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
747 /* ALTER USER,ROLE <name> */
748 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
749 (pg_strcasecmp(prev2_wd, "USER") == 0 ||
750 pg_strcasecmp(prev2_wd, "ROLE") == 0))
752 static const char *const list_ALTERUSER[] =
753 {"ENCRYPTED", "UNENCRYPTED", "CREATEDB", "NOCREATEDB", "CREATEUSER",
754 "NOCREATEUSER", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT",
755 "LOGIN", "NOLOGIN", "CONNECTION LIMIT", "VALID UNTIL", "RENAME TO",
756 "SUPERUSER", "NOSUPERUSER", "SET", "RESET", NULL};
758 COMPLETE_WITH_LIST(list_ALTERUSER);
761 /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
762 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
763 (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
764 (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
766 COMPLETE_WITH_CONST("PASSWORD");
768 /* ALTER DOMAIN <name> */
769 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
770 pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
772 static const char *const list_ALTERDOMAIN[] =
773 {"ADD", "DROP", "OWNER TO", "SET", NULL};
775 COMPLETE_WITH_LIST(list_ALTERDOMAIN);
777 /* ALTER DOMAIN <sth> DROP */
778 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
779 pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
780 pg_strcasecmp(prev_wd, "DROP") == 0)
782 static const char *const list_ALTERDOMAIN2[] =
783 {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL};
785 COMPLETE_WITH_LIST(list_ALTERDOMAIN2);
787 /* ALTER DOMAIN <sth> SET */
788 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
789 pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
790 pg_strcasecmp(prev_wd, "SET") == 0)
792 static const char *const list_ALTERDOMAIN3[] =
793 {"DEFAULT", "NOT NULL", "SCHEMA", NULL};
795 COMPLETE_WITH_LIST(list_ALTERDOMAIN3);
797 /* ALTER SEQUENCE <name> */
798 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
799 pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)
801 static const char *const list_ALTERSEQUENCE[] =
802 {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
803 "SET SCHEMA", "OWNED BY", "RENAME TO", NULL};
805 COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
807 /* ALTER SEQUENCE <name> NO */
808 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
809 pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 &&
810 pg_strcasecmp(prev_wd, "NO") == 0)
812 static const char *const list_ALTERSEQUENCE2[] =
813 {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
815 COMPLETE_WITH_LIST(list_ALTERSEQUENCE2);
817 /* ALTER VIEW <name> */
818 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
819 pg_strcasecmp(prev2_wd, "VIEW") == 0)
821 static const char *const list_ALTERVIEW[] = {"RENAME TO", NULL};
823 COMPLETE_WITH_LIST(list_ALTERVIEW);
825 /* ALTER TRIGGER <name>, add ON */
826 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
827 pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
828 COMPLETE_WITH_CONST("ON");
830 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
831 pg_strcasecmp(prev3_wd, "TRIGGER") == 0)
833 completion_info_charp = prev2_wd;
834 COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
838 * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
840 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
841 pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
842 pg_strcasecmp(prev_wd, "ON") == 0)
843 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
845 /* ALTER TRIGGER <name> ON <name> */
846 else if (pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
847 pg_strcasecmp(prev2_wd, "ON") == 0)
848 COMPLETE_WITH_CONST("RENAME TO");
851 * If we detect ALTER TABLE <name>, suggest either ADD, DROP, ALTER,
852 * RENAME, CLUSTER ON or OWNER
854 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
855 pg_strcasecmp(prev2_wd, "TABLE") == 0)
857 static const char *const list_ALTER2[] =
858 {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
859 "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", NULL};
861 COMPLETE_WITH_LIST(list_ALTER2);
863 /* ALTER TABLE xxx ENABLE */
864 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
865 pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
866 pg_strcasecmp(prev_wd, "ENABLE") == 0)
868 static const char *const list_ALTERENABLE[] =
869 {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL};
871 COMPLETE_WITH_LIST(list_ALTERENABLE);
873 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
874 pg_strcasecmp(prev2_wd, "ENABLE") == 0 &&
875 (pg_strcasecmp(prev_wd, "REPLICA") == 0 ||
876 pg_strcasecmp(prev_wd, "ALWAYS") == 0))
878 static const char *const list_ALTERENABLE2[] =
879 {"RULE", "TRIGGER", NULL};
881 COMPLETE_WITH_LIST(list_ALTERENABLE2);
883 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
884 pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
885 pg_strcasecmp(prev_wd, "DISABLE") == 0)
887 static const char *const list_ALTERDISABLE[] =
888 {"RULE", "TRIGGER", NULL};
890 COMPLETE_WITH_LIST(list_ALTERDISABLE);
893 /* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
894 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
895 (pg_strcasecmp(prev_wd, "ALTER") == 0 ||
896 pg_strcasecmp(prev_wd, "RENAME") == 0))
897 COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN'");
900 * If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
901 * columns
903 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
904 (pg_strcasecmp(prev2_wd, "ALTER") == 0 ||
905 pg_strcasecmp(prev2_wd, "RENAME") == 0) &&
906 pg_strcasecmp(prev_wd, "COLUMN") == 0)
907 COMPLETE_WITH_ATTR(prev3_wd, "");
909 /* ALTER TABLE xxx RENAME yyy */
910 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
911 pg_strcasecmp(prev2_wd, "RENAME") == 0 &&
912 pg_strcasecmp(prev_wd, "TO") != 0)
913 COMPLETE_WITH_CONST("TO");
915 /* ALTER TABLE xxx RENAME COLUMN yyy */
916 else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
917 pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
918 pg_strcasecmp(prev2_wd, "COLUMN") == 0 &&
919 pg_strcasecmp(prev_wd, "TO") != 0)
920 COMPLETE_WITH_CONST("TO");
922 /* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
923 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
924 pg_strcasecmp(prev_wd, "DROP") == 0)
926 static const char *const list_TABLEDROP[] =
927 {"COLUMN", "CONSTRAINT", NULL};
929 COMPLETE_WITH_LIST(list_TABLEDROP);
931 /* If we have TABLE <sth> DROP COLUMN, provide list of columns */
932 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
933 pg_strcasecmp(prev2_wd, "DROP") == 0 &&
934 pg_strcasecmp(prev_wd, "COLUMN") == 0)
935 COMPLETE_WITH_ATTR(prev3_wd, "");
936 /* ALTER TABLE ALTER [COLUMN] <foo> */
937 else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
938 pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
939 (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
940 pg_strcasecmp(prev2_wd, "ALTER") == 0))
942 /* DROP ... does not work well yet */
943 static const char *const list_COLUMNALTER[] =
944 {"TYPE", "SET DEFAULT", "DROP DEFAULT", "SET NOT NULL",
945 "DROP NOT NULL", "SET STATISTICS", "SET STORAGE", NULL};
947 COMPLETE_WITH_LIST(list_COLUMNALTER);
949 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
950 pg_strcasecmp(prev_wd, "CLUSTER") == 0)
951 COMPLETE_WITH_CONST("ON");
952 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
953 pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
954 pg_strcasecmp(prev_wd, "ON") == 0)
956 completion_info_charp = prev3_wd;
957 COMPLETE_WITH_QUERY(Query_for_index_of_table);
959 /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
960 else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
961 pg_strcasecmp(prev_wd, "SET") == 0)
963 static const char *const list_TABLESET[] =
964 {"WITHOUT", "TABLESPACE", "SCHEMA", NULL};
966 COMPLETE_WITH_LIST(list_TABLESET);
968 /* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
969 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
970 pg_strcasecmp(prev2_wd, "SET") == 0 &&
971 pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
972 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
973 /* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
974 else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
975 pg_strcasecmp(prev2_wd, "SET") == 0 &&
976 pg_strcasecmp(prev_wd, "WITHOUT") == 0)
978 static const char *const list_TABLESET2[] =
979 {"CLUSTER", "OIDS", NULL};
981 COMPLETE_WITH_LIST(list_TABLESET2);
983 /* we have ALTER TABLESPACE, so suggest RENAME TO, OWNER TO */
984 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
985 pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
987 static const char *const list_ALTERTSPC[] =
988 {"RENAME TO", "OWNER TO", NULL};
990 COMPLETE_WITH_LIST(list_ALTERTSPC);
992 /* ALTER TEXT SEARCH */
993 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
994 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
995 pg_strcasecmp(prev_wd, "SEARCH") == 0)
997 static const char *const list_ALTERTEXTSEARCH[] =
998 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1000 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
1002 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1003 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1004 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1005 (pg_strcasecmp(prev2_wd, "TEMPLATE") == 0 ||
1006 pg_strcasecmp(prev2_wd, "PARSER") == 0))
1007 COMPLETE_WITH_CONST("RENAME TO");
1009 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1010 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1011 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1012 pg_strcasecmp(prev2_wd, "DICTIONARY") == 0)
1014 static const char *const list_ALTERTEXTSEARCH2[] =
1015 {"OWNER TO", "RENAME TO", NULL};
1017 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2);
1020 else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
1021 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1022 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1023 pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
1025 static const char *const list_ALTERTEXTSEARCH3[] =
1026 {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", NULL};
1028 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
1031 /* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */
1032 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1033 pg_strcasecmp(prev2_wd, "TYPE") == 0)
1035 static const char *const list_ALTERTYPE[] =
1036 {"OWNER TO", "SET SCHEMA", NULL};
1038 COMPLETE_WITH_LIST(list_ALTERTYPE);
1040 /* complete ALTER GROUP <foo> */
1041 else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
1042 pg_strcasecmp(prev2_wd, "GROUP") == 0)
1044 static const char *const list_ALTERGROUP[] =
1045 {"ADD USER", "DROP USER", "RENAME TO", NULL};
1047 COMPLETE_WITH_LIST(list_ALTERGROUP);
1049 /* complete ALTER GROUP <foo> ADD|DROP with USER */
1050 else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
1051 pg_strcasecmp(prev3_wd, "GROUP") == 0 &&
1052 (pg_strcasecmp(prev_wd, "ADD") == 0 ||
1053 pg_strcasecmp(prev_wd, "DROP") == 0))
1054 COMPLETE_WITH_CONST("USER");
1055 /* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
1056 else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 &&
1057 (pg_strcasecmp(prev2_wd, "ADD") == 0 ||
1058 pg_strcasecmp(prev2_wd, "DROP") == 0) &&
1059 pg_strcasecmp(prev_wd, "USER") == 0)
1060 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1062 /* BEGIN, END, ABORT */
1063 else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
1064 pg_strcasecmp(prev_wd, "END") == 0 ||
1065 pg_strcasecmp(prev_wd, "ABORT") == 0)
1067 static const char *const list_TRANS[] =
1068 {"WORK", "TRANSACTION", NULL};
1070 COMPLETE_WITH_LIST(list_TRANS);
1072 /* COMMIT */
1073 else if (pg_strcasecmp(prev_wd, "COMMIT") == 0)
1075 static const char *const list_COMMIT[] =
1076 {"WORK", "TRANSACTION", "PREPARED", NULL};
1078 COMPLETE_WITH_LIST(list_COMMIT);
1080 /* RELEASE SAVEPOINT */
1081 else if (pg_strcasecmp(prev_wd, "RELEASE") == 0)
1082 COMPLETE_WITH_CONST("SAVEPOINT");
1083 /* ROLLBACK*/
1084 else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0)
1086 static const char *const list_TRANS[] =
1087 {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL};
1089 COMPLETE_WITH_LIST(list_TRANS);
1091 /* CLUSTER */
1094 * If the previous word is CLUSTER and not without produce list of tables
1096 else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 &&
1097 pg_strcasecmp(prev2_wd, "WITHOUT") != 0)
1098 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1099 /* If we have CLUSTER <sth>, then add "USING" */
1100 else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
1101 pg_strcasecmp(prev_wd, "ON") != 0)
1103 COMPLETE_WITH_CONST("USING");
1107 * If we have CLUSTER <sth> USING, then add the index as well.
1109 else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
1110 pg_strcasecmp(prev_wd, "USING") == 0)
1112 completion_info_charp = prev2_wd;
1113 COMPLETE_WITH_QUERY(Query_for_index_of_table);
1116 /* COMMENT */
1117 else if (pg_strcasecmp(prev_wd, "COMMENT") == 0)
1118 COMPLETE_WITH_CONST("ON");
1119 else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 &&
1120 pg_strcasecmp(prev_wd, "ON") == 0)
1122 static const char *const list_COMMENT[] =
1123 {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
1124 "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
1125 "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
1126 "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
1128 COMPLETE_WITH_LIST(list_COMMENT);
1130 else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
1131 pg_strcasecmp(prev3_wd, "ON") == 0 &&
1132 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1133 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1135 static const char *const list_TRANS2[] =
1136 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1138 COMPLETE_WITH_LIST(list_TRANS2);
1140 else if ((pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
1141 pg_strcasecmp(prev3_wd, "ON") == 0) ||
1142 (pg_strcasecmp(prev5_wd, "ON") == 0 &&
1143 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1144 pg_strcasecmp(prev3_wd, "SEARCH") == 0))
1145 COMPLETE_WITH_CONST("IS");
1147 /* COPY */
1150 * If we have COPY [BINARY] (which you'd have to type yourself), offer
1151 * list of tables (Also cover the analogous backslash command)
1153 else if (pg_strcasecmp(prev_wd, "COPY") == 0 ||
1154 pg_strcasecmp(prev_wd, "\\copy") == 0 ||
1155 (pg_strcasecmp(prev2_wd, "COPY") == 0 &&
1156 pg_strcasecmp(prev_wd, "BINARY") == 0))
1157 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1158 /* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
1159 else if (pg_strcasecmp(prev2_wd, "COPY") == 0 ||
1160 pg_strcasecmp(prev2_wd, "\\copy") == 0 ||
1161 pg_strcasecmp(prev2_wd, "BINARY") == 0)
1163 static const char *const list_FROMTO[] =
1164 {"FROM", "TO", NULL};
1166 COMPLETE_WITH_LIST(list_FROMTO);
1168 /* If we have COPY|BINARY <sth> FROM|TO, complete with filename */
1169 else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 ||
1170 pg_strcasecmp(prev3_wd, "\\copy") == 0 ||
1171 pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
1172 (pg_strcasecmp(prev_wd, "FROM") == 0 ||
1173 pg_strcasecmp(prev_wd, "TO") == 0))
1174 matches = completion_matches(text, filename_completion_function);
1176 /* Handle COPY|BINARY <sth> FROM|TO filename */
1177 else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
1178 pg_strcasecmp(prev4_wd, "\\copy") == 0 ||
1179 pg_strcasecmp(prev4_wd, "BINARY") == 0) &&
1180 (pg_strcasecmp(prev2_wd, "FROM") == 0 ||
1181 pg_strcasecmp(prev2_wd, "TO") == 0))
1183 static const char *const list_COPY[] =
1184 {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL};
1186 COMPLETE_WITH_LIST(list_COPY);
1189 /* Handle COPY|BINARY <sth> FROM|TO filename CSV */
1190 else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
1191 (pg_strcasecmp(prev3_wd, "FROM") == 0 ||
1192 pg_strcasecmp(prev3_wd, "TO") == 0))
1194 static const char *const list_CSV[] =
1195 {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
1197 COMPLETE_WITH_LIST(list_CSV);
1200 /* CREATE DATABASE */
1201 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1202 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
1204 static const char *const list_DATABASE[] =
1205 {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT",
1206 NULL};
1208 COMPLETE_WITH_LIST(list_DATABASE);
1211 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1212 pg_strcasecmp(prev3_wd, "DATABASE") == 0 &&
1213 pg_strcasecmp(prev_wd, "TEMPLATE") == 0)
1214 COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
1216 /* CREATE INDEX */
1217 /* First off we complete CREATE UNIQUE with "INDEX" */
1218 else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1219 pg_strcasecmp(prev_wd, "UNIQUE") == 0)
1220 COMPLETE_WITH_CONST("INDEX");
1221 /* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" */
1222 else if (pg_strcasecmp(prev2_wd, "INDEX") == 0 &&
1223 (pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
1224 pg_strcasecmp(prev3_wd, "UNIQUE") == 0))
1225 COMPLETE_WITH_CONST("ON");
1226 /* Complete ... INDEX <name> ON with a list of tables */
1227 else if (pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
1228 pg_strcasecmp(prev_wd, "ON") == 0)
1229 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1232 * Complete INDEX <name> ON <table> with a list of table columns (which
1233 * should really be in parens)
1235 else if (pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
1236 pg_strcasecmp(prev2_wd, "ON") == 0)
1238 if (find_open_parenthesis(end))
1239 COMPLETE_WITH_ATTR(prev_wd, "");
1240 else
1241 COMPLETE_WITH_CONST("(");
1243 else if (pg_strcasecmp(prev5_wd, "INDEX") == 0 &&
1244 pg_strcasecmp(prev3_wd, "ON") == 0 &&
1245 pg_strcasecmp(prev_wd, "(") == 0)
1246 COMPLETE_WITH_ATTR(prev2_wd, "");
1247 /* same if you put in USING */
1248 else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
1249 pg_strcasecmp(prev2_wd, "USING") == 0)
1250 COMPLETE_WITH_ATTR(prev3_wd, "");
1251 /* Complete USING with an index method */
1252 else if (pg_strcasecmp(prev_wd, "USING") == 0)
1254 static const char *const index_mth[] =
1255 {"BTREE", "HASH", "GIN", "GIST", NULL};
1257 COMPLETE_WITH_LIST(index_mth);
1260 /* CREATE RULE */
1261 /* Complete "CREATE RULE <sth>" with "AS" */
1262 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1263 pg_strcasecmp(prev2_wd, "RULE") == 0)
1264 COMPLETE_WITH_CONST("AS");
1265 /* Complete "CREATE RULE <sth> AS with "ON" */
1266 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1267 pg_strcasecmp(prev3_wd, "RULE") == 0 &&
1268 pg_strcasecmp(prev_wd, "AS") == 0)
1269 COMPLETE_WITH_CONST("ON");
1270 /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */
1271 else if (pg_strcasecmp(prev4_wd, "RULE") == 0 &&
1272 pg_strcasecmp(prev2_wd, "AS") == 0 &&
1273 pg_strcasecmp(prev_wd, "ON") == 0)
1275 static const char *const rule_events[] =
1276 {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
1278 COMPLETE_WITH_LIST(rule_events);
1280 /* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
1281 else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
1282 pg_strcasecmp(prev2_wd, "ON") == 0 &&
1283 (pg_toupper((unsigned char) prev_wd[4]) == 'T' ||
1284 pg_toupper((unsigned char) prev_wd[5]) == 'T'))
1285 COMPLETE_WITH_CONST("TO");
1286 /* Complete "AS ON <sth> TO" with a table name */
1287 else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
1288 pg_strcasecmp(prev3_wd, "ON") == 0 &&
1289 pg_strcasecmp(prev_wd, "TO") == 0)
1290 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1292 /* CREATE TABLE */
1293 /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
1294 else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
1295 (pg_strcasecmp(prev_wd, "TEMP") == 0 ||
1296 pg_strcasecmp(prev_wd, "TEMPORARY") == 0))
1298 static const char *const list_TEMP[] =
1299 {"SEQUENCE", "TABLE", "VIEW", NULL};
1301 COMPLETE_WITH_LIST(list_TEMP);
1304 /* CREATE TABLESPACE */
1305 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1306 pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
1308 static const char *const list_CREATETABLESPACE[] =
1309 {"OWNER", "LOCATION", NULL};
1311 COMPLETE_WITH_LIST(list_CREATETABLESPACE);
1313 /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
1314 else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1315 pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
1316 pg_strcasecmp(prev2_wd, "OWNER") == 0)
1318 COMPLETE_WITH_CONST("LOCATION");
1321 /* CREATE TEXT SEARCH */
1322 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1323 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1324 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1326 static const char *const list_CREATETEXTSEARCH[] =
1327 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1329 COMPLETE_WITH_LIST(list_CREATETEXTSEARCH);
1331 else if (pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1332 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1333 pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0)
1334 COMPLETE_WITH_CONST("(");
1336 /* CREATE TRIGGER */
1337 /* complete CREATE TRIGGER <name> with BEFORE,AFTER */
1338 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1339 pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
1341 static const char *const list_CREATETRIGGER[] =
1342 {"BEFORE", "AFTER", NULL};
1344 COMPLETE_WITH_LIST(list_CREATETRIGGER);
1346 /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
1347 else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
1348 pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
1349 (pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
1350 pg_strcasecmp(prev2_wd, "AFTER") == 0))
1352 static const char *const list_CREATETRIGGER2[] =
1353 {"ON", "OR", NULL};
1355 COMPLETE_WITH_LIST(list_CREATETRIGGER2);
1358 /* CREATE ROLE,USER,GROUP */
1359 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1360 (pg_strcasecmp(prev2_wd, "ROLE") == 0 ||
1361 pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
1363 static const char *const list_CREATEROLE[] =
1364 {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
1365 "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOINHERIT", "NOLOGIN", "NOCREATEDB",
1366 "NOCREATEROLE", "NOCREATEUSER", "NOSUPERUSER", "ROLE", "SUPERUSER", "SYSID",
1367 "UNENCRYPTED", NULL};
1369 COMPLETE_WITH_LIST(list_CREATEROLE);
1373 * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
1374 * PASSWORD
1376 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1377 (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
1378 pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1379 (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
1381 COMPLETE_WITH_CONST("PASSWORD");
1383 /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
1384 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1385 (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
1386 pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
1387 pg_strcasecmp(prev_wd, "IN") == 0)
1389 static const char *const list_CREATEROLE3[] =
1390 {"GROUP", "ROLE", NULL};
1392 COMPLETE_WITH_LIST(list_CREATEROLE3);
1395 /* CREATE VIEW */
1396 /* Complete CREATE VIEW <name> with AS */
1397 else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
1398 pg_strcasecmp(prev2_wd, "VIEW") == 0)
1399 COMPLETE_WITH_CONST("AS");
1400 /* Complete "CREATE VIEW <sth> AS with "SELECT" */
1401 else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
1402 pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
1403 pg_strcasecmp(prev_wd, "AS") == 0)
1404 COMPLETE_WITH_CONST("SELECT");
1406 /* DECLARE */
1407 else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0)
1409 static const char *const list_DECLARE[] =
1410 {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL};
1412 COMPLETE_WITH_LIST(list_DECLARE);
1415 else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
1417 static const char *const list_DECLARECURSOR[] =
1418 {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL};
1420 COMPLETE_WITH_LIST(list_DECLARECURSOR);
1424 /* DELETE */
1427 * Complete DELETE with FROM (only if the word before that is not "ON"
1428 * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT)
1430 else if (pg_strcasecmp(prev_wd, "DELETE") == 0 &&
1431 !(pg_strcasecmp(prev2_wd, "ON") == 0 ||
1432 pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
1433 pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
1434 pg_strcasecmp(prev2_wd, "AFTER") == 0))
1435 COMPLETE_WITH_CONST("FROM");
1436 /* Complete DELETE FROM with a list of tables */
1437 else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
1438 pg_strcasecmp(prev_wd, "FROM") == 0)
1439 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1440 /* Complete DELETE FROM <table> */
1441 else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
1442 pg_strcasecmp(prev2_wd, "FROM") == 0)
1444 static const char *const list_DELETE[] =
1445 {"USING", "WHERE", "SET", NULL};
1447 COMPLETE_WITH_LIST(list_DELETE);
1449 /* XXX: implement tab completion for DELETE ... USING */
1451 /* DISCARD */
1452 else if (pg_strcasecmp(prev_wd, "DISCARD") == 0)
1454 static const char *const list_DISCARD[] =
1455 {"ALL", "PLANS", "TEMP", NULL};
1457 COMPLETE_WITH_LIST(list_DISCARD);
1460 /* DROP (when not the previous word) */
1461 /* DROP AGGREGATE */
1462 else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1463 pg_strcasecmp(prev2_wd, "AGGREGATE") == 0)
1464 COMPLETE_WITH_CONST("(");
1466 /* DROP object with CASCADE / RESTRICT */
1467 else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1468 (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
1469 pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
1470 pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
1471 pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
1472 pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
1473 pg_strcasecmp(prev2_wd, "SCHEMA") == 0 ||
1474 pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 ||
1475 pg_strcasecmp(prev2_wd, "TABLE") == 0 ||
1476 pg_strcasecmp(prev2_wd, "TYPE") == 0 ||
1477 pg_strcasecmp(prev2_wd, "VIEW") == 0)) ||
1478 (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
1479 pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
1480 prev_wd[strlen(prev_wd) - 1] == ')') ||
1481 (pg_strcasecmp(prev5_wd, "DROP") == 0 &&
1482 pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
1483 pg_strcasecmp(prev3_wd, "SEARCH") == 0 &&
1484 (pg_strcasecmp(prev2_wd, "CONFIGURATION") == 0 ||
1485 pg_strcasecmp(prev2_wd, "DICTIONARY") == 0 ||
1486 pg_strcasecmp(prev2_wd, "PARSER") == 0 ||
1487 pg_strcasecmp(prev2_wd, "TEMPLATE") == 0))
1490 if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
1492 if (find_open_parenthesis(end))
1494 static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
1495 char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd));
1497 sprintf(tmp_buf, func_args_query, prev_wd);
1498 COMPLETE_WITH_QUERY(tmp_buf);
1499 free(tmp_buf);
1501 else
1503 COMPLETE_WITH_CONST("(");
1506 else
1508 static const char *const list_DROPCR[] =
1509 {"CASCADE", "RESTRICT", NULL};
1511 COMPLETE_WITH_LIST(list_DROPCR);
1514 else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
1515 pg_strcasecmp(prev3_wd, "FUNCTION") == 0 &&
1516 pg_strcasecmp(prev_wd, "(") == 0)
1518 static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
1519 char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev2_wd));
1521 sprintf(tmp_buf, func_args_query, prev2_wd);
1522 COMPLETE_WITH_QUERY(tmp_buf);
1523 free(tmp_buf);
1525 /* DROP OWNED BY */
1526 else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
1527 pg_strcasecmp(prev_wd, "OWNED") == 0)
1528 COMPLETE_WITH_CONST("BY");
1529 else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1530 pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
1531 pg_strcasecmp(prev_wd, "BY") == 0)
1532 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1533 else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
1534 pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
1535 pg_strcasecmp(prev_wd, "SEARCH") == 0)
1538 static const char *const list_ALTERTEXTSEARCH[] =
1539 {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
1541 COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
1544 /* EXPLAIN */
1547 * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
1549 else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
1551 static const char *const list_EXPLAIN[] =
1552 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL};
1554 COMPLETE_WITH_LIST(list_EXPLAIN);
1556 else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
1557 pg_strcasecmp(prev_wd, "ANALYZE") == 0)
1559 static const char *const list_EXPLAIN[] =
1560 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL};
1562 COMPLETE_WITH_LIST(list_EXPLAIN);
1564 else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
1565 pg_strcasecmp(prev3_wd, "VACUUM") != 0 &&
1566 pg_strcasecmp(prev4_wd, "VACUUM") != 0 &&
1567 (pg_strcasecmp(prev2_wd, "ANALYZE") == 0 ||
1568 pg_strcasecmp(prev2_wd, "EXPLAIN") == 0))
1570 static const char *const list_EXPLAIN[] =
1571 {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
1573 COMPLETE_WITH_LIST(list_EXPLAIN);
1576 /* FETCH && MOVE */
1577 /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
1578 else if (pg_strcasecmp(prev_wd, "FETCH") == 0 ||
1579 pg_strcasecmp(prev_wd, "MOVE") == 0)
1581 static const char *const list_FETCH1[] =
1582 {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL};
1584 COMPLETE_WITH_LIST(list_FETCH1);
1586 /* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
1587 else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 ||
1588 pg_strcasecmp(prev2_wd, "MOVE") == 0)
1590 static const char *const list_FETCH2[] =
1591 {"ALL", "NEXT", "PRIOR", NULL};
1593 COMPLETE_WITH_LIST(list_FETCH2);
1597 * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent,
1598 * but we may as well tab-complete both: perhaps some users prefer one
1599 * variant or the other.
1601 else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 ||
1602 pg_strcasecmp(prev3_wd, "MOVE") == 0)
1604 static const char *const list_FROMIN[] =
1605 {"FROM", "IN", NULL};
1607 COMPLETE_WITH_LIST(list_FROMIN);
1610 /* GRANT && REVOKE*/
1611 /* Complete GRANT/REVOKE with a list of privileges */
1612 else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
1613 pg_strcasecmp(prev_wd, "REVOKE") == 0)
1615 static const char *const list_privileg[] =
1616 {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
1617 "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
1618 "ALL", NULL};
1620 COMPLETE_WITH_LIST(list_privileg);
1622 /* Complete GRANT/REVOKE <sth> with "ON" */
1623 else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
1624 pg_strcasecmp(prev2_wd, "REVOKE") == 0)
1625 COMPLETE_WITH_CONST("ON");
1628 * Complete GRANT/REVOKE <sth> ON with a list of tables, views, sequences,
1629 * and indexes
1631 * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via
1632 * UNION; seems to work intuitively
1634 * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
1635 * here will only work if the privilege list contains exactly one
1636 * privilege
1638 else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
1639 pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
1640 pg_strcasecmp(prev_wd, "ON") == 0)
1641 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv,
1642 " UNION SELECT 'DATABASE'"
1643 " UNION SELECT 'FUNCTION'"
1644 " UNION SELECT 'LANGUAGE'"
1645 " UNION SELECT 'SCHEMA'"
1646 " UNION SELECT 'TABLESPACE'");
1648 /* Complete "GRANT/REVOKE * ON * " with "TO" */
1649 else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
1650 pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
1651 pg_strcasecmp(prev2_wd, "ON") == 0)
1653 if (pg_strcasecmp(prev_wd, "DATABASE") == 0)
1654 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
1655 else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0)
1656 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
1657 else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0)
1658 COMPLETE_WITH_QUERY(Query_for_list_of_languages);
1659 else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0)
1660 COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
1661 else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
1662 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
1663 else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
1664 COMPLETE_WITH_CONST("TO");
1665 else
1666 COMPLETE_WITH_CONST("FROM");
1669 /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
1670 else if (pg_strcasecmp(prev3_wd, "ON") == 0 &&
1671 ((pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
1672 pg_strcasecmp(prev_wd, "TO") == 0) ||
1673 (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
1674 pg_strcasecmp(prev_wd, "FROM") == 0)))
1675 COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
1677 /* GROUP BY */
1678 else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
1679 pg_strcasecmp(prev_wd, "GROUP") == 0)
1680 COMPLETE_WITH_CONST("BY");
1682 /* INSERT */
1683 /* Complete INSERT with "INTO" */
1684 else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
1685 COMPLETE_WITH_CONST("INTO");
1686 /* Complete INSERT INTO with table names */
1687 else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 &&
1688 pg_strcasecmp(prev_wd, "INTO") == 0)
1689 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1690 /* Complete "INSERT INTO <table> (" with attribute names */
1691 else if (rl_line_buffer[start - 1] == '(' &&
1692 pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
1693 pg_strcasecmp(prev2_wd, "INTO") == 0)
1694 COMPLETE_WITH_ATTR(prev_wd, "");
1697 * Complete INSERT INTO <table> with "VALUES" or "SELECT" or
1698 * "TABLE" or "DEFAULT VALUES"
1700 else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
1701 pg_strcasecmp(prev2_wd, "INTO") == 0)
1703 static const char *const list_INSERT[] =
1704 {"DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL};
1706 COMPLETE_WITH_LIST(list_INSERT);
1708 /* Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or "TABLE" */
1709 else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
1710 pg_strcasecmp(prev3_wd, "INTO") == 0 &&
1711 prev_wd[strlen(prev_wd) - 1] == ')')
1713 static const char *const list_INSERT[] =
1714 {"SELECT", "TABLE", "VALUES", NULL};
1716 COMPLETE_WITH_LIST(list_INSERT);
1719 /* Insert an open parenthesis after "VALUES" */
1720 else if (pg_strcasecmp(prev_wd, "VALUES") == 0 &&
1721 pg_strcasecmp(prev2_wd, "DEFAULT") != 0)
1722 COMPLETE_WITH_CONST("(");
1724 /* LOCK */
1725 /* Complete LOCK [TABLE] with a list of tables */
1726 else if (pg_strcasecmp(prev_wd, "LOCK") == 0)
1727 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
1728 " UNION SELECT 'TABLE'");
1729 else if (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
1730 pg_strcasecmp(prev2_wd, "LOCK") == 0)
1731 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
1733 /* For the following, handle the case of a single table only for now */
1735 /* Complete LOCK [TABLE] <table> with "IN" */
1736 else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 &&
1737 pg_strcasecmp(prev_wd, "TABLE")) ||
1738 (pg_strcasecmp(prev2_wd, "TABLE") == 0 &&
1739 pg_strcasecmp(prev3_wd, "LOCK") == 0))
1740 COMPLETE_WITH_CONST("IN");
1742 /* Complete LOCK [TABLE] <table> IN with a lock mode */
1743 else if (pg_strcasecmp(prev_wd, "IN") == 0 &&
1744 (pg_strcasecmp(prev3_wd, "LOCK") == 0 ||
1745 (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
1746 pg_strcasecmp(prev4_wd, "LOCK") == 0)))
1748 static const char *const lock_modes[] =
1749 {"ACCESS SHARE MODE",
1750 "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
1751 "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
1752 "SHARE ROW EXCLUSIVE MODE",
1753 "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL};
1755 COMPLETE_WITH_LIST(lock_modes);
1758 /* NOTIFY */
1759 else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
1760 COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'");
1762 /* OWNER TO - complete with available roles */
1763 else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
1764 pg_strcasecmp(prev_wd, "TO") == 0)
1765 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1767 /* ORDER BY */
1768 else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
1769 pg_strcasecmp(prev_wd, "ORDER") == 0)
1770 COMPLETE_WITH_CONST("BY");
1771 else if (pg_strcasecmp(prev4_wd, "FROM") == 0 &&
1772 pg_strcasecmp(prev2_wd, "ORDER") == 0 &&
1773 pg_strcasecmp(prev_wd, "BY") == 0)
1774 COMPLETE_WITH_ATTR(prev3_wd, "");
1776 /* PREPARE xx AS */
1777 else if (pg_strcasecmp(prev_wd, "AS") == 0 &&
1778 pg_strcasecmp(prev3_wd, "PREPARE") == 0)
1780 static const char *const list_PREPARE[] =
1781 {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
1783 COMPLETE_WITH_LIST(list_PREPARE);
1786 /* REASSIGN OWNED BY xxx TO yyy */
1787 else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
1788 COMPLETE_WITH_CONST("OWNED");
1789 else if (pg_strcasecmp(prev_wd, "OWNED") == 0 &&
1790 pg_strcasecmp(prev2_wd, "REASSIGN") == 0)
1791 COMPLETE_WITH_CONST("BY");
1792 else if (pg_strcasecmp(prev_wd, "BY") == 0 &&
1793 pg_strcasecmp(prev2_wd, "OWNED") == 0 &&
1794 pg_strcasecmp(prev3_wd, "REASSIGN") == 0)
1795 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1796 else if (pg_strcasecmp(prev2_wd, "BY") == 0 &&
1797 pg_strcasecmp(prev3_wd, "OWNED") == 0 &&
1798 pg_strcasecmp(prev4_wd, "REASSIGN") == 0)
1799 COMPLETE_WITH_CONST("TO");
1800 else if (pg_strcasecmp(prev_wd, "TO") == 0 &&
1801 pg_strcasecmp(prev3_wd, "BY") == 0 &&
1802 pg_strcasecmp(prev4_wd, "OWNED") == 0 &&
1803 pg_strcasecmp(prev5_wd, "REASSIGN") == 0)
1804 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1806 /* REINDEX */
1807 else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
1809 static const char *const list_REINDEX[] =
1810 {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL};
1812 COMPLETE_WITH_LIST(list_REINDEX);
1814 else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0)
1816 if (pg_strcasecmp(prev_wd, "TABLE") == 0)
1817 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1818 else if (pg_strcasecmp(prev_wd, "INDEX") == 0)
1819 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
1820 else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 ||
1821 pg_strcasecmp(prev_wd, "DATABASE") == 0)
1822 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
1825 /* SELECT */
1826 /* naah . . . */
1828 /* SET, RESET, SHOW */
1829 /* Complete with a variable name */
1830 else if ((pg_strcasecmp(prev_wd, "SET") == 0 &&
1831 pg_strcasecmp(prev3_wd, "UPDATE") != 0) ||
1832 pg_strcasecmp(prev_wd, "RESET") == 0)
1833 COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
1834 else if (pg_strcasecmp(prev_wd, "SHOW") == 0)
1835 COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
1836 /* Complete "SET TRANSACTION" */
1837 else if ((pg_strcasecmp(prev2_wd, "SET") == 0 &&
1838 pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
1839 || (pg_strcasecmp(prev2_wd, "START") == 0
1840 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
1841 || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
1842 && pg_strcasecmp(prev_wd, "WORK") == 0)
1843 || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
1844 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
1845 || (pg_strcasecmp(prev4_wd, "SESSION") == 0
1846 && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0
1847 && pg_strcasecmp(prev2_wd, "AS") == 0
1848 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0))
1850 static const char *const my_list[] =
1851 {"ISOLATION LEVEL", "READ", NULL};
1853 COMPLETE_WITH_LIST(my_list);
1855 else if ((pg_strcasecmp(prev3_wd, "SET") == 0
1856 || pg_strcasecmp(prev3_wd, "BEGIN") == 0
1857 || pg_strcasecmp(prev3_wd, "START") == 0
1858 || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0
1859 && pg_strcasecmp(prev3_wd, "AS") == 0))
1860 && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0
1861 || pg_strcasecmp(prev2_wd, "WORK") == 0)
1862 && pg_strcasecmp(prev_wd, "ISOLATION") == 0)
1863 COMPLETE_WITH_CONST("LEVEL");
1864 else if ((pg_strcasecmp(prev4_wd, "SET") == 0
1865 || pg_strcasecmp(prev4_wd, "BEGIN") == 0
1866 || pg_strcasecmp(prev4_wd, "START") == 0
1867 || pg_strcasecmp(prev4_wd, "AS") == 0)
1868 && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0
1869 || pg_strcasecmp(prev3_wd, "WORK") == 0)
1870 && pg_strcasecmp(prev2_wd, "ISOLATION") == 0
1871 && pg_strcasecmp(prev_wd, "LEVEL") == 0)
1873 static const char *const my_list[] =
1874 {"READ", "REPEATABLE", "SERIALIZABLE", NULL};
1876 COMPLETE_WITH_LIST(my_list);
1878 else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
1879 pg_strcasecmp(prev4_wd, "WORK") == 0) &&
1880 pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
1881 pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
1882 pg_strcasecmp(prev_wd, "READ") == 0)
1884 static const char *const my_list[] =
1885 {"UNCOMMITTED", "COMMITTED", NULL};
1887 COMPLETE_WITH_LIST(my_list);
1889 else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
1890 pg_strcasecmp(prev4_wd, "WORK") == 0) &&
1891 pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
1892 pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
1893 pg_strcasecmp(prev_wd, "REPEATABLE") == 0)
1894 COMPLETE_WITH_CONST("READ");
1895 else if ((pg_strcasecmp(prev3_wd, "SET") == 0 ||
1896 pg_strcasecmp(prev3_wd, "BEGIN") == 0 ||
1897 pg_strcasecmp(prev3_wd, "START") == 0 ||
1898 pg_strcasecmp(prev3_wd, "AS") == 0) &&
1899 (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 ||
1900 pg_strcasecmp(prev2_wd, "WORK") == 0) &&
1901 pg_strcasecmp(prev_wd, "READ") == 0)
1903 static const char *const my_list[] =
1904 {"ONLY", "WRITE", NULL};
1906 COMPLETE_WITH_LIST(my_list);
1908 /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
1909 else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
1910 pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
1912 static const char *const constraint_list[] =
1913 {"DEFERRED", "IMMEDIATE", NULL};
1915 COMPLETE_WITH_LIST(constraint_list);
1917 /* Complete SET ROLE */
1918 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
1919 pg_strcasecmp(prev_wd, "ROLE") == 0)
1920 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
1921 /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
1922 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
1923 pg_strcasecmp(prev_wd, "SESSION") == 0)
1925 static const char *const my_list[] =
1926 {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL};
1928 COMPLETE_WITH_LIST(my_list);
1930 /* Complete SET SESSION AUTHORIZATION with username */
1931 else if (pg_strcasecmp(prev3_wd, "SET") == 0
1932 && pg_strcasecmp(prev2_wd, "SESSION") == 0
1933 && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
1934 COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
1935 /* Complete RESET SESSION with AUTHORIZATION */
1936 else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
1937 pg_strcasecmp(prev_wd, "SESSION") == 0)
1938 COMPLETE_WITH_CONST("AUTHORIZATION");
1939 /* Complete SET <var> with "TO" */
1940 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
1941 pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
1942 pg_strcasecmp(prev_wd, "TABLESPACE") != 0 &&
1943 pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
1944 COMPLETE_WITH_CONST("TO");
1945 /* Suggest possible variable values */
1946 else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
1947 (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
1949 if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
1951 static const char *const my_list[] =
1952 {"ISO", "SQL", "Postgres", "German",
1953 "YMD", "DMY", "MDY",
1954 "US", "European", "NonEuropean",
1955 "DEFAULT", NULL};
1957 COMPLETE_WITH_LIST(my_list);
1959 else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0)
1961 static const char *const my_list[] =
1962 {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL};
1964 COMPLETE_WITH_LIST(my_list);
1966 else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
1968 static const char *const my_list[] =
1969 {"ON", "OFF", "DEFAULT", NULL};
1971 COMPLETE_WITH_LIST(my_list);
1973 else
1975 static const char *const my_list[] =
1976 {"DEFAULT", NULL};
1978 COMPLETE_WITH_LIST(my_list);
1982 /* START TRANSACTION */
1983 else if (pg_strcasecmp(prev_wd, "START") == 0)
1984 COMPLETE_WITH_CONST("TRANSACTION");
1986 /* TRUNCATE */
1987 else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0)
1988 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1990 /* UNLISTEN */
1991 else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0)
1992 COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s' UNION SELECT '*'");
1994 /* UPDATE */
1995 /* If prev. word is UPDATE suggest a list of tables */
1996 else if (pg_strcasecmp(prev_wd, "UPDATE") == 0)
1997 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
1998 /* Complete UPDATE <table> with "SET" */
1999 else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0)
2000 COMPLETE_WITH_CONST("SET");
2003 * If the previous word is SET (and it wasn't caught above as the _first_
2004 * word) the word before it was (hopefully) a table name and we'll now
2005 * make a list of attributes.
2007 else if (pg_strcasecmp(prev_wd, "SET") == 0)
2008 COMPLETE_WITH_ATTR(prev2_wd, "");
2010 /* UPDATE xx SET yy = */
2011 else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
2012 pg_strcasecmp(prev4_wd, "UPDATE") == 0)
2013 COMPLETE_WITH_CONST("=");
2016 * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
2017 * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
2019 else if (pg_strcasecmp(prev_wd, "VACUUM") == 0)
2020 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2021 " UNION SELECT 'FULL'"
2022 " UNION SELECT 'FREEZE'"
2023 " UNION SELECT 'ANALYZE'"
2024 " UNION SELECT 'VERBOSE'");
2025 else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2026 (pg_strcasecmp(prev_wd, "FULL") == 0 ||
2027 pg_strcasecmp(prev_wd, "FREEZE") == 0))
2028 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2029 " UNION SELECT 'ANALYZE'"
2030 " UNION SELECT 'VERBOSE'");
2031 else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
2032 pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
2033 (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
2034 pg_strcasecmp(prev2_wd, "FREEZE") == 0))
2035 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2036 " UNION SELECT 'VERBOSE'");
2037 else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
2038 pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
2039 (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
2040 pg_strcasecmp(prev2_wd, "FREEZE") == 0))
2041 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2042 " UNION SELECT 'ANALYZE'");
2043 else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2044 pg_strcasecmp(prev_wd, "VERBOSE") == 0)
2045 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2046 " UNION SELECT 'ANALYZE'");
2047 else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
2048 pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2049 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
2050 " UNION SELECT 'VERBOSE'");
2051 else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
2052 pg_strcasecmp(prev2_wd, "VERBOSE") == 0) ||
2053 (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
2054 pg_strcasecmp(prev2_wd, "ANALYZE") == 0))
2055 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2057 /* WITH [RECURSIVE] */
2058 else if (pg_strcasecmp(prev_wd, "WITH") == 0)
2059 COMPLETE_WITH_CONST("RECURSIVE");
2061 /* ANALYZE */
2062 /* If the previous word is ANALYZE, produce list of tables */
2063 else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0)
2064 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2066 /* WHERE */
2067 /* Simple case of the word before the where being the table name */
2068 else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
2069 COMPLETE_WITH_ATTR(prev2_wd, "");
2071 /* ... FROM ... */
2072 /* TODO: also include SRF ? */
2073 else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
2074 pg_strcasecmp(prev3_wd, "COPY") != 0 &&
2075 pg_strcasecmp(prev3_wd, "\\copy") != 0)
2076 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
2079 /* Backslash commands */
2080 /* TODO: \dc \dd \dl */
2081 else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
2082 COMPLETE_WITH_QUERY(Query_for_list_of_databases);
2083 else if (strcmp(prev_wd, "\\d") == 0 || strcmp(prev_wd, "\\d+") == 0)
2084 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
2085 else if (strcmp(prev_wd, "\\da") == 0)
2086 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
2087 else if (strcmp(prev_wd, "\\db") == 0)
2088 COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
2089 else if (strcmp(prev_wd, "\\dD") == 0)
2090 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
2091 else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0)
2092 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
2093 else if (strcmp(prev_wd, "\\dF") == 0 || strcmp(prev_wd, "\\dF+") == 0)
2094 COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
2095 else if (strcmp(prev_wd, "\\dFd") == 0 || strcmp(prev_wd, "\\dFd+") == 0)
2096 COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
2097 else if (strcmp(prev_wd, "\\dFp") == 0 || strcmp(prev_wd, "\\dFp+") == 0)
2098 COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
2099 else if (strcmp(prev_wd, "\\dFt") == 0 || strcmp(prev_wd, "\\dFt+") == 0)
2100 COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
2101 else if (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0)
2102 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
2103 else if (strcmp(prev_wd, "\\dn") == 0)
2104 COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
2105 else if (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 0)
2106 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
2107 else if (strcmp(prev_wd, "\\ds") == 0 || strcmp(prev_wd, "\\ds+") == 0)
2108 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
2109 else if (strcmp(prev_wd, "\\dS") == 0 || strcmp(prev_wd, "\\dS+") == 0)
2110 COMPLETE_WITH_QUERY(Query_for_list_of_system_relations);
2111 else if (strcmp(prev_wd, "\\dt") == 0 || strcmp(prev_wd, "\\dt+") == 0)
2112 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
2113 else if (strcmp(prev_wd, "\\dT") == 0 || strcmp(prev_wd, "\\dT+") == 0)
2114 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
2115 else if (strcmp(prev_wd, "\\du") == 0)
2116 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2117 else if (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
2118 COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
2119 else if (strcmp(prev_wd, "\\encoding") == 0)
2120 COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
2121 else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
2122 COMPLETE_WITH_LIST(sql_commands);
2123 else if (strcmp(prev_wd, "\\password") == 0)
2124 COMPLETE_WITH_QUERY(Query_for_list_of_roles);
2125 else if (strcmp(prev_wd, "\\pset") == 0)
2127 static const char *const my_list[] =
2128 {"format", "border", "expanded",
2129 "null", "fieldsep", "tuples_only", "title", "tableattr", "pager",
2130 "recordsep", NULL};
2132 COMPLETE_WITH_LIST(my_list);
2134 else if (strcmp(prev_wd, "\\cd") == 0 ||
2135 strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
2136 strcmp(prev_wd, "\\g") == 0 ||
2137 strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
2138 strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
2139 strcmp(prev_wd, "\\s") == 0 ||
2140 strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
2142 matches = completion_matches(text, filename_completion_function);
2146 * Finally, we look through the list of "things", such as TABLE, INDEX and
2147 * check if that was the previous word. If so, execute the query to get a
2148 * list of them.
2150 else
2152 int i;
2154 for (i = 0; words_after_create[i].name; i++)
2156 if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)
2158 if (words_after_create[i].query)
2159 COMPLETE_WITH_QUERY(words_after_create[i].query);
2160 else if (words_after_create[i].squery)
2161 COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
2162 NULL);
2163 break;
2169 * If we still don't have anything to match we have to fabricate some sort
2170 * of default list. If we were to just return NULL, readline automatically
2171 * attempts filename completion, and that's usually no good.
2173 if (matches == NULL)
2175 COMPLETE_WITH_CONST("");
2176 #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
2177 rl_completion_append_character = '\0';
2178 #endif
2181 /* free storage */
2182 free(prev_wd);
2183 free(prev2_wd);
2184 free(prev3_wd);
2185 free(prev4_wd);
2186 free(prev5_wd);
2188 /* Return our Grand List O' Matches */
2189 return matches;
2194 /* GENERATOR FUNCTIONS
2196 These functions do all the actual work of completing the input. They get
2197 passed the text so far and the count how many times they have been called so
2198 far with the same text.
2199 If you read the above carefully, you'll see that these don't get called
2200 directly but through the readline interface.
2201 The return value is expected to be the full completion of the text, going
2202 through a list each time, or NULL if there are no more matches. The string
2203 will be free()'d by readline, so you must run it through strdup() or
2204 something of that sort.
2207 /* This one gives you one from a list of things you can put after CREATE
2208 as defined above.
2210 static char *
2211 create_command_generator(const char *text, int state)
2213 static int list_index,
2214 string_length;
2215 const char *name;
2217 /* If this is the first time for this completion, init some values */
2218 if (state == 0)
2220 list_index = 0;
2221 string_length = strlen(text);
2224 /* find something that matches */
2225 while ((name = words_after_create[list_index++].name))
2227 if ((pg_strncasecmp(name, text, string_length) == 0) && !words_after_create[list_index - 1].noshow)
2228 return pg_strdup(name);
2230 /* if nothing matches, return NULL */
2231 return NULL;
2235 * This function gives you a list of things you can put after a DROP command.
2236 * Very similar to create_command_generator, but has an additional entry for
2237 * OWNED BY. (We do it this way in order not to duplicate the
2238 * words_after_create list.)
2240 static char *
2241 drop_command_generator(const char *text, int state)
2243 static int list_index,
2244 string_length;
2245 const char *name;
2247 if (state == 0)
2249 /* If this is the first time for this completion, init some values */
2250 list_index = 0;
2251 string_length = strlen(text);
2254 * DROP can be followed by "OWNED BY", which is not found in the list
2255 * for CREATE matches, so make it the first state. (We do not make it
2256 * the last state because it would be more difficult to detect when we
2257 * have to return NULL instead.)
2259 * Make sure we advance to the next state.
2261 list_index++;
2262 if (pg_strncasecmp("OWNED", text, string_length) == 0)
2263 return pg_strdup("OWNED");
2267 * In subsequent attempts, try to complete with the same items we use for
2268 * CREATE
2270 while ((name = words_after_create[list_index++ - 1].name))
2272 if ((pg_strncasecmp(name, text, string_length) == 0) && (!words_after_create[list_index - 2].noshow))
2273 return pg_strdup(name);
2276 /* if nothing matches, return NULL */
2277 return NULL;
2280 /* The following two functions are wrappers for _complete_from_query */
2282 static char *
2283 complete_from_query(const char *text, int state)
2285 return _complete_from_query(0, text, state);
2288 static char *
2289 complete_from_schema_query(const char *text, int state)
2291 return _complete_from_query(1, text, state);
2295 /* This creates a list of matching things, according to a query pointed to
2296 by completion_charp.
2297 The query can be one of two kinds:
2298 - A simple query which must contain a %d and a %s, which will be replaced
2299 by the string length of the text and the text itself. The query may also
2300 have up to four more %s in it; the first two such will be replaced by the
2301 value of completion_info_charp, the next two by the value of
2302 completion_info_charp2.
2304 - A schema query used for completion of both schema and relation names;
2305 these are more complex and must contain in the following order:
2306 %d %s %d %s %d %s %s %d %s
2307 where %d is the string length of the text and %s the text itself.
2309 It is assumed that strings should be escaped to become SQL literals
2310 (that is, what is in the query is actually ... '%s' ...)
2312 See top of file for examples of both kinds of query.
2315 static char *
2316 _complete_from_query(int is_schema_query, const char *text, int state)
2318 static int list_index,
2319 string_length;
2320 static PGresult *result = NULL;
2323 * If this is the first time for this completion, we fetch a list of our
2324 * "things" from the backend.
2326 if (state == 0)
2328 PQExpBufferData query_buffer;
2329 char *e_text;
2330 char *e_info_charp;
2331 char *e_info_charp2;
2333 list_index = 0;
2334 string_length = strlen(text);
2336 /* Free any prior result */
2337 PQclear(result);
2338 result = NULL;
2340 /* Set up suitably-escaped copies of textual inputs */
2341 e_text = pg_malloc(string_length * 2 + 1);
2342 PQescapeString(e_text, text, string_length);
2344 if (completion_info_charp)
2346 size_t charp_len;
2348 charp_len = strlen(completion_info_charp);
2349 e_info_charp = pg_malloc(charp_len * 2 + 1);
2350 PQescapeString(e_info_charp, completion_info_charp,
2351 charp_len);
2353 else
2354 e_info_charp = NULL;
2356 if (completion_info_charp2)
2358 size_t charp_len;
2360 charp_len = strlen(completion_info_charp2);
2361 e_info_charp2 = pg_malloc(charp_len * 2 + 1);
2362 PQescapeString(e_info_charp2, completion_info_charp2,
2363 charp_len);
2365 else
2366 e_info_charp2 = NULL;
2368 initPQExpBuffer(&query_buffer);
2370 if (is_schema_query)
2372 /* completion_squery gives us the pieces to assemble */
2373 const char *qualresult = completion_squery->qualresult;
2375 if (qualresult == NULL)
2376 qualresult = completion_squery->result;
2378 /* Get unqualified names matching the input-so-far */
2379 appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
2380 completion_squery->result,
2381 completion_squery->catname);
2382 if (completion_squery->selcondition)
2383 appendPQExpBuffer(&query_buffer, "%s AND ",
2384 completion_squery->selcondition);
2385 appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
2386 completion_squery->result,
2387 string_length, e_text);
2388 appendPQExpBuffer(&query_buffer, " AND %s",
2389 completion_squery->viscondition);
2392 * When fetching relation names, suppress system catalogs unless
2393 * the input-so-far begins with "pg_". This is a compromise
2394 * between not offering system catalogs for completion at all, and
2395 * having them swamp the result when the input is just "p".
2397 if (strcmp(completion_squery->catname,
2398 "pg_catalog.pg_class c") == 0 &&
2399 strncmp(text, "pg_", 3) !=0)
2401 appendPQExpBuffer(&query_buffer,
2402 " AND c.relnamespace <> (SELECT oid FROM"
2403 " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
2407 * Add in matching schema names, but only if there is more than
2408 * one potential match among schema names.
2410 appendPQExpBuffer(&query_buffer, "\nUNION\n"
2411 "SELECT pg_catalog.quote_ident(n.nspname) || '.' "
2412 "FROM pg_catalog.pg_namespace n "
2413 "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'",
2414 string_length, e_text);
2415 appendPQExpBuffer(&query_buffer,
2416 " AND (SELECT pg_catalog.count(*)"
2417 " FROM pg_catalog.pg_namespace"
2418 " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
2419 " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1",
2420 string_length, e_text);
2423 * Add in matching qualified names, but only if there is exactly
2424 * one schema matching the input-so-far.
2426 appendPQExpBuffer(&query_buffer, "\nUNION\n"
2427 "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
2428 "FROM %s, pg_catalog.pg_namespace n "
2429 "WHERE %s = n.oid AND ",
2430 qualresult,
2431 completion_squery->catname,
2432 completion_squery->namespace);
2433 if (completion_squery->selcondition)
2434 appendPQExpBuffer(&query_buffer, "%s AND ",
2435 completion_squery->selcondition);
2436 appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
2437 qualresult,
2438 string_length, e_text);
2441 * This condition exploits the single-matching-schema rule to
2442 * speed up the query
2444 appendPQExpBuffer(&query_buffer,
2445 " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="
2446 " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)",
2447 string_length, e_text);
2448 appendPQExpBuffer(&query_buffer,
2449 " AND (SELECT pg_catalog.count(*)"
2450 " FROM pg_catalog.pg_namespace"
2451 " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
2452 " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1",
2453 string_length, e_text);
2455 /* If an addon query was provided, use it */
2456 if (completion_charp)
2457 appendPQExpBuffer(&query_buffer, "\n%s", completion_charp);
2459 else
2461 /* completion_charp is an sprintf-style format string */
2462 appendPQExpBuffer(&query_buffer, completion_charp,
2463 string_length, e_text,
2464 e_info_charp, e_info_charp,
2465 e_info_charp2, e_info_charp2);
2468 /* Limit the number of records in the result */
2469 appendPQExpBuffer(&query_buffer, "\nLIMIT %d",
2470 completion_max_records);
2472 result = exec_query(query_buffer.data);
2474 termPQExpBuffer(&query_buffer);
2475 free(e_text);
2476 if (e_info_charp)
2477 free(e_info_charp);
2478 if (e_info_charp2)
2479 free(e_info_charp2);
2482 /* Find something that matches */
2483 if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
2485 const char *item;
2487 while (list_index < PQntuples(result) &&
2488 (item = PQgetvalue(result, list_index++, 0)))
2489 if (pg_strncasecmp(text, item, string_length) == 0)
2490 return pg_strdup(item);
2493 /* If nothing matches, free the db structure and return null */
2494 PQclear(result);
2495 result = NULL;
2496 return NULL;
2500 /* This function returns in order one of a fixed, NULL pointer terminated list
2501 of strings (if matching). This can be used if there are only a fixed number
2502 SQL words that can appear at certain spot.
2504 static char *
2505 complete_from_list(const char *text, int state)
2507 static int string_length,
2508 list_index,
2509 matches;
2510 static bool casesensitive;
2511 const char *item;
2513 /* need to have a list */
2514 psql_assert(completion_charpp);
2516 /* Initialization */
2517 if (state == 0)
2519 list_index = 0;
2520 string_length = strlen(text);
2521 casesensitive = true;
2522 matches = 0;
2525 while ((item = completion_charpp[list_index++]))
2527 /* First pass is case sensitive */
2528 if (casesensitive && strncmp(text, item, string_length) == 0)
2530 matches++;
2531 return pg_strdup(item);
2534 /* Second pass is case insensitive, don't bother counting matches */
2535 if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
2536 return pg_strdup(item);
2540 * No matches found. If we're not case insensitive already, lets switch to
2541 * being case insensitive and try again
2543 if (casesensitive && matches == 0)
2545 casesensitive = false;
2546 list_index = 0;
2547 state++;
2548 return complete_from_list(text, state);
2551 /* If no more matches, return null. */
2552 return NULL;
2556 /* This function returns one fixed string the first time even if it doesn't
2557 match what's there, and nothing the second time. This should be used if there
2558 is only one possibility that can appear at a certain spot, so misspellings
2559 will be overwritten.
2560 The string to be passed must be in completion_charp.
2562 static char *
2563 complete_from_const(const char *text, int state)
2565 (void) text; /* We don't care about what was entered
2566 * already. */
2568 psql_assert(completion_charp);
2569 if (state == 0)
2570 return pg_strdup(completion_charp);
2571 else
2572 return NULL;
2577 /* HELPER FUNCTIONS */
2581 * Execute a query and report any errors. This should be the preferred way of
2582 * talking to the database in this file.
2584 static PGresult *
2585 exec_query(const char *query)
2587 PGresult *result;
2589 if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK)
2590 return NULL;
2592 result = PQexec(pset.db, query);
2594 if (PQresultStatus(result) != PGRES_TUPLES_OK)
2596 #if 0
2597 psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
2598 PQerrorMessage(pset.db), query);
2599 #endif
2600 PQclear(result);
2601 result = NULL;
2604 return result;
2610 * Return the word (space delimited) before point. Set skip > 0 to
2611 * skip that many words; e.g. skip=1 finds the word before the
2612 * previous one. Return value is NULL or a malloc'ed string.
2614 static char *
2615 previous_word(int point, int skip)
2617 int i,
2618 start = 0,
2619 end = -1,
2620 inquotes = 0;
2621 char *s;
2623 while (skip-- >= 0)
2625 /* first we look for a space before the current word */
2626 for (i = point; i >= 0; i--)
2627 if (rl_line_buffer[i] == ' ')
2628 break;
2630 /* now find the first non-space which then constitutes the end */
2631 for (; i >= 0; i--)
2632 if (rl_line_buffer[i] != ' ')
2634 end = i;
2635 break;
2639 * If no end found we return null, because there is no word before the
2640 * point
2642 if (end == -1)
2643 return NULL;
2646 * Otherwise we now look for the start. The start is either the last
2647 * character before any space going backwards from the end, or it's
2648 * simply character 0
2650 for (start = end; start > 0; start--)
2652 if (rl_line_buffer[start] == '"')
2653 inquotes = !inquotes;
2654 if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0)
2655 break;
2658 point = start;
2661 /* make a copy */
2662 s = pg_malloc(end - start + 2);
2663 strlcpy(s, &rl_line_buffer[start], end - start + 2);
2665 return s;
2668 /* Find the parenthesis after the last word */
2671 static int
2672 find_open_parenthesis(int end)
2674 int i = end - 1;
2676 while ((rl_line_buffer[i] != ' ') && (i >= 0))
2678 if (rl_line_buffer[i] == '(')
2679 return 1;
2680 i--;
2682 while ((rl_line_buffer[i] == ' ') && (i >= 0))
2684 i--;
2686 if (rl_line_buffer[i] == '(')
2688 return 1;
2690 return 0;
2694 #if 0
2697 * Surround a string with single quotes. This works for both SQL and
2698 * psql internal. Currently disabled because it is reported not to
2699 * cooperate with certain versions of readline.
2701 static char *
2702 quote_file_name(char *text, int match_type, char *quote_pointer)
2704 char *s;
2705 size_t length;
2707 (void) quote_pointer; /* not used */
2709 length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2);
2710 s = pg_malloc(length);
2711 s[0] = '\'';
2712 strcpy(s + 1, text);
2713 if (match_type == SINGLE_MATCH)
2714 s[length - 2] = '\'';
2715 s[length - 1] = '\0';
2716 return s;
2721 static char *
2722 dequote_file_name(char *text, char quote_char)
2724 char *s;
2725 size_t length;
2727 if (!quote_char)
2728 return pg_strdup(text);
2730 length = strlen(text);
2731 s = pg_malloc(length - 2 + 1);
2732 strlcpy(s, text +1, length - 2 + 1);
2734 return s;
2736 #endif /* 0 */
2738 #endif /* USE_READLINE */