1 | /* |
2 | * psql - the PostgreSQL interactive terminal |
3 | * |
4 | * Copyright (c) 2000-2019, 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_am_d.h" |
45 | #include "catalog/pg_class_d.h" |
46 | |
47 | #include "libpq-fe.h" |
48 | #include "pqexpbuffer.h" |
49 | #include "common.h" |
50 | #include "settings.h" |
51 | #include "stringutils.h" |
52 | |
53 | #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION |
54 | #define filename_completion_function rl_filename_completion_function |
55 | #else |
56 | /* missing in some header files */ |
57 | extern char *filename_completion_function(); |
58 | #endif |
59 | |
60 | #ifdef HAVE_RL_COMPLETION_MATCHES |
61 | #define completion_matches rl_completion_matches |
62 | #endif |
63 | |
64 | /* word break characters */ |
65 | #define WORD_BREAKS "\t\n@$><=;|&{() " |
66 | |
67 | /* |
68 | * Since readline doesn't let us pass any state through to the tab completion |
69 | * callback, we have to use this global variable to let get_previous_words() |
70 | * get at the previous lines of the current command. Ick. |
71 | */ |
72 | PQExpBuffer tab_completion_query_buf = NULL; |
73 | |
74 | /* |
75 | * In some situations, the query to find out what names are available to |
76 | * complete with must vary depending on server version. We handle this by |
77 | * storing a list of queries, each tagged with the minimum server version |
78 | * it will work for. Each list must be stored in descending server version |
79 | * order, so that the first satisfactory query is the one to use. |
80 | * |
81 | * When the query string is otherwise constant, an array of VersionedQuery |
82 | * suffices. Terminate the array with an entry having min_server_version = 0. |
83 | * That entry's query string can be a query that works in all supported older |
84 | * server versions, or NULL to give up and do no completion. |
85 | */ |
86 | typedef struct VersionedQuery |
87 | { |
88 | int min_server_version; |
89 | const char *query; |
90 | } VersionedQuery; |
91 | |
92 | /* |
93 | * This struct is used to define "schema queries", which are custom-built |
94 | * to obtain possibly-schema-qualified names of database objects. There is |
95 | * enough similarity in the structure that we don't want to repeat it each |
96 | * time. So we put the components of each query into this struct and |
97 | * assemble them with the common boilerplate in _complete_from_query(). |
98 | * |
99 | * As with VersionedQuery, we can use an array of these if the query details |
100 | * must vary across versions. |
101 | */ |
102 | typedef struct SchemaQuery |
103 | { |
104 | /* |
105 | * If not zero, minimum server version this struct applies to. If not |
106 | * zero, there should be a following struct with a smaller minimum server |
107 | * version; use catname == NULL in the last entry if we should do nothing. |
108 | */ |
109 | int min_server_version; |
110 | |
111 | /* |
112 | * Name of catalog or catalogs to be queried, with alias, eg. |
113 | * "pg_catalog.pg_class c". Note that "pg_namespace n" will be added. |
114 | */ |
115 | const char *catname; |
116 | |
117 | /* |
118 | * Selection condition --- only rows meeting this condition are candidates |
119 | * to display. If catname mentions multiple tables, include the necessary |
120 | * join condition here. For example, this might look like "c.relkind = " |
121 | * CppAsString2(RELKIND_RELATION). Write NULL (not an empty string) if |
122 | * not needed. |
123 | */ |
124 | const char *selcondition; |
125 | |
126 | /* |
127 | * Visibility condition --- which rows are visible without schema |
128 | * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)". |
129 | */ |
130 | const char *viscondition; |
131 | |
132 | /* |
133 | * Namespace --- name of field to join to pg_namespace.oid. For example, |
134 | * "c.relnamespace". |
135 | */ |
136 | const char *namespace; |
137 | |
138 | /* |
139 | * Result --- the appropriately-quoted name to return, in the case of an |
140 | * unqualified name. For example, "pg_catalog.quote_ident(c.relname)". |
141 | */ |
142 | const char *result; |
143 | |
144 | /* |
145 | * In some cases a different result must be used for qualified names. |
146 | * Enter that here, or write NULL if result can be used. |
147 | */ |
148 | const char *qualresult; |
149 | } SchemaQuery; |
150 | |
151 | |
152 | /* Store maximum number of records we want from database queries |
153 | * (implemented via SELECT ... LIMIT xx). |
154 | */ |
155 | static int completion_max_records; |
156 | |
157 | /* |
158 | * Communication variables set by COMPLETE_WITH_FOO macros and then used by |
159 | * the completion callback functions. Ugly but there is no better way. |
160 | */ |
161 | static const char *completion_charp; /* to pass a string */ |
162 | static const char *const *completion_charpp; /* to pass a list of strings */ |
163 | static const char *completion_info_charp; /* to pass a second string */ |
164 | static const char *completion_info_charp2; /* to pass a third string */ |
165 | static const VersionedQuery *completion_vquery; /* to pass a VersionedQuery */ |
166 | static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ |
167 | static bool completion_case_sensitive; /* completion is case sensitive */ |
168 | |
169 | /* |
170 | * A few macros to ease typing. You can use these to complete the given |
171 | * string with |
172 | * 1) The results from a query you pass it. (Perhaps one of those below?) |
173 | * We support both simple and versioned queries. |
174 | * 2) The results from a schema query you pass it. |
175 | * We support both simple and versioned schema queries. |
176 | * 3) The items from a null-pointer-terminated list (with or without |
177 | * case-sensitive comparison); if the list is constant you can build it |
178 | * with COMPLETE_WITH() or COMPLETE_WITH_CS(). |
179 | * 4) The list of attributes of the given table (possibly schema-qualified). |
180 | * 5) The list of arguments to the given function (possibly schema-qualified). |
181 | */ |
182 | #define COMPLETE_WITH_QUERY(query) \ |
183 | do { \ |
184 | completion_charp = query; \ |
185 | matches = completion_matches(text, complete_from_query); \ |
186 | } while (0) |
187 | |
188 | #define COMPLETE_WITH_VERSIONED_QUERY(query) \ |
189 | do { \ |
190 | completion_vquery = query; \ |
191 | matches = completion_matches(text, complete_from_versioned_query); \ |
192 | } while (0) |
193 | |
194 | #define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ |
195 | do { \ |
196 | completion_squery = &(query); \ |
197 | completion_charp = addon; \ |
198 | matches = completion_matches(text, complete_from_schema_query); \ |
199 | } while (0) |
200 | |
201 | #define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \ |
202 | do { \ |
203 | completion_squery = query; \ |
204 | completion_vquery = addon; \ |
205 | matches = completion_matches(text, complete_from_versioned_schema_query); \ |
206 | } while (0) |
207 | |
208 | /* |
209 | * Caution: COMPLETE_WITH_CONST is not for general-purpose use; you probably |
210 | * want COMPLETE_WITH() with one element, instead. |
211 | */ |
212 | #define COMPLETE_WITH_CONST(cs, con) \ |
213 | do { \ |
214 | completion_case_sensitive = (cs); \ |
215 | completion_charp = (con); \ |
216 | matches = completion_matches(text, complete_from_const); \ |
217 | } while (0) |
218 | |
219 | #define COMPLETE_WITH_LIST_INT(cs, list) \ |
220 | do { \ |
221 | completion_case_sensitive = (cs); \ |
222 | completion_charpp = (list); \ |
223 | matches = completion_matches(text, complete_from_list); \ |
224 | } while (0) |
225 | |
226 | #define COMPLETE_WITH_LIST(list) COMPLETE_WITH_LIST_INT(false, list) |
227 | #define COMPLETE_WITH_LIST_CS(list) COMPLETE_WITH_LIST_INT(true, list) |
228 | |
229 | #define COMPLETE_WITH(...) \ |
230 | do { \ |
231 | static const char *const |
---|