~ubuntu-branches/debian/squeeze/pgadmin3/squeeze

« back to all changes in this revision

Viewing changes to pgadmin/utils/tab-complete.inc

  • Committer: Bazaar Package Importer
  • Author(s): Lionel Porcheron
  • Date: 2008-02-07 00:56:22 UTC
  • mto: (2.1.6 hardy) (6.1.2 sid)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20080207005622-c2ail8p4d0sk3dnw
Tags: upstream-1.8.2
ImportĀ upstreamĀ versionĀ 1.8.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
typedef struct SchemaQuery
 
2
{
 
3
        /*
 
4
         * Name of catalog or catalogs to be queried, with alias, eg.
 
5
         * "pg_catalog.pg_class c".  Note that "pg_namespace n" will be added.
 
6
         */
 
7
        const char *catname;
 
8
 
 
9
        /*
 
10
         * Selection condition --- only rows meeting this condition are candidates
 
11
         * to display.  If catname mentions multiple tables, include the necessary
 
12
         * join condition here.  For example, "c.relkind = 'r'". Write NULL (not
 
13
         * an empty string) if not needed.
 
14
         */
 
15
        const char *selcondition;
 
16
 
 
17
        /*
 
18
         * Visibility condition --- which rows are visible without schema
 
19
         * qualification?  For example, "pg_catalog.pg_table_is_visible(c.oid)".
 
20
         */
 
21
        const char *viscondition;
 
22
 
 
23
        /*
 
24
         * Namespace --- name of field to join to pg_namespace.oid. For example,
 
25
         * "c.relnamespace".
 
26
         */
 
27
        const char *namespace;
 
28
 
 
29
        /*
 
30
         * Result --- the appropriately-quoted name to return, in the case of an
 
31
         * unqualified name.  For example, "pg_catalog.quote_ident(c.relname)".
 
32
         */
 
33
        const char *result;
 
34
 
 
35
        /*
 
36
         * In some cases a different result must be used for qualified names.
 
37
         * Enter that here, or write NULL if result can be used.
 
38
         */
 
39
        const char *qualresult;
 
40
} SchemaQuery;
 
41
 
 
42
 
 
43
/*
 
44
 * Communication variables set by COMPLETE_WITH_FOO macros and then used by
 
45
 * the completion callback functions.  Ugly but there is no better way.
 
46
 */
 
47
static const char *completion_charp;    /* to pass a string */
 
48
static const char *const * completion_charpp;   /* to pass a list of strings */
 
49
static const char *completion_info_charp;               /* to pass a second string */
 
50
 
 
51
/*
 
52
 * Assembly instructions for schema queries
 
53
 */
 
54
 
 
55
static const SchemaQuery Query_for_list_of_aggregates = {
 
56
        /* catname */
 
57
        "pg_catalog.pg_proc p",
 
58
        /* selcondition */
 
59
        "p.proisagg",
 
60
        /* viscondition */
 
61
        "pg_catalog.pg_function_is_visible(p.oid)",
 
62
        /* namespace */
 
63
        "p.pronamespace",
 
64
        /* result */
 
65
        "pg_catalog.quote_ident(p.proname)",
 
66
        /* qualresult */
 
67
        NULL
 
68
};
 
69
 
 
70
static const SchemaQuery Query_for_list_of_datatypes = {
 
71
        /* catname */
 
72
        "pg_catalog.pg_type t",
 
73
        /* selcondition --- ignore table rowtypes and array types */
 
74
        "(t.typrelid = 0 "
 
75
        " OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) "
 
76
        "AND t.typname !~ '^_'",
 
77
        /* viscondition */
 
78
        "pg_catalog.pg_type_is_visible(t.oid)",
 
79
        /* namespace */
 
80
        "t.typnamespace",
 
81
        /* result */
 
82
        "pg_catalog.format_type(t.oid, NULL)",
 
83
        /* qualresult */
 
84
        "pg_catalog.quote_ident(t.typname)"
 
85
};
 
86
 
 
87
static const SchemaQuery Query_for_list_of_domains = {
 
88
        /* catname */
 
89
        "pg_catalog.pg_type t",
 
90
        /* selcondition */
 
91
        "t.typtype = 'd'",
 
92
        /* viscondition */
 
93
        "pg_catalog.pg_type_is_visible(t.oid)",
 
94
        /* namespace */
 
95
        "t.typnamespace",
 
96
        /* result */
 
97
        "pg_catalog.quote_ident(t.typname)",
 
98
        /* qualresult */
 
99
        NULL
 
100
};
 
101
 
 
102
static const SchemaQuery Query_for_list_of_functions = {
 
103
        /* catname */
 
104
        "pg_catalog.pg_proc p",
 
105
        /* selcondition */
 
106
        NULL,
 
107
        /* viscondition */
 
108
        "pg_catalog.pg_function_is_visible(p.oid)",
 
109
        /* namespace */
 
110
        "p.pronamespace",
 
111
        /* result */
 
112
        "pg_catalog.quote_ident(p.proname)",
 
113
        /* qualresult */
 
114
        NULL
 
115
};
 
116
 
 
117
static const SchemaQuery Query_for_list_of_indexes = {
 
118
        /* catname */
 
119
        "pg_catalog.pg_class c",
 
120
        /* selcondition */
 
121
        "c.relkind IN ('i')",
 
122
        /* viscondition */
 
123
        "pg_catalog.pg_table_is_visible(c.oid)",
 
124
        /* namespace */
 
125
        "c.relnamespace",
 
126
        /* result */
 
127
        "pg_catalog.quote_ident(c.relname)",
 
128
        /* qualresult */
 
129
        NULL
 
130
};
 
131
 
 
132
static const SchemaQuery Query_for_list_of_sequences = {
 
133
        /* catname */
 
134
        "pg_catalog.pg_class c",
 
135
        /* selcondition */
 
136
        "c.relkind IN ('S')",
 
137
        /* viscondition */
 
138
        "pg_catalog.pg_table_is_visible(c.oid)",
 
139
        /* namespace */
 
140
        "c.relnamespace",
 
141
        /* result */
 
142
        "pg_catalog.quote_ident(c.relname)",
 
143
        /* qualresult */
 
144
        NULL
 
145
};
 
146
 
 
147
static const SchemaQuery Query_for_list_of_tables = {
 
148
        /* catname */
 
149
        "pg_catalog.pg_class c",
 
150
        /* selcondition */
 
151
        "c.relkind IN ('r')",
 
152
        /* viscondition */
 
153
        "pg_catalog.pg_table_is_visible(c.oid)",
 
154
        /* namespace */
 
155
        "c.relnamespace",
 
156
        /* result */
 
157
        "pg_catalog.quote_ident(c.relname)",
 
158
        /* qualresult */
 
159
        NULL
 
160
};
 
161
 
 
162
static const SchemaQuery Query_for_list_of_tisv = {
 
163
        /* catname */
 
164
        "pg_catalog.pg_class c",
 
165
        /* selcondition */
 
166
        "c.relkind IN ('r', 'i', 'S', 'v')",
 
167
        /* viscondition */
 
168
        "pg_catalog.pg_table_is_visible(c.oid)",
 
169
        /* namespace */
 
170
        "c.relnamespace",
 
171
        /* result */
 
172
        "pg_catalog.quote_ident(c.relname)",
 
173
        /* qualresult */
 
174
        NULL
 
175
};
 
176
 
 
177
static const SchemaQuery Query_for_list_of_tsv = {
 
178
        /* catname */
 
179
        "pg_catalog.pg_class c",
 
180
        /* selcondition */
 
181
        "c.relkind IN ('r', 'S', 'v')",
 
182
        /* viscondition */
 
183
        "pg_catalog.pg_table_is_visible(c.oid)",
 
184
        /* namespace */
 
185
        "c.relnamespace",
 
186
        /* result */
 
187
        "pg_catalog.quote_ident(c.relname)",
 
188
        /* qualresult */
 
189
        NULL
 
190
};
 
191
 
 
192
static const SchemaQuery Query_for_list_of_views = {
 
193
        /* catname */
 
194
        "pg_catalog.pg_class c",
 
195
        /* selcondition */
 
196
        "c.relkind IN ('v')",
 
197
        /* viscondition */
 
198
        "pg_catalog.pg_table_is_visible(c.oid)",
 
199
        /* namespace */
 
200
        "c.relnamespace",
 
201
        /* result */
 
202
        "pg_catalog.quote_ident(c.relname)",
 
203
        /* qualresult */
 
204
        NULL
 
205
};
 
206
 
 
207
 
 
208
/*
 
209
 * Queries to get lists of names of various kinds of things, possibly
 
210
 * restricted to names matching a partially entered name.  In these queries,
 
211
 * %s will be replaced by the text entered so far (suitably escaped to
 
212
 * become a SQL literal string).  %d will be replaced by the length of the
 
213
 * string (in unescaped form).  A second %s, if present, will be replaced
 
214
 * by a suitably-escaped version of the string provided in
 
215
 * completion_info_charp.
 
216
 *
 
217
 * Beware that the allowed sequences of %s and %d are determined by
 
218
 * _complete_from_query().
 
219
 */
 
220
 
 
221
#define Query_for_list_of_attributes \
 
222
"SELECT pg_catalog.quote_ident(attname) "\
 
223
"  FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
 
224
" WHERE c.oid = a.attrelid "\
 
225
"   AND a.attnum > 0 "\
 
226
"   AND NOT a.attisdropped "\
 
227
"   AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\
 
228
"   AND pg_catalog.quote_ident(relname)='%s' "\
 
229
"   AND pg_catalog.pg_table_is_visible(c.oid)"
 
230
 
 
231
#define Query_for_list_of_databases \
 
232
"SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\
 
233
" WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"
 
234
 
 
235
#define Query_for_list_of_tablespaces \
 
236
"SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\
 
237
" WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"
 
238
 
 
239
#define Query_for_list_of_encodings \
 
240
" SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
 
241
"   FROM pg_catalog.pg_conversion "\
 
242
"  WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
 
243
 
 
244
#define Query_for_list_of_languages \
 
245
"SELECT pg_catalog.quote_ident(lanname) "\
 
246
"  FROM pg_language "\
 
247
" WHERE lanname != 'internal' "\
 
248
"   AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s' "
 
249
 
 
250
#define Query_for_list_of_schemas \
 
251
"SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\
 
252
" WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"
 
253
 
 
254
#define Query_for_list_of_set_vars \
 
255
"SELECT name FROM "\
 
256
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
 
257
"  WHERE context IN ('user', 'superuser') "\
 
258
"  UNION ALL SELECT 'constraints' "\
 
259
"  UNION ALL SELECT 'transaction' "\
 
260
"  UNION ALL SELECT 'session' "\
 
261
"  UNION ALL SELECT 'role' "\
 
262
"  UNION ALL SELECT 'all') ss "\
 
263
" WHERE substring(name,1,%d)='%s'"
 
264
 
 
265
#define Query_for_list_of_show_vars \
 
266
"SELECT name FROM "\
 
267
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
 
268
"  UNION ALL SELECT 'session authorization' "\
 
269
"  UNION ALL SELECT 'all') ss "\
 
270
" WHERE substring(name,1,%d)='%s'"
 
271
 
 
272
#define Query_for_list_of_system_relations \
 
273
"SELECT pg_catalog.quote_ident(relname) "\
 
274
"  FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
 
275
" WHERE c.relkind IN ('r', 'v', 's', 'S') "\
 
276
"   AND substring(pg_catalog.quote_ident(relname),1,%d)='%s' "\
 
277
"   AND c.relnamespace = n.oid "\
 
278
"   AND n.nspname = 'pg_catalog'"
 
279
 
 
280
#define Query_for_list_of_roles \
 
281
" SELECT pg_catalog.quote_ident(rolname) "\
 
282
"   FROM pg_catalog.pg_roles "\
 
283
"  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"
 
284
 
 
285
#define Query_for_list_of_grant_roles \
 
286
" SELECT pg_catalog.quote_ident(rolname) "\
 
287
"   FROM pg_catalog.pg_roles "\
 
288
"  WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\
 
289
" UNION ALL SELECT 'PUBLIC'"
 
290
 
 
291
/* the silly-looking length condition is just to eat up the current word */
 
292
#define Query_for_table_owning_index \
 
293
"SELECT pg_catalog.quote_ident(c1.relname) "\
 
294
"  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
 
295
" WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
 
296
"       and (%d = length('%s'))"\
 
297
"       and pg_catalog.quote_ident(c2.relname)='%s'"\
 
298
"       and pg_catalog.pg_table_is_visible(c2.oid)"
 
299
 
 
300
/* the silly-looking length condition is just to eat up the current word */
 
301
#define Query_for_index_of_table \
 
302
"SELECT pg_catalog.quote_ident(c2.relname) "\
 
303
"  FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
 
304
" WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
 
305
"       and (%d = length('%s'))"\
 
306
"       and pg_catalog.quote_ident(c1.relname)='%s'"\
 
307
"       and pg_catalog.pg_table_is_visible(c2.oid)"
 
308
 
 
309
/* the silly-looking length condition is just to eat up the current word */
 
310
#define Query_for_list_of_tables_for_trigger \
 
311
"SELECT pg_catalog.quote_ident(relname) "\
 
312
"  FROM pg_catalog.pg_class"\
 
313
" WHERE (%d = length('%s'))"\
 
314
"   AND oid IN "\
 
315
"       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
 
316
"         WHERE pg_catalog.quote_ident(tgname)='%s')"
 
317
 
 
318
/*
 
319
 * This is a list of all "things" in Pgsql, which can show up after CREATE or
 
320
 * DROP; and there is also a query to get a list of them.
 
321
 */
 
322
 
 
323
typedef struct
 
324
{
 
325
        const char *name;
 
326
        const char *query;                      /* simple query, or NULL */
 
327
        const SchemaQuery *squery;      /* schema query, or NULL */
 
328
} pgsql_thing_t;
 
329
 
 
330
static const pgsql_thing_t words_after_create[] = {
 
331
        {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
 
332
        {"CAST", NULL, NULL},           /* Casts have complex structures for names, so
 
333
                                                                 * skip it */
 
334
 
 
335
        /*
 
336
         * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
 
337
         * to be used only by pg_dump.
 
338
         */
 
339
        {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
 
340
        {"DATABASE", Query_for_list_of_databases},
 
341
        {"DOMAIN", NULL, &Query_for_list_of_domains},
 
342
        {"FUNCTION", NULL, &Query_for_list_of_functions},
 
343
        {"GROUP", Query_for_list_of_roles},
 
344
        {"LANGUAGE", Query_for_list_of_languages},
 
345
        {"INDEX", NULL, &Query_for_list_of_indexes},
 
346
        {"OPERATOR", NULL, NULL},       /* Querying for this is probably not such a
 
347
                                                                 * good idea. */
 
348
        {"ROLE", Query_for_list_of_roles},
 
349
        {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
 
350
        {"SCHEMA", Query_for_list_of_schemas},
 
351
        {"SEQUENCE", NULL, &Query_for_list_of_sequences},
 
352
        {"TABLE", NULL, &Query_for_list_of_tables},
 
353
        {"TABLESPACE", Query_for_list_of_tablespaces},
 
354
        {"TEMP", NULL, NULL},           /* for CREATE TEMP TABLE ... */
 
355
        {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"},
 
356
        {"TYPE", NULL, &Query_for_list_of_datatypes},
 
357
        {"UNIQUE", NULL, NULL},         /* for CREATE UNIQUE INDEX ... */
 
358
        {"USER", Query_for_list_of_roles},
 
359
        {"VIEW", NULL, &Query_for_list_of_views},
 
360
        {NULL, NULL, NULL}                      /* end of list */
 
361
};
 
362
 
 
363
 
 
364
/* The completion function. Acc. to readline spec this gets passed the text
 
365
   entered to far and its start and end in the readline buffer. The return value
 
366
   is some partially obscure list format that can be generated by the readline
 
367
   libraries completion_matches() function, so we don't have to worry about it.
 
368
*/
 
369
static char * psql_completion(char *text, int start, int end, void *dbptr)
 
370
{
 
371
        /* This is the variable we'll return. */
 
372
        char *matches = NULL;
 
373
 
 
374
        /* These are going to contain some scannage of the input line. */
 
375
        char       *prev_wd,
 
376
                           *prev2_wd,
 
377
                           *prev3_wd,
 
378
                           *prev4_wd,
 
379
                           *prev5_wd;
 
380
 
 
381
        static const char *const sql_commands[] = {
 
382
                "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
 
383
                "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE FROM", "DROP", "END", "EXECUTE",
 
384
                "EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
 
385
                "PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT",
 
386
                "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
 
387
        };
 
388
 
 
389
        static const char *const backslash_commands[] = {
 
390
                "\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright",
 
391
                "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\df",
 
392
                "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS",
 
393
                "\\dt", "\\dT", "\\dv", "\\du",
 
394
                "\\e", "\\echo", "\\encoding",
 
395
                "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
 
396
                "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
 
397
                "\\o", "\\p", "\\password", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T",
 
398
                "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
 
399
        };
 
400
 
 
401
        (void) end;                                     /* not used */
 
402
 
 
403
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
 
404
        rl_completion_append_character = ' ';
 
405
#endif
 
406
 
 
407
        /* Clear a few things. */
 
408
        completion_charp = NULL;
 
409
        completion_charpp = NULL;
 
410
        completion_info_charp = NULL;
 
411
 
 
412
        /*
 
413
         * Scan the input line before our current position for the last four
 
414
         * words. According to those we'll make some smart decisions on what the
 
415
         * user is probably intending to type. TODO: Use strtokx() to do this.
 
416
         */
 
417
        prev_wd = previous_word(start, 0);
 
418
        prev2_wd = previous_word(start, 1);
 
419
        prev3_wd = previous_word(start, 2);
 
420
        prev4_wd = previous_word(start, 3);
 
421
        prev5_wd = previous_word(start, 4);
 
422
 
 
423
        /* If a backslash command was started, continue */
 
424
        if (text[0] == '\\')
 
425
                COMPLETE_WITH_LIST(backslash_commands);
 
426
 
 
427
        /* If no previous word, suggest one of the basic sql commands */
 
428
        else if (!prev_wd)
 
429
                COMPLETE_WITH_LIST(sql_commands);
 
430
 
 
431
/* CREATE or DROP but not ALTER (TABLE|DOMAIN|GROUP) sth DROP */
 
432
        /* complete with something you can create or drop */
 
433
        else if (pg_strcasecmp(prev_wd, "CREATE") == 0 ||
 
434
                         (pg_strcasecmp(prev_wd, "DROP") == 0 &&
 
435
                          pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
 
436
                          pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
 
437
                          pg_strcasecmp(prev3_wd, "GROUP") != 0))
 
438
                matches = complete_create_command(text);
 
439
 
 
440
/* ALTER */
 
441
 
 
442
        /*
 
443
         * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're
 
444
         * in ALTER TABLE sth ALTER
 
445
         */
 
446
        else if (pg_strcasecmp(prev_wd, "ALTER") == 0 &&
 
447
                         pg_strcasecmp(prev3_wd, "TABLE") != 0)
 
448
        {
 
449
                static const char *const list_ALTER[] =
 
450
                {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION",
 
451
                        "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE",
 
452
                "TABLESPACE", "TRIGGER", "TYPE", "USER", NULL};
 
453
 
 
454
                COMPLETE_WITH_LIST(list_ALTER);
 
455
        }
 
456
        /* ALTER AGGREGATE,FUNCTION <name> */
 
457
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
458
                         (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
 
459
                          pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
 
460
        {
 
461
                static const char *const list_ALTERAGG[] =
 
462
                {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
 
463
 
 
464
                COMPLETE_WITH_LIST(list_ALTERAGG);
 
465
        }
 
466
 
 
467
        /* ALTER CONVERSION,SCHEMA <name> */
 
468
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
469
                         (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
 
470
                          pg_strcasecmp(prev2_wd, "SCHEMA") == 0))
 
471
        {
 
472
                static const char *const list_ALTERGEN[] =
 
473
                {"OWNER TO", "RENAME TO", NULL};
 
474
 
 
475
                COMPLETE_WITH_LIST(list_ALTERGEN);
 
476
        }
 
477
 
 
478
        /* ALTER DATABASE <name> */
 
479
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
480
                         pg_strcasecmp(prev2_wd, "DATABASE") == 0)
 
481
        {
 
482
                static const char *const list_ALTERDATABASE[] =
 
483
                {"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};
 
484
 
 
485
                COMPLETE_WITH_LIST(list_ALTERDATABASE);
 
486
        }
 
487
 
 
488
        /* ALTER INDEX <name> */
 
489
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
490
                         pg_strcasecmp(prev2_wd, "INDEX") == 0)
 
491
        {
 
492
                static const char *const list_ALTERINDEX[] =
 
493
                {"SET TABLESPACE", "OWNER TO", "RENAME TO", NULL};
 
494
 
 
495
                COMPLETE_WITH_LIST(list_ALTERINDEX);
 
496
        }
 
497
 
 
498
        /* ALTER LANGUAGE <name> */
 
499
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
500
                         pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
 
501
                COMPLETE_WITH_CONST("RENAME TO");
 
502
 
 
503
        /* ALTER USER,ROLE <name> */
 
504
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
505
                         (pg_strcasecmp(prev2_wd, "USER") == 0 ||
 
506
                          pg_strcasecmp(prev2_wd, "ROLE") == 0))
 
507
        {
 
508
                static const char *const list_ALTERUSER[] =
 
509
                {"ENCRYPTED", "UNENCRYPTED", "CREATEDB", "NOCREATEDB", "CREATEUSER",
 
510
                        "NOCREATEUSER", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT",
 
511
                        "LOGIN", "NOLOGIN", "CONNECTION LIMIT", "VALID UNTIL", "RENAME TO",
 
512
                "SUPERUSER", "NOSUPERUSER", "SET", "RESET", NULL};
 
513
 
 
514
                COMPLETE_WITH_LIST(list_ALTERUSER);
 
515
        }
 
516
 
 
517
        /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
 
518
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
519
                         (pg_strcasecmp(prev3_wd, "ROLE") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
 
520
                         (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
 
521
        {
 
522
                COMPLETE_WITH_CONST("PASSWORD");
 
523
        }
 
524
        /* ALTER DOMAIN <name> */
 
525
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
526
                         pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
 
527
        {
 
528
                static const char *const list_ALTERDOMAIN[] =
 
529
                {"ADD", "DROP", "OWNER TO", "SET", NULL};
 
530
 
 
531
                COMPLETE_WITH_LIST(list_ALTERDOMAIN);
 
532
        }
 
533
        /* ALTER DOMAIN <sth> DROP */
 
534
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
535
                         pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
 
536
                         pg_strcasecmp(prev_wd, "DROP") == 0)
 
537
        {
 
538
                static const char *const list_ALTERDOMAIN2[] =
 
539
                {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL};
 
540
 
 
541
                COMPLETE_WITH_LIST(list_ALTERDOMAIN2);
 
542
        }
 
543
        /* ALTER DOMAIN <sth> SET */
 
544
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
545
                         pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
 
546
                         pg_strcasecmp(prev_wd, "SET") == 0)
 
547
        {
 
548
                static const char *const list_ALTERDOMAIN3[] =
 
549
                {"DEFAULT", "NOT NULL", "SCHEMA", NULL};
 
550
 
 
551
                COMPLETE_WITH_LIST(list_ALTERDOMAIN3);
 
552
        }
 
553
        /* ALTER SEQUENCE <name> */
 
554
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
555
                         pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)
 
556
        {
 
557
                static const char *const list_ALTERSEQUENCE[] =
 
558
                {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
 
559
                "SET SCHEMA", NULL};
 
560
 
 
561
                COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
 
562
        }
 
563
        /* ALTER SEQUENCE <name> NO */
 
564
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
565
                         pg_strcasecmp(prev3_wd, "SEQUENCE") == 0 &&
 
566
                         pg_strcasecmp(prev_wd, "NO") == 0)
 
567
        {
 
568
                static const char *const list_ALTERSEQUENCE2[] =
 
569
                {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
 
570
 
 
571
                COMPLETE_WITH_LIST(list_ALTERSEQUENCE2);
 
572
        }
 
573
        /* ALTER TRIGGER <name>, add ON */
 
574
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
575
                         pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
 
576
                COMPLETE_WITH_CONST("ON");
 
577
 
 
578
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
579
                         pg_strcasecmp(prev3_wd, "TRIGGER") == 0)
 
580
        {
 
581
                completion_info_charp = prev2_wd;
 
582
                COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
 
583
        }
 
584
 
 
585
        /*
 
586
         * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
 
587
         */
 
588
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
589
                         pg_strcasecmp(prev3_wd, "TRIGGER") == 0 &&
 
590
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
591
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
592
 
 
593
        /* ALTER TRIGGER <name> ON <name> */
 
594
        else if (pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
 
595
                         pg_strcasecmp(prev2_wd, "ON") == 0)
 
596
                COMPLETE_WITH_CONST("RENAME TO");
 
597
 
 
598
        /*
 
599
         * If we detect ALTER TABLE <name>, suggest either ADD, DROP, ALTER,
 
600
         * RENAME, CLUSTER ON or OWNER
 
601
         */
 
602
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
603
                         pg_strcasecmp(prev2_wd, "TABLE") == 0)
 
604
        {
 
605
                static const char *const list_ALTER2[] =
 
606
                {"ADD", "ALTER", "CLUSTER ON", "DROP", "RENAME", "OWNER TO",
 
607
                "SET", NULL};
 
608
 
 
609
                COMPLETE_WITH_LIST(list_ALTER2);
 
610
        }
 
611
        /* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
 
612
        else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
 
613
                         (pg_strcasecmp(prev_wd, "ALTER") == 0 ||
 
614
                          pg_strcasecmp(prev_wd, "RENAME") == 0))
 
615
                COMPLETE_WITH_ATTR(prev2_wd);
 
616
 
 
617
        /* ALTER TABLE xxx RENAME yyy */
 
618
        else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 
619
                         pg_strcasecmp(prev2_wd, "RENAME") == 0 &&
 
620
                         pg_strcasecmp(prev_wd, "TO") != 0)
 
621
                COMPLETE_WITH_CONST("TO");
 
622
 
 
623
        /* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
 
624
        else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
 
625
                         pg_strcasecmp(prev_wd, "DROP") == 0)
 
626
        {
 
627
                static const char *const list_TABLEDROP[] =
 
628
                {"COLUMN", "CONSTRAINT", NULL};
 
629
 
 
630
                COMPLETE_WITH_LIST(list_TABLEDROP);
 
631
        }
 
632
        /* If we have TABLE <sth> DROP COLUMN, provide list of columns */
 
633
        else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 
634
                         pg_strcasecmp(prev2_wd, "DROP") == 0 &&
 
635
                         pg_strcasecmp(prev_wd, "COLUMN") == 0)
 
636
                COMPLETE_WITH_ATTR(prev3_wd);
 
637
        /* ALTER TABLE ALTER [COLUMN] <foo> */
 
638
        else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
639
                          pg_strcasecmp(prev2_wd, "COLUMN") == 0) ||
 
640
                         (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 
641
                          pg_strcasecmp(prev2_wd, "ALTER") == 0))
 
642
        {
 
643
                /* DROP ... does not work well yet */
 
644
                static const char *const list_COLUMNALTER[] =
 
645
                {"TYPE", "SET DEFAULT", "DROP DEFAULT", "SET NOT NULL",
 
646
                "DROP NOT NULL", "SET STATISTICS", "SET STORAGE", NULL};
 
647
 
 
648
                COMPLETE_WITH_LIST(list_COLUMNALTER);
 
649
        }
 
650
        else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
 
651
                         pg_strcasecmp(prev_wd, "CLUSTER") == 0)
 
652
                COMPLETE_WITH_CONST("ON");
 
653
        else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 
654
                         pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
 
655
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
656
        {
 
657
                completion_info_charp = prev3_wd;
 
658
                COMPLETE_WITH_QUERY(Query_for_index_of_table);
 
659
        }
 
660
        /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
 
661
        else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
 
662
                         pg_strcasecmp(prev_wd, "SET") == 0)
 
663
        {
 
664
                static const char *const list_TABLESET[] =
 
665
                {"WITHOUT", "TABLESPACE", "SCHEMA", NULL};
 
666
 
 
667
                COMPLETE_WITH_LIST(list_TABLESET);
 
668
        }
 
669
        /* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
 
670
        else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 
671
                         pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
672
                         pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
 
673
                COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
 
674
        /* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
 
675
        else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 
676
                         pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
677
                         pg_strcasecmp(prev_wd, "WITHOUT") == 0)
 
678
        {
 
679
                static const char *const list_TABLESET2[] =
 
680
                {"CLUSTER", "OIDS", NULL};
 
681
 
 
682
                COMPLETE_WITH_LIST(list_TABLESET2);
 
683
        }
 
684
        /* we have ALTER TABLESPACE, so suggest RENAME TO, OWNER TO */
 
685
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
686
                         pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
 
687
        {
 
688
                static const char *const list_ALTERTSPC[] =
 
689
                {"RENAME TO", "OWNER TO", NULL};
 
690
 
 
691
                COMPLETE_WITH_LIST(list_ALTERTSPC);
 
692
        }
 
693
        /* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */
 
694
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
695
                         pg_strcasecmp(prev2_wd, "TYPE") == 0)
 
696
        {
 
697
                static const char *const list_ALTERTYPE[] =
 
698
                {"OWNER TO", "SET SCHEMA", NULL};
 
699
 
 
700
                COMPLETE_WITH_LIST(list_ALTERTYPE);
 
701
        }
 
702
        /* complete ALTER GROUP <foo> */
 
703
        else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 
704
                         pg_strcasecmp(prev2_wd, "GROUP") == 0)
 
705
        {
 
706
                static const char *const list_ALTERGROUP[] =
 
707
                {"ADD USER", "DROP USER", "RENAME TO", NULL};
 
708
 
 
709
                COMPLETE_WITH_LIST(list_ALTERGROUP);
 
710
        }
 
711
        /* complete ALTER GROUP <foo> ADD|DROP with USER */
 
712
        else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
 
713
                         pg_strcasecmp(prev3_wd, "GROUP") == 0 &&
 
714
                         (pg_strcasecmp(prev_wd, "ADD") == 0 ||
 
715
                          pg_strcasecmp(prev_wd, "DROP") == 0))
 
716
                COMPLETE_WITH_CONST("USER");
 
717
        /* complete {ALTER} GROUP <foo> ADD|DROP USER with a user name */
 
718
        else if (pg_strcasecmp(prev4_wd, "GROUP") == 0 &&
 
719
                         (pg_strcasecmp(prev2_wd, "ADD") == 0 ||
 
720
                          pg_strcasecmp(prev2_wd, "DROP") == 0) &&
 
721
                         pg_strcasecmp(prev_wd, "USER") == 0)
 
722
                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
723
 
 
724
/* BEGIN, END, ABORT */
 
725
        else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
 
726
                         pg_strcasecmp(prev_wd, "END") == 0 ||
 
727
                         pg_strcasecmp(prev_wd, "ABORT") == 0)
 
728
        {
 
729
                static const char *const list_TRANS[] =
 
730
                {"WORK", "TRANSACTION", NULL};
 
731
 
 
732
                COMPLETE_WITH_LIST(list_TRANS);
 
733
        }
 
734
/* COMMIT */
 
735
        else if (pg_strcasecmp(prev_wd, "COMMIT") == 0)
 
736
        {
 
737
                static const char *const list_COMMIT[] =
 
738
                {"WORK", "TRANSACTION", "PREPARED", NULL};
 
739
 
 
740
                COMPLETE_WITH_LIST(list_COMMIT);
 
741
        }
 
742
/* RELEASE SAVEPOINT */
 
743
        else if (pg_strcasecmp(prev_wd, "RELEASE") == 0)
 
744
                COMPLETE_WITH_CONST("SAVEPOINT");
 
745
/* ROLLBACK*/
 
746
        else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0)
 
747
        {
 
748
                static const char *const list_TRANS[] =
 
749
                {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL};
 
750
 
 
751
                COMPLETE_WITH_LIST(list_TRANS);
 
752
        }
 
753
/* CLUSTER */
 
754
 
 
755
        /*
 
756
         * If the previous word is CLUSTER and not without produce list of
 
757
         * indexes.
 
758
         */
 
759
        else if (pg_strcasecmp(prev_wd, "CLUSTER") == 0 &&
 
760
                         pg_strcasecmp(prev2_wd, "WITHOUT") != 0)
 
761
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
 
762
        /* If we have CLUSTER <sth>, then add "ON" */
 
763
        else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
 
764
                         pg_strcasecmp(prev_wd, "ON") != 0)
 
765
                COMPLETE_WITH_CONST("ON");
 
766
 
 
767
        /*
 
768
         * If we have CLUSTER <sth> ON, then add the correct tablename as well.
 
769
         */
 
770
        else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
 
771
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
772
        {
 
773
                completion_info_charp = prev2_wd;
 
774
                COMPLETE_WITH_QUERY(Query_for_table_owning_index);
 
775
        }
 
776
 
 
777
/* COMMENT */
 
778
        else if (pg_strcasecmp(prev_wd, "COMMENT") == 0)
 
779
                COMPLETE_WITH_CONST("ON");
 
780
        else if (pg_strcasecmp(prev2_wd, "COMMENT") == 0 &&
 
781
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
782
        {
 
783
                static const char *const list_COMMENT[] =
 
784
                {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
 
785
                        "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
 
786
                "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", NULL};
 
787
 
 
788
                COMPLETE_WITH_LIST(list_COMMENT);
 
789
        }
 
790
        else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
 
791
                         pg_strcasecmp(prev3_wd, "ON") == 0)
 
792
                COMPLETE_WITH_CONST("IS");
 
793
 
 
794
/* COPY */
 
795
 
 
796
        /*
 
797
         * If we have COPY [BINARY] (which you'd have to type yourself), offer
 
798
         * list of tables (Also cover the analogous backslash command)
 
799
         */
 
800
        else if (pg_strcasecmp(prev_wd, "COPY") == 0 ||
 
801
                         pg_strcasecmp(prev_wd, "\\copy") == 0 ||
 
802
                         (pg_strcasecmp(prev2_wd, "COPY") == 0 &&
 
803
                          pg_strcasecmp(prev_wd, "BINARY") == 0))
 
804
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
805
        /* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
 
806
        else if (pg_strcasecmp(prev2_wd, "COPY") == 0 ||
 
807
                         pg_strcasecmp(prev2_wd, "\\copy") == 0 ||
 
808
                         pg_strcasecmp(prev2_wd, "BINARY") == 0)
 
809
        {
 
810
                static const char *const list_FROMTO[] =
 
811
                {"FROM", "TO", NULL};
 
812
 
 
813
                COMPLETE_WITH_LIST(list_FROMTO);
 
814
        }
 
815
        /* If we have COPY|BINARY <sth> FROM|TO, complete with filename */
 
816
        else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 ||
 
817
                          pg_strcasecmp(prev3_wd, "\\copy") == 0 ||
 
818
                          pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
 
819
                         (pg_strcasecmp(prev_wd, "FROM") == 0 ||
 
820
                          pg_strcasecmp(prev_wd, "TO") == 0))
 
821
                matches = complete_filename();
 
822
 
 
823
        /* Handle COPY|BINARY <sth> FROM|TO filename */
 
824
        else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
 
825
                          pg_strcasecmp(prev4_wd, "\\copy") == 0 ||
 
826
                          pg_strcasecmp(prev4_wd, "BINARY") == 0) &&
 
827
                         (pg_strcasecmp(prev2_wd, "FROM") == 0 ||
 
828
                          pg_strcasecmp(prev2_wd, "TO") == 0))
 
829
        {
 
830
                static const char *const list_COPY[] =
 
831
                {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL};
 
832
 
 
833
                COMPLETE_WITH_LIST(list_COPY);
 
834
        }
 
835
 
 
836
        /* Handle COPY|BINARY <sth> FROM|TO filename CSV */
 
837
        else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
 
838
                         (pg_strcasecmp(prev3_wd, "FROM") == 0 ||
 
839
                          pg_strcasecmp(prev3_wd, "TO") == 0))
 
840
        {
 
841
                static const char *const list_CSV[] =
 
842
                {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
 
843
 
 
844
                COMPLETE_WITH_LIST(list_CSV);
 
845
        }
 
846
 
 
847
        /* CREATE DATABASE */
 
848
        else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
 
849
                         pg_strcasecmp(prev2_wd, "DATABASE") == 0)
 
850
        {
 
851
                static const char *const list_DATABASE[] =
 
852
                {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT",
 
853
                NULL};
 
854
 
 
855
                COMPLETE_WITH_LIST(list_DATABASE);
 
856
        }
 
857
 
 
858
        /* CREATE INDEX */
 
859
        /* First off we complete CREATE UNIQUE with "INDEX" */
 
860
        else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
 
861
                         pg_strcasecmp(prev_wd, "UNIQUE") == 0)
 
862
                COMPLETE_WITH_CONST("INDEX");
 
863
        /* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" */
 
864
        else if (pg_strcasecmp(prev2_wd, "INDEX") == 0 &&
 
865
                         (pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
 
866
                          pg_strcasecmp(prev3_wd, "UNIQUE") == 0))
 
867
                COMPLETE_WITH_CONST("ON");
 
868
        /* Complete ... INDEX <name> ON with a list of tables  */
 
869
        else if (pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
 
870
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
871
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
872
 
 
873
        /*
 
874
         * Complete INDEX <name> ON <table> with a list of table columns (which
 
875
         * should really be in parens)
 
876
         */
 
877
        else if (pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
 
878
                         pg_strcasecmp(prev2_wd, "ON") == 0)
 
879
        {
 
880
                if (find_open_parenthesis(end))
 
881
                        COMPLETE_WITH_ATTR(prev_wd);
 
882
                else
 
883
                        COMPLETE_WITH_CONST("(");      
 
884
        }
 
885
        else if (pg_strcasecmp(prev5_wd, "INDEX") == 0 &&
 
886
                        pg_strcasecmp(prev3_wd, "ON") == 0 &&
 
887
                        pg_strcasecmp(prev_wd, "(") == 0)
 
888
                COMPLETE_WITH_ATTR(prev2_wd);
 
889
        /* same if you put in USING */
 
890
        else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
 
891
                         pg_strcasecmp(prev2_wd, "USING") == 0)
 
892
                COMPLETE_WITH_ATTR(prev3_wd);
 
893
        /* Complete USING with an index method */
 
894
        else if (pg_strcasecmp(prev_wd, "USING") == 0)
 
895
        {
 
896
                static const char *const index_mth[] =
 
897
                {"BTREE", "HASH", "GIST", NULL};
 
898
 
 
899
                COMPLETE_WITH_LIST(index_mth);
 
900
        }
 
901
 
 
902
/* CREATE RULE */
 
903
        /* Complete "CREATE RULE <sth>" with "AS" */
 
904
        else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
 
905
                         pg_strcasecmp(prev2_wd, "RULE") == 0)
 
906
                COMPLETE_WITH_CONST("AS");
 
907
        /* Complete "CREATE RULE <sth> AS with "ON" */
 
908
        else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
 
909
                         pg_strcasecmp(prev3_wd, "RULE") == 0 &&
 
910
                         pg_strcasecmp(prev_wd, "AS") == 0)
 
911
                COMPLETE_WITH_CONST("ON");
 
912
        /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */
 
913
        else if (pg_strcasecmp(prev4_wd, "RULE") == 0 &&
 
914
                         pg_strcasecmp(prev2_wd, "AS") == 0 &&
 
915
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
916
        {
 
917
                static const char *const rule_events[] =
 
918
                {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
 
919
 
 
920
                COMPLETE_WITH_LIST(rule_events);
 
921
        }
 
922
        /* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
 
923
        else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
 
924
                         pg_strcasecmp(prev2_wd, "ON") == 0 &&
 
925
                         (toupper((unsigned char) prev_wd[4]) == 'T' ||
 
926
                          toupper((unsigned char) prev_wd[5]) == 'T'))
 
927
                COMPLETE_WITH_CONST("TO");
 
928
        /* Complete "AS ON <sth> TO" with a table name */
 
929
        else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
 
930
                         pg_strcasecmp(prev3_wd, "ON") == 0 &&
 
931
                         pg_strcasecmp(prev_wd, "TO") == 0)
 
932
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
933
 
 
934
/* CREATE TABLE */
 
935
        /* Complete CREATE TEMP with "TABLE" */
 
936
        else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
 
937
                         pg_strcasecmp(prev_wd, "TEMP") == 0)
 
938
                COMPLETE_WITH_CONST("TABLE");
 
939
 
 
940
/* CREATE TABLESPACE */
 
941
        else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
 
942
                         pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
 
943
        {
 
944
                static const char *const list_CREATETABLESPACE[] =
 
945
                {"OWNER", "LOCATION", NULL};
 
946
 
 
947
                COMPLETE_WITH_LIST(list_CREATETABLESPACE);
 
948
        }
 
949
        /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
 
950
        else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
 
951
                         pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 &&
 
952
                         pg_strcasecmp(prev2_wd, "OWNER") == 0)
 
953
        {
 
954
                COMPLETE_WITH_CONST("LOCATION");
 
955
        }
 
956
 
 
957
/* CREATE TRIGGER */
 
958
        /* complete CREATE TRIGGER <name> with BEFORE,AFTER */
 
959
        else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
 
960
                         pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
 
961
        {
 
962
                static const char *const list_CREATETRIGGER[] =
 
963
                {"BEFORE", "AFTER", NULL};
 
964
 
 
965
                COMPLETE_WITH_LIST(list_CREATETRIGGER);
 
966
        }
 
967
        /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
 
968
        else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
 
969
                         pg_strcasecmp(prev4_wd, "TRIGGER") == 0 &&
 
970
                         (pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
 
971
                          pg_strcasecmp(prev2_wd, "AFTER") == 0))
 
972
        {
 
973
                static const char *const list_CREATETRIGGER2[] =
 
974
                {"ON", "OR", NULL};
 
975
 
 
976
                COMPLETE_WITH_LIST(list_CREATETRIGGER2);
 
977
        }
 
978
 
 
979
/* CREATE ROLE,USER,GROUP */
 
980
        else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
 
981
                         (pg_strcasecmp(prev2_wd, "ROLE") == 0 ||
 
982
                          pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
 
983
        {
 
984
                static const char *const list_CREATEROLE[] =
 
985
                {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
 
986
                        "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOINHERIT", "NOLOGIN", "NOCREATEDB",
 
987
                        "NOCREATEROLE", "NOCREATEUSER", "NOSUPERUSER", "ROLE", "SUPERUSER", "SYSID",
 
988
                "UNENCRYPTED", NULL};
 
989
 
 
990
                COMPLETE_WITH_LIST(list_CREATEROLE);
 
991
        }
 
992
 
 
993
        /*
 
994
         * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
 
995
         * PASSWORD
 
996
         */
 
997
        else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
 
998
                         (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
 
999
                          pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
 
1000
                         (pg_strcasecmp(prev_wd, "ENCRYPTED") == 0 || pg_strcasecmp(prev_wd, "UNENCRYPTED") == 0))
 
1001
        {
 
1002
                COMPLETE_WITH_CONST("PASSWORD");
 
1003
        }
 
1004
        /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
 
1005
        else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
 
1006
                         (pg_strcasecmp(prev3_wd, "ROLE") == 0 ||
 
1007
                          pg_strcasecmp(prev3_wd, "GROUP") == 0 || pg_strcasecmp(prev3_wd, "USER") == 0) &&
 
1008
                         pg_strcasecmp(prev_wd, "IN") == 0)
 
1009
        {
 
1010
                static const char *const list_CREATEROLE3[] =
 
1011
                {"GROUP", "ROLE", NULL};
 
1012
 
 
1013
                COMPLETE_WITH_LIST(list_CREATEROLE3);
 
1014
        }
 
1015
 
 
1016
/* CREATE VIEW */
 
1017
        /* Complete CREATE VIEW <name> with AS */
 
1018
        else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
 
1019
                         pg_strcasecmp(prev2_wd, "VIEW") == 0)
 
1020
                COMPLETE_WITH_CONST("AS");
 
1021
        /* Complete "CREATE VIEW <sth> AS with "SELECT" */
 
1022
        else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
 
1023
                         pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
 
1024
                         pg_strcasecmp(prev_wd, "AS") == 0)
 
1025
                COMPLETE_WITH_CONST("SELECT");
 
1026
 
 
1027
/* DECLARE */
 
1028
        else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0)
 
1029
        {
 
1030
                static const char *const list_DECLARE[] =
 
1031
                {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL};
 
1032
 
 
1033
                COMPLETE_WITH_LIST(list_DECLARE);
 
1034
        }
 
1035
 
 
1036
        else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
 
1037
        {
 
1038
                static const char *const list_DECLARECURSOR[] =
 
1039
                {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL};
 
1040
 
 
1041
                COMPLETE_WITH_LIST(list_DECLARECURSOR);
 
1042
        }
 
1043
 
 
1044
 
 
1045
/* DELETE */
 
1046
 
 
1047
        /*
 
1048
         * Complete DELETE with FROM (only if the word before that is not "ON"
 
1049
         * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT)
 
1050
         */
 
1051
        else if (pg_strcasecmp(prev_wd, "DELETE") == 0 &&
 
1052
                         !(pg_strcasecmp(prev2_wd, "ON") == 0 ||
 
1053
                           pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
 
1054
                           pg_strcasecmp(prev2_wd, "BEFORE") == 0 ||
 
1055
                           pg_strcasecmp(prev2_wd, "AFTER") == 0))
 
1056
                COMPLETE_WITH_CONST("FROM");
 
1057
        /* Complete DELETE FROM with a list of tables */
 
1058
        else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
 
1059
                         pg_strcasecmp(prev_wd, "FROM") == 0)
 
1060
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1061
        /* Complete DELETE FROM <table> */
 
1062
        else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
 
1063
                         pg_strcasecmp(prev2_wd, "FROM") == 0)
 
1064
        {
 
1065
                static const char *const list_DELETE[] =
 
1066
                {"USING", "WHERE", "SET", NULL};
 
1067
 
 
1068
                COMPLETE_WITH_LIST(list_DELETE);
 
1069
        }
 
1070
        /* XXX: implement tab completion for DELETE ... USING */
 
1071
 
 
1072
/* DROP (when not the previous word) */
 
1073
        /* DROP AGGREGATE */
 
1074
        else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
 
1075
                         pg_strcasecmp(prev2_wd, "AGGREGATE") == 0)
 
1076
                COMPLETE_WITH_CONST("(");
 
1077
 
 
1078
        /* DROP object with CASCADE / RESTRICT */
 
1079
        else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
 
1080
                          (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
 
1081
                           pg_strcasecmp(prev2_wd, "DOMAIN") == 0 ||
 
1082
                           pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
 
1083
                           pg_strcasecmp(prev2_wd, "INDEX") == 0 ||
 
1084
                           pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 ||
 
1085
                           pg_strcasecmp(prev2_wd, "SCHEMA") == 0 ||
 
1086
                           pg_strcasecmp(prev2_wd, "SEQUENCE") == 0 ||
 
1087
                           pg_strcasecmp(prev2_wd, "TABLE") == 0 ||
 
1088
                           pg_strcasecmp(prev2_wd, "TYPE") == 0 ||
 
1089
                           pg_strcasecmp(prev2_wd, "VIEW") == 0)) ||
 
1090
                         (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
 
1091
                          pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 &&
 
1092
                          prev_wd[strlen(prev_wd) - 1] == ')'))
 
1093
        {
 
1094
                
 
1095
                if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
 
1096
                {
 
1097
                        if (find_open_parenthesis(end))
 
1098
                        {
 
1099
                                static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
 
1100
                                char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd));
 
1101
                                sprintf(tmp_buf, func_args_query, prev_wd);
 
1102
                                COMPLETE_WITH_QUERY(tmp_buf);
 
1103
                                free(tmp_buf);
 
1104
                        }
 
1105
                        else
 
1106
                        {
 
1107
                                COMPLETE_WITH_CONST("(");
 
1108
                        }
 
1109
                }
 
1110
                else
 
1111
                {
 
1112
                        static const char *const list_DROPCR[] =
 
1113
                        {"CASCADE", "RESTRICT", NULL};
 
1114
                
 
1115
                        COMPLETE_WITH_LIST(list_DROPCR);
 
1116
                }
 
1117
        }
 
1118
        else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
 
1119
                        pg_strcasecmp(prev3_wd, "FUNCTION") == 0 &&
 
1120
                        pg_strcasecmp(prev_wd, "(") == 0 )
 
1121
        {
 
1122
                static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
 
1123
                char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev2_wd));
 
1124
                sprintf(tmp_buf, func_args_query, prev2_wd);
 
1125
                COMPLETE_WITH_QUERY(tmp_buf);
 
1126
                free(tmp_buf);
 
1127
        }
 
1128
 
 
1129
 
 
1130
 
 
1131
/* EXPLAIN */
 
1132
 
 
1133
        /*
 
1134
         * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
 
1135
         */
 
1136
        else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
 
1137
        {
 
1138
                static const char *const list_EXPLAIN[] =
 
1139
                {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL};
 
1140
 
 
1141
                COMPLETE_WITH_LIST(list_EXPLAIN);
 
1142
        }
 
1143
        else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
 
1144
                         pg_strcasecmp(prev_wd, "ANALYZE") == 0)
 
1145
        {
 
1146
                static const char *const list_EXPLAIN[] =
 
1147
                {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL};
 
1148
 
 
1149
                COMPLETE_WITH_LIST(list_EXPLAIN);
 
1150
        }
 
1151
        else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
 
1152
                         pg_strcasecmp(prev3_wd, "VACUUM") != 0 &&
 
1153
                         pg_strcasecmp(prev4_wd, "VACUUM") != 0 &&
 
1154
                         (pg_strcasecmp(prev2_wd, "ANALYZE") == 0 ||
 
1155
                          pg_strcasecmp(prev2_wd, "EXPLAIN") == 0))
 
1156
        {
 
1157
                static const char *const list_EXPLAIN[] =
 
1158
                {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
 
1159
 
 
1160
                COMPLETE_WITH_LIST(list_EXPLAIN);
 
1161
        }
 
1162
 
 
1163
/* FETCH && MOVE */
 
1164
        /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
 
1165
        else if (pg_strcasecmp(prev_wd, "FETCH") == 0 ||
 
1166
                         pg_strcasecmp(prev_wd, "MOVE") == 0)
 
1167
        {
 
1168
                static const char *const list_FETCH1[] =
 
1169
                {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL};
 
1170
 
 
1171
                COMPLETE_WITH_LIST(list_FETCH1);
 
1172
        }
 
1173
        /* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
 
1174
        else if (pg_strcasecmp(prev2_wd, "FETCH") == 0 ||
 
1175
                         pg_strcasecmp(prev2_wd, "MOVE") == 0)
 
1176
        {
 
1177
                static const char *const list_FETCH2[] =
 
1178
                {"ALL", "NEXT", "PRIOR", NULL};
 
1179
 
 
1180
                COMPLETE_WITH_LIST(list_FETCH2);
 
1181
        }
 
1182
 
 
1183
        /*
 
1184
         * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent,
 
1185
         * but we may as well tab-complete both: perhaps some users prefer one
 
1186
         * variant or the other.
 
1187
         */
 
1188
        else if (pg_strcasecmp(prev3_wd, "FETCH") == 0 ||
 
1189
                         pg_strcasecmp(prev3_wd, "MOVE") == 0)
 
1190
        {
 
1191
                static const char *const list_FROMIN[] =
 
1192
                {"FROM", "IN", NULL};
 
1193
 
 
1194
                COMPLETE_WITH_LIST(list_FROMIN);
 
1195
        }
 
1196
 
 
1197
/* GRANT && REVOKE*/
 
1198
        /* Complete GRANT/REVOKE with a list of privileges */
 
1199
        else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
 
1200
                         pg_strcasecmp(prev_wd, "REVOKE") == 0)
 
1201
        {
 
1202
                static const char *const list_privileg[] =
 
1203
                {"SELECT", "INSERT", "UPDATE", "DELETE", "RULE", "REFERENCES",
 
1204
                "TRIGGER", "CREATE", "TEMPORARY", "EXECUTE", "USAGE", "ALL", NULL};
 
1205
 
 
1206
                COMPLETE_WITH_LIST(list_privileg);
 
1207
        }
 
1208
        /* Complete GRANT/REVOKE <sth> with "ON" */
 
1209
        else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
 
1210
                         pg_strcasecmp(prev2_wd, "REVOKE") == 0)
 
1211
                COMPLETE_WITH_CONST("ON");
 
1212
 
 
1213
        /*
 
1214
         * Complete GRANT/REVOKE <sth> ON with a list of tables, views, sequences,
 
1215
         * and indexes
 
1216
         *
 
1217
         * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via
 
1218
         * UNION; seems to work intuitively
 
1219
         *
 
1220
         * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
 
1221
         * here will only work if the privilege list contains exactly one
 
1222
         * privilege
 
1223
         */
 
1224
        else if ((pg_strcasecmp(prev3_wd, "GRANT") == 0 ||
 
1225
                          pg_strcasecmp(prev3_wd, "REVOKE") == 0) &&
 
1226
                         pg_strcasecmp(prev_wd, "ON") == 0)
 
1227
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv,
 
1228
                                                                   " UNION SELECT 'DATABASE'"
 
1229
                                                                   " UNION SELECT 'FUNCTION'"
 
1230
                                                                   " UNION SELECT 'LANGUAGE'"
 
1231
                                                                   " UNION SELECT 'SCHEMA'"
 
1232
                                                                   " UNION SELECT 'TABLESPACE'");
 
1233
 
 
1234
        /* Complete "GRANT/REVOKE * ON * " with "TO" */
 
1235
        else if ((pg_strcasecmp(prev4_wd, "GRANT") == 0 ||
 
1236
                          pg_strcasecmp(prev4_wd, "REVOKE") == 0) &&
 
1237
                         pg_strcasecmp(prev2_wd, "ON") == 0)
 
1238
        {
 
1239
                if (pg_strcasecmp(prev_wd, "DATABASE") == 0)
 
1240
                        COMPLETE_WITH_QUERY(Query_for_list_of_databases);
 
1241
                else if (pg_strcasecmp(prev_wd, "FUNCTION") == 0)
 
1242
                        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
 
1243
                else if (pg_strcasecmp(prev_wd, "LANGUAGE") == 0)
 
1244
                        COMPLETE_WITH_QUERY(Query_for_list_of_languages);
 
1245
                else if (pg_strcasecmp(prev_wd, "SCHEMA") == 0)
 
1246
                        COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 
1247
                else if (pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
 
1248
                        COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
 
1249
                else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
 
1250
                        COMPLETE_WITH_CONST("TO");
 
1251
                else
 
1252
                        COMPLETE_WITH_CONST("FROM");
 
1253
        }
 
1254
 
 
1255
        /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
 
1256
        else if (pg_strcasecmp(prev3_wd, "ON") == 0 &&
 
1257
                         ((pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
 
1258
                           pg_strcasecmp(prev_wd, "TO") == 0) ||
 
1259
                          (pg_strcasecmp(prev5_wd, "REVOKE") == 0 &&
 
1260
                           pg_strcasecmp(prev_wd, "FROM") == 0)))
 
1261
                COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
 
1262
 
 
1263
/* GROUP BY */
 
1264
        else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
 
1265
                         pg_strcasecmp(prev_wd, "GROUP") == 0)
 
1266
                COMPLETE_WITH_CONST("BY");
 
1267
 
 
1268
/* INSERT */
 
1269
        /* Complete INSERT with "INTO" */
 
1270
        else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
 
1271
                COMPLETE_WITH_CONST("INTO");
 
1272
        /* Complete INSERT INTO with table names */
 
1273
        else if (pg_strcasecmp(prev2_wd, "INSERT") == 0 &&
 
1274
                         pg_strcasecmp(prev_wd, "INTO") == 0)
 
1275
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1276
        /* Complete "INSERT INTO <table> (" with attribute names */
 
1277
        else if (rl_line_buffer[start - 1] == '(' &&
 
1278
                         pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
 
1279
                         pg_strcasecmp(prev2_wd, "INTO") == 0)
 
1280
                COMPLETE_WITH_ATTR(prev_wd);
 
1281
 
 
1282
        /*
 
1283
         * Complete INSERT INTO <table> with "VALUES" or "SELECT" or "DEFAULT
 
1284
         * VALUES"
 
1285
         */
 
1286
        else if (pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
 
1287
                         pg_strcasecmp(prev2_wd, "INTO") == 0)
 
1288
        {
 
1289
                static const char *const list_INSERT[] =
 
1290
                {"DEFAULT VALUES", "SELECT", "VALUES", NULL};
 
1291
 
 
1292
                COMPLETE_WITH_LIST(list_INSERT);
 
1293
        }
 
1294
        /* Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" */
 
1295
        else if (pg_strcasecmp(prev4_wd, "INSERT") == 0 &&
 
1296
                         pg_strcasecmp(prev3_wd, "INTO") == 0 &&
 
1297
                         prev_wd[strlen(prev_wd) - 1] == ')')
 
1298
        {
 
1299
                static const char *const list_INSERT[] =
 
1300
                {"SELECT", "VALUES", NULL};
 
1301
 
 
1302
                COMPLETE_WITH_LIST(list_INSERT);
 
1303
        }
 
1304
 
 
1305
        /* Insert an open parenthesis after "VALUES" */
 
1306
        else if (pg_strcasecmp(prev_wd, "VALUES") == 0 &&
 
1307
                         pg_strcasecmp(prev2_wd, "DEFAULT") != 0)
 
1308
                COMPLETE_WITH_CONST("(");
 
1309
 
 
1310
/* LOCK */
 
1311
        /* Complete LOCK [TABLE] with a list of tables */
 
1312
        else if (pg_strcasecmp(prev_wd, "LOCK") == 0 ||
 
1313
                         (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
 
1314
                          pg_strcasecmp(prev2_wd, "LOCK") == 0))
 
1315
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1316
 
 
1317
        /* For the following, handle the case of a single table only for now */
 
1318
 
 
1319
        /* Complete LOCK [TABLE] <table> with "IN" */
 
1320
        else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 &&
 
1321
                          pg_strcasecmp(prev_wd, "TABLE")) ||
 
1322
                         (pg_strcasecmp(prev2_wd, "TABLE") == 0 &&
 
1323
                          pg_strcasecmp(prev3_wd, "LOCK") == 0))
 
1324
                COMPLETE_WITH_CONST("IN");
 
1325
 
 
1326
        /* Complete LOCK [TABLE] <table> IN with a lock mode */
 
1327
        else if (pg_strcasecmp(prev_wd, "IN") == 0 &&
 
1328
                         (pg_strcasecmp(prev3_wd, "LOCK") == 0 ||
 
1329
                          (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
 
1330
                           pg_strcasecmp(prev4_wd, "LOCK") == 0)))
 
1331
        {
 
1332
                static const char *const lock_modes[] =
 
1333
                {"ACCESS SHARE MODE",
 
1334
                        "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
 
1335
                        "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
 
1336
                        "SHARE ROW EXCLUSIVE MODE",
 
1337
                "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL};
 
1338
 
 
1339
                COMPLETE_WITH_LIST(lock_modes);
 
1340
        }
 
1341
 
 
1342
/* NOTIFY */
 
1343
        else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
 
1344
                COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'");
 
1345
 
 
1346
/* OWNER TO  - complete with available roles*/
 
1347
        else if (pg_strcasecmp(prev2_wd, "OWNER") == 0 &&
 
1348
                         pg_strcasecmp(prev_wd, "TO") == 0)
 
1349
                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
1350
 
 
1351
/* ORDER BY */
 
1352
        else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
 
1353
                         pg_strcasecmp(prev_wd, "ORDER") == 0)
 
1354
                COMPLETE_WITH_CONST("BY");
 
1355
        else if (pg_strcasecmp(prev4_wd, "FROM") == 0 &&
 
1356
                         pg_strcasecmp(prev2_wd, "ORDER") == 0 &&
 
1357
                         pg_strcasecmp(prev_wd, "BY") == 0)
 
1358
                COMPLETE_WITH_ATTR(prev3_wd);
 
1359
 
 
1360
/* PREPARE xx AS */
 
1361
        else if (pg_strcasecmp(prev_wd, "AS") == 0 &&
 
1362
                         pg_strcasecmp(prev3_wd, "PREPARE") == 0)
 
1363
        {
 
1364
                static const char *const list_PREPARE[] =
 
1365
                {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
 
1366
 
 
1367
                COMPLETE_WITH_LIST(list_PREPARE);
 
1368
        }
 
1369
 
 
1370
 
 
1371
/* REINDEX */
 
1372
        else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
 
1373
        {
 
1374
                static const char *const list_REINDEX[] =
 
1375
                {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL};
 
1376
 
 
1377
                COMPLETE_WITH_LIST(list_REINDEX);
 
1378
        }
 
1379
        else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0)
 
1380
        {
 
1381
                if (pg_strcasecmp(prev_wd, "TABLE") == 0)
 
1382
                        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1383
                else if (pg_strcasecmp(prev_wd, "INDEX") == 0)
 
1384
                        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
 
1385
                else if (pg_strcasecmp(prev_wd, "SYSTEM") == 0 ||
 
1386
                                 pg_strcasecmp(prev_wd, "DATABASE") == 0)
 
1387
                        COMPLETE_WITH_QUERY(Query_for_list_of_databases);
 
1388
        }
 
1389
 
 
1390
/* SELECT */
 
1391
        /* naah . . . */
 
1392
 
 
1393
/* SET, RESET, SHOW */
 
1394
        /* Complete with a variable name */
 
1395
        else if ((pg_strcasecmp(prev_wd, "SET") == 0 &&
 
1396
                          pg_strcasecmp(prev3_wd, "UPDATE") != 0) ||
 
1397
                         pg_strcasecmp(prev_wd, "RESET") == 0)
 
1398
                COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
 
1399
        else if (pg_strcasecmp(prev_wd, "SHOW") == 0)
 
1400
                COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
 
1401
        /* Complete "SET TRANSACTION" */
 
1402
        else if ((pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
1403
                          pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
 
1404
                         || (pg_strcasecmp(prev2_wd, "START") == 0
 
1405
                                 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
 
1406
                         || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
 
1407
                                 && pg_strcasecmp(prev_wd, "WORK") == 0)
 
1408
                         || (pg_strcasecmp(prev2_wd, "BEGIN") == 0
 
1409
                                 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0)
 
1410
                         || (pg_strcasecmp(prev4_wd, "SESSION") == 0
 
1411
                                 && pg_strcasecmp(prev3_wd, "CHARACTERISTICS") == 0
 
1412
                                 && pg_strcasecmp(prev2_wd, "AS") == 0
 
1413
                                 && pg_strcasecmp(prev_wd, "TRANSACTION") == 0))
 
1414
        {
 
1415
                static const char *const my_list[] =
 
1416
                {"ISOLATION LEVEL", "READ", NULL};
 
1417
 
 
1418
                COMPLETE_WITH_LIST(my_list);
 
1419
        }
 
1420
        else if ((pg_strcasecmp(prev3_wd, "SET") == 0
 
1421
                          || pg_strcasecmp(prev3_wd, "BEGIN") == 0
 
1422
                          || pg_strcasecmp(prev3_wd, "START") == 0
 
1423
                          || (pg_strcasecmp(prev4_wd, "CHARACTERISTICS") == 0
 
1424
                                  && pg_strcasecmp(prev3_wd, "AS") == 0))
 
1425
                         && (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0
 
1426
                                 || pg_strcasecmp(prev2_wd, "WORK") == 0)
 
1427
                         && pg_strcasecmp(prev_wd, "ISOLATION") == 0)
 
1428
                COMPLETE_WITH_CONST("LEVEL");
 
1429
        else if ((pg_strcasecmp(prev4_wd, "SET") == 0
 
1430
                          || pg_strcasecmp(prev4_wd, "BEGIN") == 0
 
1431
                          || pg_strcasecmp(prev4_wd, "START") == 0
 
1432
                          || pg_strcasecmp(prev4_wd, "AS") == 0)
 
1433
                         && (pg_strcasecmp(prev3_wd, "TRANSACTION") == 0
 
1434
                                 || pg_strcasecmp(prev3_wd, "WORK") == 0)
 
1435
                         && pg_strcasecmp(prev2_wd, "ISOLATION") == 0
 
1436
                         && pg_strcasecmp(prev_wd, "LEVEL") == 0)
 
1437
        {
 
1438
                static const char *const my_list[] =
 
1439
                {"READ", "REPEATABLE", "SERIALIZABLE", NULL};
 
1440
 
 
1441
                COMPLETE_WITH_LIST(my_list);
 
1442
        }
 
1443
        else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
 
1444
                          pg_strcasecmp(prev4_wd, "WORK") == 0) &&
 
1445
                         pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
 
1446
                         pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
 
1447
                         pg_strcasecmp(prev_wd, "READ") == 0)
 
1448
        {
 
1449
                static const char *const my_list[] =
 
1450
                {"UNCOMMITTED", "COMMITTED", NULL};
 
1451
 
 
1452
                COMPLETE_WITH_LIST(my_list);
 
1453
        }
 
1454
        else if ((pg_strcasecmp(prev4_wd, "TRANSACTION") == 0 ||
 
1455
                          pg_strcasecmp(prev4_wd, "WORK") == 0) &&
 
1456
                         pg_strcasecmp(prev3_wd, "ISOLATION") == 0 &&
 
1457
                         pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
 
1458
                         pg_strcasecmp(prev_wd, "REPEATABLE") == 0)
 
1459
                COMPLETE_WITH_CONST("READ");
 
1460
        else if ((pg_strcasecmp(prev3_wd, "SET") == 0 ||
 
1461
                          pg_strcasecmp(prev3_wd, "BEGIN") == 0 ||
 
1462
                          pg_strcasecmp(prev3_wd, "START") == 0 ||
 
1463
                          pg_strcasecmp(prev3_wd, "AS") == 0) &&
 
1464
                         (pg_strcasecmp(prev2_wd, "TRANSACTION") == 0 ||
 
1465
                          pg_strcasecmp(prev2_wd, "WORK") == 0) &&
 
1466
                         pg_strcasecmp(prev_wd, "READ") == 0)
 
1467
        {
 
1468
                static const char *const my_list[] =
 
1469
                {"ONLY", "WRITE", NULL};
 
1470
 
 
1471
                COMPLETE_WITH_LIST(my_list);
 
1472
        }
 
1473
        /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
 
1474
        else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
 
1475
                         pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
 
1476
        {
 
1477
                static const char *const constraint_list[] =
 
1478
                {"DEFERRED", "IMMEDIATE", NULL};
 
1479
 
 
1480
                COMPLETE_WITH_LIST(constraint_list);
 
1481
        }
 
1482
        /* Complete SET ROLE */
 
1483
        else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
1484
                         pg_strcasecmp(prev_wd, "ROLE") == 0)
 
1485
                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
1486
        /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
 
1487
        else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
1488
                         pg_strcasecmp(prev_wd, "SESSION") == 0)
 
1489
        {
 
1490
                static const char *const my_list[] =
 
1491
                {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL};
 
1492
 
 
1493
                COMPLETE_WITH_LIST(my_list);
 
1494
        }
 
1495
        /* Complete SET SESSION AUTHORIZATION with username */
 
1496
        else if (pg_strcasecmp(prev3_wd, "SET") == 0
 
1497
                         && pg_strcasecmp(prev2_wd, "SESSION") == 0
 
1498
                         && pg_strcasecmp(prev_wd, "AUTHORIZATION") == 0)
 
1499
                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
1500
        /* Complete RESET SESSION with AUTHORIZATION */
 
1501
        else if (pg_strcasecmp(prev2_wd, "RESET") == 0 &&
 
1502
                         pg_strcasecmp(prev_wd, "SESSION") == 0)
 
1503
                COMPLETE_WITH_CONST("AUTHORIZATION");
 
1504
        /* Complete SET <var> with "TO" */
 
1505
        else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
1506
                         pg_strcasecmp(prev4_wd, "UPDATE") != 0 &&
 
1507
                         pg_strcasecmp(prev_wd, "TABLESPACE") != 0 &&
 
1508
                         pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
 
1509
                COMPLETE_WITH_CONST("TO");
 
1510
        /* Suggest possible variable values */
 
1511
        else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
 
1512
                         (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
 
1513
        {
 
1514
                if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
 
1515
                {
 
1516
                        static const char *const my_list[] =
 
1517
                        {"ISO", "SQL", "Postgres", "German",
 
1518
                                "YMD", "DMY", "MDY",
 
1519
                                "US", "European", "NonEuropean",
 
1520
                        "DEFAULT", NULL};
 
1521
 
 
1522
                        COMPLETE_WITH_LIST(my_list);
 
1523
                }
 
1524
                else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
 
1525
                {
 
1526
                        static const char *const my_list[] =
 
1527
                        {"ON", "OFF", "DEFAULT", NULL};
 
1528
 
 
1529
                        COMPLETE_WITH_LIST(my_list);
 
1530
                }
 
1531
                else
 
1532
                {
 
1533
                        static const char *const my_list[] =
 
1534
                        {"DEFAULT", NULL};
 
1535
 
 
1536
                        COMPLETE_WITH_LIST(my_list);
 
1537
                }
 
1538
        }
 
1539
 
 
1540
/* START TRANSACTION */
 
1541
        else if (pg_strcasecmp(prev_wd, "START") == 0)
 
1542
                COMPLETE_WITH_CONST("TRANSACTION");
 
1543
 
 
1544
/* TRUNCATE */
 
1545
        else if (pg_strcasecmp(prev_wd, "TRUNCATE") == 0)
 
1546
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1547
 
 
1548
/* UNLISTEN */
 
1549
        else if (pg_strcasecmp(prev_wd, "UNLISTEN") == 0)
 
1550
                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 '*'");
 
1551
 
 
1552
/* UPDATE */
 
1553
        /* If prev. word is UPDATE suggest a list of tables */
 
1554
        else if (pg_strcasecmp(prev_wd, "UPDATE") == 0)
 
1555
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1556
        /* Complete UPDATE <table> with "SET" */
 
1557
        else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0)
 
1558
                COMPLETE_WITH_CONST("SET");
 
1559
 
 
1560
        /*
 
1561
         * If the previous word is SET (and it wasn't caught above as the _first_
 
1562
         * word) the word before it was (hopefully) a table name and we'll now
 
1563
         * make a list of attributes.
 
1564
         */
 
1565
        else if (pg_strcasecmp(prev_wd, "SET") == 0)
 
1566
                COMPLETE_WITH_ATTR(prev2_wd);
 
1567
 
 
1568
/* UPDATE xx SET yy = */
 
1569
        else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
 
1570
                         pg_strcasecmp(prev4_wd, "UPDATE") == 0)
 
1571
                COMPLETE_WITH_CONST("=");
 
1572
 
 
1573
/*
 
1574
 * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
 
1575
 * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
 
1576
 */
 
1577
        else if (pg_strcasecmp(prev_wd, "VACUUM") == 0)
 
1578
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 
1579
                                                                   " UNION SELECT 'FULL'"
 
1580
                                                                   " UNION SELECT 'FREEZE'"
 
1581
                                                                   " UNION SELECT 'ANALYZE'"
 
1582
                                                                   " UNION SELECT 'VERBOSE'");
 
1583
        else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
 
1584
                         (pg_strcasecmp(prev_wd, "FULL") == 0 ||
 
1585
                          pg_strcasecmp(prev_wd, "FREEZE") == 0))
 
1586
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 
1587
                                                                   " UNION SELECT 'ANALYZE'"
 
1588
                                                                   " UNION SELECT 'VERBOSE'");
 
1589
        else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
 
1590
                         pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
 
1591
                         (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
 
1592
                          pg_strcasecmp(prev2_wd, "FREEZE") == 0))
 
1593
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 
1594
                                                                   " UNION SELECT 'VERBOSE'");
 
1595
        else if (pg_strcasecmp(prev3_wd, "VACUUM") == 0 &&
 
1596
                         pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
 
1597
                         (pg_strcasecmp(prev2_wd, "FULL") == 0 ||
 
1598
                          pg_strcasecmp(prev2_wd, "FREEZE") == 0))
 
1599
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 
1600
                                                                   " UNION SELECT 'ANALYZE'");
 
1601
        else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
 
1602
                         pg_strcasecmp(prev_wd, "VERBOSE") == 0)
 
1603
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 
1604
                                                                   " UNION SELECT 'ANALYZE'");
 
1605
        else if (pg_strcasecmp(prev2_wd, "VACUUM") == 0 &&
 
1606
                         pg_strcasecmp(prev_wd, "ANALYZE") == 0)
 
1607
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 
1608
                                                                   " UNION SELECT 'VERBOSE'");
 
1609
        else if ((pg_strcasecmp(prev_wd, "ANALYZE") == 0 &&
 
1610
                          pg_strcasecmp(prev2_wd, "VERBOSE") == 0) ||
 
1611
                         (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
 
1612
                          pg_strcasecmp(prev2_wd, "ANALYZE") == 0))
 
1613
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1614
 
 
1615
/* ANALYZE */
 
1616
        /* If the previous word is ANALYZE, produce list of tables */
 
1617
        else if (pg_strcasecmp(prev_wd, "ANALYZE") == 0)
 
1618
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1619
 
 
1620
/* WHERE */
 
1621
        /* Simple case of the word before the where being the table name */
 
1622
        else if (pg_strcasecmp(prev_wd, "WHERE") == 0)
 
1623
                COMPLETE_WITH_ATTR(prev2_wd);
 
1624
 
 
1625
/* ... FROM ... */
 
1626
/* TODO: also include SRF ? */
 
1627
        else if (pg_strcasecmp(prev_wd, "FROM") == 0 &&
 
1628
                         pg_strcasecmp(prev3_wd, "COPY") != 0 &&
 
1629
                         pg_strcasecmp(prev3_wd, "\\copy") != 0)
 
1630
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
 
1631
 
 
1632
 
 
1633
/* Backslash commands */
 
1634
/* TODO:  \dc \dd \dl */
 
1635
        else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
 
1636
                COMPLETE_WITH_QUERY(Query_for_list_of_databases);
 
1637
        else if (strcmp(prev_wd, "\\d") == 0 || strcmp(prev_wd, "\\d+") == 0)
 
1638
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
 
1639
        else if (strcmp(prev_wd, "\\da") == 0)
 
1640
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
 
1641
        else if (strcmp(prev_wd, "\\db") == 0)
 
1642
                COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
 
1643
        else if (strcmp(prev_wd, "\\dD") == 0)
 
1644
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
 
1645
        else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0)
 
1646
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
 
1647
        else if (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0)
 
1648
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
 
1649
        else if (strcmp(prev_wd, "\\dn") == 0)
 
1650
                COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 
1651
        else if (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 0)
 
1652
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
 
1653
        else if (strcmp(prev_wd, "\\ds") == 0 || strcmp(prev_wd, "\\ds+") == 0)
 
1654
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
 
1655
        else if (strcmp(prev_wd, "\\dS") == 0 || strcmp(prev_wd, "\\dS+") == 0)
 
1656
                COMPLETE_WITH_QUERY(Query_for_list_of_system_relations);
 
1657
        else if (strcmp(prev_wd, "\\dt") == 0 || strcmp(prev_wd, "\\dt+") == 0)
 
1658
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
1659
        else if (strcmp(prev_wd, "\\dT") == 0 || strcmp(prev_wd, "\\dT+") == 0)
 
1660
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
 
1661
        else if (strcmp(prev_wd, "\\du") == 0)
 
1662
                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
1663
        else if (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
 
1664
                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 
1665
        else if (strcmp(prev_wd, "\\encoding") == 0)
 
1666
                COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
 
1667
        else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
 
1668
                COMPLETE_WITH_LIST(sql_commands);
 
1669
        else if (strcmp(prev_wd, "\\password") == 0)
 
1670
                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
1671
        else if (strcmp(prev_wd, "\\pset") == 0)
 
1672
        {
 
1673
                static const char *const my_list[] =
 
1674
                {"format", "border", "expanded",
 
1675
                        "null", "fieldsep", "tuples_only", "title", "tableattr", "pager",
 
1676
                "recordsep", NULL};
 
1677
 
 
1678
                COMPLETE_WITH_LIST(my_list);
 
1679
        }
 
1680
        else if (strcmp(prev_wd, "\\cd") == 0 ||
 
1681
                         strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
 
1682
                         strcmp(prev_wd, "\\g") == 0 ||
 
1683
                  strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
 
1684
                         strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
 
1685
                         strcmp(prev_wd, "\\s") == 0 ||
 
1686
                         strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
 
1687
                )
 
1688
                matches = complete_filename();
 
1689
 
 
1690
 
 
1691
        /*
 
1692
         * Finally, we look through the list of "things", such as TABLE, INDEX and
 
1693
         * check if that was the previous word. If so, execute the query to get a
 
1694
         * list of them.
 
1695
         */
 
1696
        else
 
1697
        {
 
1698
                int                     i;
 
1699
 
 
1700
                for (i = 0; words_after_create[i].name; i++)
 
1701
                {
 
1702
                        if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)
 
1703
                        {
 
1704
                                if (words_after_create[i].query)
 
1705
                                        COMPLETE_WITH_QUERY(words_after_create[i].query);
 
1706
                                else if (words_after_create[i].squery)
 
1707
                                        COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
 
1708
                                                                                           NULL);
 
1709
                                break;
 
1710
                        }
 
1711
                }
 
1712
        }
 
1713
 
 
1714
        /*
 
1715
         * If we still don't have anything to match we have to fabricate some sort
 
1716
         * of default list. If we were to just return NULL, readline automatically
 
1717
         * attempts filename completion, and that's usually no good.
 
1718
         */
 
1719
        if (matches == NULL)
 
1720
        {
 
1721
                COMPLETE_WITH_CONST("");
 
1722
#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
 
1723
                rl_completion_append_character = '\0';
 
1724
#endif
 
1725
        }
 
1726
 
 
1727
        /* free storage */
 
1728
        free(prev_wd);
 
1729
        free(prev2_wd);
 
1730
        free(prev3_wd);
 
1731
        free(prev4_wd);
 
1732
        free(prev5_wd);
 
1733
 
 
1734
        /* Return our Grand List O' Matches */
 
1735
        return matches;
 
1736
}
 
1737
 
 
1738
 
 
1739
 
 
1740
 
 
1741