/Users/deen/code/yugabyte-db/src/postgres/src/bin/psql/tab-complete.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * psql - the PostgreSQL interactive terminal |
3 | | * |
4 | | * Copyright (c) 2000-2018, 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. |
28 | | * |
29 | | * BUGS: |
30 | | * - Quotes, parentheses, and other funny characters are not handled |
31 | | * all that gracefully. |
32 | | *---------------------------------------------------------------------- |
33 | | */ |
34 | | |
35 | | #include "postgres_fe.h" |
36 | | #include "tab-complete.h" |
37 | | #include "input.h" |
38 | | |
39 | | /* If we don't have this, we might as well forget about the whole thing: */ |
40 | | #ifdef USE_READLINE |
41 | | |
42 | | #include <ctype.h> |
43 | | |
44 | | #include "catalog/pg_class_d.h" |
45 | | |
46 | | #include "libpq-fe.h" |
47 | | #include "pqexpbuffer.h" |
48 | | #include "common.h" |
49 | | #include "settings.h" |
50 | | #include "stringutils.h" |
51 | | |
52 | | #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION |
53 | 0 | #define filename_completion_function rl_filename_completion_function |
54 | | #else |
55 | | /* missing in some header files */ |
56 | | extern char *filename_completion_function(); |
57 | | #endif |
58 | | |
59 | | #ifdef HAVE_RL_COMPLETION_MATCHES |
60 | 0 | #define completion_matches rl_completion_matches |
61 | | #endif |
62 | | |
63 | 0 | #define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) |
64 | | |
65 | | /* word break characters */ |
66 | 0 | #define WORD_BREAKS "\t\n@$><=;|&{() " |
67 | | |
68 | | /* |
69 | | * Since readline doesn't let us pass any state through to the tab completion |
70 | | * callback, we have to use this global variable to let get_previous_words() |
71 | | * get at the previous lines of the current command. Ick. |
72 | | */ |
73 | | PQExpBuffer tab_completion_query_buf = NULL; |
74 | | |
75 | | /* |
76 | | * In some situations, the query to find out what names are available to |
77 | | * complete with must vary depending on server version. We handle this by |
78 | | * storing a list of queries, each tagged with the minimum server version |
79 | | * it will work for. Each list must be stored in descending server version |
80 | | * order, so that the first satisfactory query is the one to use. |
81 | | * |
82 | | * When the query string is otherwise constant, an array of VersionedQuery |
83 | | * suffices. Terminate the array with an entry having min_server_version = 0. |
84 | | * That entry's query string can be a query that works in all supported older |
85 | | * server versions, or NULL to give up and do no completion. |
86 | | */ |
87 | | typedef struct VersionedQuery |
88 | | { |
89 | | int min_server_version; |
90 | | const char *query; |
91 | | } VersionedQuery; |
92 | | |
93 | | /* |
94 | | * This struct is used to define "schema queries", which are custom-built |
95 | | * to obtain possibly-schema-qualified names of database objects. There is |
96 | | * enough similarity in the structure that we don't want to repeat it each |
97 | | * time. So we put the components of each query into this struct and |
98 | | * assemble them with the common boilerplate in _complete_from_query(). |
99 | | * |
100 | | * As with VersionedQuery, we can use an array of these if the query details |
101 | | * must vary across versions. |
102 | | */ |
103 | | typedef struct SchemaQuery |
104 | | { |
105 | | /* |
106 | | * If not zero, minimum server version this struct applies to. If not |
107 | | * zero, there should be a following struct with a smaller minimum server |
108 | | * version; use catname == NULL in the last entry if we should do nothing. |
109 | | */ |
110 | | int min_server_version; |
111 | | |
112 | | /* |
113 | | * Name of catalog or catalogs to be queried, with alias, eg. |
114 | | * "pg_catalog.pg_class c". Note that "pg_namespace n" will be added. |
115 | | */ |
116 | | const char *catname; |
117 | | |
118 | | /* |
119 | | * Selection condition --- only rows meeting this condition are candidates |
120 | | * to display. If catname mentions multiple tables, include the necessary |
121 | | * join condition here. For example, this might look like "c.relkind = " |
122 | | * CppAsString2(RELKIND_RELATION). Write NULL (not an empty string) if |
123 | | * not needed. |
124 | | */ |
125 | | const char *selcondition; |
126 | | |
127 | | /* |
128 | | * Visibility condition --- which rows are visible without schema |
129 | | * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)". |
130 | | */ |
131 | | const char *viscondition; |
132 | | |
133 | | /* |
134 | | * Namespace --- name of field to join to pg_namespace.oid. For example, |
135 | | * "c.relnamespace". |
136 | | */ |
137 | | const char *namespace; |
138 | | |
139 | | /* |
140 | | * Result --- the appropriately-quoted name to return, in the case of an |
141 | | * unqualified name. For example, "pg_catalog.quote_ident(c.relname)". |
142 | | */ |
143 | | const char *result; |
144 | | |
145 | | /* |
146 | | * In some cases a different result must be used for qualified names. |
147 | | * Enter that here, or write NULL if result can be used. |
148 | | */ |
149 | | const char *qualresult; |
150 | | } SchemaQuery; |
151 | | |
152 | | |
153 | | /* Store maximum number of records we want from database queries |
154 | | * (implemented via SELECT ... LIMIT xx). |
155 | | */ |
156 | | static int completion_max_records; |
157 | | |
158 | | /* |
159 | | * Communication variables set by COMPLETE_WITH_FOO macros and then used by |
160 | | * the completion callback functions. Ugly but there is no better way. |
161 | | */ |
162 | | static const char *completion_charp; /* to pass a string */ |
163 | | static const char *const *completion_charpp; /* to pass a list of strings */ |
164 | | static const char *completion_info_charp; /* to pass a second string */ |
165 | | static const char *completion_info_charp2; /* to pass a third string */ |
166 | | static const VersionedQuery *completion_vquery; /* to pass a VersionedQuery */ |
167 | | static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ |
168 | | static bool completion_case_sensitive; /* completion is case sensitive */ |
169 | | |
170 | | /* |
171 | | * A few macros to ease typing. You can use these to complete the given |
172 | | * string with |
173 | | * 1) The results from a query you pass it. (Perhaps one of those below?) |
174 | | * We support both simple and versioned queries. |
175 | | * 2) The results from a schema query you pass it. |
176 | | * We support both simple and versioned schema queries. |
177 | | * 3) The items from a null-pointer-terminated list (with or without |
178 | | * case-sensitive comparison; see also COMPLETE_WITH_LISTn, below). |
179 | | * 4) A string constant. |
180 | | * 5) The list of attributes of the given table (possibly schema-qualified). |
181 | | * 6/ The list of arguments to the given function (possibly schema-qualified). |
182 | | */ |
183 | 0 | #define COMPLETE_WITH_QUERY(query) \ |
184 | 0 | do { \ |
185 | 0 | completion_charp = query; \ |
186 | 0 | matches = completion_matches(text, complete_from_query); \ |
187 | 0 | } while (0) |
188 | | |
189 | 0 | #define COMPLETE_WITH_VERSIONED_QUERY(query) \ |
190 | 0 | do { \ |
191 | 0 | completion_vquery = query; \ |
192 | 0 | matches = completion_matches(text, complete_from_versioned_query); \ |
193 | 0 | } while (0) |
194 | | |
195 | 0 | #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ |
196 | 0 | do { \ |
197 | 0 | completion_squery = &(query); \ |
198 | 0 | completion_charp = addon; \ |
199 | 0 | matches = completion_matches(text, complete_from_schema_query); \ |
200 | 0 | } while (0) |
201 | | |
202 | 0 | #define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \ |
203 | 0 | do { \ |
204 | 0 | completion_squery = query; \ |
205 | 0 | completion_vquery = addon; \ |
206 | 0 | matches = completion_matches(text, complete_from_versioned_schema_query); \ |
207 | 0 | } while (0) |
208 | | |
209 | 0 | #define COMPLETE_WITH_LIST_CS(list) \ |
210 | 0 | do { \ |
211 | 0 | completion_charpp = list; \ |
212 | 0 | completion_case_sensitive = true; \ |
213 | 0 | matches = completion_matches(text, complete_from_list); \ |
214 | 0 | } while (0) |
215 | | |
216 | 0 | #define COMPLETE_WITH_LIST(list) \ |
217 | 0 | do { \ |
218 | 0 | completion_charpp = list; \ |
219 | 0 | completion_case_sensitive = false; \ |
220 | 0 | matches = completion_matches(text, complete_from_list); \ |
221 | 0 | } while (0) |
222 | | |
223 | 0 | #define COMPLETE_WITH_CONST(string) \ |
224 | 0 | do { \ |
225 | 0 | completion_charp = string; \ |
226 | 0 | completion_case_sensitive = false; \ |
227 | 0 | matches = completion_matches(text, complete_from_const); \ |
228 | 0 | } while (0) |
229 | | |
230 | 0 | #define COMPLETE_WITH_ATTR(relation, addon) \ |
231 | 0 | do { \ |
232 | 0 | char *_completion_schema; \ |
233 | 0 | char *_completion_table; \ |
234 | 0 | \ |
235 | 0 | _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \ |
236 | 0 | false, false, pset.encoding); \ |
237 | 0 | (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \ |
238 | 0 | false, false, pset.encoding); \ |
239 | 0 | _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \ |
240 | 0 | false, false, pset.encoding); \ |
241 | 0 | if (_completion_table == NULL) \ |
242 | 0 | { \ |
243 | 0 | completion_charp = Query_for_list_of_attributes addon; \ |
244 | 0 | completion_info_charp = relation; \ |
245 | 0 | } \ |
246 | 0 | else \ |
247 | 0 | { \ |
248 | 0 | completion_charp = Query_for_list_of_attributes_with_schema addon; \ |
249 | 0 | completion_info_charp = _completion_table; \ |
250 | 0 | completion_info_charp2 = _completion_schema; \ |
251 | 0 | } \ |
252 | 0 | matches = completion_matches(text, complete_from_query); \ |
253 | 0 | } while (0) |
254 | | |
255 | 0 | #define COMPLETE_WITH_ENUM_VALUE(type) \ |
256 | 0 | do { \ |
257 | 0 | char *_completion_schema; \ |
258 | 0 | char *_completion_type; \ |
259 | 0 | \ |
260 | 0 | _completion_schema = strtokx(type, " \t\n\r", ".", "\"", 0, \ |
261 | 0 | false, false, pset.encoding); \ |
262 | 0 | (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \ |
263 | 0 | false, false, pset.encoding); \ |
264 | 0 | _completion_type = strtokx(NULL, " \t\n\r", ".", "\"", 0, \ |
265 | 0 | false, false, pset.encoding); \ |
266 | 0 | if (_completion_type == NULL)\ |
267 | 0 | { \ |
268 | 0 | completion_charp = Query_for_list_of_enum_values; \ |
269 | 0 | completion_info_charp = type; \ |
270 | 0 | } \ |
271 | 0 | else \ |
272 | 0 | { \ |
273 | 0 | completion_charp = Query_for_list_of_enum_values_with_schema; \ |
274 | 0 | completion_info_charp = _completion_type; \ |
275 | 0 | completion_info_charp2 = _completion_schema; \ |
276 | 0 | } \ |
277 | 0 | matches = completion_matches(text, complete_from_query); \ |
278 | 0 | } while (0) |
279 | | |
280 | 0 | #define COMPLETE_WITH_FUNCTION_ARG(function) \ |
281 | 0 | do { \ |
282 | 0 | char *_completion_schema; \ |
283 | 0 | char *_completion_function; \ |
284 | 0 | \ |
285 | 0 | _completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \ |
286 | 0 | false, false, pset.encoding); \ |
287 | 0 | (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \ |
288 | 0 | false, false, pset.encoding); \ |
289 | 0 | _completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \ |
290 | 0 | false, false, pset.encoding); \ |
291 | 0 | if (_completion_function == NULL) \ |
292 | 0 | { \ |
293 | 0 | completion_charp = Query_for_list_of_arguments; \ |
294 | 0 | completion_info_charp = function; \ |
295 | 0 | } \ |
296 | 0 | else \ |
297 | 0 | { \ |
298 | 0 | completion_charp = Query_for_list_of_arguments_with_schema; \ |
299 | 0 | completion_info_charp = _completion_function; \ |
300 | 0 | completion_info_charp2 = _completion_schema; \ |
301 | 0 | } \ |
302 | 0 | matches = completion_matches(text, complete_from_query); \ |
303 | 0 | } while (0) |
304 | | |
305 | | /* |
306 | | * These macros simplify use of COMPLETE_WITH_LIST for short, fixed lists. |
307 | | * There is no COMPLETE_WITH_LIST1; use COMPLETE_WITH_CONST for that case. |
308 | | */ |
309 | 0 | #define COMPLETE_WITH_LIST2(s1, s2) \ |
310 | 0 | do { \ |
311 | 0 | static const char *const list[] = { s1, s2, NULL }; \ |
312 | 0 | COMPLETE_WITH_LIST(list); \ |
313 | 0 | } while (0) |
314 | | |
315 | 0 | #define COMPLETE_WITH_LIST3(s1, s2, s3) \ |
316 | 0 | do { \ |
317 | 0 | static const char *const list[] = { s1, s2, s3, NULL }; \ |
318 | 0 | COMPLETE_WITH_LIST(list); \ |
319 | 0 | } while (0) |
320 | | |
321 | 0 | #define COMPLETE_WITH_LIST4(s1, s2, s3, s4) \ |
322 | 0 | do { \ |
323 | 0 | static const char *const list[] = { s1, s2, s3, s4, NULL }; \ |
324 | 0 | COMPLETE_WITH_LIST(list); \ |
325 | 0 | } while (0) |
326 | | |
327 | 0 | #define COMPLETE_WITH_LIST5(s1, s2, s3, s4, s5) \ |
328 | 0 | do { \ |
329 | 0 | static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \ |
330 | 0 | COMPLETE_WITH_LIST(list); \ |
331 | 0 | } while (0) |
332 | | |
333 | 0 | #define COMPLETE_WITH_LIST6(s1, s2, s3, s4, s5, s6) \ |
334 | 0 | do { \ |
335 | 0 | static const char *const list[] = { s1, s2, s3, s4, s5, s6, NULL }; \ |
336 | 0 | COMPLETE_WITH_LIST(list); \ |
337 | 0 | } while (0) |
338 | | |
339 | 0 | #define COMPLETE_WITH_LIST7(s1, s2, s3, s4, s5, s6, s7) \ |
340 | 0 | do { \ |
341 | 0 | static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, NULL }; \ |
342 | 0 | COMPLETE_WITH_LIST(list); \ |
343 | 0 | } while (0) |
344 | | |
345 | 0 | #define COMPLETE_WITH_LIST8(s1, s2, s3, s4, s5, s6, s7, s8) \ |
346 | 0 | do { \ |
347 | 0 | static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, NULL }; \ |
348 | 0 | COMPLETE_WITH_LIST(list); \ |
349 | 0 | } while (0) |
350 | | |
351 | 0 | #define COMPLETE_WITH_LIST9(s1, s2, s3, s4, s5, s6, s7, s8, s9) \ |
352 | 0 | do { \ |
353 | 0 | static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, NULL }; \ |
354 | 0 | COMPLETE_WITH_LIST(list); \ |
355 | 0 | } while (0) |
356 | | |
357 | 0 | #define COMPLETE_WITH_LIST10(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10) \ |
358 | 0 | do { \ |
359 | 0 | static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, NULL }; \ |
360 | 0 | COMPLETE_WITH_LIST(list); \ |
361 | 0 | } while (0) |
362 | | |
363 | | /* |
364 | | * Likewise for COMPLETE_WITH_LIST_CS. |
365 | | */ |
366 | 0 | #define COMPLETE_WITH_LIST_CS2(s1, s2) \ |
367 | 0 | do { \ |
368 | 0 | static const char *const list[] = { s1, s2, NULL }; \ |
369 | 0 | COMPLETE_WITH_LIST_CS(list); \ |
370 | 0 | } while (0) |
371 | | |
372 | 0 | #define COMPLETE_WITH_LIST_CS3(s1, s2, s3) \ |
373 | 0 | do { \ |
374 | 0 | static const char *const list[] = { s1, s2, s3, NULL }; \ |
375 | 0 | COMPLETE_WITH_LIST_CS(list); \ |
376 | 0 | } while (0) |
377 | | |
378 | 0 | #define COMPLETE_WITH_LIST_CS4(s1, s2, s3, s4) \ |
379 | 0 | do { \ |
380 | 0 | static const char *const list[] = { s1, s2, s3, s4, NULL }; \ |
381 | 0 | COMPLETE_WITH_LIST_CS(list); \ |
382 | 0 | } while (0) |
383 | | |
384 | | #define COMPLETE_WITH_LIST_CS5(s1, s2, s3, s4, s5) \ |
385 | | do { \ |
386 | | static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \ |
387 | | COMPLETE_WITH_LIST_CS(list); \ |
388 | | } while (0) |
389 | | |
390 | | /* |
391 | | * Assembly instructions for schema queries |
392 | | */ |
393 | | |
394 | | static const SchemaQuery Query_for_list_of_aggregates[] = { |
395 | | { |
396 | | /* min_server_version */ |
397 | | 110000, |
398 | | /* catname */ |
399 | | "pg_catalog.pg_proc p", |
400 | | /* selcondition */ |
401 | | "p.prokind = 'a'", |
402 | | /* viscondition */ |
403 | | "pg_catalog.pg_function_is_visible(p.oid)", |
404 | | /* namespace */ |
405 | | "p.pronamespace", |
406 | | /* result */ |
407 | | "pg_catalog.quote_ident(p.proname)", |
408 | | /* qualresult */ |
409 | | NULL |
410 | | }, |
411 | | { |
412 | | /* min_server_version */ |
413 | | 0, |
414 | | /* catname */ |
415 | | "pg_catalog.pg_proc p", |
416 | | /* selcondition */ |
417 | | "p.proisagg", |
418 | | /* viscondition */ |
419 | | "pg_catalog.pg_function_is_visible(p.oid)", |
420 | | /* namespace */ |
421 | | "p.pronamespace", |
422 | | /* result */ |
423 | | "pg_catalog.quote_ident(p.proname)", |
424 | | /* qualresult */ |
425 | | NULL |
426 | | } |
427 | | }; |
428 | | |
429 | | static const SchemaQuery Query_for_list_of_datatypes = { |
430 | | /* min_server_version */ |
431 | | 0, |
432 | | /* catname */ |
433 | | "pg_catalog.pg_type t", |
434 | | /* selcondition --- ignore table rowtypes and array types */ |
435 | | "(t.typrelid = 0 " |
436 | | " OR (SELECT c.relkind = " CppAsString2(RELKIND_COMPOSITE_TYPE) |
437 | | " FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) " |
438 | | "AND t.typname !~ '^_'", |
439 | | /* viscondition */ |
440 | | "pg_catalog.pg_type_is_visible(t.oid)", |
441 | | /* namespace */ |
442 | | "t.typnamespace", |
443 | | /* result */ |
444 | | "pg_catalog.format_type(t.oid, NULL)", |
445 | | /* qualresult */ |
446 | | "pg_catalog.quote_ident(t.typname)" |
447 | | }; |
448 | | |
449 | | static const SchemaQuery Query_for_list_of_domains = { |
450 | | /* min_server_version */ |
451 | | 0, |
452 | | /* catname */ |
453 | | "pg_catalog.pg_type t", |
454 | | /* selcondition */ |
455 | | "t.typtype = 'd'", |
456 | | /* viscondition */ |
457 | | "pg_catalog.pg_type_is_visible(t.oid)", |
458 | | /* namespace */ |
459 | | "t.typnamespace", |
460 | | /* result */ |
461 | | "pg_catalog.quote_ident(t.typname)", |
462 | | /* qualresult */ |
463 | | NULL |
464 | | }; |
465 | | |
466 | | /* Note: this intentionally accepts aggregates as well as plain functions */ |
467 | | static const SchemaQuery Query_for_list_of_functions[] = { |
468 | | { |
469 | | /* min_server_version */ |
470 | | 110000, |
471 | | /* catname */ |
472 | | "pg_catalog.pg_proc p", |
473 | | /* selcondition */ |
474 | | "p.prokind != 'p'", |
475 | | /* viscondition */ |
476 | | "pg_catalog.pg_function_is_visible(p.oid)", |
477 | | /* namespace */ |
478 | | "p.pronamespace", |
479 | | /* result */ |
480 | | "pg_catalog.quote_ident(p.proname)", |
481 | | /* qualresult */ |
482 | | NULL |
483 | | }, |
484 | | { |
485 | | /* min_server_version */ |
486 | | 0, |
487 | | /* catname */ |
488 | | "pg_catalog.pg_proc p", |
489 | | /* selcondition */ |
490 | | NULL, |
491 | | /* viscondition */ |
492 | | "pg_catalog.pg_function_is_visible(p.oid)", |
493 | | /* namespace */ |
494 | | "p.pronamespace", |
495 | | /* result */ |
496 | | "pg_catalog.quote_ident(p.proname)", |
497 | | /* qualresult */ |
498 | | NULL |
499 | | } |
500 | | }; |
501 | | |
502 | | static const SchemaQuery Query_for_list_of_indexes = { |
503 | | /* min_server_version */ |
504 | | 0, |
505 | | /* catname */ |
506 | | "pg_catalog.pg_class c", |
507 | | /* selcondition */ |
508 | | "c.relkind IN (" CppAsString2(RELKIND_INDEX) ", " |
509 | | CppAsString2(RELKIND_PARTITIONED_INDEX) ")", |
510 | | /* viscondition */ |
511 | | "pg_catalog.pg_table_is_visible(c.oid)", |
512 | | /* namespace */ |
513 | | "c.relnamespace", |
514 | | /* result */ |
515 | | "pg_catalog.quote_ident(c.relname)", |
516 | | /* qualresult */ |
517 | | NULL |
518 | | }; |
519 | | |
520 | | static const SchemaQuery Query_for_list_of_procedures[] = { |
521 | | { |
522 | | /* min_server_version */ |
523 | | 110000, |
524 | | /* catname */ |
525 | | "pg_catalog.pg_proc p", |
526 | | /* selcondition */ |
527 | | "p.prokind = 'p'", |
528 | | /* viscondition */ |
529 | | "pg_catalog.pg_function_is_visible(p.oid)", |
530 | | /* namespace */ |
531 | | "p.pronamespace", |
532 | | /* result */ |
533 | | "pg_catalog.quote_ident(p.proname)", |
534 | | /* qualresult */ |
535 | | NULL |
536 | | }, |
537 | | {0, NULL} |
538 | | }; |
539 | | |
540 | | static const SchemaQuery Query_for_list_of_routines = { |
541 | | /* min_server_version */ |
542 | | 0, |
543 | | /* catname */ |
544 | | "pg_catalog.pg_proc p", |
545 | | /* selcondition */ |
546 | | NULL, |
547 | | /* viscondition */ |
548 | | "pg_catalog.pg_function_is_visible(p.oid)", |
549 | | /* namespace */ |
550 | | "p.pronamespace", |
551 | | /* result */ |
552 | | "pg_catalog.quote_ident(p.proname)", |
553 | | /* qualresult */ |
554 | | NULL |
555 | | }; |
556 | | |
557 | | static const SchemaQuery Query_for_list_of_sequences = { |
558 | | /* min_server_version */ |
559 | | 0, |
560 | | /* catname */ |
561 | | "pg_catalog.pg_class c", |
562 | | /* selcondition */ |
563 | | "c.relkind IN (" CppAsString2(RELKIND_SEQUENCE) ")", |
564 | | /* viscondition */ |
565 | | "pg_catalog.pg_table_is_visible(c.oid)", |
566 | | /* namespace */ |
567 | | "c.relnamespace", |
568 | | /* result */ |
569 | | "pg_catalog.quote_ident(c.relname)", |
570 | | /* qualresult */ |
571 | | NULL |
572 | | }; |
573 | | |
574 | | static const SchemaQuery Query_for_list_of_foreign_tables = { |
575 | | /* min_server_version */ |
576 | | 0, |
577 | | /* catname */ |
578 | | "pg_catalog.pg_class c", |
579 | | /* selcondition */ |
580 | | "c.relkind IN (" CppAsString2(RELKIND_FOREIGN_TABLE) ")", |
581 | | /* viscondition */ |
582 | | "pg_catalog.pg_table_is_visible(c.oid)", |
583 | | /* namespace */ |
584 | | "c.relnamespace", |
585 | | /* result */ |
586 | | "pg_catalog.quote_ident(c.relname)", |
587 | | /* qualresult */ |
588 | | NULL |
589 | | }; |
590 | | |
591 | | static const SchemaQuery Query_for_list_of_tables = { |
592 | | /* min_server_version */ |
593 | | 0, |
594 | | /* catname */ |
595 | | "pg_catalog.pg_class c", |
596 | | /* selcondition */ |
597 | | "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " |
598 | | CppAsString2(RELKIND_PARTITIONED_TABLE) ")", |
599 | | /* viscondition */ |
600 | | "pg_catalog.pg_table_is_visible(c.oid)", |
601 | | /* namespace */ |
602 | | "c.relnamespace", |
603 | | /* result */ |
604 | | "pg_catalog.quote_ident(c.relname)", |
605 | | /* qualresult */ |
606 | | NULL |
607 | | }; |
608 | | |
609 | | static const SchemaQuery Query_for_list_of_partitioned_tables = { |
610 | | /* min_server_version */ |
611 | | 0, |
612 | | /* catname */ |
613 | | "pg_catalog.pg_class c", |
614 | | /* selcondition */ |
615 | | "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE) ")", |
616 | | /* viscondition */ |
617 | | "pg_catalog.pg_table_is_visible(c.oid)", |
618 | | /* namespace */ |
619 | | "c.relnamespace", |
620 | | /* result */ |
621 | | "pg_catalog.quote_ident(c.relname)", |
622 | | /* qualresult */ |
623 | | NULL |
624 | | }; |
625 | | |
626 | | static const SchemaQuery Query_for_list_of_constraints_with_schema = { |
627 | | /* min_server_version */ |
628 | | 0, |
629 | | /* catname */ |
630 | | "pg_catalog.pg_constraint c", |
631 | | /* selcondition */ |
632 | | "c.conrelid <> 0", |
633 | | /* viscondition */ |
634 | | "true", /* there is no pg_constraint_is_visible */ |
635 | | /* namespace */ |
636 | | "c.connamespace", |
637 | | /* result */ |
638 | | "pg_catalog.quote_ident(c.conname)", |
639 | | /* qualresult */ |
640 | | NULL |
641 | | }; |
642 | | |
643 | | /* Relations supporting INSERT, UPDATE or DELETE */ |
644 | | static const SchemaQuery Query_for_list_of_updatables = { |
645 | | /* min_server_version */ |
646 | | 0, |
647 | | /* catname */ |
648 | | "pg_catalog.pg_class c", |
649 | | /* selcondition */ |
650 | | "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " |
651 | | CppAsString2(RELKIND_FOREIGN_TABLE) ", " |
652 | | CppAsString2(RELKIND_VIEW) ", " |
653 | | CppAsString2(RELKIND_PARTITIONED_TABLE) ")", |
654 | | /* viscondition */ |
655 | | "pg_catalog.pg_table_is_visible(c.oid)", |
656 | | /* namespace */ |
657 | | "c.relnamespace", |
658 | | /* result */ |
659 | | "pg_catalog.quote_ident(c.relname)", |
660 | | /* qualresult */ |
661 | | NULL |
662 | | }; |
663 | | |
664 | | static const SchemaQuery Query_for_list_of_relations = { |
665 | | /* min_server_version */ |
666 | | 0, |
667 | | /* catname */ |
668 | | "pg_catalog.pg_class c", |
669 | | /* selcondition */ |
670 | | NULL, |
671 | | /* viscondition */ |
672 | | "pg_catalog.pg_table_is_visible(c.oid)", |
673 | | /* namespace */ |
674 | | "c.relnamespace", |
675 | | /* result */ |
676 | | "pg_catalog.quote_ident(c.relname)", |
677 | | /* qualresult */ |
678 | | NULL |
679 | | }; |
680 | | |
681 | | static const SchemaQuery Query_for_list_of_tsvmf = { |
682 | | /* min_server_version */ |
683 | | 0, |
684 | | /* catname */ |
685 | | "pg_catalog.pg_class c", |
686 | | /* selcondition */ |
687 | | "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " |
688 | | CppAsString2(RELKIND_SEQUENCE) ", " |
689 | | CppAsString2(RELKIND_VIEW) ", " |
690 | | CppAsString2(RELKIND_MATVIEW) ", " |
691 | | CppAsString2(RELKIND_FOREIGN_TABLE) ", " |
692 | | CppAsString2(RELKIND_PARTITIONED_TABLE) ")", |
693 | | /* viscondition */ |
694 | | "pg_catalog.pg_table_is_visible(c.oid)", |
695 | | /* namespace */ |
696 | | "c.relnamespace", |
697 | | /* result */ |
698 | | "pg_catalog.quote_ident(c.relname)", |
699 | | /* qualresult */ |
700 | | NULL |
701 | | }; |
702 | | |
703 | | static const SchemaQuery Query_for_list_of_tmf = { |
704 | | /* min_server_version */ |
705 | | 0, |
706 | | /* catname */ |
707 | | "pg_catalog.pg_class c", |
708 | | /* selcondition */ |
709 | | "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " |
710 | | CppAsString2(RELKIND_PARTITIONED_TABLE) ", " |
711 | | CppAsString2(RELKIND_MATVIEW) ", " |
712 | | CppAsString2(RELKIND_FOREIGN_TABLE) ")", |
713 | | /* viscondition */ |
714 | | "pg_catalog.pg_table_is_visible(c.oid)", |
715 | | /* namespace */ |
716 | | "c.relnamespace", |
717 | | /* result */ |
718 | | "pg_catalog.quote_ident(c.relname)", |
719 | | /* qualresult */ |
720 | | NULL |
721 | | }; |
722 | | |
723 | | static const SchemaQuery Query_for_list_of_tpm = { |
724 | | /* min_server_version */ |
725 | | 0, |
726 | | /* catname */ |
727 | | "pg_catalog.pg_class c", |
728 | | /* selcondition */ |
729 | | "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " |
730 | | CppAsString2(RELKIND_PARTITIONED_TABLE) ", " |
731 | | CppAsString2(RELKIND_MATVIEW) ")", |
732 | | /* viscondition */ |
733 | | "pg_catalog.pg_table_is_visible(c.oid)", |
734 | | /* namespace */ |
735 | | "c.relnamespace", |
736 | | /* result */ |
737 | | "pg_catalog.quote_ident(c.relname)", |
738 | | /* qualresult */ |
739 | | NULL |
740 | | }; |
741 | | |
742 | | static const SchemaQuery Query_for_list_of_tm = { |
743 | | /* min_server_version */ |
744 | | 0, |
745 | | /* catname */ |
746 | | "pg_catalog.pg_class c", |
747 | | /* selcondition */ |
748 | | "c.relkind IN (" CppAsString2(RELKIND_RELATION) ", " |
749 | | CppAsString2(RELKIND_MATVIEW) ")", |
750 | | /* viscondition */ |
751 | | "pg_catalog.pg_table_is_visible(c.oid)", |
752 | | /* namespace */ |
753 | | "c.relnamespace", |
754 | | /* result */ |
755 | | "pg_catalog.quote_ident(c.relname)", |
756 | | /* qualresult */ |
757 | | NULL |
758 | | }; |
759 | | |
760 | | static const SchemaQuery Query_for_list_of_views = { |
761 | | /* min_server_version */ |
762 | | 0, |
763 | | /* catname */ |
764 | | "pg_catalog.pg_class c", |
765 | | /* selcondition */ |
766 | | "c.relkind IN (" CppAsString2(RELKIND_VIEW) ")", |
767 | | /* viscondition */ |
768 | | "pg_catalog.pg_table_is_visible(c.oid)", |
769 | | /* namespace */ |
770 | | "c.relnamespace", |
771 | | /* result */ |
772 | | "pg_catalog.quote_ident(c.relname)", |
773 | | /* qualresult */ |
774 | | NULL |
775 | | }; |
776 | | |
777 | | static const SchemaQuery Query_for_list_of_matviews = { |
778 | | /* min_server_version */ |
779 | | 0, |
780 | | /* catname */ |
781 | | "pg_catalog.pg_class c", |
782 | | /* selcondition */ |
783 | | "c.relkind IN (" CppAsString2(RELKIND_MATVIEW) ")", |
784 | | /* viscondition */ |
785 | | "pg_catalog.pg_table_is_visible(c.oid)", |
786 | | /* namespace */ |
787 | | "c.relnamespace", |
788 | | /* result */ |
789 | | "pg_catalog.quote_ident(c.relname)", |
790 | | /* qualresult */ |
791 | | NULL |
792 | | }; |
793 | | |
794 | | static const SchemaQuery Query_for_list_of_statistics = { |
795 | | /* min_server_version */ |
796 | | 0, |
797 | | /* catname */ |
798 | | "pg_catalog.pg_statistic_ext s", |
799 | | /* selcondition */ |
800 | | NULL, |
801 | | /* viscondition */ |
802 | | "pg_catalog.pg_statistics_obj_is_visible(s.oid)", |
803 | | /* namespace */ |
804 | | "s.stxnamespace", |
805 | | /* result */ |
806 | | "pg_catalog.quote_ident(s.stxname)", |
807 | | /* qualresult */ |
808 | | NULL |
809 | | }; |
810 | | |
811 | | |
812 | | /* |
813 | | * Queries to get lists of names of various kinds of things, possibly |
814 | | * restricted to names matching a partially entered name. In these queries, |
815 | | * the first %s will be replaced by the text entered so far (suitably escaped |
816 | | * to become a SQL literal string). %d will be replaced by the length of the |
817 | | * string (in unescaped form). A second and third %s, if present, will be |
818 | | * replaced by a suitably-escaped version of the string provided in |
819 | | * completion_info_charp. A fourth and fifth %s are similarly replaced by |
820 | | * completion_info_charp2. |
821 | | * |
822 | | * Beware that the allowed sequences of %s and %d are determined by |
823 | | * _complete_from_query(). |
824 | | */ |
825 | | |
826 | 0 | #define Query_for_list_of_attributes \ |
827 | 0 | "SELECT pg_catalog.quote_ident(attname) "\ |
828 | 0 | " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\ |
829 | 0 | " WHERE c.oid = a.attrelid "\ |
830 | 0 | " AND a.attnum > 0 "\ |
831 | 0 | " AND NOT a.attisdropped "\ |
832 | 0 | " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ |
833 | 0 | " AND (pg_catalog.quote_ident(relname)='%s' "\ |
834 | 0 | " OR '\"' || relname || '\"'='%s') "\ |
835 | 0 | " AND pg_catalog.pg_table_is_visible(c.oid)" |
836 | | |
837 | 0 | #define Query_for_list_of_attributes_with_schema \ |
838 | 0 | "SELECT pg_catalog.quote_ident(attname) "\ |
839 | 0 | " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\ |
840 | 0 | " WHERE c.oid = a.attrelid "\ |
841 | 0 | " AND n.oid = c.relnamespace "\ |
842 | 0 | " AND a.attnum > 0 "\ |
843 | 0 | " AND NOT a.attisdropped "\ |
844 | 0 | " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\ |
845 | 0 | " AND (pg_catalog.quote_ident(relname)='%s' "\ |
846 | 0 | " OR '\"' || relname || '\"' ='%s') "\ |
847 | 0 | " AND (pg_catalog.quote_ident(nspname)='%s' "\ |
848 | 0 | " OR '\"' || nspname || '\"' ='%s') " |
849 | | |
850 | 0 | #define Query_for_list_of_enum_values \ |
851 | 0 | "SELECT pg_catalog.quote_literal(enumlabel) "\ |
852 | 0 | " FROM pg_catalog.pg_enum e, pg_catalog.pg_type t "\ |
853 | 0 | " WHERE t.oid = e.enumtypid "\ |
854 | 0 | " AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\ |
855 | 0 | " AND (pg_catalog.quote_ident(typname)='%s' "\ |
856 | 0 | " OR '\"' || typname || '\"'='%s') "\ |
857 | 0 | " AND pg_catalog.pg_type_is_visible(t.oid)" |
858 | | |
859 | 0 | #define Query_for_list_of_enum_values_with_schema \ |
860 | 0 | "SELECT pg_catalog.quote_literal(enumlabel) "\ |
861 | 0 | " FROM pg_catalog.pg_enum e, pg_catalog.pg_type t, pg_catalog.pg_namespace n "\ |
862 | 0 | " WHERE t.oid = e.enumtypid "\ |
863 | 0 | " AND n.oid = t.typnamespace "\ |
864 | 0 | " AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\ |
865 | 0 | " AND (pg_catalog.quote_ident(typname)='%s' "\ |
866 | 0 | " OR '\"' || typname || '\"'='%s') "\ |
867 | 0 | " AND (pg_catalog.quote_ident(nspname)='%s' "\ |
868 | 0 | " OR '\"' || nspname || '\"' ='%s') " |
869 | | |
870 | | #define Query_for_list_of_template_databases \ |
871 | | "SELECT pg_catalog.quote_ident(d.datname) "\ |
872 | | " FROM pg_catalog.pg_database d "\ |
873 | | " WHERE substring(pg_catalog.quote_ident(d.datname),1,%d)='%s' "\ |
874 | | " AND (d.datistemplate OR pg_catalog.pg_has_role(d.datdba, 'USAGE'))" |
875 | | |
876 | | #define Query_for_list_of_databases \ |
877 | | "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\ |
878 | | " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'" |
879 | | |
880 | | #define Query_for_list_of_tablespaces \ |
881 | | "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\ |
882 | | " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'" |
883 | | |
884 | | #define Query_for_list_of_tablegroups \ |
885 | | "SELECT pg_catalog.quote_ident(grpname) FROM pg_catalog.pg_yb_tablegroup "\ |
886 | | " WHERE substring(pg_catalog.quote_ident(grpname),1,%d)='%s'" |
887 | | |
888 | | #define Query_for_list_of_encodings \ |
889 | | " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\ |
890 | | " FROM pg_catalog.pg_conversion "\ |
891 | | " WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')" |
892 | | |
893 | | #define Query_for_list_of_languages \ |
894 | | "SELECT pg_catalog.quote_ident(lanname) "\ |
895 | | " FROM pg_catalog.pg_language "\ |
896 | | " WHERE lanname != 'internal' "\ |
897 | | " AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'" |
898 | | |
899 | | #define Query_for_list_of_schemas \ |
900 | | "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\ |
901 | | " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'" |
902 | | |
903 | | #define Query_for_list_of_alter_system_set_vars \ |
904 | | "SELECT name FROM "\ |
905 | | " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\ |
906 | | " WHERE context != 'internal' "\ |
907 | | " UNION ALL SELECT 'all') ss "\ |
908 | | " WHERE substring(name,1,%d)='%s'" |
909 | | |
910 | | #define Query_for_list_of_set_vars \ |
911 | | "SELECT name FROM "\ |
912 | | " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\ |
913 | | " WHERE context IN ('user', 'superuser') "\ |
914 | | " UNION ALL SELECT 'constraints' "\ |
915 | | " UNION ALL SELECT 'transaction' "\ |
916 | | " UNION ALL SELECT 'session' "\ |
917 | | " UNION ALL SELECT 'role' "\ |
918 | | " UNION ALL SELECT 'tablespace' "\ |
919 | | " UNION ALL SELECT 'all') ss "\ |
920 | | " WHERE substring(name,1,%d)='%s'" |
921 | | |
922 | | #define Query_for_list_of_show_vars \ |
923 | | "SELECT name FROM "\ |
924 | | " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\ |
925 | | " UNION ALL SELECT 'session authorization' "\ |
926 | | " UNION ALL SELECT 'all') ss "\ |
927 | | " WHERE substring(name,1,%d)='%s'" |
928 | | |
929 | | #define Query_for_list_of_roles \ |
930 | | " SELECT pg_catalog.quote_ident(rolname) "\ |
931 | | " FROM pg_catalog.pg_roles "\ |
932 | | " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" |
933 | | |
934 | | #define Query_for_list_of_grant_roles \ |
935 | | " SELECT pg_catalog.quote_ident(rolname) "\ |
936 | | " FROM pg_catalog.pg_roles "\ |
937 | | " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\ |
938 | | " UNION ALL SELECT 'PUBLIC'"\ |
939 | | " UNION ALL SELECT 'CURRENT_USER'"\ |
940 | | " UNION ALL SELECT 'SESSION_USER'" |
941 | | |
942 | | /* the silly-looking length condition is just to eat up the current word */ |
943 | | #define Query_for_table_owning_index \ |
944 | | "SELECT pg_catalog.quote_ident(c1.relname) "\ |
945 | | " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\ |
946 | | " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\ |
947 | | " and (%d = pg_catalog.length('%s'))"\ |
948 | | " and pg_catalog.quote_ident(c2.relname)='%s'"\ |
949 | | " and pg_catalog.pg_table_is_visible(c2.oid)" |
950 | | |
951 | | /* the silly-looking length condition is just to eat up the current word */ |
952 | | #define Query_for_index_of_table \ |
953 | | "SELECT pg_catalog.quote_ident(c2.relname) "\ |
954 | | " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\ |
955 | | " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\ |
956 | | " and (%d = pg_catalog.length('%s'))"\ |
957 | | " and pg_catalog.quote_ident(c1.relname)='%s'"\ |
958 | | " and pg_catalog.pg_table_is_visible(c2.oid)" |
959 | | |
960 | | /* the silly-looking length condition is just to eat up the current word */ |
961 | | #define Query_for_constraint_of_table \ |
962 | | "SELECT pg_catalog.quote_ident(conname) "\ |
963 | | " FROM pg_catalog.pg_class c1, pg_catalog.pg_constraint con "\ |
964 | | " WHERE c1.oid=conrelid and (%d = pg_catalog.length('%s'))"\ |
965 | | " and pg_catalog.quote_ident(c1.relname)='%s'"\ |
966 | | " and pg_catalog.pg_table_is_visible(c1.oid)" |
967 | | |
968 | | #define Query_for_all_table_constraints \ |
969 | | "SELECT pg_catalog.quote_ident(conname) "\ |
970 | | " FROM pg_catalog.pg_constraint c "\ |
971 | | " WHERE c.conrelid <> 0 " |
972 | | |
973 | | /* the silly-looking length condition is just to eat up the current word */ |
974 | | #define Query_for_constraint_of_type \ |
975 | | "SELECT pg_catalog.quote_ident(conname) "\ |
976 | | " FROM pg_catalog.pg_type t, pg_catalog.pg_constraint con "\ |
977 | | " WHERE t.oid=contypid and (%d = pg_catalog.length('%s'))"\ |
978 | | " and pg_catalog.quote_ident(t.typname)='%s'"\ |
979 | | " and pg_catalog.pg_type_is_visible(t.oid)" |
980 | | |
981 | | /* the silly-looking length condition is just to eat up the current word */ |
982 | | #define Query_for_list_of_tables_for_constraint \ |
983 | | "SELECT pg_catalog.quote_ident(relname) "\ |
984 | | " FROM pg_catalog.pg_class"\ |
985 | | " WHERE (%d = pg_catalog.length('%s'))"\ |
986 | | " AND oid IN "\ |
987 | | " (SELECT conrelid FROM pg_catalog.pg_constraint "\ |
988 | | " WHERE pg_catalog.quote_ident(conname)='%s')" |
989 | | |
990 | | /* the silly-looking length condition is just to eat up the current word */ |
991 | | #define Query_for_rule_of_table \ |
992 | | "SELECT pg_catalog.quote_ident(rulename) "\ |
993 | | " FROM pg_catalog.pg_class c1, pg_catalog.pg_rewrite "\ |
994 | | " WHERE c1.oid=ev_class and (%d = pg_catalog.length('%s'))"\ |
995 | | " and pg_catalog.quote_ident(c1.relname)='%s'"\ |
996 | | " and pg_catalog.pg_table_is_visible(c1.oid)" |
997 | | |
998 | | /* the silly-looking length condition is just to eat up the current word */ |
999 | | #define Query_for_list_of_tables_for_rule \ |
1000 | | "SELECT pg_catalog.quote_ident(relname) "\ |
1001 | | " FROM pg_catalog.pg_class"\ |
1002 | | " WHERE (%d = pg_catalog.length('%s'))"\ |
1003 | | " AND oid IN "\ |
1004 | | " (SELECT ev_class FROM pg_catalog.pg_rewrite "\ |
1005 | | " WHERE pg_catalog.quote_ident(rulename)='%s')" |
1006 | | |
1007 | | /* the silly-looking length condition is just to eat up the current word */ |
1008 | | #define Query_for_trigger_of_table \ |
1009 | | "SELECT pg_catalog.quote_ident(tgname) "\ |
1010 | | " FROM pg_catalog.pg_class c1, pg_catalog.pg_trigger "\ |
1011 | | " WHERE c1.oid=tgrelid and (%d = pg_catalog.length('%s'))"\ |
1012 | | " and pg_catalog.quote_ident(c1.relname)='%s'"\ |
1013 | | " and pg_catalog.pg_table_is_visible(c1.oid)"\ |
1014 | | " and not tgisinternal" |
1015 | | |
1016 | | /* the silly-looking length condition is just to eat up the current word */ |
1017 | | #define Query_for_list_of_tables_for_trigger \ |
1018 | | "SELECT pg_catalog.quote_ident(relname) "\ |
1019 | | " FROM pg_catalog.pg_class"\ |
1020 | | " WHERE (%d = pg_catalog.length('%s'))"\ |
1021 | | " AND oid IN "\ |
1022 | | " (SELECT tgrelid FROM pg_catalog.pg_trigger "\ |
1023 | | " WHERE pg_catalog.quote_ident(tgname)='%s')" |
1024 | | |
1025 | | #define Query_for_list_of_ts_configurations \ |
1026 | | "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\ |
1027 | | " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'" |
1028 | | |
1029 | | #define Query_for_list_of_ts_dictionaries \ |
1030 | | "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\ |
1031 | | " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'" |
1032 | | |
1033 | | #define Query_for_list_of_ts_parsers \ |
1034 | | "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\ |
1035 | | " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'" |
1036 | | |
1037 | | #define Query_for_list_of_ts_templates \ |
1038 | | "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\ |
1039 | | " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'" |
1040 | | |
1041 | | #define Query_for_list_of_fdws \ |
1042 | | " SELECT pg_catalog.quote_ident(fdwname) "\ |
1043 | | " FROM pg_catalog.pg_foreign_data_wrapper "\ |
1044 | | " WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'" |
1045 | | |
1046 | | #define Query_for_list_of_servers \ |
1047 | | " SELECT pg_catalog.quote_ident(srvname) "\ |
1048 | | " FROM pg_catalog.pg_foreign_server "\ |
1049 | | " WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'" |
1050 | | |
1051 | | #define Query_for_list_of_user_mappings \ |
1052 | | " SELECT pg_catalog.quote_ident(usename) "\ |
1053 | | " FROM pg_catalog.pg_user_mappings "\ |
1054 | | " WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" |
1055 | | |
1056 | | #define Query_for_list_of_access_methods \ |
1057 | | " SELECT pg_catalog.quote_ident(amname) "\ |
1058 | | " FROM pg_catalog.pg_am "\ |
1059 | | " WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'" |
1060 | | |
1061 | | /* the silly-looking length condition is just to eat up the current word */ |
1062 | 0 | #define Query_for_list_of_arguments \ |
1063 | 0 | "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\ |
1064 | 0 | " FROM pg_catalog.pg_proc "\ |
1065 | 0 | " WHERE (%d = pg_catalog.length('%s'))"\ |
1066 | 0 | " AND (pg_catalog.quote_ident(proname)='%s'"\ |
1067 | 0 | " OR '\"' || proname || '\"'='%s') "\ |
1068 | 0 | " AND (pg_catalog.pg_function_is_visible(pg_proc.oid))" |
1069 | | |
1070 | | /* the silly-looking length condition is just to eat up the current word */ |
1071 | 0 | #define Query_for_list_of_arguments_with_schema \ |
1072 | 0 | "SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\ |
1073 | 0 | " FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\ |
1074 | 0 | " WHERE (%d = pg_catalog.length('%s'))"\ |
1075 | 0 | " AND n.oid = p.pronamespace "\ |
1076 | 0 | " AND (pg_catalog.quote_ident(proname)='%s' "\ |
1077 | 0 | " OR '\"' || proname || '\"' ='%s') "\ |
1078 | 0 | " AND (pg_catalog.quote_ident(nspname)='%s' "\ |
1079 | 0 | " OR '\"' || nspname || '\"' ='%s') " |
1080 | | |
1081 | | #define Query_for_list_of_extensions \ |
1082 | | " SELECT pg_catalog.quote_ident(extname) "\ |
1083 | | " FROM pg_catalog.pg_extension "\ |
1084 | | " WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'" |
1085 | | |
1086 | | #define Query_for_list_of_available_extensions \ |
1087 | | " SELECT pg_catalog.quote_ident(name) "\ |
1088 | | " FROM pg_catalog.pg_available_extensions "\ |
1089 | | " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL" |
1090 | | |
1091 | | /* the silly-looking length condition is just to eat up the current word */ |
1092 | | #define Query_for_list_of_available_extension_versions \ |
1093 | | " SELECT pg_catalog.quote_ident(version) "\ |
1094 | | " FROM pg_catalog.pg_available_extension_versions "\ |
1095 | | " WHERE (%d = pg_catalog.length('%s'))"\ |
1096 | | " AND pg_catalog.quote_ident(name)='%s'" |
1097 | | |
1098 | | /* the silly-looking length condition is just to eat up the current word */ |
1099 | | #define Query_for_list_of_available_extension_versions_with_TO \ |
1100 | | " SELECT 'TO ' || pg_catalog.quote_ident(version) "\ |
1101 | | " FROM pg_catalog.pg_available_extension_versions "\ |
1102 | | " WHERE (%d = pg_catalog.length('%s'))"\ |
1103 | | " AND pg_catalog.quote_ident(name)='%s'" |
1104 | | |
1105 | | #define Query_for_list_of_prepared_statements \ |
1106 | | " SELECT pg_catalog.quote_ident(name) "\ |
1107 | | " FROM pg_catalog.pg_prepared_statements "\ |
1108 | | " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'" |
1109 | | |
1110 | | #define Query_for_list_of_event_triggers \ |
1111 | | " SELECT pg_catalog.quote_ident(evtname) "\ |
1112 | | " FROM pg_catalog.pg_event_trigger "\ |
1113 | | " WHERE substring(pg_catalog.quote_ident(evtname),1,%d)='%s'" |
1114 | | |
1115 | | #define Query_for_list_of_tablesample_methods \ |
1116 | | " SELECT pg_catalog.quote_ident(proname) "\ |
1117 | | " FROM pg_catalog.pg_proc "\ |
1118 | | " WHERE prorettype = 'pg_catalog.tsm_handler'::pg_catalog.regtype AND "\ |
1119 | | " proargtypes[0] = 'pg_catalog.internal'::pg_catalog.regtype AND "\ |
1120 | | " substring(pg_catalog.quote_ident(proname),1,%d)='%s'" |
1121 | | |
1122 | | #define Query_for_list_of_policies \ |
1123 | | " SELECT pg_catalog.quote_ident(polname) "\ |
1124 | | " FROM pg_catalog.pg_policy "\ |
1125 | | " WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'" |
1126 | | |
1127 | | #define Query_for_list_of_tables_for_policy \ |
1128 | | "SELECT pg_catalog.quote_ident(relname) "\ |
1129 | | " FROM pg_catalog.pg_class"\ |
1130 | | " WHERE (%d = pg_catalog.length('%s'))"\ |
1131 | | " AND oid IN "\ |
1132 | | " (SELECT polrelid FROM pg_catalog.pg_policy "\ |
1133 | | " WHERE pg_catalog.quote_ident(polname)='%s')" |
1134 | | |
1135 | | #define Query_for_enum \ |
1136 | | " SELECT name FROM ( "\ |
1137 | | " SELECT pg_catalog.quote_ident(pg_catalog.unnest(enumvals)) AS name "\ |
1138 | | " FROM pg_catalog.pg_settings "\ |
1139 | | " WHERE pg_catalog.lower(name)=pg_catalog.lower('%s') "\ |
1140 | | " UNION ALL " \ |
1141 | | " SELECT 'DEFAULT' ) ss "\ |
1142 | | " WHERE pg_catalog.substring(name,1,%%d)='%%s'" |
1143 | | |
1144 | | /* the silly-looking length condition is just to eat up the current word */ |
1145 | | #define Query_for_partition_of_table \ |
1146 | | "SELECT pg_catalog.quote_ident(c2.relname) "\ |
1147 | | " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i"\ |
1148 | | " WHERE c1.oid=i.inhparent and i.inhrelid=c2.oid"\ |
1149 | | " and (%d = pg_catalog.length('%s'))"\ |
1150 | | " and pg_catalog.quote_ident(c1.relname)='%s'"\ |
1151 | | " and pg_catalog.pg_table_is_visible(c2.oid)"\ |
1152 | | " and c2.relispartition = 'true'" |
1153 | | |
1154 | | /* |
1155 | | * These object types were introduced later than our support cutoff of |
1156 | | * server version 7.4. We use the VersionedQuery infrastructure so that |
1157 | | * we don't send certain-to-fail queries to older servers. |
1158 | | */ |
1159 | | |
1160 | | static const VersionedQuery Query_for_list_of_publications[] = { |
1161 | | {100000, |
1162 | | " SELECT pg_catalog.quote_ident(pubname) " |
1163 | | " FROM pg_catalog.pg_publication " |
1164 | | " WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'" |
1165 | | }, |
1166 | | {0, NULL} |
1167 | | }; |
1168 | | |
1169 | | static const VersionedQuery Query_for_list_of_subscriptions[] = { |
1170 | | {100000, |
1171 | | " SELECT pg_catalog.quote_ident(s.subname) " |
1172 | | " FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d " |
1173 | | " WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' " |
1174 | | " AND d.datname = pg_catalog.current_database() " |
1175 | | " AND s.subdbid = d.oid" |
1176 | | }, |
1177 | | {0, NULL} |
1178 | | }; |
1179 | | |
1180 | | /* |
1181 | | * This is a list of all "things" in Pgsql, which can show up after CREATE or |
1182 | | * DROP; and there is also a query to get a list of them. |
1183 | | */ |
1184 | | |
1185 | | typedef struct |
1186 | | { |
1187 | | const char *name; |
1188 | | const char *query; /* simple query, or NULL */ |
1189 | | const VersionedQuery *vquery; /* versioned query, or NULL */ |
1190 | | const SchemaQuery *squery; /* schema query, or NULL */ |
1191 | | const bits32 flags; /* visibility flags, see below */ |
1192 | | } pgsql_thing_t; |
1193 | | |
1194 | 0 | #define THING_NO_CREATE (1 << 0) /* should not show up after CREATE */ |
1195 | 0 | #define THING_NO_DROP (1 << 1) /* should not show up after DROP */ |
1196 | 0 | #define THING_NO_ALTER (1 << 2) /* should not show up after ALTER */ |
1197 | | #define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER) |
1198 | | |
1199 | | static const pgsql_thing_t words_after_create[] = { |
1200 | | {"ACCESS METHOD", NULL, NULL, NULL, THING_NO_ALTER}, |
1201 | | {"AGGREGATE", NULL, NULL, Query_for_list_of_aggregates}, |
1202 | | {"CAST", NULL, NULL, NULL}, /* Casts have complex structures for names, so |
1203 | | * skip it */ |
1204 | | {"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'"}, |
1205 | | |
1206 | | /* |
1207 | | * CREATE CONSTRAINT TRIGGER is not supported here because it is designed |
1208 | | * to be used only by pg_dump. |
1209 | | */ |
1210 | | {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, NULL, THING_NO_SHOW}, |
1211 | | {"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"}, |
1212 | | {"DATABASE", Query_for_list_of_databases}, |
1213 | | {"DEFAULT PRIVILEGES", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP}, |
1214 | | {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, THING_NO_SHOW}, |
1215 | | {"DOMAIN", NULL, NULL, &Query_for_list_of_domains}, |
1216 | | {"EVENT TRIGGER", NULL, NULL, NULL}, |
1217 | | {"EXTENSION", Query_for_list_of_extensions}, |
1218 | | {"FOREIGN DATA WRAPPER", NULL, NULL, NULL}, |
1219 | | {"FOREIGN TABLE", NULL, NULL, NULL}, |
1220 | | {"FUNCTION", NULL, NULL, Query_for_list_of_functions}, |
1221 | | {"GROUP", Query_for_list_of_roles}, |
1222 | | {"INDEX", NULL, NULL, &Query_for_list_of_indexes}, |
1223 | | {"LANGUAGE", Query_for_list_of_languages}, |
1224 | | {"LARGE OBJECT", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP}, |
1225 | | {"MATERIALIZED VIEW", NULL, NULL, &Query_for_list_of_matviews}, |
1226 | | {"OPERATOR", NULL, NULL, NULL}, /* Querying for this is probably not such |
1227 | | * a good idea. */ |
1228 | | {"OWNED", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_ALTER}, /* for DROP OWNED BY ... */ |
1229 | | {"PARSER", Query_for_list_of_ts_parsers, NULL, NULL, THING_NO_SHOW}, |
1230 | | {"POLICY", NULL, NULL, NULL}, |
1231 | | {"PROCEDURE", NULL, NULL, Query_for_list_of_procedures}, |
1232 | | {"PUBLICATION", NULL, Query_for_list_of_publications}, |
1233 | | {"ROLE", Query_for_list_of_roles}, |
1234 | | {"ROUTINE", NULL, NULL, &Query_for_list_of_routines, THING_NO_CREATE}, |
1235 | | {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"}, |
1236 | | {"SCHEMA", Query_for_list_of_schemas}, |
1237 | | {"SEQUENCE", NULL, NULL, &Query_for_list_of_sequences}, |
1238 | | {"SERVER", Query_for_list_of_servers}, |
1239 | | {"STATISTICS", NULL, NULL, &Query_for_list_of_statistics}, |
1240 | | {"SUBSCRIPTION", NULL, Query_for_list_of_subscriptions}, |
1241 | | {"SYSTEM", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP}, |
1242 | | {"TABLE", NULL, NULL, &Query_for_list_of_tables}, |
1243 | | {"TABLESPACE", Query_for_list_of_tablespaces}, |
1244 | | {"TABLEGROUP", Query_for_list_of_tablegroups}, |
1245 | | {"TEMP", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE |
1246 | | * ... */ |
1247 | | {"TEMPLATE", Query_for_list_of_ts_templates, NULL, NULL, THING_NO_SHOW}, |
1248 | | {"TEMPORARY", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMPORARY |
1249 | | * TABLE ... */ |
1250 | | {"TEXT SEARCH", NULL, NULL, NULL}, |
1251 | | {"TRANSFORM", NULL, NULL, NULL}, |
1252 | | {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s' AND NOT tgisinternal"}, |
1253 | | {"TYPE", NULL, NULL, &Query_for_list_of_datatypes}, |
1254 | | {"UNIQUE", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE |
1255 | | * INDEX ... */ |
1256 | | {"UNLOGGED", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED |
1257 | | * TABLE ... */ |
1258 | | {"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"}, |
1259 | | {"USER MAPPING FOR", NULL, NULL, NULL}, |
1260 | | {"VIEW", NULL, NULL, &Query_for_list_of_views}, |
1261 | | {NULL} /* end of list */ |
1262 | | }; |
1263 | | |
1264 | | |
1265 | | /* Forward declaration of functions */ |
1266 | | static char **psql_completion(const char *text, int start, int end); |
1267 | | static char *create_command_generator(const char *text, int state); |
1268 | | static char *drop_command_generator(const char *text, int state); |
1269 | | static char *alter_command_generator(const char *text, int state); |
1270 | | static char *complete_from_query(const char *text, int state); |
1271 | | static char *complete_from_versioned_query(const char *text, int state); |
1272 | | static char *complete_from_schema_query(const char *text, int state); |
1273 | | static char *complete_from_versioned_schema_query(const char *text, int state); |
1274 | | static char *_complete_from_query(const char *simple_query, |
1275 | | const SchemaQuery *schema_query, |
1276 | | const char *text, int state); |
1277 | | static char *complete_from_list(const char *text, int state); |
1278 | | static char *complete_from_const(const char *text, int state); |
1279 | | static void append_variable_names(char ***varnames, int *nvars, |
1280 | | int *maxvars, const char *varname, |
1281 | | const char *prefix, const char *suffix); |
1282 | | static char **complete_from_variables(const char *text, |
1283 | | const char *prefix, const char *suffix, bool need_value); |
1284 | | static char *complete_from_files(const char *text, int state); |
1285 | | |
1286 | | static char *pg_strdup_keyword_case(const char *s, const char *ref); |
1287 | | static char *escape_string(const char *text); |
1288 | | static PGresult *exec_query(const char *query); |
1289 | | |
1290 | | static char **get_previous_words(int point, char **buffer, int *nwords); |
1291 | | |
1292 | | static char *get_guctype(const char *varname); |
1293 | | |
1294 | | #ifdef NOT_USED |
1295 | | static char *quote_file_name(char *text, int match_type, char *quote_pointer); |
1296 | | static char *dequote_file_name(char *text, char quote_char); |
1297 | | #endif |
1298 | | |
1299 | | |
1300 | | /* |
1301 | | * Initialize the readline library for our purposes. |
1302 | | */ |
1303 | | void |
1304 | | initialize_readline(void) |
1305 | 0 | { |
1306 | 0 | rl_readline_name = (char *) pset.progname; |
1307 | 0 | rl_attempted_completion_function = psql_completion; |
1308 | |
|
1309 | 0 | rl_basic_word_break_characters = WORD_BREAKS; |
1310 | |
|
1311 | 0 | completion_max_records = 1000; |
1312 | | |
1313 | | /* |
1314 | | * There is a variable rl_completion_query_items for this but apparently |
1315 | | * it's not defined everywhere. |
1316 | | */ |
1317 | 0 | } |
1318 | | |
1319 | | /* |
1320 | | * Check if 'word' matches any of the '|'-separated strings in 'pattern', |
1321 | | * using case-insensitive or case-sensitive comparisons. |
1322 | | * |
1323 | | * If pattern is NULL, it's a wild card that matches any word. |
1324 | | * If pattern begins with '!', the result is negated, ie we check that 'word' |
1325 | | * does *not* match any alternative appearing in the rest of 'pattern'. |
1326 | | * Any alternative can end with '*' which is a wild card, i.e., it means |
1327 | | * match any word that matches the characters so far. (We do not currently |
1328 | | * support '*' elsewhere than the end of an alternative.) |
1329 | | * |
1330 | | * For readability, callers should use the macros MatchAny and MatchAnyExcept |
1331 | | * to invoke those two special cases for 'pattern'. (But '|' and '*' must |
1332 | | * just be written directly in patterns.) |
1333 | | */ |
1334 | | #define MatchAny NULL |
1335 | | #define MatchAnyExcept(pattern) ("!" pattern) |
1336 | | |
1337 | | static bool |
1338 | | word_matches_internal(const char *pattern, |
1339 | | const char *word, |
1340 | | bool case_sensitive) |
1341 | 0 | { |
1342 | 0 | size_t wordlen, |
1343 | 0 | patternlen; |
1344 | | |
1345 | | /* NULL pattern matches anything. */ |
1346 | 0 | if (pattern == NULL) |
1347 | 0 | return true; |
1348 | | |
1349 | | /* Handle negated patterns from the MatchAnyExcept macro. */ |
1350 | 0 | if (*pattern == '!') |
1351 | 0 | return !word_matches_internal(pattern + 1, word, case_sensitive); |
1352 | | |
1353 | | /* Else consider each alternative in the pattern. */ |
1354 | 0 | wordlen = strlen(word); |
1355 | 0 | for (;;) |
1356 | 0 | { |
1357 | 0 | const char *c; |
1358 | | |
1359 | | /* Find end of current alternative. */ |
1360 | 0 | c = pattern; |
1361 | 0 | while (*c != '\0' && *c != '|') |
1362 | 0 | c++; |
1363 | | /* Was there a wild card? (Assumes first alternative is not empty) */ |
1364 | 0 | if (c[-1] == '*') |
1365 | 0 | { |
1366 | | /* Yes, wildcard match? */ |
1367 | 0 | patternlen = c - pattern - 1; |
1368 | 0 | if (wordlen >= patternlen && |
1369 | 0 | (case_sensitive ? |
1370 | 0 | strncmp(word, pattern, patternlen) == 0 : |
1371 | 0 | pg_strncasecmp(word, pattern, patternlen) == 0)) |
1372 | 0 | return true; |
1373 | 0 | } |
1374 | 0 | else |
1375 | 0 | { |
1376 | | /* No, plain match? */ |
1377 | 0 | patternlen = c - pattern; |
1378 | 0 | if (wordlen == patternlen && |
1379 | 0 | (case_sensitive ? |
1380 | 0 | strncmp(word, pattern, wordlen) == 0 : |
1381 | 0 | pg_strncasecmp(word, pattern, wordlen) == 0)) |
1382 | 0 | return true; |
1383 | 0 | } |
1384 | | /* Out of alternatives? */ |
1385 | 0 | if (*c == '\0') |
1386 | 0 | break; |
1387 | | /* Nope, try next alternative. */ |
1388 | 0 | pattern = c + 1; |
1389 | 0 | } |
1390 | | |
1391 | 0 | return false; |
1392 | 0 | } |
1393 | | |
1394 | | /* |
1395 | | * There are enough matching calls below that it seems worth having these two |
1396 | | * interface routines rather than including a third parameter in every call. |
1397 | | * |
1398 | | * word_matches --- match case-insensitively. |
1399 | | */ |
1400 | | static bool |
1401 | | word_matches(const char *pattern, const char *word) |
1402 | 0 | { |
1403 | 0 | return word_matches_internal(pattern, word, false); |
1404 | 0 | } |
1405 | | |
1406 | | /* |
1407 | | * word_matches_cs --- match case-sensitively. |
1408 | | */ |
1409 | | static bool |
1410 | | word_matches_cs(const char *pattern, const char *word) |
1411 | 0 | { |
1412 | 0 | return word_matches_internal(pattern, word, true); |
1413 | 0 | } |
1414 | | |
1415 | | /* |
1416 | | * Check if the final character of 's' is 'c'. |
1417 | | */ |
1418 | | static bool |
1419 | | ends_with(const char *s, char c) |
1420 | 0 | { |
1421 | 0 | size_t length = strlen(s); |
1422 | |
|
1423 | 0 | return (length > 0 && s[length - 1] == c); |
1424 | 0 | } |
1425 | | |
1426 | | /* |
1427 | | * The completion function. |
1428 | | * |
1429 | | * According to readline spec this gets passed the text entered so far and its |
1430 | | * start and end positions in the readline buffer. The return value is some |
1431 | | * partially obscure list format that can be generated by readline's |
1432 | | * completion_matches() function, so we don't have to worry about it. |
1433 | | */ |
1434 | | static char ** |
1435 | | psql_completion(const char *text, int start, int end) |
1436 | 0 | { |
1437 | | /* This is the variable we'll return. */ |
1438 | 0 | char **matches = NULL; |
1439 | | |
1440 | | /* Workspace for parsed words. */ |
1441 | 0 | char *words_buffer; |
1442 | | |
1443 | | /* This array will contain pointers to parsed words. */ |
1444 | 0 | char **previous_words; |
1445 | | |
1446 | | /* The number of words found on the input line. */ |
1447 | 0 | int previous_words_count; |
1448 | | |
1449 | | /* |
1450 | | * For compactness, we use these macros to reference previous_words[]. |
1451 | | * Caution: do not access a previous_words[] entry without having checked |
1452 | | * previous_words_count to be sure it's valid. In most cases below, that |
1453 | | * check is implicit in a TailMatches() or similar macro, but in some |
1454 | | * places we have to check it explicitly. |
1455 | | */ |
1456 | 0 | #define prev_wd (previous_words[0]) |
1457 | 0 | #define prev2_wd (previous_words[1]) |
1458 | 0 | #define prev3_wd (previous_words[2]) |
1459 | 0 | #define prev4_wd (previous_words[3]) |
1460 | 0 | #define prev5_wd (previous_words[4]) |
1461 | 0 | #define prev6_wd (previous_words[5]) |
1462 | 0 | #define prev7_wd (previous_words[6]) |
1463 | 0 | #define prev8_wd (previous_words[7]) |
1464 | 0 | #define prev9_wd (previous_words[8]) |
1465 | | |
1466 | | /* Macros for matching the last N words before point, case-insensitively. */ |
1467 | 0 | #define TailMatches1(p1) \ |
1468 | 0 | (previous_words_count >= 1 && \ |
1469 | 0 | word_matches(p1, prev_wd)) |
1470 | |
|
1471 | 0 | #define TailMatches2(p2, p1) \ |
1472 | 0 | (previous_words_count >= 2 && \ |
1473 | 0 | word_matches(p1, prev_wd) && \ |
1474 | 0 | word_matches(p2, prev2_wd)) |
1475 | |
|
1476 | 0 | #define TailMatches3(p3, p2, p1) \ |
1477 | 0 | (previous_words_count >= 3 && \ |
1478 | 0 | word_matches(p1, prev_wd) && \ |
1479 | 0 | word_matches(p2, prev2_wd) && \ |
1480 | 0 | word_matches(p3, prev3_wd)) |
1481 | |
|
1482 | 0 | #define TailMatches4(p4, p3, p2, p1) \ |
1483 | 0 | (previous_words_count >= 4 && \ |
1484 | 0 | word_matches(p1, prev_wd) && \ |
1485 | 0 | word_matches(p2, prev2_wd) && \ |
1486 | 0 | word_matches(p3, prev3_wd) && \ |
1487 | 0 | word_matches(p4, prev4_wd)) |
1488 | |
|
1489 | 0 | #define TailMatches5(p5, p4, p3, p2, p1) \ |
1490 | 0 | (previous_words_count >= 5 && \ |
1491 | 0 | word_matches(p1, prev_wd) && \ |
1492 | 0 | word_matches(p2, prev2_wd) && \ |
1493 | 0 | word_matches(p3, prev3_wd) && \ |
1494 | 0 | word_matches(p4, prev4_wd) && \ |
1495 | 0 | word_matches(p5, prev5_wd)) |
1496 | |
|
1497 | 0 | #define TailMatches6(p6, p5, p4, p3, p2, p1) \ |
1498 | 0 | (previous_words_count >= 6 && \ |
1499 | 0 | word_matches(p1, prev_wd) && \ |
1500 | 0 | word_matches(p2, prev2_wd) && \ |
1501 | 0 | word_matches(p3, prev3_wd) && \ |
1502 | 0 | word_matches(p4, prev4_wd) && \ |
1503 | 0 | word_matches(p5, prev5_wd) && \ |
1504 | 0 | word_matches(p6, prev6_wd)) |
1505 | |
|
1506 | 0 | #define TailMatches7(p7, p6, p5, p4, p3, p2, p1) \ |
1507 | 0 | (previous_words_count >= 7 && \ |
1508 | 0 | word_matches(p1, prev_wd) && \ |
1509 | 0 | word_matches(p2, prev2_wd) && \ |
1510 | 0 | word_matches(p3, prev3_wd) && \ |
1511 | 0 | word_matches(p4, prev4_wd) && \ |
1512 | 0 | word_matches(p5, prev5_wd) && \ |
1513 | 0 | word_matches(p6, prev6_wd) && \ |
1514 | 0 | word_matches(p7, prev7_wd)) |
1515 | |
|
1516 | 0 | #define TailMatches8(p8, p7, p6, p5, p4, p3, p2, p1) \ |
1517 | 0 | (previous_words_count >= 8 && \ |
1518 | 0 | word_matches(p1, prev_wd) && \ |
1519 | 0 | word_matches(p2, prev2_wd) && \ |
1520 | 0 | word_matches(p3, prev3_wd) && \ |
1521 | 0 | word_matches(p4, prev4_wd) && \ |
1522 | 0 | word_matches(p5, prev5_wd) && \ |
1523 | 0 | word_matches(p6, prev6_wd) && \ |
1524 | 0 | word_matches(p7, prev7_wd) && \ |
1525 | 0 | word_matches(p8, prev8_wd)) |
1526 | |
|
1527 | 0 | #define TailMatches9(p9, p8, p7, p6, p5, p4, p3, p2, p1) \ |
1528 | 0 | (previous_words_count >= 9 && \ |
1529 | 0 | word_matches(p1, prev_wd) && \ |
1530 | 0 | word_matches(p2, prev2_wd) && \ |
1531 | 0 | word_matches(p3, prev3_wd) && \ |
1532 | 0 | word_matches(p4, prev4_wd) && \ |
1533 | 0 | word_matches(p5, prev5_wd) && \ |
1534 | 0 | word_matches(p6, prev6_wd) && \ |
1535 | 0 | word_matches(p7, prev7_wd) && \ |
1536 | 0 | word_matches(p8, prev8_wd) && \ |
1537 | 0 | word_matches(p9, prev9_wd)) |
1538 | | |
1539 | | /* Macros for matching the last N words before point, case-sensitively. */ |
1540 | 0 | #define TailMatchesCS1(p1) \ |
1541 | 0 | (previous_words_count >= 1 && \ |
1542 | 0 | word_matches_cs(p1, prev_wd)) |
1543 | 0 | #define TailMatchesCS2(p2, p1) \ |
1544 | 0 | (previous_words_count >= 2 && \ |
1545 | 0 | word_matches_cs(p1, prev_wd) && \ |
1546 | 0 | word_matches_cs(p2, prev2_wd)) |
1547 | 0 | #define TailMatchesCS3(p3, p2, p1) \ |
1548 | 0 | (previous_words_count >= 3 && \ |
1549 | 0 | word_matches_cs(p1, prev_wd) && \ |
1550 | 0 | word_matches_cs(p2, prev2_wd) && \ |
1551 | 0 | word_matches_cs(p3, prev3_wd)) |
1552 | 0 | #define TailMatchesCS4(p4, p3, p2, p1) \ |
1553 | 0 | (previous_words_count >= 4 && \ |
1554 | 0 | word_matches_cs(p1, prev_wd) && \ |
1555 | 0 | word_matches_cs(p2, prev2_wd) && \ |
1556 | 0 | word_matches_cs(p3, prev3_wd) && \ |
1557 | 0 | word_matches_cs(p4, prev4_wd)) |
1558 | | |
1559 | | /* |
1560 | | * Macros for matching N words beginning at the start of the line, |
1561 | | * case-insensitively. |
1562 | | */ |
1563 | 0 | #define Matches1(p1) \ |
1564 | 0 | (previous_words_count == 1 && \ |
1565 | 0 | TailMatches1(p1)) |
1566 | 0 | #define Matches2(p1, p2) \ |
1567 | 0 | (previous_words_count == 2 && \ |
1568 | 0 | TailMatches2(p1, p2)) |
1569 | 0 | #define Matches3(p1, p2, p3) \ |
1570 | 0 | (previous_words_count == 3 && \ |
1571 | 0 | TailMatches3(p1, p2, p3)) |
1572 | 0 | #define Matches4(p1, p2, p3, p4) \ |
1573 | 0 | (previous_words_count == 4 && \ |
1574 | 0 | TailMatches4(p1, p2, p3, p4)) |
1575 | 0 | #define Matches5(p1, p2, p3, p4, p5) \ |
1576 | 0 | (previous_words_count == 5 && \ |
1577 | 0 | TailMatches5(p1, p2, p3, p4, p5)) |
1578 | 0 | #define Matches6(p1, p2, p3, p4, p5, p6) \ |
1579 | 0 | (previous_words_count == 6 && \ |
1580 | 0 | TailMatches6(p1, p2, p3, p4, p5, p6)) |
1581 | 0 | #define Matches7(p1, p2, p3, p4, p5, p6, p7) \ |
1582 | 0 | (previous_words_count == 7 && \ |
1583 | 0 | TailMatches7(p1, p2, p3, p4, p5, p6, p7)) |
1584 | 0 | #define Matches8(p1, p2, p3, p4, p5, p6, p7, p8) \ |
1585 | 0 | (previous_words_count == 8 && \ |
1586 | 0 | TailMatches8(p1, p2, p3, p4, p5, p6, p7, p8)) |
1587 | 0 | #define Matches9(p1, p2, p3, p4, p5, p6, p7, p8, p9) \ |
1588 | 0 | (previous_words_count == 9 && \ |
1589 | 0 | TailMatches9(p1, p2, p3, p4, p5, p6, p7, p8, p9)) |
1590 | | |
1591 | | /* |
1592 | | * Macros for matching N words at the start of the line, regardless of |
1593 | | * what is after them, case-insensitively. |
1594 | | */ |
1595 | 0 | #define HeadMatches1(p1) \ |
1596 | 0 | (previous_words_count >= 1 && \ |
1597 | 0 | word_matches(p1, previous_words[previous_words_count - 1])) |
1598 | |
|
1599 | 0 | #define HeadMatches2(p1, p2) \ |
1600 | 0 | (previous_words_count >= 2 && \ |
1601 | 0 | word_matches(p1, previous_words[previous_words_count - 1]) && \ |
1602 | 0 | word_matches(p2, previous_words[previous_words_count - 2])) |
1603 | |
|
1604 | 0 | #define HeadMatches3(p1, p2, p3) \ |
1605 | 0 | (previous_words_count >= 3 && \ |
1606 | 0 | word_matches(p1, previous_words[previous_words_count - 1]) && \ |
1607 | 0 | word_matches(p2, previous_words[previous_words_count - 2]) && \ |
1608 | 0 | word_matches(p3, previous_words[previous_words_count - 3])) |
1609 | |
|
1610 | 0 | #define HeadMatches4(p1, p2, p3, p4) \ |
1611 | 0 | (previous_words_count >= 4 && \ |
1612 | 0 | word_matches(p1, previous_words[previous_words_count - 1]) && \ |
1613 | 0 | word_matches(p2, previous_words[previous_words_count - 2]) && \ |
1614 | 0 | word_matches(p3, previous_words[previous_words_count - 3]) && \ |
1615 | 0 | word_matches(p4, previous_words[previous_words_count - 4])) |
1616 | |
|
1617 | 0 | #define HeadMatches5(p1, p2, p3, p4, p5) \ |
1618 | 0 | (previous_words_count >= 5 && \ |
1619 | 0 | word_matches(p1, previous_words[previous_words_count - 1]) && \ |
1620 | 0 | word_matches(p2, previous_words[previous_words_count - 2]) && \ |
1621 | 0 | word_matches(p3, previous_words[previous_words_count - 3]) && \ |
1622 | 0 | word_matches(p4, previous_words[previous_words_count - 4]) && \ |
1623 | 0 | word_matches(p5, previous_words[previous_words_count - 5])) |
1624 | | |
1625 | | /* Known command-starting keywords. */ |
1626 | 0 | static const char *const sql_commands[] = { |
1627 | 0 | "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER", |
1628 | 0 | "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", |
1629 | 0 | "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", |
1630 | 0 | "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK", |
1631 | 0 | "MOVE", "NOTIFY", "PREPARE", |
1632 | 0 | "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE", |
1633 | 0 | "RESET", "REVOKE", "ROLLBACK", |
1634 | 0 | "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START", |
1635 | 0 | "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH", |
1636 | 0 | NULL |
1637 | 0 | }; |
1638 | | |
1639 | | /* psql's backslash commands. */ |
1640 | 0 | static const char *const backslash_commands[] = { |
1641 | 0 | "\\a", |
1642 | 0 | "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", |
1643 | 0 | "\\copyright", "\\crosstabview", |
1644 | 0 | "\\d", "\\da", "\\dA", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD", |
1645 | 0 | "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df", |
1646 | 0 | "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL", |
1647 | 0 | "\\dm", "\\dn", "\\do", "\\dO", "\\dp", |
1648 | 0 | "\\drds", "\\dRs", "\\dRp", "\\ds", "\\dS", |
1649 | 0 | "\\dt", "\\dT", "\\dv", "\\du", "\\dx", "\\dy", |
1650 | 0 | "\\e", "\\echo", "\\ef", "\\elif", "\\else", "\\encoding", |
1651 | 0 | "\\endif", "\\errverbose", "\\ev", |
1652 | 0 | "\\f", |
1653 | 0 | "\\g", "\\gdesc", "\\gexec", "\\gset", "\\gx", |
1654 | 0 | "\\h", "\\help", "\\H", |
1655 | 0 | "\\i", "\\if", "\\ir", |
1656 | 0 | "\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", |
1657 | 0 | "\\o", |
1658 | 0 | "\\p", "\\password", "\\prompt", "\\pset", |
1659 | 0 | "\\q", "\\qecho", |
1660 | 0 | "\\r", |
1661 | 0 | "\\s", "\\set", "\\setenv", "\\sf", "\\sv", |
1662 | 0 | "\\t", "\\T", "\\timing", |
1663 | 0 | "\\unset", |
1664 | 0 | "\\x", |
1665 | 0 | "\\w", "\\watch", |
1666 | 0 | "\\z", |
1667 | 0 | "\\!", "\\?", |
1668 | 0 | NULL |
1669 | 0 | }; |
1670 | |
|
1671 | 0 | (void) end; /* "end" is not used */ |
1672 | |
|
1673 | | #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER |
1674 | | rl_completion_append_character = ' '; |
1675 | | #endif |
1676 | | |
1677 | | /* Clear a few things. */ |
1678 | 0 | completion_charp = NULL; |
1679 | 0 | completion_charpp = NULL; |
1680 | 0 | completion_info_charp = NULL; |
1681 | 0 | completion_info_charp2 = NULL; |
1682 | | |
1683 | | /* |
1684 | | * Scan the input line to extract the words before our current position. |
1685 | | * According to those we'll make some smart decisions on what the user is |
1686 | | * probably intending to type. |
1687 | | */ |
1688 | 0 | previous_words = get_previous_words(start, |
1689 | 0 | &words_buffer, |
1690 | 0 | &previous_words_count); |
1691 | | |
1692 | | /* If current word is a backslash command, offer completions for that */ |
1693 | 0 | if (text[0] == '\\') |
1694 | 0 | COMPLETE_WITH_LIST_CS(backslash_commands); |
1695 | | |
1696 | | /* If current word is a variable interpolation, handle that case */ |
1697 | 0 | else if (text[0] == ':' && text[1] != ':') |
1698 | 0 | { |
1699 | 0 | if (text[1] == '\'') |
1700 | 0 | matches = complete_from_variables(text, ":'", "'", true); |
1701 | 0 | else if (text[1] == '"') |
1702 | 0 | matches = complete_from_variables(text, ":\"", "\"", true); |
1703 | 0 | else |
1704 | 0 | matches = complete_from_variables(text, ":", "", true); |
1705 | 0 | } |
1706 | | |
1707 | | /* If no previous word, suggest one of the basic sql commands */ |
1708 | 0 | else if (previous_words_count == 0) |
1709 | 0 | COMPLETE_WITH_LIST(sql_commands); |
1710 | | |
1711 | | /* CREATE */ |
1712 | | /* complete with something you can create */ |
1713 | 0 | else if (TailMatches1("CREATE")) |
1714 | 0 | matches = completion_matches(text, create_command_generator); |
1715 | | |
1716 | | /* DROP, but not DROP embedded in other commands */ |
1717 | | /* complete with something you can drop */ |
1718 | 0 | else if (Matches1("DROP")) |
1719 | 0 | matches = completion_matches(text, drop_command_generator); |
1720 | | |
1721 | | /* ALTER */ |
1722 | | |
1723 | | /* ALTER TABLE */ |
1724 | 0 | else if (Matches2("ALTER", "TABLE")) |
1725 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, |
1726 | 0 | "UNION SELECT 'ALL IN TABLESPACE'"); |
1727 | | |
1728 | | /* ALTER something */ |
1729 | 0 | else if (Matches1("ALTER")) |
1730 | 0 | matches = completion_matches(text, alter_command_generator); |
1731 | | /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */ |
1732 | 0 | else if (TailMatches4("ALL", "IN", "TABLESPACE", MatchAny)) |
1733 | 0 | COMPLETE_WITH_LIST2("SET TABLESPACE", "OWNED BY"); |
1734 | | /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */ |
1735 | 0 | else if (TailMatches6("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY")) |
1736 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
1737 | | /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */ |
1738 | 0 | else if (TailMatches7("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny)) |
1739 | 0 | COMPLETE_WITH_CONST("SET TABLESPACE"); |
1740 | | /* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> */ |
1741 | 0 | else if (Matches3("ALTER", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny)) |
1742 | 0 | COMPLETE_WITH_CONST("("); |
1743 | | /* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> (...) */ |
1744 | 0 | else if (Matches4("ALTER", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny)) |
1745 | 0 | { |
1746 | 0 | if (ends_with(prev_wd, ')')) |
1747 | 0 | COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); |
1748 | 0 | else |
1749 | 0 | COMPLETE_WITH_FUNCTION_ARG(prev2_wd); |
1750 | 0 | } |
1751 | | /* ALTER PUBLICATION <name> */ |
1752 | 0 | else if (Matches3("ALTER", "PUBLICATION", MatchAny)) |
1753 | 0 | COMPLETE_WITH_LIST5("ADD TABLE", "DROP TABLE", "OWNER TO", "RENAME TO", "SET"); |
1754 | | /* ALTER PUBLICATION <name> SET */ |
1755 | 0 | else if (Matches4("ALTER", "PUBLICATION", MatchAny, "SET")) |
1756 | 0 | COMPLETE_WITH_LIST2("(", "TABLE"); |
1757 | | /* ALTER PUBLICATION <name> SET ( */ |
1758 | 0 | else if (HeadMatches3("ALTER", "PUBLICATION", MatchAny) && TailMatches2("SET", "(")) |
1759 | 0 | COMPLETE_WITH_CONST("publish"); |
1760 | | /* ALTER SUBSCRIPTION <name> */ |
1761 | 0 | else if (Matches3("ALTER", "SUBSCRIPTION", MatchAny)) |
1762 | 0 | COMPLETE_WITH_LIST7("CONNECTION", "ENABLE", "DISABLE", "OWNER TO", |
1763 | 0 | "RENAME TO", "REFRESH PUBLICATION", "SET"); |
1764 | | /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */ |
1765 | 0 | else if (HeadMatches3("ALTER", "SUBSCRIPTION", MatchAny) && |
1766 | 0 | TailMatches2("REFRESH", "PUBLICATION")) |
1767 | 0 | COMPLETE_WITH_CONST("WITH ("); |
1768 | | /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */ |
1769 | 0 | else if (HeadMatches3("ALTER", "SUBSCRIPTION", MatchAny) && |
1770 | 0 | TailMatches4("REFRESH", "PUBLICATION", "WITH", "(")) |
1771 | 0 | COMPLETE_WITH_CONST("copy_data"); |
1772 | | /* ALTER SUBSCRIPTION <name> SET */ |
1773 | 0 | else if (Matches4("ALTER", "SUBSCRIPTION", MatchAny, "SET")) |
1774 | 0 | COMPLETE_WITH_LIST2("(", "PUBLICATION"); |
1775 | | /* ALTER SUBSCRIPTION <name> SET ( */ |
1776 | 0 | else if (HeadMatches3("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches2("SET", "(")) |
1777 | 0 | COMPLETE_WITH_LIST2("slot_name", "synchronous_commit"); |
1778 | | /* ALTER SUBSCRIPTION <name> SET PUBLICATION */ |
1779 | 0 | else if (HeadMatches3("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches2("SET", "PUBLICATION")) |
1780 | 0 | { |
1781 | | /* complete with nothing here as this refers to remote publications */ |
1782 | 0 | } |
1783 | | /* ALTER SUBSCRIPTION <name> SET PUBLICATION <name> */ |
1784 | 0 | else if (HeadMatches3("ALTER", "SUBSCRIPTION", MatchAny) && |
1785 | 0 | TailMatches3("SET", "PUBLICATION", MatchAny)) |
1786 | 0 | COMPLETE_WITH_CONST("WITH ("); |
1787 | | /* ALTER SUBSCRIPTION <name> SET PUBLICATION <name> WITH ( */ |
1788 | 0 | else if (HeadMatches3("ALTER", "SUBSCRIPTION", MatchAny) && |
1789 | 0 | TailMatches5("SET", "PUBLICATION", MatchAny, "WITH", "(")) |
1790 | 0 | COMPLETE_WITH_LIST2("copy_data", "refresh"); |
1791 | | /* ALTER SCHEMA <name> */ |
1792 | 0 | else if (Matches3("ALTER", "SCHEMA", MatchAny)) |
1793 | 0 | COMPLETE_WITH_LIST2("OWNER TO", "RENAME TO"); |
1794 | | |
1795 | | /* ALTER COLLATION <name> */ |
1796 | 0 | else if (Matches3("ALTER", "COLLATION", MatchAny)) |
1797 | 0 | COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); |
1798 | | |
1799 | | /* ALTER CONVERSION <name> */ |
1800 | 0 | else if (Matches3("ALTER", "CONVERSION", MatchAny)) |
1801 | 0 | COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); |
1802 | | |
1803 | | /* ALTER DATABASE <name> */ |
1804 | 0 | else if (Matches3("ALTER", "DATABASE", MatchAny)) |
1805 | 0 | COMPLETE_WITH_LIST7("RESET", "SET", "OWNER TO", "RENAME TO", |
1806 | 0 | "IS_TEMPLATE", "ALLOW_CONNECTIONS", |
1807 | 0 | "CONNECTION LIMIT"); |
1808 | | |
1809 | | /* ALTER EVENT TRIGGER */ |
1810 | 0 | else if (Matches3("ALTER", "EVENT", "TRIGGER")) |
1811 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); |
1812 | | |
1813 | | /* ALTER EVENT TRIGGER <name> */ |
1814 | 0 | else if (Matches4("ALTER", "EVENT", "TRIGGER", MatchAny)) |
1815 | 0 | COMPLETE_WITH_LIST4("DISABLE", "ENABLE", "OWNER TO", "RENAME TO"); |
1816 | | |
1817 | | /* ALTER EVENT TRIGGER <name> ENABLE */ |
1818 | 0 | else if (Matches5("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE")) |
1819 | 0 | COMPLETE_WITH_LIST2("REPLICA", "ALWAYS"); |
1820 | | |
1821 | | /* ALTER EXTENSION <name> */ |
1822 | 0 | else if (Matches3("ALTER", "EXTENSION", MatchAny)) |
1823 | 0 | COMPLETE_WITH_LIST4("ADD", "DROP", "UPDATE", "SET SCHEMA"); |
1824 | | |
1825 | | /* ALTER EXTENSION <name> UPDATE */ |
1826 | 0 | else if (Matches4("ALTER", "EXTENSION", MatchAny, "UPDATE")) |
1827 | 0 | { |
1828 | 0 | completion_info_charp = prev2_wd; |
1829 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions_with_TO); |
1830 | 0 | } |
1831 | | |
1832 | | /* ALTER EXTENSION <name> UPDATE TO */ |
1833 | 0 | else if (Matches5("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO")) |
1834 | 0 | { |
1835 | 0 | completion_info_charp = prev3_wd; |
1836 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions); |
1837 | 0 | } |
1838 | | |
1839 | | /* ALTER FOREIGN */ |
1840 | 0 | else if (Matches2("ALTER", "FOREIGN")) |
1841 | 0 | COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); |
1842 | | |
1843 | | /* ALTER FOREIGN DATA WRAPPER <name> */ |
1844 | 0 | else if (Matches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny)) |
1845 | 0 | COMPLETE_WITH_LIST5("HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", "RENAME TO"); |
1846 | | |
1847 | | /* ALTER FOREIGN TABLE <name> */ |
1848 | 0 | else if (Matches4("ALTER", "FOREIGN", "TABLE", MatchAny)) |
1849 | 0 | { |
1850 | 0 | static const char *const list_ALTER_FOREIGN_TABLE[] = |
1851 | 0 | {"ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE", "INHERIT", |
1852 | 0 | "NO INHERIT", "OPTIONS", "OWNER TO", "RENAME", "SET", |
1853 | 0 | "VALIDATE CONSTRAINT", NULL}; |
1854 | |
|
1855 | 0 | COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE); |
1856 | 0 | } |
1857 | | |
1858 | | /* ALTER INDEX */ |
1859 | 0 | else if (Matches2("ALTER", "INDEX")) |
1860 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, |
1861 | 0 | "UNION SELECT 'ALL IN TABLESPACE'"); |
1862 | | /* ALTER INDEX <name> */ |
1863 | 0 | else if (Matches3("ALTER", "INDEX", MatchAny)) |
1864 | 0 | COMPLETE_WITH_LIST6("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET", |
1865 | 0 | "RESET", "ATTACH PARTITION"); |
1866 | 0 | else if (Matches4("ALTER", "INDEX", MatchAny, "ATTACH")) |
1867 | 0 | COMPLETE_WITH_CONST("PARTITION"); |
1868 | 0 | else if (Matches5("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION")) |
1869 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); |
1870 | | /* ALTER INDEX <name> ALTER COLUMN <colnum> */ |
1871 | 0 | else if (Matches6("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny)) |
1872 | 0 | COMPLETE_WITH_CONST("SET STATISTICS"); |
1873 | | /* ALTER INDEX <name> SET */ |
1874 | 0 | else if (Matches4("ALTER", "INDEX", MatchAny, "SET")) |
1875 | 0 | COMPLETE_WITH_LIST2("(", "TABLESPACE"); |
1876 | | /* ALTER INDEX <name> RESET */ |
1877 | 0 | else if (Matches4("ALTER", "INDEX", MatchAny, "RESET")) |
1878 | 0 | COMPLETE_WITH_CONST("("); |
1879 | | /* ALTER INDEX <foo> SET|RESET ( */ |
1880 | 0 | else if (Matches5("ALTER", "INDEX", MatchAny, "RESET", "(")) |
1881 | 0 | COMPLETE_WITH_LIST8("fillfactor", "recheck_on_update", |
1882 | 0 | "vacuum_cleanup_index_scale_factor", /* BTREE */ |
1883 | 0 | "fastupdate", "gin_pending_list_limit", /* GIN */ |
1884 | 0 | "buffering", /* GiST */ |
1885 | 0 | "pages_per_range", "autosummarize" /* BRIN */ |
1886 | 0 | ); |
1887 | 0 | else if (Matches5("ALTER", "INDEX", MatchAny, "SET", "(")) |
1888 | 0 | COMPLETE_WITH_LIST8("fillfactor =", "recheck_on_update =", |
1889 | 0 | "vacuum_cleanup_index_scale_factor =", /* BTREE */ |
1890 | 0 | "fastupdate =", "gin_pending_list_limit =", /* GIN */ |
1891 | 0 | "buffering =", /* GiST */ |
1892 | 0 | "pages_per_range =", "autosummarize =" /* BRIN */ |
1893 | 0 | ); |
1894 | | |
1895 | | /* ALTER LANGUAGE <name> */ |
1896 | 0 | else if (Matches3("ALTER", "LANGUAGE", MatchAny)) |
1897 | 0 | COMPLETE_WITH_LIST2("OWNER_TO", "RENAME TO"); |
1898 | | |
1899 | | /* ALTER LARGE OBJECT <oid> */ |
1900 | 0 | else if (Matches4("ALTER", "LARGE", "OBJECT", MatchAny)) |
1901 | 0 | COMPLETE_WITH_CONST("OWNER TO"); |
1902 | | |
1903 | | /* ALTER MATERIALIZED VIEW */ |
1904 | 0 | else if (Matches3("ALTER", "MATERIALIZED", "VIEW")) |
1905 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, |
1906 | 0 | "UNION SELECT 'ALL IN TABLESPACE'"); |
1907 | | |
1908 | | /* ALTER USER,ROLE <name> */ |
1909 | 0 | else if (Matches3("ALTER", "USER|ROLE", MatchAny) && |
1910 | 0 | !TailMatches2("USER", "MAPPING")) |
1911 | 0 | { |
1912 | 0 | static const char *const list_ALTERUSER[] = |
1913 | 0 | {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", |
1914 | 0 | "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS", |
1915 | 0 | "NOCREATEDB", "NOCREATEROLE", "NOINHERIT", |
1916 | 0 | "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", "RENAME TO", |
1917 | 0 | "REPLICATION", "RESET", "SET", "SUPERUSER", |
1918 | 0 | "VALID UNTIL", "WITH", NULL}; |
1919 | |
|
1920 | 0 | COMPLETE_WITH_LIST(list_ALTERUSER); |
1921 | 0 | } |
1922 | | |
1923 | | /* ALTER USER,ROLE <name> WITH */ |
1924 | 0 | else if (Matches4("ALTER", "USER|ROLE", MatchAny, "WITH")) |
1925 | 0 | { |
1926 | | /* Similar to the above, but don't complete "WITH" again. */ |
1927 | 0 | static const char *const list_ALTERUSER_WITH[] = |
1928 | 0 | {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", |
1929 | 0 | "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS", |
1930 | 0 | "NOCREATEDB", "NOCREATEROLE", "NOINHERIT", |
1931 | 0 | "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", "RENAME TO", |
1932 | 0 | "REPLICATION", "RESET", "SET", "SUPERUSER", |
1933 | 0 | "VALID UNTIL", NULL}; |
1934 | |
|
1935 | 0 | COMPLETE_WITH_LIST(list_ALTERUSER_WITH); |
1936 | 0 | } |
1937 | | |
1938 | | /* ALTER DEFAULT PRIVILEGES */ |
1939 | 0 | else if (Matches3("ALTER", "DEFAULT", "PRIVILEGES")) |
1940 | 0 | COMPLETE_WITH_LIST2("FOR ROLE", "IN SCHEMA"); |
1941 | | /* ALTER DEFAULT PRIVILEGES FOR */ |
1942 | 0 | else if (Matches4("ALTER", "DEFAULT", "PRIVILEGES", "FOR")) |
1943 | 0 | COMPLETE_WITH_CONST("ROLE"); |
1944 | | /* ALTER DEFAULT PRIVILEGES IN */ |
1945 | 0 | else if (Matches4("ALTER", "DEFAULT", "PRIVILEGES", "IN")) |
1946 | 0 | COMPLETE_WITH_CONST("SCHEMA"); |
1947 | | /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... */ |
1948 | 0 | else if (Matches6("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", |
1949 | 0 | MatchAny)) |
1950 | 0 | COMPLETE_WITH_LIST3("GRANT", "REVOKE", "IN SCHEMA"); |
1951 | | /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... */ |
1952 | 0 | else if (Matches6("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", |
1953 | 0 | MatchAny)) |
1954 | 0 | COMPLETE_WITH_LIST3("GRANT", "REVOKE", "FOR ROLE"); |
1955 | | /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR */ |
1956 | 0 | else if (Matches7("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", |
1957 | 0 | MatchAny, "FOR")) |
1958 | 0 | COMPLETE_WITH_CONST("ROLE"); |
1959 | | /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... IN SCHEMA ... */ |
1960 | | /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR ROLE|USER ... */ |
1961 | 0 | else if (Matches9("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", |
1962 | 0 | MatchAny, "IN", "SCHEMA", MatchAny) || |
1963 | 0 | Matches9("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", |
1964 | 0 | MatchAny, "FOR", "ROLE|USER", MatchAny)) |
1965 | 0 | COMPLETE_WITH_LIST2("GRANT", "REVOKE"); |
1966 | | /* ALTER DOMAIN <name> */ |
1967 | 0 | else if (Matches3("ALTER", "DOMAIN", MatchAny)) |
1968 | 0 | COMPLETE_WITH_LIST6("ADD", "DROP", "OWNER TO", "RENAME", "SET", |
1969 | 0 | "VALIDATE CONSTRAINT"); |
1970 | | /* ALTER DOMAIN <sth> DROP */ |
1971 | 0 | else if (Matches4("ALTER", "DOMAIN", MatchAny, "DROP")) |
1972 | 0 | COMPLETE_WITH_LIST3("CONSTRAINT", "DEFAULT", "NOT NULL"); |
1973 | | /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */ |
1974 | 0 | else if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT")) |
1975 | 0 | { |
1976 | 0 | completion_info_charp = prev3_wd; |
1977 | 0 | COMPLETE_WITH_QUERY(Query_for_constraint_of_type); |
1978 | 0 | } |
1979 | | /* ALTER DOMAIN <sth> RENAME */ |
1980 | 0 | else if (Matches4("ALTER", "DOMAIN", MatchAny, "RENAME")) |
1981 | 0 | COMPLETE_WITH_LIST2("CONSTRAINT", "TO"); |
1982 | | /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */ |
1983 | 0 | else if (Matches6("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny)) |
1984 | 0 | COMPLETE_WITH_CONST("TO"); |
1985 | | |
1986 | | /* ALTER DOMAIN <sth> SET */ |
1987 | 0 | else if (Matches4("ALTER", "DOMAIN", MatchAny, "SET")) |
1988 | 0 | COMPLETE_WITH_LIST3("DEFAULT", "NOT NULL", "SCHEMA"); |
1989 | | /* ALTER SEQUENCE <name> */ |
1990 | 0 | else if (Matches3("ALTER", "SEQUENCE", MatchAny)) |
1991 | 0 | { |
1992 | 0 | static const char *const list_ALTERSEQUENCE[] = |
1993 | 0 | {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", |
1994 | 0 | "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL}; |
1995 | |
|
1996 | 0 | COMPLETE_WITH_LIST(list_ALTERSEQUENCE); |
1997 | 0 | } |
1998 | | /* ALTER SEQUENCE <name> NO */ |
1999 | 0 | else if (Matches4("ALTER", "SEQUENCE", MatchAny, "NO")) |
2000 | 0 | COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE"); |
2001 | | /* ALTER SERVER <name> */ |
2002 | 0 | else if (Matches3("ALTER", "SERVER", MatchAny)) |
2003 | 0 | COMPLETE_WITH_LIST4("VERSION", "OPTIONS", "OWNER TO", "RENAME TO"); |
2004 | | /* ALTER SERVER <name> VERSION <version> */ |
2005 | 0 | else if (Matches5("ALTER", "SERVER", MatchAny, "VERSION", MatchAny)) |
2006 | 0 | COMPLETE_WITH_CONST("OPTIONS"); |
2007 | | /* ALTER SYSTEM SET, RESET, RESET ALL */ |
2008 | 0 | else if (Matches2("ALTER", "SYSTEM")) |
2009 | 0 | COMPLETE_WITH_LIST2("SET", "RESET"); |
2010 | 0 | else if (Matches3("ALTER", "SYSTEM", "SET|RESET")) |
2011 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars); |
2012 | 0 | else if (Matches4("ALTER", "SYSTEM", "SET", MatchAny)) |
2013 | 0 | COMPLETE_WITH_CONST("TO"); |
2014 | | /* ALTER VIEW <name> */ |
2015 | 0 | else if (Matches3("ALTER", "VIEW", MatchAny)) |
2016 | 0 | COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO", |
2017 | 0 | "SET SCHEMA"); |
2018 | | /* ALTER MATERIALIZED VIEW <name> */ |
2019 | 0 | else if (Matches4("ALTER", "MATERIALIZED", "VIEW", MatchAny)) |
2020 | 0 | COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO", |
2021 | 0 | "SET SCHEMA"); |
2022 | | |
2023 | | /* ALTER POLICY <name> */ |
2024 | 0 | else if (Matches2("ALTER", "POLICY")) |
2025 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_policies); |
2026 | | /* ALTER POLICY <name> ON */ |
2027 | 0 | else if (Matches3("ALTER", "POLICY", MatchAny)) |
2028 | 0 | COMPLETE_WITH_CONST("ON"); |
2029 | | /* ALTER POLICY <name> ON <table> */ |
2030 | 0 | else if (Matches4("ALTER", "POLICY", MatchAny, "ON")) |
2031 | 0 | { |
2032 | 0 | completion_info_charp = prev2_wd; |
2033 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy); |
2034 | 0 | } |
2035 | | /* ALTER POLICY <name> ON <table> - show options */ |
2036 | 0 | else if (Matches5("ALTER", "POLICY", MatchAny, "ON", MatchAny)) |
2037 | 0 | COMPLETE_WITH_LIST4("RENAME TO", "TO", "USING (", "WITH CHECK ("); |
2038 | | /* ALTER POLICY <name> ON <table> TO <role> */ |
2039 | 0 | else if (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO")) |
2040 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); |
2041 | | /* ALTER POLICY <name> ON <table> USING ( */ |
2042 | 0 | else if (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING")) |
2043 | 0 | COMPLETE_WITH_CONST("("); |
2044 | | /* ALTER POLICY <name> ON <table> WITH CHECK ( */ |
2045 | 0 | else if (Matches7("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK")) |
2046 | 0 | COMPLETE_WITH_CONST("("); |
2047 | | |
2048 | | /* ALTER RULE <name>, add ON */ |
2049 | 0 | else if (Matches3("ALTER", "RULE", MatchAny)) |
2050 | 0 | COMPLETE_WITH_CONST("ON"); |
2051 | | |
2052 | | /* If we have ALTER RULE <name> ON, then add the correct tablename */ |
2053 | 0 | else if (Matches4("ALTER", "RULE", MatchAny, "ON")) |
2054 | 0 | { |
2055 | 0 | completion_info_charp = prev2_wd; |
2056 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule); |
2057 | 0 | } |
2058 | | |
2059 | | /* ALTER RULE <name> ON <name> */ |
2060 | 0 | else if (Matches5("ALTER", "RULE", MatchAny, "ON", MatchAny)) |
2061 | 0 | COMPLETE_WITH_CONST("RENAME TO"); |
2062 | | |
2063 | | /* ALTER STATISTICS <name> */ |
2064 | 0 | else if (Matches3("ALTER", "STATISTICS", MatchAny)) |
2065 | 0 | COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); |
2066 | | |
2067 | | /* ALTER TRIGGER <name>, add ON */ |
2068 | 0 | else if (Matches3("ALTER", "TRIGGER", MatchAny)) |
2069 | 0 | COMPLETE_WITH_CONST("ON"); |
2070 | | |
2071 | 0 | else if (Matches4("ALTER", "TRIGGER", MatchAny, MatchAny)) |
2072 | 0 | { |
2073 | 0 | completion_info_charp = prev2_wd; |
2074 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); |
2075 | 0 | } |
2076 | | |
2077 | | /* |
2078 | | * If we have ALTER TRIGGER <sth> ON, then add the correct tablename |
2079 | | */ |
2080 | 0 | else if (Matches4("ALTER", "TRIGGER", MatchAny, "ON")) |
2081 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2082 | | |
2083 | | /* ALTER TRIGGER <name> ON <name> */ |
2084 | 0 | else if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny)) |
2085 | 0 | COMPLETE_WITH_CONST("RENAME TO"); |
2086 | | |
2087 | | /* |
2088 | | * If we detect ALTER TABLE <name>, suggest sub commands |
2089 | | */ |
2090 | 0 | else if (Matches3("ALTER", "TABLE", MatchAny)) |
2091 | 0 | { |
2092 | 0 | static const char *const list_ALTER2[] = |
2093 | 0 | {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT", |
2094 | 0 | "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", |
2095 | 0 | "VALIDATE CONSTRAINT", "REPLICA IDENTITY", "ATTACH PARTITION", |
2096 | 0 | "DETACH PARTITION", NULL}; |
2097 | |
|
2098 | 0 | COMPLETE_WITH_LIST(list_ALTER2); |
2099 | 0 | } |
2100 | | /* ALTER TABLE xxx ENABLE */ |
2101 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "ENABLE")) |
2102 | 0 | COMPLETE_WITH_LIST5("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE", |
2103 | 0 | "TRIGGER"); |
2104 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS")) |
2105 | 0 | COMPLETE_WITH_LIST2("RULE", "TRIGGER"); |
2106 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "RULE")) |
2107 | 0 | { |
2108 | 0 | completion_info_charp = prev3_wd; |
2109 | 0 | COMPLETE_WITH_QUERY(Query_for_rule_of_table); |
2110 | 0 | } |
2111 | 0 | else if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE")) |
2112 | 0 | { |
2113 | 0 | completion_info_charp = prev4_wd; |
2114 | 0 | COMPLETE_WITH_QUERY(Query_for_rule_of_table); |
2115 | 0 | } |
2116 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER")) |
2117 | 0 | { |
2118 | 0 | completion_info_charp = prev3_wd; |
2119 | 0 | COMPLETE_WITH_QUERY(Query_for_trigger_of_table); |
2120 | 0 | } |
2121 | 0 | else if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER")) |
2122 | 0 | { |
2123 | 0 | completion_info_charp = prev4_wd; |
2124 | 0 | COMPLETE_WITH_QUERY(Query_for_trigger_of_table); |
2125 | 0 | } |
2126 | | /* ALTER TABLE xxx INHERIT */ |
2127 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "INHERIT")) |
2128 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); |
2129 | | /* ALTER TABLE xxx NO INHERIT */ |
2130 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT")) |
2131 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); |
2132 | | /* ALTER TABLE xxx DISABLE */ |
2133 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE")) |
2134 | 0 | COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER"); |
2135 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "RULE")) |
2136 | 0 | { |
2137 | 0 | completion_info_charp = prev3_wd; |
2138 | 0 | COMPLETE_WITH_QUERY(Query_for_rule_of_table); |
2139 | 0 | } |
2140 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER")) |
2141 | 0 | { |
2142 | 0 | completion_info_charp = prev3_wd; |
2143 | 0 | COMPLETE_WITH_QUERY(Query_for_trigger_of_table); |
2144 | 0 | } |
2145 | | |
2146 | | /* ALTER TABLE xxx ALTER */ |
2147 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "ALTER")) |
2148 | 0 | COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'"); |
2149 | | |
2150 | | /* ALTER TABLE xxx RENAME */ |
2151 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "RENAME")) |
2152 | 0 | COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); |
2153 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN")) |
2154 | 0 | COMPLETE_WITH_ATTR(prev3_wd, ""); |
2155 | | |
2156 | | /* ALTER TABLE xxx RENAME yyy */ |
2157 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO"))) |
2158 | 0 | COMPLETE_WITH_CONST("TO"); |
2159 | | |
2160 | | /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */ |
2161 | 0 | else if (Matches6("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO"))) |
2162 | 0 | COMPLETE_WITH_CONST("TO"); |
2163 | | |
2164 | | /* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */ |
2165 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "DROP")) |
2166 | 0 | COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT"); |
2167 | | /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */ |
2168 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN")) |
2169 | 0 | COMPLETE_WITH_ATTR(prev3_wd, ""); |
2170 | | |
2171 | | /* |
2172 | | * If we have ALTER TABLE <sth> ALTER|DROP|RENAME|VALIDATE CONSTRAINT, |
2173 | | * provide list of constraints |
2174 | | */ |
2175 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME|VALIDATE", "CONSTRAINT")) |
2176 | 0 | { |
2177 | 0 | completion_info_charp = prev3_wd; |
2178 | 0 | COMPLETE_WITH_QUERY(Query_for_constraint_of_table); |
2179 | 0 | } |
2180 | | /* ALTER TABLE ALTER [COLUMN] <foo> */ |
2181 | 0 | else if (Matches6("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny) || |
2182 | 0 | Matches5("ALTER", "TABLE", MatchAny, "ALTER", MatchAny)) |
2183 | 0 | COMPLETE_WITH_LIST6("TYPE", "SET", "RESET", "RESTART", "ADD", "DROP"); |
2184 | | /* ALTER TABLE ALTER [COLUMN] <foo> SET */ |
2185 | 0 | else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") || |
2186 | 0 | Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET")) |
2187 | 0 | COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE"); |
2188 | | /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */ |
2189 | 0 | else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") || |
2190 | 0 | Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "(")) |
2191 | 0 | COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited"); |
2192 | | /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */ |
2193 | 0 | else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") || |
2194 | 0 | Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE")) |
2195 | 0 | COMPLETE_WITH_LIST4("PLAIN", "EXTERNAL", "EXTENDED", "MAIN"); |
2196 | | /* ALTER TABLE ALTER [COLUMN] <foo> DROP */ |
2197 | 0 | else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP") || |
2198 | 0 | Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "DROP")) |
2199 | 0 | COMPLETE_WITH_LIST3("DEFAULT", "IDENTITY", "NOT NULL"); |
2200 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "CLUSTER")) |
2201 | 0 | COMPLETE_WITH_CONST("ON"); |
2202 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "CLUSTER", "ON")) |
2203 | 0 | { |
2204 | 0 | completion_info_charp = prev3_wd; |
2205 | 0 | COMPLETE_WITH_QUERY(Query_for_index_of_table); |
2206 | 0 | } |
2207 | | /* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */ |
2208 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "SET")) |
2209 | 0 | COMPLETE_WITH_LIST7("(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", |
2210 | 0 | "WITH", "WITHOUT"); |
2211 | | |
2212 | | /* |
2213 | | * If we have ALTER TABLE <sth> SET TABLESPACE provide a list of |
2214 | | * tablespaces |
2215 | | */ |
2216 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE")) |
2217 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); |
2218 | | /* If we have ALTER TABLE <sth> SET WITH provide OIDS */ |
2219 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITH")) |
2220 | 0 | COMPLETE_WITH_CONST("OIDS"); |
2221 | | /* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */ |
2222 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITHOUT")) |
2223 | 0 | COMPLETE_WITH_LIST2("CLUSTER", "OIDS"); |
2224 | | /* ALTER TABLE <foo> RESET */ |
2225 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "RESET")) |
2226 | 0 | COMPLETE_WITH_CONST("("); |
2227 | | /* ALTER TABLE <foo> SET|RESET ( */ |
2228 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "SET|RESET", "(")) |
2229 | 0 | { |
2230 | 0 | static const char *const list_TABLEOPTIONS[] = |
2231 | 0 | { |
2232 | 0 | "autovacuum_analyze_scale_factor", |
2233 | 0 | "autovacuum_analyze_threshold", |
2234 | 0 | "autovacuum_enabled", |
2235 | 0 | "autovacuum_freeze_max_age", |
2236 | 0 | "autovacuum_freeze_min_age", |
2237 | 0 | "autovacuum_freeze_table_age", |
2238 | 0 | "autovacuum_multixact_freeze_max_age", |
2239 | 0 | "autovacuum_multixact_freeze_min_age", |
2240 | 0 | "autovacuum_multixact_freeze_table_age", |
2241 | 0 | "autovacuum_vacuum_cost_delay", |
2242 | 0 | "autovacuum_vacuum_cost_limit", |
2243 | 0 | "autovacuum_vacuum_scale_factor", |
2244 | 0 | "autovacuum_vacuum_threshold", |
2245 | 0 | "fillfactor", |
2246 | 0 | "parallel_workers", |
2247 | 0 | "log_autovacuum_min_duration", |
2248 | 0 | "toast_tuple_target", |
2249 | 0 | "toast.autovacuum_enabled", |
2250 | 0 | "toast.autovacuum_freeze_max_age", |
2251 | 0 | "toast.autovacuum_freeze_min_age", |
2252 | 0 | "toast.autovacuum_freeze_table_age", |
2253 | 0 | "toast.autovacuum_multixact_freeze_max_age", |
2254 | 0 | "toast.autovacuum_multixact_freeze_min_age", |
2255 | 0 | "toast.autovacuum_multixact_freeze_table_age", |
2256 | 0 | "toast.autovacuum_vacuum_cost_delay", |
2257 | 0 | "toast.autovacuum_vacuum_cost_limit", |
2258 | 0 | "toast.autovacuum_vacuum_scale_factor", |
2259 | 0 | "toast.autovacuum_vacuum_threshold", |
2260 | 0 | "toast.log_autovacuum_min_duration", |
2261 | 0 | "user_catalog_table", |
2262 | 0 | NULL |
2263 | 0 | }; |
2264 | |
|
2265 | 0 | COMPLETE_WITH_LIST(list_TABLEOPTIONS); |
2266 | 0 | } |
2267 | 0 | else if (Matches7("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX")) |
2268 | 0 | { |
2269 | 0 | completion_info_charp = prev5_wd; |
2270 | 0 | COMPLETE_WITH_QUERY(Query_for_index_of_table); |
2271 | 0 | } |
2272 | 0 | else if (Matches6("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING")) |
2273 | 0 | COMPLETE_WITH_CONST("INDEX"); |
2274 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY")) |
2275 | 0 | COMPLETE_WITH_LIST4("FULL", "NOTHING", "DEFAULT", "USING"); |
2276 | 0 | else if (Matches4("ALTER", "TABLE", MatchAny, "REPLICA")) |
2277 | 0 | COMPLETE_WITH_CONST("IDENTITY"); |
2278 | | |
2279 | | /* |
2280 | | * If we have ALTER TABLE <foo> ATTACH PARTITION, provide a list of |
2281 | | * tables. |
2282 | | */ |
2283 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "ATTACH", "PARTITION")) |
2284 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); |
2285 | | /* Limited completion support for partition bound specification */ |
2286 | 0 | else if (TailMatches3("ATTACH", "PARTITION", MatchAny)) |
2287 | 0 | COMPLETE_WITH_LIST2("FOR VALUES", "DEFAULT"); |
2288 | 0 | else if (TailMatches2("FOR", "VALUES")) |
2289 | 0 | COMPLETE_WITH_LIST3("FROM (", "IN (", "WITH ("); |
2290 | | |
2291 | | /* |
2292 | | * If we have ALTER TABLE <foo> DETACH PARTITION, provide a list of |
2293 | | * partitions of <foo>. |
2294 | | */ |
2295 | 0 | else if (Matches5("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION")) |
2296 | 0 | { |
2297 | 0 | completion_info_charp = prev3_wd; |
2298 | 0 | COMPLETE_WITH_QUERY(Query_for_partition_of_table); |
2299 | 0 | } |
2300 | | |
2301 | | /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */ |
2302 | 0 | else if (Matches3("ALTER", "TABLESPACE", MatchAny)) |
2303 | 0 | COMPLETE_WITH_LIST4("RENAME TO", "OWNER TO", "SET", "RESET"); |
2304 | | /* ALTER TABLESPACE <foo> SET|RESET */ |
2305 | 0 | else if (Matches4("ALTER", "TABLESPACE", MatchAny, "SET|RESET")) |
2306 | 0 | COMPLETE_WITH_CONST("("); |
2307 | | /* ALTER TABLESPACE <foo> SET|RESET ( */ |
2308 | 0 | else if (Matches5("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "(")) |
2309 | 0 | COMPLETE_WITH_LIST3("seq_page_cost", "random_page_cost", |
2310 | 0 | "effective_io_concurrency"); |
2311 | | /* ALTER TABLEGROUP <foo> with RENAME TO, OWNER TO */ |
2312 | 0 | else if (Matches3("ALTER", "TABLEGROUP", MatchAny)) |
2313 | 0 | COMPLETE_WITH_LIST2("RENAME TO", "OWNER TO"); |
2314 | | |
2315 | | /* ALTER TEXT SEARCH */ |
2316 | 0 | else if (Matches3("ALTER", "TEXT", "SEARCH")) |
2317 | 0 | COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); |
2318 | 0 | else if (Matches5("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny)) |
2319 | 0 | COMPLETE_WITH_LIST2("RENAME TO", "SET SCHEMA"); |
2320 | 0 | else if (Matches5("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny)) |
2321 | 0 | COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); |
2322 | 0 | else if (Matches5("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny)) |
2323 | 0 | COMPLETE_WITH_LIST6("ADD MAPPING FOR", "ALTER MAPPING", |
2324 | 0 | "DROP MAPPING FOR", |
2325 | 0 | "OWNER TO", "RENAME TO", "SET SCHEMA"); |
2326 | | |
2327 | | /* complete ALTER TYPE <foo> with actions */ |
2328 | 0 | else if (Matches3("ALTER", "TYPE", MatchAny)) |
2329 | 0 | COMPLETE_WITH_LIST7("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", |
2330 | 0 | "DROP ATTRIBUTE", |
2331 | 0 | "OWNER TO", "RENAME", "SET SCHEMA"); |
2332 | | /* complete ALTER TYPE <foo> ADD with actions */ |
2333 | 0 | else if (Matches4("ALTER", "TYPE", MatchAny, "ADD")) |
2334 | 0 | COMPLETE_WITH_LIST2("ATTRIBUTE", "VALUE"); |
2335 | | /* ALTER TYPE <foo> RENAME */ |
2336 | 0 | else if (Matches4("ALTER", "TYPE", MatchAny, "RENAME")) |
2337 | 0 | COMPLETE_WITH_LIST3("ATTRIBUTE", "TO", "VALUE"); |
2338 | | /* ALTER TYPE xxx RENAME (ATTRIBUTE|VALUE) yyy */ |
2339 | 0 | else if (Matches6("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE|VALUE", MatchAny)) |
2340 | 0 | COMPLETE_WITH_CONST("TO"); |
2341 | | |
2342 | | /* |
2343 | | * If we have ALTER TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list |
2344 | | * of attributes |
2345 | | */ |
2346 | 0 | else if (Matches5("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE")) |
2347 | 0 | COMPLETE_WITH_ATTR(prev3_wd, ""); |
2348 | | /* ALTER TYPE ALTER ATTRIBUTE <foo> */ |
2349 | 0 | else if (Matches6("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny)) |
2350 | 0 | COMPLETE_WITH_CONST("TYPE"); |
2351 | | /* complete ALTER GROUP <foo> */ |
2352 | 0 | else if (Matches3("ALTER", "GROUP", MatchAny)) |
2353 | 0 | COMPLETE_WITH_LIST3("ADD USER", "DROP USER", "RENAME TO"); |
2354 | | /* complete ALTER GROUP <foo> ADD|DROP with USER */ |
2355 | 0 | else if (Matches4("ALTER", "GROUP", MatchAny, "ADD|DROP")) |
2356 | 0 | COMPLETE_WITH_CONST("USER"); |
2357 | | /* complete ALTER GROUP <foo> ADD|DROP USER with a user name */ |
2358 | 0 | else if (Matches5("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER")) |
2359 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
2360 | | |
2361 | | /* |
2362 | | * If we have ALTER TYPE <sth> RENAME VALUE, provide list of enum values |
2363 | | */ |
2364 | 0 | else if (Matches5("ALTER", "TYPE", MatchAny, "RENAME", "VALUE")) |
2365 | 0 | COMPLETE_WITH_ENUM_VALUE(prev3_wd); |
2366 | | |
2367 | | /* BEGIN */ |
2368 | 0 | else if (Matches1("BEGIN")) |
2369 | 0 | COMPLETE_WITH_LIST6("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE"); |
2370 | | /* END, ABORT */ |
2371 | 0 | else if (Matches1("END|ABORT")) |
2372 | 0 | COMPLETE_WITH_LIST2("WORK", "TRANSACTION"); |
2373 | | /* COMMIT */ |
2374 | 0 | else if (Matches1("COMMIT")) |
2375 | 0 | COMPLETE_WITH_LIST3("WORK", "TRANSACTION", "PREPARED"); |
2376 | | /* RELEASE SAVEPOINT */ |
2377 | 0 | else if (Matches1("RELEASE")) |
2378 | 0 | COMPLETE_WITH_CONST("SAVEPOINT"); |
2379 | | /* ROLLBACK */ |
2380 | 0 | else if (Matches1("ROLLBACK")) |
2381 | 0 | COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED"); |
2382 | | /* CALL */ |
2383 | 0 | else if (Matches1("CALL")) |
2384 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL); |
2385 | 0 | else if (Matches2("CALL", MatchAny)) |
2386 | 0 | COMPLETE_WITH_CONST("("); |
2387 | | /* CLUSTER */ |
2388 | 0 | else if (Matches1("CLUSTER")) |
2389 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'"); |
2390 | 0 | else if (Matches2("CLUSTER", "VERBOSE")) |
2391 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); |
2392 | | /* If we have CLUSTER <sth>, then add "USING" */ |
2393 | 0 | else if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON"))) |
2394 | 0 | COMPLETE_WITH_CONST("USING"); |
2395 | | /* If we have CLUSTER VERBOSE <sth>, then add "USING" */ |
2396 | 0 | else if (Matches3("CLUSTER", "VERBOSE", MatchAny)) |
2397 | 0 | COMPLETE_WITH_CONST("USING"); |
2398 | | /* If we have CLUSTER <sth> USING, then add the index as well */ |
2399 | 0 | else if (Matches3("CLUSTER", MatchAny, "USING") || |
2400 | 0 | Matches4("CLUSTER", "VERBOSE", MatchAny, "USING")) |
2401 | 0 | { |
2402 | 0 | completion_info_charp = prev2_wd; |
2403 | 0 | COMPLETE_WITH_QUERY(Query_for_index_of_table); |
2404 | 0 | } |
2405 | | |
2406 | | /* COMMENT */ |
2407 | 0 | else if (Matches1("COMMENT")) |
2408 | 0 | COMPLETE_WITH_CONST("ON"); |
2409 | 0 | else if (Matches2("COMMENT", "ON")) |
2410 | 0 | { |
2411 | 0 | static const char *const list_COMMENT[] = |
2412 | 0 | {"ACCESS METHOD", "CAST", "COLLATION", "CONVERSION", "DATABASE", |
2413 | 0 | "EVENT TRIGGER", "EXTENSION", |
2414 | 0 | "FOREIGN DATA WRAPPER", "FOREIGN TABLE", |
2415 | 0 | "SERVER", "INDEX", "LANGUAGE", "POLICY", "PUBLICATION", "RULE", |
2416 | 0 | "SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION", |
2417 | 0 | "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", |
2418 | 0 | "PROCEDURE", "ROUTINE", |
2419 | 0 | "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", "TABLEGROUP", |
2420 | 0 | "TABLESPACE", "TEXT SEARCH", "ROLE", NULL}; |
2421 | |
|
2422 | 0 | COMPLETE_WITH_LIST(list_COMMENT); |
2423 | 0 | } |
2424 | 0 | else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD")) |
2425 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); |
2426 | 0 | else if (Matches3("COMMENT", "ON", "FOREIGN")) |
2427 | 0 | COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); |
2428 | 0 | else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH")) |
2429 | 0 | COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); |
2430 | 0 | else if (Matches3("COMMENT", "ON", "CONSTRAINT")) |
2431 | 0 | COMPLETE_WITH_QUERY(Query_for_all_table_constraints); |
2432 | 0 | else if (Matches4("COMMENT", "ON", "CONSTRAINT", MatchAny)) |
2433 | 0 | COMPLETE_WITH_CONST("ON"); |
2434 | 0 | else if (Matches5("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON")) |
2435 | 0 | { |
2436 | 0 | completion_info_charp = prev2_wd; |
2437 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint); |
2438 | 0 | } |
2439 | 0 | else if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW")) |
2440 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); |
2441 | 0 | else if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER")) |
2442 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); |
2443 | 0 | else if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) || |
2444 | 0 | Matches5("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) || |
2445 | 0 | Matches6("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS"))) |
2446 | 0 | COMPLETE_WITH_CONST("IS"); |
2447 | | |
2448 | | /* COPY */ |
2449 | | |
2450 | | /* |
2451 | | * If we have COPY, offer list of tables or "(" (Also cover the analogous |
2452 | | * backslash command). |
2453 | | */ |
2454 | 0 | else if (Matches1("COPY|\\copy")) |
2455 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, |
2456 | 0 | " UNION ALL SELECT '('"); |
2457 | | /* If we have COPY BINARY, complete with list of tables */ |
2458 | 0 | else if (Matches2("COPY", "BINARY")) |
2459 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2460 | | /* If we have COPY (, complete it with legal commands */ |
2461 | 0 | else if (Matches2("COPY|\\copy", "(")) |
2462 | 0 | COMPLETE_WITH_LIST7("SELECT", "TABLE", "VALUES", "INSERT", "UPDATE", "DELETE", "WITH"); |
2463 | | /* If we have COPY [BINARY] <sth>, complete it with "TO" or "FROM" */ |
2464 | 0 | else if (Matches2("COPY|\\copy", MatchAny) || |
2465 | 0 | Matches3("COPY", "BINARY", MatchAny)) |
2466 | 0 | COMPLETE_WITH_LIST2("FROM", "TO"); |
2467 | | /* If we have COPY [BINARY] <sth> FROM|TO, complete with filename */ |
2468 | 0 | else if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") || |
2469 | 0 | Matches4("COPY", "BINARY", MatchAny, "FROM|TO")) |
2470 | 0 | { |
2471 | 0 | completion_charp = ""; |
2472 | 0 | matches = completion_matches(text, complete_from_files); |
2473 | 0 | } |
2474 | | |
2475 | | /* Handle COPY [BINARY] <sth> FROM|TO filename */ |
2476 | 0 | else if (Matches4("COPY|\\copy", MatchAny, "FROM|TO", MatchAny) || |
2477 | 0 | Matches5("COPY", "BINARY", MatchAny, "FROM|TO", MatchAny)) |
2478 | 0 | COMPLETE_WITH_LIST6("BINARY", "OIDS", "DELIMITER", "NULL", "CSV", |
2479 | 0 | "ENCODING"); |
2480 | | |
2481 | | /* Handle COPY [BINARY] <sth> FROM|TO filename CSV */ |
2482 | 0 | else if (Matches5("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "CSV") || |
2483 | 0 | Matches6("COPY", "BINARY", MatchAny, "FROM|TO", MatchAny, "CSV")) |
2484 | 0 | COMPLETE_WITH_LIST5("HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", |
2485 | 0 | "FORCE NOT NULL"); |
2486 | | |
2487 | | /* CREATE ACCESS METHOD */ |
2488 | | /* Complete "CREATE ACCESS METHOD <name>" */ |
2489 | 0 | else if (Matches4("CREATE", "ACCESS", "METHOD", MatchAny)) |
2490 | 0 | COMPLETE_WITH_CONST("TYPE"); |
2491 | | /* Complete "CREATE ACCESS METHOD <name> TYPE" */ |
2492 | 0 | else if (Matches5("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE")) |
2493 | 0 | COMPLETE_WITH_CONST("INDEX"); |
2494 | | /* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */ |
2495 | 0 | else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny)) |
2496 | 0 | COMPLETE_WITH_CONST("HANDLER"); |
2497 | | |
2498 | | /* CREATE DATABASE */ |
2499 | 0 | else if (Matches3("CREATE", "DATABASE", MatchAny)) |
2500 | 0 | COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", |
2501 | 0 | "IS_TEMPLATE", |
2502 | 0 | "ALLOW_CONNECTIONS", "CONNECTION LIMIT", |
2503 | 0 | "LC_COLLATE", "LC_CTYPE"); |
2504 | | |
2505 | 0 | else if (Matches4("CREATE", "DATABASE", MatchAny, "TEMPLATE")) |
2506 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); |
2507 | | |
2508 | | /* CREATE EXTENSION */ |
2509 | | /* Complete with available extensions rather than installed ones. */ |
2510 | 0 | else if (Matches2("CREATE", "EXTENSION")) |
2511 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions); |
2512 | | /* CREATE EXTENSION <name> */ |
2513 | 0 | else if (Matches3("CREATE", "EXTENSION", MatchAny)) |
2514 | 0 | COMPLETE_WITH_LIST3("WITH SCHEMA", "CASCADE", "VERSION"); |
2515 | | /* CREATE EXTENSION <name> VERSION */ |
2516 | 0 | else if (Matches4("CREATE", "EXTENSION", MatchAny, "VERSION")) |
2517 | 0 | { |
2518 | 0 | completion_info_charp = prev2_wd; |
2519 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions); |
2520 | 0 | } |
2521 | | |
2522 | | /* CREATE FOREIGN */ |
2523 | 0 | else if (Matches2("CREATE", "FOREIGN")) |
2524 | 0 | COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); |
2525 | | |
2526 | | /* CREATE FOREIGN DATA WRAPPER */ |
2527 | 0 | else if (Matches5("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny)) |
2528 | 0 | COMPLETE_WITH_LIST3("HANDLER", "VALIDATOR", "OPTIONS"); |
2529 | | |
2530 | | /* CREATE INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */ |
2531 | | /* First off we complete CREATE UNIQUE with "INDEX" */ |
2532 | 0 | else if (TailMatches2("CREATE", "UNIQUE")) |
2533 | 0 | COMPLETE_WITH_CONST("INDEX"); |
2534 | | |
2535 | | /* |
2536 | | * If we have CREATE|UNIQUE INDEX, then add "ON", "CONCURRENTLY", and |
2537 | | * existing indexes |
2538 | | */ |
2539 | 0 | else if (TailMatches2("CREATE|UNIQUE", "INDEX")) |
2540 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, |
2541 | 0 | " UNION SELECT 'ON'" |
2542 | 0 | " UNION SELECT 'CONCURRENTLY'"); |
2543 | | |
2544 | | /* |
2545 | | * Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of relations |
2546 | | * that can indexes can be created on |
2547 | | */ |
2548 | 0 | else if (TailMatches3("INDEX|CONCURRENTLY", MatchAny, "ON") || |
2549 | 0 | TailMatches2("INDEX|CONCURRENTLY", "ON")) |
2550 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tpm, NULL); |
2551 | | |
2552 | | /* |
2553 | | * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing |
2554 | | * indexes |
2555 | | */ |
2556 | 0 | else if (TailMatches3("CREATE|UNIQUE", "INDEX", "CONCURRENTLY")) |
2557 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, |
2558 | 0 | " UNION SELECT 'ON'"); |
2559 | | /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */ |
2560 | 0 | else if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny) || |
2561 | 0 | TailMatches4("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny)) |
2562 | 0 | COMPLETE_WITH_CONST("ON"); |
2563 | | |
2564 | | /* |
2565 | | * Complete INDEX <name> ON <table> with a list of table columns (which |
2566 | | * should really be in parens) |
2567 | | */ |
2568 | 0 | else if (TailMatches4("INDEX", MatchAny, "ON", MatchAny) || |
2569 | 0 | TailMatches3("INDEX|CONCURRENTLY", "ON", MatchAny)) |
2570 | 0 | COMPLETE_WITH_LIST2("(", "USING"); |
2571 | 0 | else if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "(") || |
2572 | 0 | TailMatches4("INDEX|CONCURRENTLY", "ON", MatchAny, "(")) |
2573 | 0 | COMPLETE_WITH_ATTR(prev2_wd, ""); |
2574 | | /* same if you put in USING */ |
2575 | 0 | else if (TailMatches5("ON", MatchAny, "USING", MatchAny, "(")) |
2576 | 0 | COMPLETE_WITH_ATTR(prev4_wd, ""); |
2577 | | /* Complete USING with an index method */ |
2578 | 0 | else if (TailMatches6("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") || |
2579 | 0 | TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") || |
2580 | 0 | TailMatches4("INDEX", "ON", MatchAny, "USING")) |
2581 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); |
2582 | 0 | else if (TailMatches4("ON", MatchAny, "USING", MatchAny) && |
2583 | 0 | !TailMatches6("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) && |
2584 | 0 | !TailMatches4("FOR", MatchAny, MatchAny, MatchAny)) |
2585 | 0 | COMPLETE_WITH_CONST("("); |
2586 | | |
2587 | | /* CREATE POLICY */ |
2588 | | /* Complete "CREATE POLICY <name> ON" */ |
2589 | 0 | else if (Matches3("CREATE", "POLICY", MatchAny)) |
2590 | 0 | COMPLETE_WITH_CONST("ON"); |
2591 | | /* Complete "CREATE POLICY <name> ON <table>" */ |
2592 | 0 | else if (Matches4("CREATE", "POLICY", MatchAny, "ON")) |
2593 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2594 | | /* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */ |
2595 | 0 | else if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny)) |
2596 | 0 | COMPLETE_WITH_LIST5("AS", "FOR", "TO", "USING (", "WITH CHECK ("); |
2597 | | /* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */ |
2598 | 0 | else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS")) |
2599 | 0 | COMPLETE_WITH_LIST2("PERMISSIVE", "RESTRICTIVE"); |
2600 | | |
2601 | | /* |
2602 | | * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE |
2603 | | * FOR|TO|USING|WITH CHECK |
2604 | | */ |
2605 | 0 | else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny)) |
2606 | 0 | COMPLETE_WITH_LIST4("FOR", "TO", "USING", "WITH CHECK"); |
2607 | | /* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE */ |
2608 | 0 | else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR")) |
2609 | 0 | COMPLETE_WITH_LIST5("ALL", "SELECT", "INSERT", "UPDATE", "DELETE"); |
2610 | | /* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */ |
2611 | 0 | else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT")) |
2612 | 0 | COMPLETE_WITH_LIST2("TO", "WITH CHECK ("); |
2613 | | /* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE TO|USING" */ |
2614 | 0 | else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE")) |
2615 | 0 | COMPLETE_WITH_LIST2("TO", "USING ("); |
2616 | | /* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */ |
2617 | 0 | else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE")) |
2618 | 0 | COMPLETE_WITH_LIST3("TO", "USING (", "WITH CHECK ("); |
2619 | | /* Complete "CREATE POLICY <name> ON <table> TO <role>" */ |
2620 | 0 | else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO")) |
2621 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); |
2622 | | /* Complete "CREATE POLICY <name> ON <table> USING (" */ |
2623 | 0 | else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING")) |
2624 | 0 | COMPLETE_WITH_CONST("("); |
2625 | | |
2626 | | /* |
2627 | | * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR |
2628 | | * ALL|SELECT|INSERT|UPDATE|DELETE |
2629 | | */ |
2630 | 0 | else if (Matches8("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR")) |
2631 | 0 | COMPLETE_WITH_LIST5("ALL", "SELECT", "INSERT", "UPDATE", "DELETE"); |
2632 | | |
2633 | | /* |
2634 | | * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR |
2635 | | * INSERT TO|WITH CHECK" |
2636 | | */ |
2637 | 0 | else if (Matches9("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "INSERT")) |
2638 | 0 | COMPLETE_WITH_LIST2("TO", "WITH CHECK ("); |
2639 | | |
2640 | | /* |
2641 | | * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR |
2642 | | * SELECT|DELETE TO|USING" |
2643 | | */ |
2644 | 0 | else if (Matches9("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "SELECT|DELETE")) |
2645 | 0 | COMPLETE_WITH_LIST2("TO", "USING ("); |
2646 | | |
2647 | | /* |
2648 | | * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR |
2649 | | * ALL|UPDATE TO|USING|WITH CHECK |
2650 | | */ |
2651 | 0 | else if (Matches9("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "ALL|UPDATE")) |
2652 | 0 | COMPLETE_WITH_LIST3("TO", "USING (", "WITH CHECK ("); |
2653 | | |
2654 | | /* |
2655 | | * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE TO |
2656 | | * <role>" |
2657 | | */ |
2658 | 0 | else if (Matches8("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "TO")) |
2659 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); |
2660 | | |
2661 | | /* |
2662 | | * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE |
2663 | | * USING (" |
2664 | | */ |
2665 | 0 | else if (Matches8("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "USING")) |
2666 | 0 | COMPLETE_WITH_CONST("("); |
2667 | | |
2668 | | |
2669 | | /* CREATE PUBLICATION */ |
2670 | 0 | else if (Matches3("CREATE", "PUBLICATION", MatchAny)) |
2671 | 0 | COMPLETE_WITH_LIST3("FOR TABLE", "FOR ALL TABLES", "WITH ("); |
2672 | 0 | else if (Matches4("CREATE", "PUBLICATION", MatchAny, "FOR")) |
2673 | 0 | COMPLETE_WITH_LIST2("TABLE", "ALL TABLES"); |
2674 | | /* Complete "CREATE PUBLICATION <name> FOR TABLE <table>, ..." */ |
2675 | 0 | else if (HeadMatches5("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE")) |
2676 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2677 | | /* Complete "CREATE PUBLICATION <name> [...] WITH" */ |
2678 | 0 | else if (HeadMatches2("CREATE", "PUBLICATION") && TailMatches2("WITH", "(")) |
2679 | 0 | COMPLETE_WITH_CONST("publish"); |
2680 | | |
2681 | | /* CREATE RULE */ |
2682 | | /* Complete "CREATE RULE <sth>" with "AS ON" */ |
2683 | 0 | else if (Matches3("CREATE", "RULE", MatchAny)) |
2684 | 0 | COMPLETE_WITH_CONST("AS ON"); |
2685 | | /* Complete "CREATE RULE <sth> AS" with "ON" */ |
2686 | 0 | else if (Matches4("CREATE", "RULE", MatchAny, "AS")) |
2687 | 0 | COMPLETE_WITH_CONST("ON"); |
2688 | | /* Complete "CREATE RULE <sth> AS ON" with SELECT|UPDATE|INSERT|DELETE */ |
2689 | 0 | else if (Matches5("CREATE", "RULE", MatchAny, "AS", "ON")) |
2690 | 0 | COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE"); |
2691 | | /* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */ |
2692 | 0 | else if (TailMatches3("AS", "ON", "SELECT|UPDATE|INSERT|DELETE")) |
2693 | 0 | COMPLETE_WITH_CONST("TO"); |
2694 | | /* Complete "AS ON <sth> TO" with a table name */ |
2695 | 0 | else if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO")) |
2696 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2697 | | |
2698 | | /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */ |
2699 | 0 | else if (TailMatches3("CREATE", "SEQUENCE", MatchAny) || |
2700 | 0 | TailMatches4("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny)) |
2701 | 0 | COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE", |
2702 | 0 | "CYCLE", "OWNED BY", "START WITH"); |
2703 | 0 | else if (TailMatches4("CREATE", "SEQUENCE", MatchAny, "NO") || |
2704 | 0 | TailMatches5("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO")) |
2705 | 0 | COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE"); |
2706 | | |
2707 | | /* CREATE SERVER <name> */ |
2708 | 0 | else if (Matches3("CREATE", "SERVER", MatchAny)) |
2709 | 0 | COMPLETE_WITH_LIST3("TYPE", "VERSION", "FOREIGN DATA WRAPPER"); |
2710 | | |
2711 | | /* CREATE STATISTICS <name> */ |
2712 | 0 | else if (Matches3("CREATE", "STATISTICS", MatchAny)) |
2713 | 0 | COMPLETE_WITH_LIST2("(", "ON"); |
2714 | 0 | else if (Matches4("CREATE", "STATISTICS", MatchAny, "(")) |
2715 | 0 | COMPLETE_WITH_LIST2("ndistinct", "dependencies"); |
2716 | 0 | else if (HeadMatches3("CREATE", "STATISTICS", MatchAny) && |
2717 | 0 | previous_words[0][0] == '(' && |
2718 | 0 | previous_words[0][strlen(previous_words[0]) - 1] == ')') |
2719 | 0 | COMPLETE_WITH_CONST("ON"); |
2720 | 0 | else if (HeadMatches3("CREATE", "STATISTICS", MatchAny) && |
2721 | 0 | TailMatches1("FROM")) |
2722 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2723 | | |
2724 | | /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */ |
2725 | | /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */ |
2726 | 0 | else if (TailMatches2("CREATE", "TEMP|TEMPORARY")) |
2727 | 0 | COMPLETE_WITH_LIST3("SEQUENCE", "TABLE", "VIEW"); |
2728 | | /* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */ |
2729 | 0 | else if (TailMatches2("CREATE", "UNLOGGED")) |
2730 | 0 | COMPLETE_WITH_LIST2("TABLE", "MATERIALIZED VIEW"); |
2731 | | /* Complete PARTITION BY with RANGE ( or LIST ( or ... */ |
2732 | 0 | else if (TailMatches2("PARTITION", "BY")) |
2733 | 0 | COMPLETE_WITH_LIST3("RANGE (", "LIST (", "HASH ("); |
2734 | | /* If we have xxx PARTITION OF, provide a list of partitioned tables */ |
2735 | 0 | else if (TailMatches2("PARTITION", "OF")) |
2736 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, ""); |
2737 | | /* Limited completion support for partition bound specification */ |
2738 | 0 | else if (TailMatches3("PARTITION", "OF", MatchAny)) |
2739 | 0 | COMPLETE_WITH_LIST2("FOR VALUES", "DEFAULT"); |
2740 | | |
2741 | | /* CREATE TABLEGROUP */ |
2742 | 0 | else if (Matches3("CREATE", "TABLEGROUP", MatchAny)) |
2743 | 0 | COMPLETE_WITH_LIST2("OWNER", "TABLESPACE"); |
2744 | | /* Complete CREATE TABLEGROUP name OWNER name with "TABLESPACE" */ |
2745 | 0 | else if (Matches5("CREATE", "TABLEGROUP", MatchAny, "OWNER", MatchAny)) |
2746 | 0 | COMPLETE_WITH_CONST("TABLESPACE"); |
2747 | | |
2748 | | /* CREATE TABLESPACE */ |
2749 | 0 | else if (Matches3("CREATE", "TABLESPACE", MatchAny)) |
2750 | 0 | COMPLETE_WITH_LIST2("OWNER", "WITH"); |
2751 | | /* Complete CREATE TABLESPACE name OWNER name with "WITH (" */ |
2752 | 0 | else if (Matches5("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny)) |
2753 | 0 | COMPLETE_WITH_CONST("WITH ("); |
2754 | | |
2755 | | /* CREATE TEXT SEARCH */ |
2756 | 0 | else if (Matches3("CREATE", "TEXT", "SEARCH")) |
2757 | 0 | COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); |
2758 | 0 | else if (Matches5("CREATE", "TEXT", "SEARCH", "CONFIGURATION", MatchAny)) |
2759 | 0 | COMPLETE_WITH_CONST("("); |
2760 | | |
2761 | | /* CREATE SUBSCRIPTION */ |
2762 | 0 | else if (Matches3("CREATE", "SUBSCRIPTION", MatchAny)) |
2763 | 0 | COMPLETE_WITH_CONST("CONNECTION"); |
2764 | 0 | else if (Matches5("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny)) |
2765 | 0 | COMPLETE_WITH_CONST("PUBLICATION"); |
2766 | 0 | else if (Matches6("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", |
2767 | 0 | MatchAny, "PUBLICATION")) |
2768 | 0 | { |
2769 | | /* complete with nothing here as this refers to remote publications */ |
2770 | 0 | } |
2771 | 0 | else if (HeadMatches2("CREATE", "SUBSCRIPTION") && TailMatches2("PUBLICATION", MatchAny)) |
2772 | 0 | COMPLETE_WITH_CONST("WITH ("); |
2773 | | /* Complete "CREATE SUBSCRIPTION <name> ... WITH ( <opt>" */ |
2774 | 0 | else if (HeadMatches2("CREATE", "SUBSCRIPTION") && TailMatches2("WITH", "(")) |
2775 | 0 | COMPLETE_WITH_LIST6("copy_data", "connect", "create_slot", "enabled", |
2776 | 0 | "slot_name", "synchronous_commit"); |
2777 | | |
2778 | | /* CREATE TRIGGER --- is allowed inside CREATE SCHEMA, so use TailMatches */ |
2779 | | /* complete CREATE TRIGGER <name> with BEFORE,AFTER,INSTEAD OF */ |
2780 | 0 | else if (TailMatches3("CREATE", "TRIGGER", MatchAny)) |
2781 | 0 | COMPLETE_WITH_LIST3("BEFORE", "AFTER", "INSTEAD OF"); |
2782 | | /* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */ |
2783 | 0 | else if (TailMatches4("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER")) |
2784 | 0 | COMPLETE_WITH_LIST4("INSERT", "DELETE", "UPDATE", "TRUNCATE"); |
2785 | | /* complete CREATE TRIGGER <name> INSTEAD OF with an event */ |
2786 | 0 | else if (TailMatches5("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF")) |
2787 | 0 | COMPLETE_WITH_LIST3("INSERT", "DELETE", "UPDATE"); |
2788 | | /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */ |
2789 | 0 | else if (TailMatches5("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) || |
2790 | 0 | TailMatches6("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny)) |
2791 | 0 | COMPLETE_WITH_LIST2("ON", "OR"); |
2792 | | |
2793 | | /* |
2794 | | * complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of |
2795 | | * tables. EXECUTE FUNCTION is the recommended grammar instead of EXECUTE |
2796 | | * PROCEDURE in version 11 and upwards. |
2797 | | */ |
2798 | 0 | else if (TailMatches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON")) |
2799 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
2800 | | /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ |
2801 | 0 | else if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON")) |
2802 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); |
2803 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("ON", MatchAny)) |
2804 | 0 | { |
2805 | 0 | if (pset.sversion >= 110000) |
2806 | 0 | COMPLETE_WITH_LIST7("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY", |
2807 | 0 | "REFERENCING", "FOR", "WHEN (", |
2808 | 0 | "EXECUTE FUNCTION"); |
2809 | 0 | else |
2810 | 0 | COMPLETE_WITH_LIST7("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY", |
2811 | 0 | "REFERENCING", "FOR", "WHEN (", |
2812 | 0 | "EXECUTE PROCEDURE"); |
2813 | 0 | } |
2814 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && |
2815 | 0 | (TailMatches1("DEFERRABLE") || TailMatches2("INITIALLY", "IMMEDIATE|DEFERRED"))) |
2816 | 0 | { |
2817 | 0 | if (pset.sversion >= 110000) |
2818 | 0 | COMPLETE_WITH_LIST4("REFERENCING", "FOR", "WHEN (", |
2819 | 0 | "EXECUTE FUNCTION"); |
2820 | 0 | else |
2821 | 0 | COMPLETE_WITH_LIST4("REFERENCING", "FOR", "WHEN (", |
2822 | 0 | "EXECUTE PROCEDURE"); |
2823 | 0 | } |
2824 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("REFERENCING")) |
2825 | 0 | COMPLETE_WITH_LIST2("OLD TABLE", "NEW TABLE"); |
2826 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("OLD|NEW", "TABLE")) |
2827 | 0 | COMPLETE_WITH_CONST("AS"); |
2828 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && |
2829 | 0 | (TailMatches5("REFERENCING", "OLD", "TABLE", "AS", MatchAny) || |
2830 | 0 | TailMatches4("REFERENCING", "OLD", "TABLE", MatchAny))) |
2831 | 0 | { |
2832 | 0 | if (pset.sversion >= 110000) |
2833 | 0 | COMPLETE_WITH_LIST4("NEW TABLE", "FOR", "WHEN (", |
2834 | 0 | "EXECUTE FUNCTION"); |
2835 | 0 | else |
2836 | 0 | COMPLETE_WITH_LIST4("NEW TABLE", "FOR", "WHEN (", |
2837 | 0 | "EXECUTE PROCEDURE"); |
2838 | 0 | } |
2839 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && |
2840 | 0 | (TailMatches5("REFERENCING", "NEW", "TABLE", "AS", MatchAny) || |
2841 | 0 | TailMatches4("REFERENCING", "NEW", "TABLE", MatchAny))) |
2842 | 0 | { |
2843 | 0 | if (pset.sversion >= 110000) |
2844 | 0 | COMPLETE_WITH_LIST4("OLD TABLE", "FOR", "WHEN (", |
2845 | 0 | "EXECUTE FUNCTION"); |
2846 | 0 | else |
2847 | 0 | COMPLETE_WITH_LIST4("OLD TABLE", "FOR", "WHEN (", |
2848 | 0 | "EXECUTE PROCEDURE"); |
2849 | 0 | } |
2850 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && |
2851 | 0 | (TailMatches9("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || |
2852 | 0 | TailMatches8("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) || |
2853 | 0 | TailMatches8("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) || |
2854 | 0 | TailMatches7("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))) |
2855 | 0 | { |
2856 | 0 | if (pset.sversion >= 110000) |
2857 | 0 | COMPLETE_WITH_LIST3("FOR", "WHEN (", "EXECUTE FUNCTION"); |
2858 | 0 | else |
2859 | 0 | COMPLETE_WITH_LIST3("FOR", "WHEN (", "EXECUTE PROCEDURE"); |
2860 | 0 | } |
2861 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("FOR")) |
2862 | 0 | COMPLETE_WITH_LIST3("EACH", "ROW", "STATEMENT"); |
2863 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("FOR", "EACH")) |
2864 | 0 | COMPLETE_WITH_LIST2("ROW", "STATEMENT"); |
2865 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && |
2866 | 0 | (TailMatches3("FOR", "EACH", "ROW|STATEMENT") || |
2867 | 0 | TailMatches2("FOR", "ROW|STATEMENT"))) |
2868 | 0 | { |
2869 | 0 | if (pset.sversion >= 110000) |
2870 | 0 | COMPLETE_WITH_LIST2("WHEN (", "EXECUTE FUNCTION"); |
2871 | 0 | else |
2872 | 0 | COMPLETE_WITH_LIST2("WHEN (", "EXECUTE PROCEDURE"); |
2873 | 0 | } |
2874 | | /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ |
2875 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE")) |
2876 | 0 | { |
2877 | 0 | if (pset.sversion >= 110000) |
2878 | 0 | COMPLETE_WITH_CONST("FUNCTION"); |
2879 | 0 | else |
2880 | 0 | COMPLETE_WITH_CONST("PROCEDURE"); |
2881 | 0 | } |
2882 | 0 | else if (HeadMatches2("CREATE", "TRIGGER") && |
2883 | 0 | TailMatches2("EXECUTE", "FUNCTION|PROCEDURE")) |
2884 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL); |
2885 | | |
2886 | | /* CREATE ROLE,USER,GROUP <name> */ |
2887 | 0 | else if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) && |
2888 | 0 | !TailMatches2("USER", "MAPPING")) |
2889 | 0 | { |
2890 | 0 | static const char *const list_CREATEROLE[] = |
2891 | 0 | {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", |
2892 | 0 | "ENCRYPTED PASSWORD", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS", |
2893 | 0 | "NOCREATEDB", "NOCREATEROLE", "NOINHERIT", |
2894 | 0 | "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", |
2895 | 0 | "REPLICATION", "ROLE", "SUPERUSER", "SYSID", |
2896 | 0 | "VALID UNTIL", "WITH", NULL}; |
2897 | |
|
2898 | 0 | COMPLETE_WITH_LIST(list_CREATEROLE); |
2899 | 0 | } |
2900 | | |
2901 | | /* CREATE ROLE,USER,GROUP <name> WITH */ |
2902 | 0 | else if (Matches4("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH")) |
2903 | 0 | { |
2904 | | /* Similar to the above, but don't complete "WITH" again. */ |
2905 | 0 | static const char *const list_CREATEROLE_WITH[] = |
2906 | 0 | {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", |
2907 | 0 | "ENCRYPTED PASSWORD", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS", |
2908 | 0 | "NOCREATEDB", "NOCREATEROLE", "NOINHERIT", |
2909 | 0 | "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", |
2910 | 0 | "REPLICATION", "ROLE", "SUPERUSER", "SYSID", |
2911 | 0 | "VALID UNTIL", NULL}; |
2912 | |
|
2913 | 0 | COMPLETE_WITH_LIST(list_CREATEROLE_WITH); |
2914 | 0 | } |
2915 | | |
2916 | | /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */ |
2917 | 0 | else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN")) |
2918 | 0 | COMPLETE_WITH_LIST2("GROUP", "ROLE"); |
2919 | | |
2920 | | /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */ |
2921 | | /* Complete CREATE VIEW <name> with AS */ |
2922 | 0 | else if (TailMatches3("CREATE", "VIEW", MatchAny)) |
2923 | 0 | COMPLETE_WITH_CONST("AS"); |
2924 | | /* Complete "CREATE VIEW <sth> AS with "SELECT" */ |
2925 | 0 | else if (TailMatches4("CREATE", "VIEW", MatchAny, "AS")) |
2926 | 0 | COMPLETE_WITH_CONST("SELECT"); |
2927 | | |
2928 | | /* CREATE MATERIALIZED VIEW */ |
2929 | 0 | else if (Matches2("CREATE", "MATERIALIZED")) |
2930 | 0 | COMPLETE_WITH_CONST("VIEW"); |
2931 | | /* Complete CREATE MATERIALIZED VIEW <name> with AS */ |
2932 | 0 | else if (Matches4("CREATE", "MATERIALIZED", "VIEW", MatchAny)) |
2933 | 0 | COMPLETE_WITH_CONST("AS"); |
2934 | | /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */ |
2935 | 0 | else if (Matches5("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS")) |
2936 | 0 | COMPLETE_WITH_CONST("SELECT"); |
2937 | | |
2938 | | /* CREATE EVENT TRIGGER */ |
2939 | 0 | else if (Matches2("CREATE", "EVENT")) |
2940 | 0 | COMPLETE_WITH_CONST("TRIGGER"); |
2941 | | /* Complete CREATE EVENT TRIGGER <name> with ON */ |
2942 | 0 | else if (Matches4("CREATE", "EVENT", "TRIGGER", MatchAny)) |
2943 | 0 | COMPLETE_WITH_CONST("ON"); |
2944 | | /* Complete CREATE EVENT TRIGGER <name> ON with event_type */ |
2945 | 0 | else if (Matches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON")) |
2946 | 0 | COMPLETE_WITH_LIST3("ddl_command_start", "ddl_command_end", "sql_drop"); |
2947 | | |
2948 | | /* DEALLOCATE */ |
2949 | 0 | else if (Matches1("DEALLOCATE")) |
2950 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements); |
2951 | | |
2952 | | /* DECLARE */ |
2953 | 0 | else if (Matches2("DECLARE", MatchAny)) |
2954 | 0 | COMPLETE_WITH_LIST5("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", |
2955 | 0 | "CURSOR"); |
2956 | 0 | else if (HeadMatches1("DECLARE") && TailMatches1("CURSOR")) |
2957 | 0 | COMPLETE_WITH_LIST3("WITH HOLD", "WITHOUT HOLD", "FOR"); |
2958 | | |
2959 | | /* DELETE --- can be inside EXPLAIN, RULE, etc */ |
2960 | | /* ... despite which, only complete DELETE with FROM at start of line */ |
2961 | 0 | else if (Matches1("DELETE")) |
2962 | 0 | COMPLETE_WITH_CONST("FROM"); |
2963 | | /* Complete DELETE FROM with a list of tables */ |
2964 | 0 | else if (TailMatches2("DELETE", "FROM")) |
2965 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); |
2966 | | /* Complete DELETE FROM <table> */ |
2967 | 0 | else if (TailMatches3("DELETE", "FROM", MatchAny)) |
2968 | 0 | COMPLETE_WITH_LIST2("USING", "WHERE"); |
2969 | | /* XXX: implement tab completion for DELETE ... USING */ |
2970 | | |
2971 | | /* DISCARD */ |
2972 | 0 | else if (Matches1("DISCARD")) |
2973 | 0 | COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP"); |
2974 | | |
2975 | | /* DO */ |
2976 | 0 | else if (Matches1("DO")) |
2977 | 0 | COMPLETE_WITH_CONST("LANGUAGE"); |
2978 | | |
2979 | | /* DROP */ |
2980 | | /* Complete DROP object with CASCADE / RESTRICT */ |
2981 | 0 | else if (Matches3("DROP", |
2982 | 0 | "COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TABLEGROUP|TYPE|VIEW", |
2983 | 0 | MatchAny) || |
2984 | 0 | Matches4("DROP", "ACCESS", "METHOD", MatchAny) || |
2985 | 0 | (Matches4("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) && |
2986 | 0 | ends_with(prev_wd, ')')) || |
2987 | 0 | Matches4("DROP", "EVENT", "TRIGGER", MatchAny) || |
2988 | 0 | Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) || |
2989 | 0 | Matches4("DROP", "FOREIGN", "TABLE", MatchAny) || |
2990 | 0 | Matches5("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny)) |
2991 | 0 | COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); |
2992 | | |
2993 | | /* help completing some of the variants */ |
2994 | 0 | else if (Matches3("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny)) |
2995 | 0 | COMPLETE_WITH_CONST("("); |
2996 | 0 | else if (Matches4("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, "(")) |
2997 | 0 | COMPLETE_WITH_FUNCTION_ARG(prev2_wd); |
2998 | 0 | else if (Matches2("DROP", "FOREIGN")) |
2999 | 0 | COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); |
3000 | 0 | else if (Matches2("DROP", "DATABASE")) |
3001 | 0 | COMPLETE_WITH_CONST("WITH ("); |
3002 | 0 | else if (HeadMatches2("DROP", "DATABASE") && (ends_with(prev_wd, '('))) |
3003 | 0 | COMPLETE_WITH_CONST("FORCE"); |
3004 | | |
3005 | | /* DROP INDEX */ |
3006 | 0 | else if (Matches2("DROP", "INDEX")) |
3007 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, |
3008 | 0 | " UNION SELECT 'CONCURRENTLY'"); |
3009 | 0 | else if (Matches3("DROP", "INDEX", "CONCURRENTLY")) |
3010 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); |
3011 | 0 | else if (Matches3("DROP", "INDEX", MatchAny)) |
3012 | 0 | COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); |
3013 | 0 | else if (Matches4("DROP", "INDEX", "CONCURRENTLY", MatchAny)) |
3014 | 0 | COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); |
3015 | | |
3016 | | /* DROP MATERIALIZED VIEW */ |
3017 | 0 | else if (Matches2("DROP", "MATERIALIZED")) |
3018 | 0 | COMPLETE_WITH_CONST("VIEW"); |
3019 | 0 | else if (Matches3("DROP", "MATERIALIZED", "VIEW")) |
3020 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); |
3021 | | |
3022 | | /* DROP OWNED BY */ |
3023 | 0 | else if (Matches2("DROP", "OWNED")) |
3024 | 0 | COMPLETE_WITH_CONST("BY"); |
3025 | 0 | else if (Matches3("DROP", "OWNED", "BY")) |
3026 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3027 | | |
3028 | | /* DROP TEXT SEARCH */ |
3029 | 0 | else if (Matches3("DROP", "TEXT", "SEARCH")) |
3030 | 0 | COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); |
3031 | | |
3032 | | /* DROP TRIGGER */ |
3033 | 0 | else if (Matches3("DROP", "TRIGGER", MatchAny)) |
3034 | 0 | COMPLETE_WITH_CONST("ON"); |
3035 | 0 | else if (Matches4("DROP", "TRIGGER", MatchAny, "ON")) |
3036 | 0 | { |
3037 | 0 | completion_info_charp = prev2_wd; |
3038 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); |
3039 | 0 | } |
3040 | 0 | else if (Matches5("DROP", "TRIGGER", MatchAny, "ON", MatchAny)) |
3041 | 0 | COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); |
3042 | | |
3043 | | /* DROP ACCESS METHOD */ |
3044 | 0 | else if (Matches2("DROP", "ACCESS")) |
3045 | 0 | COMPLETE_WITH_CONST("METHOD"); |
3046 | 0 | else if (Matches3("DROP", "ACCESS", "METHOD")) |
3047 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); |
3048 | | |
3049 | | /* DROP EVENT TRIGGER */ |
3050 | 0 | else if (Matches2("DROP", "EVENT")) |
3051 | 0 | COMPLETE_WITH_CONST("TRIGGER"); |
3052 | 0 | else if (Matches3("DROP", "EVENT", "TRIGGER")) |
3053 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); |
3054 | | |
3055 | | /* DROP POLICY <name> */ |
3056 | 0 | else if (Matches2("DROP", "POLICY")) |
3057 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_policies); |
3058 | | /* DROP POLICY <name> ON */ |
3059 | 0 | else if (Matches3("DROP", "POLICY", MatchAny)) |
3060 | 0 | COMPLETE_WITH_CONST("ON"); |
3061 | | /* DROP POLICY <name> ON <table> */ |
3062 | 0 | else if (Matches4("DROP", "POLICY", MatchAny, "ON")) |
3063 | 0 | { |
3064 | 0 | completion_info_charp = prev2_wd; |
3065 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy); |
3066 | 0 | } |
3067 | | |
3068 | | /* DROP RULE */ |
3069 | 0 | else if (Matches3("DROP", "RULE", MatchAny)) |
3070 | 0 | COMPLETE_WITH_CONST("ON"); |
3071 | 0 | else if (Matches4("DROP", "RULE", MatchAny, "ON")) |
3072 | 0 | { |
3073 | 0 | completion_info_charp = prev2_wd; |
3074 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule); |
3075 | 0 | } |
3076 | 0 | else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny)) |
3077 | 0 | COMPLETE_WITH_LIST2("CASCADE", "RESTRICT"); |
3078 | | |
3079 | | /* EXECUTE */ |
3080 | 0 | else if (Matches1("EXECUTE")) |
3081 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements); |
3082 | | |
3083 | | /* EXPLAIN */ |
3084 | | |
3085 | | /* |
3086 | | * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands |
3087 | | */ |
3088 | 0 | else if (Matches1("EXPLAIN")) |
3089 | 0 | COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", |
3090 | 0 | "ANALYZE", "VERBOSE"); |
3091 | 0 | else if (Matches2("EXPLAIN", "ANALYZE")) |
3092 | 0 | COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", |
3093 | 0 | "VERBOSE"); |
3094 | 0 | else if (Matches2("EXPLAIN", "VERBOSE") || |
3095 | 0 | Matches3("EXPLAIN", "ANALYZE", "VERBOSE")) |
3096 | 0 | COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE"); |
3097 | | |
3098 | | /* FETCH && MOVE */ |
3099 | | /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */ |
3100 | 0 | else if (Matches1("FETCH|MOVE")) |
3101 | 0 | COMPLETE_WITH_LIST4("ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE"); |
3102 | | /* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */ |
3103 | 0 | else if (Matches2("FETCH|MOVE", MatchAny)) |
3104 | 0 | COMPLETE_WITH_LIST3("ALL", "NEXT", "PRIOR"); |
3105 | | |
3106 | | /* |
3107 | | * Complete FETCH <sth1> <sth2> with "FROM" or "IN". These are equivalent, |
3108 | | * but we may as well tab-complete both: perhaps some users prefer one |
3109 | | * variant or the other. |
3110 | | */ |
3111 | 0 | else if (Matches3("FETCH|MOVE", MatchAny, MatchAny)) |
3112 | 0 | COMPLETE_WITH_LIST2("FROM", "IN"); |
3113 | | |
3114 | | /* FOREIGN DATA WRAPPER */ |
3115 | | /* applies in ALTER/DROP FDW and in CREATE SERVER */ |
3116 | 0 | else if (TailMatches3("FOREIGN", "DATA", "WRAPPER") && |
3117 | 0 | !TailMatches4("CREATE", MatchAny, MatchAny, MatchAny)) |
3118 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_fdws); |
3119 | | /* applies in CREATE SERVER */ |
3120 | 0 | else if (TailMatches4("FOREIGN", "DATA", "WRAPPER", MatchAny) && |
3121 | 0 | HeadMatches2("CREATE", "SERVER")) |
3122 | 0 | COMPLETE_WITH_CONST("OPTIONS"); |
3123 | | |
3124 | | /* FOREIGN TABLE */ |
3125 | 0 | else if (TailMatches2("FOREIGN", "TABLE") && |
3126 | 0 | !TailMatches3("CREATE", MatchAny, MatchAny)) |
3127 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); |
3128 | | |
3129 | | /* FOREIGN SERVER */ |
3130 | 0 | else if (TailMatches2("FOREIGN", "SERVER")) |
3131 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_servers); |
3132 | | |
3133 | | /* |
3134 | | * GRANT and REVOKE are allowed inside CREATE SCHEMA and |
3135 | | * ALTER DEFAULT PRIVILEGES, so use TailMatches |
3136 | | */ |
3137 | | /* Complete GRANT/REVOKE with a list of roles and privileges */ |
3138 | 0 | else if (TailMatches1("GRANT|REVOKE")) |
3139 | 0 | { |
3140 | | /* |
3141 | | * With ALTER DEFAULT PRIVILEGES, restrict completion to grantable |
3142 | | * privileges (can't grant roles) |
3143 | | */ |
3144 | 0 | if (HeadMatches3("ALTER", "DEFAULT", "PRIVILEGES")) |
3145 | 0 | COMPLETE_WITH_LIST10("SELECT", "INSERT", "UPDATE", |
3146 | 0 | "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", |
3147 | 0 | "EXECUTE", "USAGE", "ALL"); |
3148 | 0 | else |
3149 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles |
3150 | 0 | " UNION SELECT 'SELECT'" |
3151 | 0 | " UNION SELECT 'INSERT'" |
3152 | 0 | " UNION SELECT 'UPDATE'" |
3153 | 0 | " UNION SELECT 'DELETE'" |
3154 | 0 | " UNION SELECT 'TRUNCATE'" |
3155 | 0 | " UNION SELECT 'REFERENCES'" |
3156 | 0 | " UNION SELECT 'TRIGGER'" |
3157 | 0 | " UNION SELECT 'CREATE'" |
3158 | 0 | " UNION SELECT 'CONNECT'" |
3159 | 0 | " UNION SELECT 'TEMPORARY'" |
3160 | 0 | " UNION SELECT 'EXECUTE'" |
3161 | 0 | " UNION SELECT 'USAGE'" |
3162 | 0 | " UNION SELECT 'ALL'"); |
3163 | 0 | } |
3164 | | |
3165 | | /* |
3166 | | * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with |
3167 | | * TO/FROM |
3168 | | */ |
3169 | 0 | else if (TailMatches2("GRANT|REVOKE", MatchAny)) |
3170 | 0 | { |
3171 | 0 | if (TailMatches1("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL")) |
3172 | 0 | COMPLETE_WITH_CONST("ON"); |
3173 | 0 | else if (TailMatches2("GRANT", MatchAny)) |
3174 | 0 | COMPLETE_WITH_CONST("TO"); |
3175 | 0 | else |
3176 | 0 | COMPLETE_WITH_CONST("FROM"); |
3177 | 0 | } |
3178 | | |
3179 | | /* |
3180 | | * Complete GRANT/REVOKE <sth> ON with a list of tables, views, and |
3181 | | * sequences. |
3182 | | * |
3183 | | * Keywords like DATABASE, FUNCTION, LANGUAGE and SCHEMA added to query |
3184 | | * result via UNION; seems to work intuitively. |
3185 | | * |
3186 | | * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented |
3187 | | * here will only work if the privilege list contains exactly one |
3188 | | * privilege. |
3189 | | */ |
3190 | 0 | else if (TailMatches3("GRANT|REVOKE", MatchAny, "ON")) |
3191 | 0 | { |
3192 | | /* |
3193 | | * With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds of |
3194 | | * objects supported. |
3195 | | */ |
3196 | 0 | if (HeadMatches3("ALTER", "DEFAULT", "PRIVILEGES")) |
3197 | 0 | COMPLETE_WITH_LIST7("TABLES", "SEQUENCES", "FUNCTIONS", "PROCEDURES", "ROUTINES", "TYPES", "SCHEMAS"); |
3198 | 0 | else |
3199 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, |
3200 | 0 | " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'" |
3201 | 0 | " UNION SELECT 'ALL PROCEDURES IN SCHEMA'" |
3202 | 0 | " UNION SELECT 'ALL ROUTINES IN SCHEMA'" |
3203 | 0 | " UNION SELECT 'ALL SEQUENCES IN SCHEMA'" |
3204 | 0 | " UNION SELECT 'ALL TABLES IN SCHEMA'" |
3205 | 0 | " UNION SELECT 'DATABASE'" |
3206 | 0 | " UNION SELECT 'DOMAIN'" |
3207 | 0 | " UNION SELECT 'FOREIGN DATA WRAPPER'" |
3208 | 0 | " UNION SELECT 'FOREIGN SERVER'" |
3209 | 0 | " UNION SELECT 'FUNCTION'" |
3210 | 0 | " UNION SELECT 'LANGUAGE'" |
3211 | 0 | " UNION SELECT 'LARGE OBJECT'" |
3212 | 0 | " UNION SELECT 'PROCEDURE'" |
3213 | 0 | " UNION SELECT 'ROUTINE'" |
3214 | 0 | " UNION SELECT 'SCHEMA'" |
3215 | 0 | " UNION SELECT 'SEQUENCE'" |
3216 | 0 | " UNION SELECT 'TABLE'" |
3217 | 0 | " UNION SELECT 'TABLESPACE'" |
3218 | 0 | " UNION SELECT 'TYPE'"); |
3219 | 0 | } |
3220 | 0 | else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL")) |
3221 | 0 | COMPLETE_WITH_LIST5("FUNCTIONS IN SCHEMA", |
3222 | 0 | "PROCEDURES IN SCHEMA", |
3223 | 0 | "ROUTINES IN SCHEMA", |
3224 | 0 | "SEQUENCES IN SCHEMA", |
3225 | 0 | "TABLES IN SCHEMA"); |
3226 | 0 | else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN")) |
3227 | 0 | COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER"); |
3228 | | |
3229 | | /* |
3230 | | * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of |
3231 | | * appropriate objects. |
3232 | | * |
3233 | | * Complete "GRANT/REVOKE * ON *" with "TO/FROM". |
3234 | | */ |
3235 | 0 | else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", MatchAny)) |
3236 | 0 | { |
3237 | 0 | if (TailMatches1("DATABASE")) |
3238 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_databases); |
3239 | 0 | else if (TailMatches1("DOMAIN")) |
3240 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); |
3241 | 0 | else if (TailMatches1("FUNCTION")) |
3242 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL); |
3243 | 0 | else if (TailMatches1("LANGUAGE")) |
3244 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_languages); |
3245 | 0 | else if (TailMatches1("PROCEDURE")) |
3246 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL); |
3247 | 0 | else if (TailMatches1("ROUTINE")) |
3248 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL); |
3249 | 0 | else if (TailMatches1("SCHEMA")) |
3250 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_schemas); |
3251 | 0 | else if (TailMatches1("SEQUENCE")) |
3252 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); |
3253 | 0 | else if (TailMatches1("TABLE")) |
3254 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); |
3255 | 0 | else if (TailMatches1("TABLEGROUP")) |
3256 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tablegroups); |
3257 | 0 | else if (TailMatches1("TABLESPACE")) |
3258 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); |
3259 | 0 | else if (TailMatches1("TYPE")) |
3260 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); |
3261 | 0 | else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny)) |
3262 | 0 | COMPLETE_WITH_CONST("TO"); |
3263 | 0 | else |
3264 | 0 | COMPLETE_WITH_CONST("FROM"); |
3265 | 0 | } |
3266 | | |
3267 | | /* |
3268 | | * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC, |
3269 | | * CURRENT_USER, or SESSION_USER. |
3270 | | */ |
3271 | 0 | else if ((HeadMatches1("GRANT") && TailMatches1("TO")) || |
3272 | 0 | (HeadMatches1("REVOKE") && TailMatches1("FROM"))) |
3273 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); |
3274 | | /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */ |
3275 | 0 | else if (HeadMatches3("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches1("TO|FROM")) |
3276 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); |
3277 | | /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */ |
3278 | 0 | else if (HeadMatches1("GRANT") && TailMatches3("ON", MatchAny, MatchAny)) |
3279 | 0 | COMPLETE_WITH_CONST("TO"); |
3280 | 0 | else if (HeadMatches1("REVOKE") && TailMatches3("ON", MatchAny, MatchAny)) |
3281 | 0 | COMPLETE_WITH_CONST("FROM"); |
3282 | | |
3283 | | /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */ |
3284 | 0 | else if (TailMatches8("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny)) |
3285 | 0 | { |
3286 | 0 | if (TailMatches8("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny)) |
3287 | 0 | COMPLETE_WITH_CONST("TO"); |
3288 | 0 | else |
3289 | 0 | COMPLETE_WITH_CONST("FROM"); |
3290 | 0 | } |
3291 | | |
3292 | | /* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */ |
3293 | 0 | else if (TailMatches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny)) |
3294 | 0 | { |
3295 | 0 | if (TailMatches7("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny)) |
3296 | 0 | COMPLETE_WITH_CONST("TO"); |
3297 | 0 | else |
3298 | 0 | COMPLETE_WITH_CONST("FROM"); |
3299 | 0 | } |
3300 | | |
3301 | | /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */ |
3302 | 0 | else if (TailMatches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny)) |
3303 | 0 | { |
3304 | 0 | if (TailMatches6("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny)) |
3305 | 0 | COMPLETE_WITH_CONST("TO"); |
3306 | 0 | else |
3307 | 0 | COMPLETE_WITH_CONST("FROM"); |
3308 | 0 | } |
3309 | | |
3310 | | /* GROUP BY */ |
3311 | 0 | else if (TailMatches3("FROM", MatchAny, "GROUP")) |
3312 | 0 | COMPLETE_WITH_CONST("BY"); |
3313 | | |
3314 | | /* IMPORT FOREIGN SCHEMA */ |
3315 | 0 | else if (Matches1("IMPORT")) |
3316 | 0 | COMPLETE_WITH_CONST("FOREIGN SCHEMA"); |
3317 | 0 | else if (Matches2("IMPORT", "FOREIGN")) |
3318 | 0 | COMPLETE_WITH_CONST("SCHEMA"); |
3319 | | |
3320 | | /* INSERT --- can be inside EXPLAIN, RULE, etc */ |
3321 | | /* Complete INSERT with "INTO" */ |
3322 | 0 | else if (TailMatches1("INSERT")) |
3323 | 0 | COMPLETE_WITH_CONST("INTO"); |
3324 | | /* Complete INSERT INTO with table names */ |
3325 | 0 | else if (TailMatches2("INSERT", "INTO")) |
3326 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); |
3327 | | /* Complete "INSERT INTO <table> (" with attribute names */ |
3328 | 0 | else if (TailMatches4("INSERT", "INTO", MatchAny, "(")) |
3329 | 0 | COMPLETE_WITH_ATTR(prev2_wd, ""); |
3330 | | |
3331 | | /* |
3332 | | * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or |
3333 | | * "TABLE" or "DEFAULT VALUES" or "OVERRIDING" |
3334 | | */ |
3335 | 0 | else if (TailMatches3("INSERT", "INTO", MatchAny)) |
3336 | 0 | COMPLETE_WITH_LIST6("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", "OVERRIDING"); |
3337 | | |
3338 | | /* |
3339 | | * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or |
3340 | | * "TABLE" or "OVERRIDING" |
3341 | | */ |
3342 | 0 | else if (TailMatches4("INSERT", "INTO", MatchAny, MatchAny) && |
3343 | 0 | ends_with(prev_wd, ')')) |
3344 | 0 | COMPLETE_WITH_LIST4("SELECT", "TABLE", "VALUES", "OVERRIDING"); |
3345 | | |
3346 | | /* Complete OVERRIDING */ |
3347 | 0 | else if (TailMatches1("OVERRIDING")) |
3348 | 0 | COMPLETE_WITH_LIST2("SYSTEM VALUE", "USER VALUE"); |
3349 | | |
3350 | | /* Complete after OVERRIDING clause */ |
3351 | 0 | else if (TailMatches3("OVERRIDING", MatchAny, "VALUE")) |
3352 | 0 | COMPLETE_WITH_LIST3("SELECT", "TABLE", "VALUES"); |
3353 | | |
3354 | | /* Insert an open parenthesis after "VALUES" */ |
3355 | 0 | else if (TailMatches1("VALUES") && !TailMatches2("DEFAULT", "VALUES")) |
3356 | 0 | COMPLETE_WITH_CONST("("); |
3357 | | |
3358 | | /* LOCK */ |
3359 | | /* Complete LOCK [TABLE] with a list of tables */ |
3360 | 0 | else if (Matches1("LOCK")) |
3361 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, |
3362 | 0 | " UNION SELECT 'TABLE'"); |
3363 | 0 | else if (Matches2("LOCK", "TABLE")) |
3364 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); |
3365 | | |
3366 | | /* For the following, handle the case of a single table only for now */ |
3367 | | |
3368 | | /* Complete LOCK [TABLE] <table> with "IN" */ |
3369 | 0 | else if (Matches2("LOCK", MatchAnyExcept("TABLE")) || |
3370 | 0 | Matches3("LOCK", "TABLE", MatchAny)) |
3371 | 0 | COMPLETE_WITH_CONST("IN"); |
3372 | | |
3373 | | /* Complete LOCK [TABLE] <table> IN with a lock mode */ |
3374 | 0 | else if (Matches3("LOCK", MatchAny, "IN") || |
3375 | 0 | Matches4("LOCK", "TABLE", MatchAny, "IN")) |
3376 | 0 | COMPLETE_WITH_LIST8("ACCESS SHARE MODE", |
3377 | 0 | "ROW SHARE MODE", "ROW EXCLUSIVE MODE", |
3378 | 0 | "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE", |
3379 | 0 | "SHARE ROW EXCLUSIVE MODE", |
3380 | 0 | "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE"); |
3381 | | |
3382 | | /* Complete LOCK [TABLE] <table> IN ACCESS|ROW with rest of lock mode */ |
3383 | 0 | else if (Matches4("LOCK", MatchAny, "IN", "ACCESS|ROW") || |
3384 | 0 | Matches5("LOCK", "TABLE", MatchAny, "IN", "ACCESS|ROW")) |
3385 | 0 | COMPLETE_WITH_LIST2("EXCLUSIVE MODE", "SHARE MODE"); |
3386 | | |
3387 | | /* Complete LOCK [TABLE] <table> IN SHARE with rest of lock mode */ |
3388 | 0 | else if (Matches4("LOCK", MatchAny, "IN", "SHARE") || |
3389 | 0 | Matches5("LOCK", "TABLE", MatchAny, "IN", "SHARE")) |
3390 | 0 | COMPLETE_WITH_LIST3("MODE", "ROW EXCLUSIVE MODE", |
3391 | 0 | "UPDATE EXCLUSIVE MODE"); |
3392 | | |
3393 | | /* NOTIFY --- can be inside EXPLAIN, RULE, etc */ |
3394 | 0 | else if (TailMatches1("NOTIFY")) |
3395 | 0 | 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'"); |
3396 | | |
3397 | | /* OPTIONS */ |
3398 | 0 | else if (TailMatches1("OPTIONS")) |
3399 | 0 | COMPLETE_WITH_CONST("("); |
3400 | | |
3401 | | /* OWNER TO - complete with available roles */ |
3402 | 0 | else if (TailMatches2("OWNER", "TO")) |
3403 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3404 | | |
3405 | | /* OWNER - complete with available roles */ |
3406 | 0 | else if (TailMatches1("OWNER")) |
3407 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3408 | | |
3409 | | /* ORDER BY */ |
3410 | 0 | else if (TailMatches3("FROM", MatchAny, "ORDER")) |
3411 | 0 | COMPLETE_WITH_CONST("BY"); |
3412 | 0 | else if (TailMatches4("FROM", MatchAny, "ORDER", "BY")) |
3413 | 0 | COMPLETE_WITH_ATTR(prev3_wd, ""); |
3414 | | |
3415 | | /* PREPARE xx AS */ |
3416 | 0 | else if (Matches3("PREPARE", MatchAny, "AS")) |
3417 | 0 | COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM"); |
3418 | | |
3419 | | /* |
3420 | | * PREPARE TRANSACTION is missing on purpose. It's intended for transaction |
3421 | | * managers, not for manual use in interactive sessions. |
3422 | | */ |
3423 | | |
3424 | | /* REASSIGN OWNED BY xxx TO yyy */ |
3425 | 0 | else if (Matches1("REASSIGN")) |
3426 | 0 | COMPLETE_WITH_CONST("OWNED BY"); |
3427 | 0 | else if (Matches2("REASSIGN", "OWNED")) |
3428 | 0 | COMPLETE_WITH_CONST("BY"); |
3429 | 0 | else if (Matches3("REASSIGN", "OWNED", "BY")) |
3430 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3431 | 0 | else if (Matches4("REASSIGN", "OWNED", "BY", MatchAny)) |
3432 | 0 | COMPLETE_WITH_CONST("TO"); |
3433 | 0 | else if (Matches5("REASSIGN", "OWNED", "BY", MatchAny, "TO")) |
3434 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3435 | | |
3436 | | /* REFRESH MATERIALIZED VIEW */ |
3437 | 0 | else if (Matches1("REFRESH")) |
3438 | 0 | COMPLETE_WITH_CONST("MATERIALIZED VIEW"); |
3439 | 0 | else if (Matches2("REFRESH", "MATERIALIZED")) |
3440 | 0 | COMPLETE_WITH_CONST("VIEW"); |
3441 | 0 | else if (Matches3("REFRESH", "MATERIALIZED", "VIEW")) |
3442 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, |
3443 | 0 | " UNION SELECT 'CONCURRENTLY'"); |
3444 | 0 | else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY")) |
3445 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); |
3446 | 0 | else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny)) |
3447 | 0 | COMPLETE_WITH_CONST("WITH"); |
3448 | 0 | else if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny)) |
3449 | 0 | COMPLETE_WITH_CONST("WITH"); |
3450 | 0 | else if (Matches5("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH")) |
3451 | 0 | COMPLETE_WITH_LIST2("NO DATA", "DATA"); |
3452 | 0 | else if (Matches6("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH")) |
3453 | 0 | COMPLETE_WITH_LIST2("NO DATA", "DATA"); |
3454 | 0 | else if (Matches6("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO")) |
3455 | 0 | COMPLETE_WITH_CONST("DATA"); |
3456 | 0 | else if (Matches7("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO")) |
3457 | 0 | COMPLETE_WITH_CONST("DATA"); |
3458 | | |
3459 | | /* REINDEX */ |
3460 | 0 | else if (Matches1("REINDEX")) |
3461 | 0 | COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE"); |
3462 | 0 | else if (Matches2("REINDEX", "TABLE")) |
3463 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); |
3464 | 0 | else if (Matches2("REINDEX", "INDEX")) |
3465 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); |
3466 | 0 | else if (Matches2("REINDEX", "SCHEMA")) |
3467 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_schemas); |
3468 | 0 | else if (Matches2("REINDEX", "SYSTEM|DATABASE")) |
3469 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_databases); |
3470 | | |
3471 | | /* SECURITY LABEL */ |
3472 | 0 | else if (Matches1("SECURITY")) |
3473 | 0 | COMPLETE_WITH_CONST("LABEL"); |
3474 | 0 | else if (Matches2("SECURITY", "LABEL")) |
3475 | 0 | COMPLETE_WITH_LIST2("ON", "FOR"); |
3476 | 0 | else if (Matches4("SECURITY", "LABEL", "FOR", MatchAny)) |
3477 | 0 | COMPLETE_WITH_CONST("ON"); |
3478 | 0 | else if (Matches3("SECURITY", "LABEL", "ON") || |
3479 | 0 | Matches5("SECURITY", "LABEL", "FOR", MatchAny, "ON")) |
3480 | 0 | { |
3481 | 0 | static const char *const list_SECURITY_LABEL[] = |
3482 | 0 | {"TABLE", "COLUMN", "AGGREGATE", "DATABASE", "DOMAIN", |
3483 | 0 | "EVENT TRIGGER", "FOREIGN TABLE", "FUNCTION", "LARGE OBJECT", |
3484 | 0 | "MATERIALIZED VIEW", "LANGUAGE", "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA", |
3485 | 0 | "SEQUENCE", "SUBSCRIPTION", "TABLESPACE", "TYPE", "VIEW", NULL}; |
3486 | |
|
3487 | 0 | COMPLETE_WITH_LIST(list_SECURITY_LABEL); |
3488 | 0 | } |
3489 | 0 | else if (Matches5("SECURITY", "LABEL", "ON", MatchAny, MatchAny)) |
3490 | 0 | COMPLETE_WITH_CONST("IS"); |
3491 | | |
3492 | | /* SELECT */ |
3493 | | /* naah . . . */ |
3494 | | |
3495 | | /* SET, RESET, SHOW */ |
3496 | | /* Complete with a variable name */ |
3497 | 0 | else if (TailMatches1("SET|RESET") && !TailMatches3("UPDATE", MatchAny, "SET")) |
3498 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_set_vars); |
3499 | 0 | else if (Matches1("SHOW")) |
3500 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_show_vars); |
3501 | | /* Complete "SET TRANSACTION" */ |
3502 | 0 | else if (Matches2("SET", "TRANSACTION")) |
3503 | 0 | COMPLETE_WITH_LIST5("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE"); |
3504 | 0 | else if (Matches2("BEGIN|START", "TRANSACTION") || |
3505 | 0 | Matches2("BEGIN", "WORK") || |
3506 | 0 | Matches1("BEGIN") || |
3507 | 0 | Matches5("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION")) |
3508 | 0 | COMPLETE_WITH_LIST4("ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE"); |
3509 | 0 | else if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "NOT") || |
3510 | 0 | Matches2("BEGIN", "NOT") || |
3511 | 0 | Matches6("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "NOT")) |
3512 | 0 | COMPLETE_WITH_CONST("DEFERRABLE"); |
3513 | 0 | else if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") || |
3514 | 0 | Matches2("BEGIN", "ISOLATION") || |
3515 | 0 | Matches6("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION")) |
3516 | 0 | COMPLETE_WITH_CONST("LEVEL"); |
3517 | 0 | else if (Matches4("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL") || |
3518 | 0 | Matches3("BEGIN", "ISOLATION", "LEVEL") || |
3519 | 0 | Matches7("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL")) |
3520 | 0 | COMPLETE_WITH_LIST3("READ", "REPEATABLE READ", "SERIALIZABLE"); |
3521 | 0 | else if (Matches5("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ") || |
3522 | 0 | Matches4("BEGIN", "ISOLATION", "LEVEL", "READ") || |
3523 | 0 | Matches8("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "READ")) |
3524 | 0 | COMPLETE_WITH_LIST2("UNCOMMITTED", "COMMITTED"); |
3525 | 0 | else if (Matches5("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE") || |
3526 | 0 | Matches4("BEGIN", "ISOLATION", "LEVEL", "REPEATABLE") || |
3527 | 0 | Matches8("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "REPEATABLE")) |
3528 | 0 | COMPLETE_WITH_CONST("READ"); |
3529 | 0 | else if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "READ") || |
3530 | 0 | Matches2("BEGIN", "READ") || |
3531 | 0 | Matches6("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "READ")) |
3532 | 0 | COMPLETE_WITH_LIST2("ONLY", "WRITE"); |
3533 | | /* SET CONSTRAINTS */ |
3534 | 0 | else if (Matches2("SET", "CONSTRAINTS")) |
3535 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'"); |
3536 | | /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */ |
3537 | 0 | else if (Matches3("SET", "CONSTRAINTS", MatchAny)) |
3538 | 0 | COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE"); |
3539 | | /* Complete SET ROLE */ |
3540 | 0 | else if (Matches2("SET", "ROLE")) |
3541 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3542 | | /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */ |
3543 | 0 | else if (Matches2("SET", "SESSION")) |
3544 | 0 | COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"); |
3545 | | /* Complete SET SESSION AUTHORIZATION with username */ |
3546 | 0 | else if (Matches3("SET", "SESSION", "AUTHORIZATION")) |
3547 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); |
3548 | | /* Complete RESET SESSION with AUTHORIZATION */ |
3549 | 0 | else if (Matches2("RESET", "SESSION")) |
3550 | 0 | COMPLETE_WITH_CONST("AUTHORIZATION"); |
3551 | | /* Complete SET <var> with "TO" */ |
3552 | 0 | else if (Matches2("SET", MatchAny)) |
3553 | 0 | COMPLETE_WITH_CONST("TO"); |
3554 | | |
3555 | | /* |
3556 | | * Complete ALTER DATABASE|FUNCTION||PROCEDURE|ROLE|ROUTINE|USER ... SET |
3557 | | * <name> |
3558 | | */ |
3559 | 0 | else if (HeadMatches2("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER") && |
3560 | 0 | TailMatches2("SET", MatchAny)) |
3561 | 0 | COMPLETE_WITH_LIST2("FROM CURRENT", "TO"); |
3562 | | /* Suggest possible variable values */ |
3563 | 0 | else if (TailMatches3("SET", MatchAny, "TO|=")) |
3564 | 0 | { |
3565 | | /* special cased code for individual GUCs */ |
3566 | 0 | if (TailMatches2("DateStyle", "TO|=")) |
3567 | 0 | { |
3568 | 0 | static const char *const my_list[] = |
3569 | 0 | {"ISO", "SQL", "Postgres", "German", |
3570 | 0 | "YMD", "DMY", "MDY", |
3571 | 0 | "US", "European", "NonEuropean", |
3572 | 0 | "DEFAULT", NULL}; |
3573 | |
|
3574 | 0 | COMPLETE_WITH_LIST(my_list); |
3575 | 0 | } |
3576 | 0 | else if (TailMatches2("search_path", "TO|=")) |
3577 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_schemas |
3578 | 0 | " AND nspname not like 'pg\\_toast%%' " |
3579 | 0 | " AND nspname not like 'pg\\_temp%%' " |
3580 | 0 | " UNION SELECT 'DEFAULT' "); |
3581 | 0 | else |
3582 | 0 | { |
3583 | | /* generic, type based, GUC support */ |
3584 | 0 | char *guctype = get_guctype(prev2_wd); |
3585 | |
|
3586 | 0 | if (guctype && strcmp(guctype, "enum") == 0) |
3587 | 0 | { |
3588 | 0 | char querybuf[1024]; |
3589 | |
|
3590 | 0 | snprintf(querybuf, sizeof(querybuf), Query_for_enum, prev2_wd); |
3591 | 0 | COMPLETE_WITH_QUERY(querybuf); |
3592 | 0 | } |
3593 | 0 | else if (guctype && strcmp(guctype, "bool") == 0) |
3594 | 0 | COMPLETE_WITH_LIST9("on", "off", "true", "false", "yes", "no", |
3595 | 0 | "1", "0", "DEFAULT"); |
3596 | 0 | else |
3597 | 0 | COMPLETE_WITH_CONST("DEFAULT"); |
3598 | |
|
3599 | 0 | if (guctype) |
3600 | 0 | free(guctype); |
3601 | 0 | } |
3602 | 0 | } |
3603 | | |
3604 | | /* START TRANSACTION */ |
3605 | 0 | else if (Matches1("START")) |
3606 | 0 | COMPLETE_WITH_CONST("TRANSACTION"); |
3607 | | |
3608 | | /* TABLE, but not TABLE embedded in other commands */ |
3609 | 0 | else if (Matches1("TABLE")) |
3610 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); |
3611 | | |
3612 | | /* TABLESAMPLE */ |
3613 | 0 | else if (TailMatches1("TABLESAMPLE")) |
3614 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods); |
3615 | 0 | else if (TailMatches2("TABLESAMPLE", MatchAny)) |
3616 | 0 | COMPLETE_WITH_CONST("("); |
3617 | | |
3618 | | /* TRUNCATE */ |
3619 | 0 | else if (Matches1("TRUNCATE")) |
3620 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
3621 | | |
3622 | | /* UNLISTEN */ |
3623 | 0 | else if (Matches1("UNLISTEN")) |
3624 | 0 | 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 '*'"); |
3625 | | |
3626 | | /* UPDATE --- can be inside EXPLAIN, RULE, etc */ |
3627 | | /* If prev. word is UPDATE suggest a list of tables */ |
3628 | 0 | else if (TailMatches1("UPDATE")) |
3629 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); |
3630 | | /* Complete UPDATE <table> with "SET" */ |
3631 | 0 | else if (TailMatches2("UPDATE", MatchAny)) |
3632 | 0 | COMPLETE_WITH_CONST("SET"); |
3633 | | /* Complete UPDATE <table> SET with list of attributes */ |
3634 | 0 | else if (TailMatches3("UPDATE", MatchAny, "SET")) |
3635 | 0 | COMPLETE_WITH_ATTR(prev2_wd, ""); |
3636 | | /* UPDATE <table> SET <attr> = */ |
3637 | 0 | else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny)) |
3638 | 0 | COMPLETE_WITH_CONST("="); |
3639 | | |
3640 | | /* USER MAPPING */ |
3641 | 0 | else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING")) |
3642 | 0 | COMPLETE_WITH_CONST("FOR"); |
3643 | 0 | else if (Matches4("CREATE", "USER", "MAPPING", "FOR")) |
3644 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles |
3645 | 0 | " UNION SELECT 'CURRENT_USER'" |
3646 | 0 | " UNION SELECT 'PUBLIC'" |
3647 | 0 | " UNION SELECT 'USER'"); |
3648 | 0 | else if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR")) |
3649 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); |
3650 | 0 | else if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny)) |
3651 | 0 | COMPLETE_WITH_CONST("SERVER"); |
3652 | 0 | else if (Matches7("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny)) |
3653 | 0 | COMPLETE_WITH_CONST("OPTIONS"); |
3654 | | |
3655 | | /* |
3656 | | * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ] |
3657 | | * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] |
3658 | | */ |
3659 | 0 | else if (Matches1("VACUUM")) |
3660 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, |
3661 | 0 | " UNION SELECT 'FULL'" |
3662 | 0 | " UNION SELECT 'FREEZE'" |
3663 | 0 | " UNION SELECT 'ANALYZE'" |
3664 | 0 | " UNION SELECT 'VERBOSE'"); |
3665 | 0 | else if (Matches2("VACUUM", "FULL|FREEZE")) |
3666 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, |
3667 | 0 | " UNION SELECT 'ANALYZE'" |
3668 | 0 | " UNION SELECT 'VERBOSE'"); |
3669 | 0 | else if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE")) |
3670 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, |
3671 | 0 | " UNION SELECT 'VERBOSE'"); |
3672 | 0 | else if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE")) |
3673 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, |
3674 | 0 | " UNION SELECT 'ANALYZE'"); |
3675 | 0 | else if (Matches2("VACUUM", "VERBOSE")) |
3676 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, |
3677 | 0 | " UNION SELECT 'ANALYZE'"); |
3678 | 0 | else if (Matches2("VACUUM", "ANALYZE")) |
3679 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, |
3680 | 0 | " UNION SELECT 'VERBOSE'"); |
3681 | 0 | else if (HeadMatches1("VACUUM")) |
3682 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); |
3683 | | |
3684 | | /* WITH [RECURSIVE] */ |
3685 | | |
3686 | | /* |
3687 | | * Only match when WITH is the first word, as WITH may appear in many |
3688 | | * other contexts. |
3689 | | */ |
3690 | 0 | else if (Matches1("WITH")) |
3691 | 0 | COMPLETE_WITH_CONST("RECURSIVE"); |
3692 | | |
3693 | | /* ANALYZE */ |
3694 | | /* Complete with list of tables */ |
3695 | 0 | else if (Matches1("ANALYZE")) |
3696 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); |
3697 | | |
3698 | | /* WHERE */ |
3699 | | /* Simple case of the word before the where being the table name */ |
3700 | 0 | else if (TailMatches2(MatchAny, "WHERE")) |
3701 | 0 | COMPLETE_WITH_ATTR(prev2_wd, ""); |
3702 | | |
3703 | | /* ... FROM ... */ |
3704 | | /* TODO: also include SRF ? */ |
3705 | 0 | else if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM")) |
3706 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); |
3707 | | |
3708 | | /* ... JOIN ... */ |
3709 | 0 | else if (TailMatches1("JOIN")) |
3710 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); |
3711 | | |
3712 | | /* Backslash commands */ |
3713 | | /* TODO: \dc \dd \dl */ |
3714 | 0 | else if (TailMatchesCS1("\\?")) |
3715 | 0 | COMPLETE_WITH_LIST_CS3("commands", "options", "variables"); |
3716 | 0 | else if (TailMatchesCS1("\\connect|\\c")) |
3717 | 0 | { |
3718 | 0 | if (!recognized_connection_string(text)) |
3719 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_databases); |
3720 | 0 | } |
3721 | 0 | else if (TailMatchesCS2("\\connect|\\c", MatchAny)) |
3722 | 0 | { |
3723 | 0 | if (!recognized_connection_string(prev_wd)) |
3724 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3725 | 0 | } |
3726 | 0 | else if (TailMatchesCS1("\\da*")) |
3727 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); |
3728 | 0 | else if (TailMatchesCS1("\\dA*")) |
3729 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); |
3730 | 0 | else if (TailMatchesCS1("\\db*")) |
3731 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); |
3732 | 0 | else if (TailMatchesCS1("\\dD*")) |
3733 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); |
3734 | 0 | else if (TailMatchesCS1("\\des*")) |
3735 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_servers); |
3736 | 0 | else if (TailMatchesCS1("\\deu*")) |
3737 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); |
3738 | 0 | else if (TailMatchesCS1("\\dew*")) |
3739 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_fdws); |
3740 | 0 | else if (TailMatchesCS1("\\df*")) |
3741 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL); |
3742 | | |
3743 | 0 | else if (TailMatchesCS1("\\dFd*")) |
3744 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); |
3745 | 0 | else if (TailMatchesCS1("\\dFp*")) |
3746 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers); |
3747 | 0 | else if (TailMatchesCS1("\\dFt*")) |
3748 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates); |
3749 | | /* must be at end of \dF alternatives: */ |
3750 | 0 | else if (TailMatchesCS1("\\dF*")) |
3751 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); |
3752 | | |
3753 | 0 | else if (TailMatchesCS1("\\dgr*")) |
3754 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_tablegroups); |
3755 | 0 | else if (TailMatchesCS1("\\di*")) |
3756 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); |
3757 | 0 | else if (TailMatchesCS1("\\dL*")) |
3758 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_languages); |
3759 | 0 | else if (TailMatchesCS1("\\dn*")) |
3760 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_schemas); |
3761 | 0 | else if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z")) |
3762 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); |
3763 | 0 | else if (TailMatchesCS1("\\ds*")) |
3764 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); |
3765 | 0 | else if (TailMatchesCS1("\\dt*")) |
3766 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); |
3767 | 0 | else if (TailMatchesCS1("\\dT*")) |
3768 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); |
3769 | 0 | else if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*")) |
3770 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3771 | 0 | else if (TailMatchesCS1("\\dv*")) |
3772 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); |
3773 | 0 | else if (TailMatchesCS1("\\dx*")) |
3774 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_extensions); |
3775 | 0 | else if (TailMatchesCS1("\\dm*")) |
3776 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); |
3777 | 0 | else if (TailMatchesCS1("\\dE*")) |
3778 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); |
3779 | 0 | else if (TailMatchesCS1("\\dy*")) |
3780 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); |
3781 | | |
3782 | | /* must be at end of \d alternatives: */ |
3783 | 0 | else if (TailMatchesCS1("\\d*")) |
3784 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); |
3785 | | |
3786 | 0 | else if (TailMatchesCS1("\\ef")) |
3787 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL); |
3788 | 0 | else if (TailMatchesCS1("\\ev")) |
3789 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); |
3790 | | |
3791 | 0 | else if (TailMatchesCS1("\\encoding")) |
3792 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_encodings); |
3793 | 0 | else if (TailMatchesCS1("\\h|\\help")) |
3794 | 0 | COMPLETE_WITH_LIST(sql_commands); |
3795 | 0 | else if (TailMatchesCS2("\\h|\\help", MatchAny)) |
3796 | 0 | { |
3797 | 0 | if (TailMatches1("DROP")) |
3798 | 0 | matches = completion_matches(text, drop_command_generator); |
3799 | 0 | else if (TailMatches1("ALTER")) |
3800 | 0 | matches = completion_matches(text, alter_command_generator); |
3801 | | |
3802 | | /* |
3803 | | * CREATE is recognized by tail match elsewhere, so doesn't need to be |
3804 | | * repeated here |
3805 | | */ |
3806 | 0 | } |
3807 | 0 | else if (TailMatchesCS3("\\h|\\help", MatchAny, MatchAny)) |
3808 | 0 | { |
3809 | 0 | if (TailMatches2("CREATE|DROP", "ACCESS")) |
3810 | 0 | COMPLETE_WITH_CONST("METHOD"); |
3811 | 0 | else if (TailMatches2("ALTER", "DEFAULT")) |
3812 | 0 | COMPLETE_WITH_CONST("PRIVILEGES"); |
3813 | 0 | else if (TailMatches2("CREATE|ALTER|DROP", "EVENT")) |
3814 | 0 | COMPLETE_WITH_CONST("TRIGGER"); |
3815 | 0 | else if (TailMatches2("CREATE|ALTER|DROP", "FOREIGN")) |
3816 | 0 | COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE"); |
3817 | 0 | else if (TailMatches2("ALTER", "LARGE")) |
3818 | 0 | COMPLETE_WITH_CONST("OBJECT"); |
3819 | 0 | else if (TailMatches2("CREATE|ALTER|DROP", "MATERIALIZED")) |
3820 | 0 | COMPLETE_WITH_CONST("VIEW"); |
3821 | 0 | else if (TailMatches2("CREATE|ALTER|DROP", "TEXT")) |
3822 | 0 | COMPLETE_WITH_CONST("SEARCH"); |
3823 | 0 | else if (TailMatches2("CREATE|ALTER|DROP", "USER")) |
3824 | 0 | COMPLETE_WITH_CONST("MAPPING FOR"); |
3825 | 0 | } |
3826 | 0 | else if (TailMatchesCS4("\\h|\\help", MatchAny, MatchAny, MatchAny)) |
3827 | 0 | { |
3828 | 0 | if (TailMatches3("CREATE|ALTER|DROP", "FOREIGN", "DATA")) |
3829 | 0 | COMPLETE_WITH_CONST("WRAPPER"); |
3830 | 0 | else if (TailMatches3("CREATE|ALTER|DROP", "TEXT", "SEARCH")) |
3831 | 0 | COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE"); |
3832 | 0 | else if (TailMatches3("CREATE|ALTER|DROP", "USER", "MAPPING")) |
3833 | 0 | COMPLETE_WITH_CONST("FOR"); |
3834 | 0 | } |
3835 | 0 | else if (TailMatchesCS1("\\l*") && !TailMatchesCS1("\\lo*")) |
3836 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_databases); |
3837 | 0 | else if (TailMatchesCS1("\\password")) |
3838 | 0 | COMPLETE_WITH_QUERY(Query_for_list_of_roles); |
3839 | 0 | else if (TailMatchesCS1("\\pset")) |
3840 | 0 | { |
3841 | 0 | static const char *const my_list[] = |
3842 | 0 | {"border", "columns", "expanded", "fieldsep", "fieldsep_zero", |
3843 | 0 | "footer", "format", "linestyle", "null", "numericlocale", |
3844 | 0 | "pager", "pager_min_lines", "recordsep", "recordsep_zero", |
3845 | 0 | "tableattr", "title", "tuples_only", "unicode_border_linestyle", |
3846 | 0 | "unicode_column_linestyle", "unicode_header_linestyle", NULL}; |
3847 | |
|
3848 | 0 | COMPLETE_WITH_LIST_CS(my_list); |
3849 | 0 | } |
3850 | 0 | else if (TailMatchesCS2("\\pset", MatchAny)) |
3851 | 0 | { |
3852 | 0 | if (TailMatchesCS1("format")) |
3853 | 0 | { |
3854 | 0 | static const char *const my_list[] = |
3855 | 0 | {"unaligned", "aligned", "wrapped", "html", "asciidoc", |
3856 | 0 | "latex", "latex-longtable", "troff-ms", NULL}; |
3857 | |
|
3858 | 0 | COMPLETE_WITH_LIST_CS(my_list); |
3859 | 0 | } |
3860 | 0 | else if (TailMatchesCS1("linestyle")) |
3861 | 0 | COMPLETE_WITH_LIST_CS3("ascii", "old-ascii", "unicode"); |
3862 | 0 | else if (TailMatchesCS1("pager")) |
3863 | 0 | COMPLETE_WITH_LIST_CS3("on", "off", "always"); |
3864 | 0 | else if (TailMatchesCS1("unicode_border_linestyle|" |
3865 | 0 | "unicode_column_linestyle|" |
3866 | 0 | "unicode_header_linestyle")) |
3867 | 0 | COMPLETE_WITH_LIST_CS2("single", "double"); |
3868 | 0 | } |
3869 | 0 | else if (TailMatchesCS1("\\unset")) |
3870 | 0 | matches = complete_from_variables(text, "", "", true); |
3871 | 0 | else if (TailMatchesCS1("\\set")) |
3872 | 0 | matches = complete_from_variables(text, "", "", false); |
3873 | 0 | else if (TailMatchesCS2("\\set", MatchAny)) |
3874 | 0 | { |
3875 | 0 | if (TailMatchesCS1("AUTOCOMMIT|ON_ERROR_STOP|QUIET|" |
3876 | 0 | "SINGLELINE|SINGLESTEP")) |
3877 | 0 | COMPLETE_WITH_LIST_CS2("on", "off"); |
3878 | 0 | else if (TailMatchesCS1("COMP_KEYWORD_CASE")) |
3879 | 0 | COMPLETE_WITH_LIST_CS4("lower", "upper", |
3880 | 0 | "preserve-lower", "preserve-upper"); |
3881 | 0 | else if (TailMatchesCS1("ECHO")) |
3882 | 0 | COMPLETE_WITH_LIST_CS4("errors", "queries", "all", "none"); |
3883 | 0 | else if (TailMatchesCS1("ECHO_HIDDEN")) |
3884 | 0 | COMPLETE_WITH_LIST_CS3("noexec", "off", "on"); |
3885 | 0 | else if (TailMatchesCS1("HISTCONTROL")) |
3886 | 0 | COMPLETE_WITH_LIST_CS4("ignorespace", "ignoredups", |
3887 | 0 | "ignoreboth", "none"); |
3888 | 0 | else if (TailMatchesCS1("ON_ERROR_ROLLBACK")) |
3889 | 0 | COMPLETE_WITH_LIST_CS3("on", "off", "interactive"); |
3890 | 0 | else if (TailMatchesCS1("SHOW_CONTEXT")) |
3891 | 0 | COMPLETE_WITH_LIST_CS3("never", "errors", "always"); |
3892 | 0 | else if (TailMatchesCS1("VERBOSITY")) |
3893 | 0 | COMPLETE_WITH_LIST_CS3("default", "verbose", "terse"); |
3894 | 0 | } |
3895 | 0 | else if (TailMatchesCS1("\\sf*")) |
3896 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL); |
3897 | 0 | else if (TailMatchesCS1("\\sv*")) |
3898 | 0 | COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); |
3899 | 0 | else if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|" |
3900 | 0 | "\\ir|\\include_relative|\\o|\\out|" |
3901 | 0 | "\\s|\\w|\\write|\\lo_import")) |
3902 | 0 | { |
3903 | 0 | completion_charp = "\\"; |
3904 | 0 | matches = completion_matches(text, complete_from_files); |
3905 | 0 | } |
3906 | | |
3907 | | /* |
3908 | | * Finally, we look through the list of "things", such as TABLE, INDEX and |
3909 | | * check if that was the previous word. If so, execute the query to get a |
3910 | | * list of them. |
3911 | | */ |
3912 | 0 | else |
3913 | 0 | { |
3914 | 0 | int i; |
3915 | |
|
3916 | 0 | for (i = 0; words_after_create[i].name; i++) |
3917 | 0 | { |
3918 | 0 | if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0) |
3919 | 0 | { |
3920 | 0 | if (words_after_create[i].query) |
3921 | 0 | COMPLETE_WITH_QUERY(words_after_create[i].query); |
3922 | 0 | else if (words_after_create[i].vquery) |
3923 | 0 | COMPLETE_WITH_VERSIONED_QUERY(words_after_create[i].vquery); |
3924 | 0 | else if (words_after_create[i].squery) |
3925 | 0 | COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(words_after_create[i].squery, |
3926 | 0 | NULL); |
3927 | 0 | break; |
3928 | 0 | } |
3929 | 0 | } |
3930 | 0 | } |
3931 | | |
3932 | | /* |
3933 | | * If we still don't have anything to match we have to fabricate some sort |
3934 | | * of default list. If we were to just return NULL, readline automatically |
3935 | | * attempts filename completion, and that's usually no good. |
3936 | | */ |
3937 | 0 | if (matches == NULL) |
3938 | 0 | { |
3939 | 0 | COMPLETE_WITH_CONST(""); |
3940 | | #ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER |
3941 | | rl_completion_append_character = '\0'; |
3942 | | #endif |
3943 | 0 | } |
3944 | | |
3945 | | /* free storage */ |
3946 | 0 | free(previous_words); |
3947 | 0 | free(words_buffer); |
3948 | | |
3949 | | /* Return our Grand List O' Matches */ |
3950 | 0 | return matches; |
3951 | 0 | } |
3952 | | |
3953 | | |
3954 | | /* |
3955 | | * GENERATOR FUNCTIONS |
3956 | | * |
3957 | | * These functions do all the actual work of completing the input. They get |
3958 | | * passed the text so far and the count how many times they have been called |
3959 | | * so far with the same text. |
3960 | | * If you read the above carefully, you'll see that these don't get called |
3961 | | * directly but through the readline interface. |
3962 | | * The return value is expected to be the full completion of the text, going |
3963 | | * through a list each time, or NULL if there are no more matches. The string |
3964 | | * will be free()'d by readline, so you must run it through strdup() or |
3965 | | * something of that sort. |
3966 | | */ |
3967 | | |
3968 | | /* |
3969 | | * Common routine for create_command_generator and drop_command_generator. |
3970 | | * Entries that have 'excluded' flags are not returned. |
3971 | | */ |
3972 | | static char * |
3973 | | create_or_drop_command_generator(const char *text, int state, bits32 excluded) |
3974 | 0 | { |
3975 | 0 | static int list_index, |
3976 | 0 | string_length; |
3977 | 0 | const char *name; |
3978 | | |
3979 | | /* If this is the first time for this completion, init some values */ |
3980 | 0 | if (state == 0) |
3981 | 0 | { |
3982 | 0 | list_index = 0; |
3983 | 0 | string_length = strlen(text); |
3984 | 0 | } |
3985 | | |
3986 | | /* find something that matches */ |
3987 | 0 | while ((name = words_after_create[list_index++].name)) |
3988 | 0 | { |
3989 | 0 | if ((pg_strncasecmp(name, text, string_length) == 0) && |
3990 | 0 | !(words_after_create[list_index - 1].flags & excluded)) |
3991 | 0 | return pg_strdup_keyword_case(name, text); |
3992 | 0 | } |
3993 | | /* if nothing matches, return NULL */ |
3994 | 0 | return NULL; |
3995 | 0 | } |
3996 | | |
3997 | | /* |
3998 | | * This one gives you one from a list of things you can put after CREATE |
3999 | | * as defined above. |
4000 | | */ |
4001 | | static char * |
4002 | | create_command_generator(const char *text, int state) |
4003 | 0 | { |
4004 | 0 | return create_or_drop_command_generator(text, state, THING_NO_CREATE); |
4005 | 0 | } |
4006 | | |
4007 | | /* |
4008 | | * This function gives you a list of things you can put after a DROP command. |
4009 | | */ |
4010 | | static char * |
4011 | | drop_command_generator(const char *text, int state) |
4012 | 0 | { |
4013 | 0 | return create_or_drop_command_generator(text, state, THING_NO_DROP); |
4014 | 0 | } |
4015 | | |
4016 | | /* |
4017 | | * This function gives you a list of things you can put after an ALTER command. |
4018 | | */ |
4019 | | static char * |
4020 | | alter_command_generator(const char *text, int state) |
4021 | 0 | { |
4022 | 0 | return create_or_drop_command_generator(text, state, THING_NO_ALTER); |
4023 | 0 | } |
4024 | | |
4025 | | /* |
4026 | | * These functions generate lists using server queries. |
4027 | | * They are all wrappers for _complete_from_query. |
4028 | | */ |
4029 | | |
4030 | | static char * |
4031 | | complete_from_query(const char *text, int state) |
4032 | 0 | { |
4033 | | /* query is assumed to work for any server version */ |
4034 | 0 | return _complete_from_query(completion_charp, NULL, text, state); |
4035 | 0 | } |
4036 | | |
4037 | | static char * |
4038 | | complete_from_versioned_query(const char *text, int state) |
4039 | 0 | { |
4040 | 0 | const VersionedQuery *vquery = completion_vquery; |
4041 | | |
4042 | | /* Find appropriate array element */ |
4043 | 0 | while (pset.sversion < vquery->min_server_version) |
4044 | 0 | vquery++; |
4045 | | /* Fail completion if server is too old */ |
4046 | 0 | if (vquery->query == NULL) |
4047 | 0 | return NULL; |
4048 | | |
4049 | 0 | return _complete_from_query(vquery->query, NULL, text, state); |
4050 | 0 | } |
4051 | | |
4052 | | static char * |
4053 | | complete_from_schema_query(const char *text, int state) |
4054 | 0 | { |
4055 | | /* query is assumed to work for any server version */ |
4056 | 0 | return _complete_from_query(completion_charp, completion_squery, |
4057 | 0 | text, state); |
4058 | 0 | } |
4059 | | |
4060 | | static char * |
4061 | | complete_from_versioned_schema_query(const char *text, int state) |
4062 | 0 | { |
4063 | 0 | const SchemaQuery *squery = completion_squery; |
4064 | 0 | const VersionedQuery *vquery = completion_vquery; |
4065 | | |
4066 | | /* Find appropriate array element */ |
4067 | 0 | while (pset.sversion < squery->min_server_version) |
4068 | 0 | squery++; |
4069 | | /* Fail completion if server is too old */ |
4070 | 0 | if (squery->catname == NULL) |
4071 | 0 | return NULL; |
4072 | | |
4073 | | /* Likewise for the add-on text, if any */ |
4074 | 0 | if (vquery) |
4075 | 0 | { |
4076 | 0 | while (pset.sversion < vquery->min_server_version) |
4077 | 0 | vquery++; |
4078 | 0 | if (vquery->query == NULL) |
4079 | 0 | return NULL; |
4080 | 0 | } |
4081 | | |
4082 | 0 | return _complete_from_query(vquery ? vquery->query : NULL, |
4083 | 0 | squery, text, state); |
4084 | 0 | } |
4085 | | |
4086 | | |
4087 | | /* |
4088 | | * This creates a list of matching things, according to a query described by |
4089 | | * the initial arguments. The caller has already done any work needed to |
4090 | | * select the appropriate query for the server's version. |
4091 | | * |
4092 | | * The query can be one of two kinds: |
4093 | | * |
4094 | | * 1. A simple query which must contain a %d and a %s, which will be replaced |
4095 | | * by the string length of the text and the text itself. The query may also |
4096 | | * have up to four more %s in it; the first two such will be replaced by the |
4097 | | * value of completion_info_charp, the next two by the value of |
4098 | | * completion_info_charp2. |
4099 | | * |
4100 | | * 2. A schema query used for completion of both schema and relation names. |
4101 | | * These are more complex and must contain in the following order: |
4102 | | * %d %s %d %s %d %s %s %d %s |
4103 | | * where %d is the string length of the text and %s the text itself. |
4104 | | * |
4105 | | * If both simple_query and schema_query are non-NULL, then we construct |
4106 | | * a schema query and append the (uninterpreted) string simple_query to it. |
4107 | | * |
4108 | | * It is assumed that strings should be escaped to become SQL literals |
4109 | | * (that is, what is in the query is actually ... '%s' ...) |
4110 | | * |
4111 | | * See top of file for examples of both kinds of query. |
4112 | | * |
4113 | | * "text" and "state" are supplied by readline. |
4114 | | */ |
4115 | | static char * |
4116 | | _complete_from_query(const char *simple_query, |
4117 | | const SchemaQuery *schema_query, |
4118 | | const char *text, int state) |
4119 | 0 | { |
4120 | 0 | static int list_index, |
4121 | 0 | byte_length; |
4122 | 0 | static PGresult *result = NULL; |
4123 | | |
4124 | | /* |
4125 | | * If this is the first time for this completion, we fetch a list of our |
4126 | | * "things" from the backend. |
4127 | | */ |
4128 | 0 | if (state == 0) |
4129 | 0 | { |
4130 | 0 | PQExpBufferData query_buffer; |
4131 | 0 | char *e_text; |
4132 | 0 | char *e_info_charp; |
4133 | 0 | char *e_info_charp2; |
4134 | 0 | const char *pstr = text; |
4135 | 0 | int char_length = 0; |
4136 | |
|
4137 | 0 | list_index = 0; |
4138 | 0 | byte_length = strlen(text); |
4139 | | |
4140 | | /* |
4141 | | * Count length as number of characters (not bytes), for passing to |
4142 | | * substring |
4143 | | */ |
4144 | 0 | while (*pstr) |
4145 | 0 | { |
4146 | 0 | char_length++; |
4147 | 0 | pstr += PQmblenBounded(pstr, pset.encoding); |
4148 | 0 | } |
4149 | | |
4150 | | /* Free any prior result */ |
4151 | 0 | PQclear(result); |
4152 | 0 | result = NULL; |
4153 | | |
4154 | | /* Set up suitably-escaped copies of textual inputs */ |
4155 | 0 | e_text = escape_string(text); |
4156 | |
|
4157 | 0 | if (completion_info_charp) |
4158 | 0 | e_info_charp = escape_string(completion_info_charp); |
4159 | 0 | else |
4160 | 0 | e_info_charp = NULL; |
4161 | |
|
4162 | 0 | if (completion_info_charp2) |
4163 | 0 | e_info_charp2 = escape_string(completion_info_charp2); |
4164 | 0 | else |
4165 | 0 | e_info_charp2 = NULL; |
4166 | |
|
4167 | 0 | initPQExpBuffer(&query_buffer); |
4168 | |
|
4169 | 0 | if (schema_query) |
4170 | 0 | { |
4171 | | /* schema_query gives us the pieces to assemble */ |
4172 | 0 | const char *qualresult = schema_query->qualresult; |
4173 | |
|
4174 | 0 | if (qualresult == NULL) |
4175 | 0 | qualresult = schema_query->result; |
4176 | | |
4177 | | /* Get unqualified names matching the input-so-far */ |
4178 | 0 | appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ", |
4179 | 0 | schema_query->result, |
4180 | 0 | schema_query->catname); |
4181 | 0 | if (schema_query->selcondition) |
4182 | 0 | appendPQExpBuffer(&query_buffer, "%s AND ", |
4183 | 0 | schema_query->selcondition); |
4184 | 0 | appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'", |
4185 | 0 | schema_query->result, |
4186 | 0 | char_length, e_text); |
4187 | 0 | appendPQExpBuffer(&query_buffer, " AND %s", |
4188 | 0 | schema_query->viscondition); |
4189 | | |
4190 | | /* |
4191 | | * When fetching relation names, suppress system catalogs unless |
4192 | | * the input-so-far begins with "pg_". This is a compromise |
4193 | | * between not offering system catalogs for completion at all, and |
4194 | | * having them swamp the result when the input is just "p". |
4195 | | */ |
4196 | 0 | if (strcmp(schema_query->catname, |
4197 | 0 | "pg_catalog.pg_class c") == 0 && |
4198 | 0 | strncmp(text, "pg_", 3) !=0) |
4199 | 0 | { |
4200 | 0 | appendPQExpBufferStr(&query_buffer, |
4201 | 0 | " AND c.relnamespace <> (SELECT oid FROM" |
4202 | 0 | " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')"); |
4203 | 0 | } |
4204 | | |
4205 | | /* |
4206 | | * Add in matching schema names, but only if there is more than |
4207 | | * one potential match among schema names. |
4208 | | */ |
4209 | 0 | appendPQExpBuffer(&query_buffer, "\nUNION\n" |
4210 | 0 | "SELECT pg_catalog.quote_ident(n.nspname) || '.' " |
4211 | 0 | "FROM pg_catalog.pg_namespace n " |
4212 | 0 | "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'", |
4213 | 0 | char_length, e_text); |
4214 | 0 | appendPQExpBuffer(&query_buffer, |
4215 | 0 | " AND (SELECT pg_catalog.count(*)" |
4216 | 0 | " FROM pg_catalog.pg_namespace" |
4217 | 0 | " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" |
4218 | 0 | " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1", |
4219 | 0 | char_length, e_text); |
4220 | | |
4221 | | /* |
4222 | | * Add in matching qualified names, but only if there is exactly |
4223 | | * one schema matching the input-so-far. |
4224 | | */ |
4225 | 0 | appendPQExpBuffer(&query_buffer, "\nUNION\n" |
4226 | 0 | "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s " |
4227 | 0 | "FROM %s, pg_catalog.pg_namespace n " |
4228 | 0 | "WHERE %s = n.oid AND ", |
4229 | 0 | qualresult, |
4230 | 0 | schema_query->catname, |
4231 | 0 | schema_query->namespace); |
4232 | 0 | if (schema_query->selcondition) |
4233 | 0 | appendPQExpBuffer(&query_buffer, "%s AND ", |
4234 | 0 | schema_query->selcondition); |
4235 | 0 | appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", |
4236 | 0 | qualresult, |
4237 | 0 | char_length, e_text); |
4238 | | |
4239 | | /* |
4240 | | * This condition exploits the single-matching-schema rule to |
4241 | | * speed up the query |
4242 | | */ |
4243 | 0 | appendPQExpBuffer(&query_buffer, |
4244 | 0 | " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) =" |
4245 | 0 | " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)", |
4246 | 0 | char_length, e_text); |
4247 | 0 | appendPQExpBuffer(&query_buffer, |
4248 | 0 | " AND (SELECT pg_catalog.count(*)" |
4249 | 0 | " FROM pg_catalog.pg_namespace" |
4250 | 0 | " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" |
4251 | 0 | " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1", |
4252 | 0 | char_length, e_text); |
4253 | | |
4254 | | /* If an addon query was provided, use it */ |
4255 | 0 | if (simple_query) |
4256 | 0 | appendPQExpBuffer(&query_buffer, "\n%s", simple_query); |
4257 | 0 | } |
4258 | 0 | else |
4259 | 0 | { |
4260 | 0 | Assert(simple_query); |
4261 | | /* simple_query is an sprintf-style format string */ |
4262 | 0 | appendPQExpBuffer(&query_buffer, simple_query, |
4263 | 0 | char_length, e_text, |
4264 | 0 | e_info_charp, e_info_charp, |
4265 | 0 | e_info_charp2, e_info_charp2); |
4266 | 0 | } |
4267 | | |
4268 | | /* Limit the number of records in the result */ |
4269 | 0 | appendPQExpBuffer(&query_buffer, "\nLIMIT %d", |
4270 | 0 | completion_max_records); |
4271 | |
|
4272 | 0 | result = exec_query(query_buffer.data); |
4273 | |
|
4274 | 0 | termPQExpBuffer(&query_buffer); |
4275 | 0 | free(e_text); |
4276 | 0 | if (e_info_charp) |
4277 | 0 | free(e_info_charp); |
4278 | 0 | if (e_info_charp2) |
4279 | 0 | free(e_info_charp2); |
4280 | 0 | } |
4281 | | |
4282 | | /* Find something that matches */ |
4283 | 0 | if (result && PQresultStatus(result) == PGRES_TUPLES_OK) |
4284 | 0 | { |
4285 | 0 | const char *item; |
4286 | |
|
4287 | 0 | while (list_index < PQntuples(result) && |
4288 | 0 | (item = PQgetvalue(result, list_index++, 0))) |
4289 | 0 | if (pg_strncasecmp(text, item, byte_length) == 0) |
4290 | 0 | return pg_strdup(item); |
4291 | 0 | } |
4292 | | |
4293 | | /* If nothing matches, free the db structure and return null */ |
4294 | 0 | PQclear(result); |
4295 | 0 | result = NULL; |
4296 | 0 | return NULL; |
4297 | 0 | } |
4298 | | |
4299 | | |
4300 | | /* |
4301 | | * This function returns in order one of a fixed, NULL pointer terminated list |
4302 | | * of strings (if matching). This can be used if there are only a fixed number |
4303 | | * SQL words that can appear at certain spot. |
4304 | | */ |
4305 | | static char * |
4306 | | complete_from_list(const char *text, int state) |
4307 | 0 | { |
4308 | 0 | static int string_length, |
4309 | 0 | list_index, |
4310 | 0 | matches; |
4311 | 0 | static bool casesensitive; |
4312 | 0 | const char *item; |
4313 | | |
4314 | | /* need to have a list */ |
4315 | 0 | Assert(completion_charpp != NULL); |
4316 | | |
4317 | | /* Initialization */ |
4318 | 0 | if (state == 0) |
4319 | 0 | { |
4320 | 0 | list_index = 0; |
4321 | 0 | string_length = strlen(text); |
4322 | 0 | casesensitive = completion_case_sensitive; |
4323 | 0 | matches = 0; |
4324 | 0 | } |
4325 | |
|
4326 | 0 | while ((item = completion_charpp[list_index++])) |
4327 | 0 | { |
4328 | | /* First pass is case sensitive */ |
4329 | 0 | if (casesensitive && strncmp(text, item, string_length) == 0) |
4330 | 0 | { |
4331 | 0 | matches++; |
4332 | 0 | return pg_strdup(item); |
4333 | 0 | } |
4334 | | |
4335 | | /* Second pass is case insensitive, don't bother counting matches */ |
4336 | 0 | if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0) |
4337 | 0 | { |
4338 | 0 | if (completion_case_sensitive) |
4339 | 0 | return pg_strdup(item); |
4340 | 0 | else |
4341 | | |
4342 | | /* |
4343 | | * If case insensitive matching was requested initially, |
4344 | | * adjust the case according to setting. |
4345 | | */ |
4346 | 0 | return pg_strdup_keyword_case(item, text); |
4347 | 0 | } |
4348 | 0 | } |
4349 | | |
4350 | | /* |
4351 | | * No matches found. If we're not case insensitive already, lets switch to |
4352 | | * being case insensitive and try again |
4353 | | */ |
4354 | 0 | if (casesensitive && matches == 0) |
4355 | 0 | { |
4356 | 0 | casesensitive = false; |
4357 | 0 | list_index = 0; |
4358 | 0 | state++; |
4359 | 0 | return complete_from_list(text, state); |
4360 | 0 | } |
4361 | | |
4362 | | /* If no more matches, return null. */ |
4363 | 0 | return NULL; |
4364 | 0 | } |
4365 | | |
4366 | | |
4367 | | /* |
4368 | | * This function returns one fixed string the first time even if it doesn't |
4369 | | * match what's there, and nothing the second time. This should be used if |
4370 | | * there is only one possibility that can appear at a certain spot, so |
4371 | | * misspellings will be overwritten. The string to be passed must be in |
4372 | | * completion_charp. |
4373 | | */ |
4374 | | static char * |
4375 | | complete_from_const(const char *text, int state) |
4376 | 0 | { |
4377 | 0 | Assert(completion_charp != NULL); |
4378 | 0 | if (state == 0) |
4379 | 0 | { |
4380 | 0 | if (completion_case_sensitive) |
4381 | 0 | return pg_strdup(completion_charp); |
4382 | 0 | else |
4383 | | |
4384 | | /* |
4385 | | * If case insensitive matching was requested initially, adjust |
4386 | | * the case according to setting. |
4387 | | */ |
4388 | 0 | return pg_strdup_keyword_case(completion_charp, text); |
4389 | 0 | } |
4390 | 0 | else |
4391 | 0 | return NULL; |
4392 | 0 | } |
4393 | | |
4394 | | |
4395 | | /* |
4396 | | * This function appends the variable name with prefix and suffix to |
4397 | | * the variable names array. |
4398 | | */ |
4399 | | static void |
4400 | | append_variable_names(char ***varnames, int *nvars, |
4401 | | int *maxvars, const char *varname, |
4402 | | const char *prefix, const char *suffix) |
4403 | 0 | { |
4404 | 0 | if (*nvars >= *maxvars) |
4405 | 0 | { |
4406 | 0 | *maxvars *= 2; |
4407 | 0 | *varnames = (char **) pg_realloc(*varnames, |
4408 | 0 | ((*maxvars) + 1) * sizeof(char *)); |
4409 | 0 | } |
4410 | |
|
4411 | 0 | (*varnames)[(*nvars)++] = psprintf("%s%s%s", prefix, varname, suffix); |
4412 | 0 | } |
4413 | | |
4414 | | |
4415 | | /* |
4416 | | * This function supports completion with the name of a psql variable. |
4417 | | * The variable names can be prefixed and suffixed with additional text |
4418 | | * to support quoting usages. If need_value is true, only variables |
4419 | | * that are currently set are included; otherwise, special variables |
4420 | | * (those that have hooks) are included even if currently unset. |
4421 | | */ |
4422 | | static char ** |
4423 | | complete_from_variables(const char *text, const char *prefix, const char *suffix, |
4424 | | bool need_value) |
4425 | 0 | { |
4426 | 0 | char **matches; |
4427 | 0 | char **varnames; |
4428 | 0 | int nvars = 0; |
4429 | 0 | int maxvars = 100; |
4430 | 0 | int i; |
4431 | 0 | struct _variable *ptr; |
4432 | |
|
4433 | 0 | varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *)); |
4434 | |
|
4435 | 0 | for (ptr = pset.vars->next; ptr; ptr = ptr->next) |
4436 | 0 | { |
4437 | 0 | if (need_value && !(ptr->value)) |
4438 | 0 | continue; |
4439 | 0 | append_variable_names(&varnames, &nvars, &maxvars, ptr->name, |
4440 | 0 | prefix, suffix); |
4441 | 0 | } |
4442 | |
|
4443 | 0 | varnames[nvars] = NULL; |
4444 | 0 | COMPLETE_WITH_LIST_CS((const char *const *) varnames); |
4445 | |
|
4446 | 0 | for (i = 0; i < nvars; i++) |
4447 | 0 | free(varnames[i]); |
4448 | 0 | free(varnames); |
4449 | |
|
4450 | 0 | return matches; |
4451 | 0 | } |
4452 | | |
4453 | | |
4454 | | /* |
4455 | | * This function wraps rl_filename_completion_function() to strip quotes from |
4456 | | * the input before searching for matches and to quote any matches for which |
4457 | | * the consuming command will require it. |
4458 | | */ |
4459 | | static char * |
4460 | | complete_from_files(const char *text, int state) |
4461 | 0 | { |
4462 | 0 | static const char *unquoted_text; |
4463 | 0 | char *unquoted_match; |
4464 | 0 | char *ret = NULL; |
4465 | |
|
4466 | 0 | if (state == 0) |
4467 | 0 | { |
4468 | | /* Initialization: stash the unquoted input. */ |
4469 | 0 | unquoted_text = strtokx(text, "", NULL, "'", *completion_charp, |
4470 | 0 | false, true, pset.encoding); |
4471 | | /* expect a NULL return for the empty string only */ |
4472 | 0 | if (!unquoted_text) |
4473 | 0 | { |
4474 | 0 | Assert(*text == '\0'); |
4475 | 0 | unquoted_text = text; |
4476 | 0 | } |
4477 | 0 | } |
4478 | | |
4479 | 0 | unquoted_match = filename_completion_function(unquoted_text, state); |
4480 | 0 | if (unquoted_match) |
4481 | 0 | { |
4482 | | /* |
4483 | | * Caller sets completion_charp to a zero- or one-character string |
4484 | | * containing the escape character. This is necessary since \copy has |
4485 | | * no escape character, but every other backslash command recognizes |
4486 | | * "\" as an escape character. Since we have only two callers, don't |
4487 | | * bother providing a macro to simplify this. |
4488 | | */ |
4489 | 0 | ret = quote_if_needed(unquoted_match, " \t\r\n\"`", |
4490 | 0 | '\'', *completion_charp, pset.encoding); |
4491 | 0 | if (ret) |
4492 | 0 | free(unquoted_match); |
4493 | 0 | else |
4494 | 0 | ret = unquoted_match; |
4495 | 0 | } |
4496 | |
|
4497 | 0 | return ret; |
4498 | 0 | } |
4499 | | |
4500 | | |
4501 | | /* HELPER FUNCTIONS */ |
4502 | | |
4503 | | |
4504 | | /* |
4505 | | * Make a pg_strdup copy of s and convert the case according to |
4506 | | * COMP_KEYWORD_CASE setting, using ref as the text that was already entered. |
4507 | | */ |
4508 | | static char * |
4509 | | pg_strdup_keyword_case(const char *s, const char *ref) |
4510 | 0 | { |
4511 | 0 | char *ret, |
4512 | 0 | *p; |
4513 | 0 | unsigned char first = ref[0]; |
4514 | |
|
4515 | 0 | ret = pg_strdup(s); |
4516 | |
|
4517 | 0 | if (pset.comp_case == PSQL_COMP_CASE_LOWER || |
4518 | 0 | ((pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER || |
4519 | 0 | pset.comp_case == PSQL_COMP_CASE_PRESERVE_UPPER) && islower(first)) || |
4520 | 0 | (pset.comp_case == PSQL_COMP_CASE_PRESERVE_LOWER && !isalpha(first))) |
4521 | 0 | { |
4522 | 0 | for (p = ret; *p; p++) |
4523 | 0 | *p = pg_tolower((unsigned char) *p); |
4524 | 0 | } |
4525 | 0 | else |
4526 | 0 | { |
4527 | 0 | for (p = ret; *p; p++) |
4528 | 0 | *p = pg_toupper((unsigned char) *p); |
4529 | 0 | } |
4530 | |
|
4531 | 0 | return ret; |
4532 | 0 | } |
4533 | | |
4534 | | |
4535 | | /* |
4536 | | * escape_string - Escape argument for use as string literal. |
4537 | | * |
4538 | | * The returned value has to be freed. |
4539 | | */ |
4540 | | static char * |
4541 | | escape_string(const char *text) |
4542 | 0 | { |
4543 | 0 | size_t text_length; |
4544 | 0 | char *result; |
4545 | |
|
4546 | 0 | text_length = strlen(text); |
4547 | |
|
4548 | 0 | result = pg_malloc(text_length * 2 + 1); |
4549 | 0 | PQescapeStringConn(pset.db, result, text, text_length, NULL); |
4550 | |
|
4551 | 0 | return result; |
4552 | 0 | } |
4553 | | |
4554 | | |
4555 | | /* |
4556 | | * Execute a query and report any errors. This should be the preferred way of |
4557 | | * talking to the database in this file. |
4558 | | */ |
4559 | | static PGresult * |
4560 | | exec_query(const char *query) |
4561 | 0 | { |
4562 | 0 | PGresult *result; |
4563 | |
|
4564 | 0 | if (query == NULL || !pset.db || PQstatus(pset.db) != CONNECTION_OK) |
4565 | 0 | return NULL; |
4566 | | |
4567 | 0 | result = PQexec(pset.db, query); |
4568 | |
|
4569 | 0 | if (PQresultStatus(result) != PGRES_TUPLES_OK) |
4570 | 0 | { |
4571 | | #ifdef NOT_USED |
4572 | | psql_error("tab completion query failed: %s\nQuery was:\n%s\n", |
4573 | | PQerrorMessage(pset.db), query); |
4574 | | #endif |
4575 | 0 | PQclear(result); |
4576 | 0 | result = NULL; |
4577 | 0 | } |
4578 | |
|
4579 | 0 | return result; |
4580 | 0 | } |
4581 | | |
4582 | | |
4583 | | /* |
4584 | | * Parse all the word(s) before point. |
4585 | | * |
4586 | | * Returns a malloc'd array of character pointers that point into the malloc'd |
4587 | | * data array returned to *buffer; caller must free() both of these when done. |
4588 | | * *nwords receives the number of words found, ie, the valid length of the |
4589 | | * return array. |
4590 | | * |
4591 | | * Words are returned right to left, that is, previous_words[0] gets the last |
4592 | | * word before point, previous_words[1] the next-to-last, etc. |
4593 | | */ |
4594 | | static char ** |
4595 | | get_previous_words(int point, char **buffer, int *nwords) |
4596 | 0 | { |
4597 | 0 | char **previous_words; |
4598 | 0 | char *buf; |
4599 | 0 | char *outptr; |
4600 | 0 | int words_found = 0; |
4601 | 0 | int i; |
4602 | | |
4603 | | /* |
4604 | | * If we have anything in tab_completion_query_buf, paste it together with |
4605 | | * rl_line_buffer to construct the full query. Otherwise we can just use |
4606 | | * rl_line_buffer as the input string. |
4607 | | */ |
4608 | 0 | if (tab_completion_query_buf && tab_completion_query_buf->len > 0) |
4609 | 0 | { |
4610 | 0 | i = tab_completion_query_buf->len; |
4611 | 0 | buf = pg_malloc(point + i + 2); |
4612 | 0 | memcpy(buf, tab_completion_query_buf->data, i); |
4613 | 0 | buf[i++] = '\n'; |
4614 | 0 | memcpy(buf + i, rl_line_buffer, point); |
4615 | 0 | i += point; |
4616 | 0 | buf[i] = '\0'; |
4617 | | /* Readjust point to reference appropriate offset in buf */ |
4618 | 0 | point = i; |
4619 | 0 | } |
4620 | 0 | else |
4621 | 0 | buf = rl_line_buffer; |
4622 | | |
4623 | | /* |
4624 | | * Allocate an array of string pointers and a buffer to hold the strings |
4625 | | * themselves. The worst case is that the line contains only |
4626 | | * non-whitespace WORD_BREAKS characters, making each one a separate word. |
4627 | | * This is usually much more space than we need, but it's cheaper than |
4628 | | * doing a separate malloc() for each word. |
4629 | | */ |
4630 | 0 | previous_words = (char **) pg_malloc(point * sizeof(char *)); |
4631 | 0 | *buffer = outptr = (char *) pg_malloc(point * 2); |
4632 | | |
4633 | | /* |
4634 | | * First we look for a non-word char before the current point. (This is |
4635 | | * probably useless, if readline is on the same page as we are about what |
4636 | | * is a word, but if so it's cheap.) |
4637 | | */ |
4638 | 0 | for (i = point - 1; i >= 0; i--) |
4639 | 0 | { |
4640 | 0 | if (strchr(WORD_BREAKS, buf[i])) |
4641 | 0 | break; |
4642 | 0 | } |
4643 | 0 | point = i; |
4644 | | |
4645 | | /* |
4646 | | * Now parse words, working backwards, until we hit start of line. The |
4647 | | * backwards scan has some interesting but intentional properties |
4648 | | * concerning parenthesis handling. |
4649 | | */ |
4650 | 0 | while (point >= 0) |
4651 | 0 | { |
4652 | 0 | int start, |
4653 | 0 | end; |
4654 | 0 | bool inquotes = false; |
4655 | 0 | int parentheses = 0; |
4656 | | |
4657 | | /* now find the first non-space which then constitutes the end */ |
4658 | 0 | end = -1; |
4659 | 0 | for (i = point; i >= 0; i--) |
4660 | 0 | { |
4661 | 0 | if (!isspace((unsigned char) buf[i])) |
4662 | 0 | { |
4663 | 0 | end = i; |
4664 | 0 | break; |
4665 | 0 | } |
4666 | 0 | } |
4667 | | /* if no end found, we're done */ |
4668 | 0 | if (end < 0) |
4669 | 0 | break; |
4670 | | |
4671 | | /* |
4672 | | * Otherwise we now look for the start. The start is either the last |
4673 | | * character before any word-break character going backwards from the |
4674 | | * end, or it's simply character 0. We also handle open quotes and |
4675 | | * parentheses. |
4676 | | */ |
4677 | 0 | for (start = end; start > 0; start--) |
4678 | 0 | { |
4679 | 0 | if (buf[start] == '"') |
4680 | 0 | inquotes = !inquotes; |
4681 | 0 | if (!inquotes) |
4682 | 0 | { |
4683 | 0 | if (buf[start] == ')') |
4684 | 0 | parentheses++; |
4685 | 0 | else if (buf[start] == '(') |
4686 | 0 | { |
4687 | 0 | if (--parentheses <= 0) |
4688 | 0 | break; |
4689 | 0 | } |
4690 | 0 | else if (parentheses == 0 && |
4691 | 0 | strchr(WORD_BREAKS, buf[start - 1])) |
4692 | 0 | break; |
4693 | 0 | } |
4694 | 0 | } |
4695 | | |
4696 | | /* Return the word located at start to end inclusive */ |
4697 | 0 | previous_words[words_found++] = outptr; |
4698 | 0 | i = end - start + 1; |
4699 | 0 | memcpy(outptr, &buf[start], i); |
4700 | 0 | outptr += i; |
4701 | 0 | *outptr++ = '\0'; |
4702 | | |
4703 | | /* Continue searching */ |
4704 | 0 | point = start - 1; |
4705 | 0 | } |
4706 | | |
4707 | | /* Release parsing input workspace, if we made one above */ |
4708 | 0 | if (buf != rl_line_buffer) |
4709 | 0 | free(buf); |
4710 | |
|
4711 | 0 | *nwords = words_found; |
4712 | 0 | return previous_words; |
4713 | 0 | } |
4714 | | |
4715 | | /* |
4716 | | * Look up the type for the GUC variable with the passed name. |
4717 | | * |
4718 | | * Returns NULL if the variable is unknown. Otherwise the returned string, |
4719 | | * containing the type, has to be freed. |
4720 | | */ |
4721 | | static char * |
4722 | | get_guctype(const char *varname) |
4723 | 0 | { |
4724 | 0 | PQExpBufferData query_buffer; |
4725 | 0 | char *e_varname; |
4726 | 0 | PGresult *result; |
4727 | 0 | char *guctype = NULL; |
4728 | |
|
4729 | 0 | e_varname = escape_string(varname); |
4730 | |
|
4731 | 0 | initPQExpBuffer(&query_buffer); |
4732 | 0 | appendPQExpBuffer(&query_buffer, |
4733 | 0 | "SELECT vartype FROM pg_catalog.pg_settings " |
4734 | 0 | "WHERE pg_catalog.lower(name) = pg_catalog.lower('%s')", |
4735 | 0 | e_varname); |
4736 | |
|
4737 | 0 | result = exec_query(query_buffer.data); |
4738 | 0 | termPQExpBuffer(&query_buffer); |
4739 | 0 | free(e_varname); |
4740 | |
|
4741 | 0 | if (PQresultStatus(result) == PGRES_TUPLES_OK && PQntuples(result) > 0) |
4742 | 0 | guctype = pg_strdup(PQgetvalue(result, 0, 0)); |
4743 | |
|
4744 | 0 | PQclear(result); |
4745 | |
|
4746 | 0 | return guctype; |
4747 | 0 | } |
4748 | | |
4749 | | #ifdef NOT_USED |
4750 | | |
4751 | | /* |
4752 | | * Surround a string with single quotes. This works for both SQL and |
4753 | | * psql internal. Currently disabled because it is reported not to |
4754 | | * cooperate with certain versions of readline. |
4755 | | */ |
4756 | | static char * |
4757 | | quote_file_name(char *text, int match_type, char *quote_pointer) |
4758 | | { |
4759 | | char *s; |
4760 | | size_t length; |
4761 | | |
4762 | | (void) quote_pointer; /* not used */ |
4763 | | |
4764 | | length = strlen(text) +(match_type == SINGLE_MATCH ? 3 : 2); |
4765 | | s = pg_malloc(length); |
4766 | | s[0] = '\''; |
4767 | | strcpy(s + 1, text); |
4768 | | if (match_type == SINGLE_MATCH) |
4769 | | s[length - 2] = '\''; |
4770 | | s[length - 1] = '\0'; |
4771 | | return s; |
4772 | | } |
4773 | | |
4774 | | static char * |
4775 | | dequote_file_name(char *text, char quote_char) |
4776 | | { |
4777 | | char *s; |
4778 | | size_t length; |
4779 | | |
4780 | | if (!quote_char) |
4781 | | return pg_strdup(text); |
4782 | | |
4783 | | length = strlen(text); |
4784 | | s = pg_malloc(length - 2 + 1); |
4785 | | strlcpy(s, text +1, length - 2 + 1); |
4786 | | |
4787 | | return s; |
4788 | | } |
4789 | | #endif /* NOT_USED */ |
4790 | | |
4791 | | #endif /* USE_READLINE */ |