| 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 |
|---|