~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/bin/psql/tab-complete.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

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