diff --git a/README.md b/README.md index b437950ad..4db19c63e 100644 --- a/README.md +++ b/README.md @@ -391,6 +391,7 @@ openGauss支持以下操作系统: | glibc-devel | 2.17-111 | | patch | 2.7.1-10 | | lsb_release | 4.1 | +| readline-devel| 7.0-13 | ### 下载openGauss @@ -511,19 +512,19 @@ openGauss-server中的build.sh是编译过程中的重要脚本工具。该工 **debug**版本: ``` - ./configure --gcc-version=7.3.0 CC=g++ CFLAGS='-O0' --prefix=$GAUSSHOME --3rd=$BINARYLIBS --enable-debug --enable-cassert --enable-thread-safety --without-readline --without-zlib + ./configure --gcc-version=7.3.0 CC=g++ CFLAGS='-O0' --prefix=$GAUSSHOME --3rd=$BINARYLIBS --enable-debug --enable-cassert --enable-thread-safety --with-readline --without-zlib ``` **release**版本: ``` - ./configure --gcc-version=7.3.0 CC=g++ CFLAGS="-O2 -g3" --prefix=$GAUSSHOME --3rd=$BINARYLIBS --enable-thread-safety --without-readline --without-zlib + ./configure --gcc-version=7.3.0 CC=g++ CFLAGS="-O2 -g3" --prefix=$GAUSSHOME --3rd=$BINARYLIBS --enable-thread-safety --with-readline --without-zlib ``` **memcheck**版本: ``` - ./configure --gcc-version=7.3.0 CC=g++ CFLAGS='-O0' --prefix=$GAUSSHOME --3rd=$BINARYLIBS --enable-debug --enable-cassert --enable-thread-safety --without-readline --without-zlib --enable-memory-check + ./configure --gcc-version=7.3.0 CC=g++ CFLAGS='-O0' --prefix=$GAUSSHOME --3rd=$BINARYLIBS --enable-debug --enable-cassert --enable-thread-safety --with-readline --without-zlib --enable-memory-check ``` > **注意** diff --git a/build/script/mpp_package.sh b/build/script/mpp_package.sh index bbcb6e4fe..1e5f87b1c 100755 --- a/build/script/mpp_package.sh +++ b/build/script/mpp_package.sh @@ -682,7 +682,7 @@ function install_gaussdb() echo "Begin configure." >> "$LOG_FILE" 2>&1 chmod 755 configure - shared_opt="--gcc-version=${gcc_version}.0 --prefix="${BUILD_DIR}" --3rd=${binarylibs_path} --enable-thread-safety --without-readline --without-zlib" + shared_opt="--gcc-version=${gcc_version}.0 --prefix="${BUILD_DIR}" --3rd=${binarylibs_path} --enable-thread-safety --with-readline --without-zlib" if [ "$product_mode"x == "multiple"x ]; then if [ "$version_mode"x == "release"x ]; then ./configure $shared_opt CFLAGS="-O2 -g3 ${GAUSSDB_EXTRA_FLAGS}" CC="${USE_CCACHE}g++" ${ENABLE_CCACHE} --enable-multiple-nodes $extra_config_opt >> "$LOG_FILE" 2>&1 diff --git a/doc/src/sgml/ref/psql-ref.sgmlin b/doc/src/sgml/ref/psql-ref.sgmlin index 1481dfbe6..bfbc6b136 100644 --- a/doc/src/sgml/ref/psql-ref.sgmlin +++ b/doc/src/sgml/ref/psql-ref.sgmlin @@ -2833,44 +2833,6 @@ bar - - HISTFILE - - - The file name that will be used to store the history list. The default - value is ~/.psql_history. For example, putting: - -\set HISTFILE ~/.psql_history- :DBNAME - - in ~/.psqlrc will cause - gsql to maintain a separate history for - each database. - - - - This feature was shamelessly plagiarized from - Bash. - - - - - - - HISTSIZE - - - The number of commands to store in the command history. The - default value is 500. - - - - This feature was shamelessly plagiarized from - Bash. - - - - - HOST diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile index edfae92bf..38d75b7a4 100644 --- a/src/bin/psql/Makefile +++ b/src/bin/psql/Makefile @@ -59,7 +59,7 @@ ifneq "$(MAKECMDGOALS)" "clean" endif OBJS= command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ startup.o prompt.o variables.o large_obj.o print.o describe.o \ - mbprint.o dumputils.o keywords.o kwlookup.o \ + mbprint.o dumputils.o keywords.o kwlookup.o tab-complete.o\ sql_help.o \ $(top_builddir)/src/lib/elog/elog.a \ $(WIN32RES) diff --git a/src/bin/psql/command.cpp b/src/bin/psql/command.cpp index 256c1f816..1e092f8d8 100644 --- a/src/bin/psql/command.cpp +++ b/src/bin/psql/command.cpp @@ -1051,10 +1051,6 @@ static backslashResult exec_command(const char* cmd, PsqlScanState scan_state, P success = false; } - if (useReadline && pset.cur_cmd_interactive) { - setHistSize(opt0, newval, false); - } - free(newval); newval = NULL; if (temp_opt != NULL) @@ -1259,10 +1255,6 @@ static backslashResult exec_command(const char* cmd, PsqlScanState scan_state, P success = false; } - if (useReadline && pset.cur_cmd_interactive) { - setHistSize(opt, NULL, true); - } - if (NULL != opt) { free(opt); opt = NULL; diff --git a/src/bin/psql/common.cpp b/src/bin/psql/common.cpp index c359c20b6..98785dcb1 100644 --- a/src/bin/psql/common.cpp +++ b/src/bin/psql/common.cpp @@ -128,6 +128,29 @@ void* pg_calloc(size_t nmemb, size_t size) return tmp; } +void* psql_realloc(void* ptr, size_t oldSize, size_t newSize) +{ + void* tmp = NULL; + errno_t rc; + + if (oldSize > newSize) { + return NULL; + } + + /* When malloc failed gsql will exit, with no memory leak for ptr. */ + tmp = pg_malloc(newSize); + if (tmp == NULL) { + psql_error("out of memory\n"); + exit(EXIT_FAILURE); + } + rc = memcpy_s(tmp, newSize, ptr, oldSize); + securec_check_c(rc, "\0", "\0"); + + free(ptr); + ptr = NULL; + return tmp; +} + /* * setQFout * -- handler for -o command line option and \o command diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index 4ec8b8341..e47402cf1 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -59,6 +59,7 @@ extern char* pg_strdup(const char* string); extern void* pg_malloc(size_t size); extern void* pg_malloc_zero(size_t size); extern void* pg_calloc(size_t nmemb, size_t size); +extern void* psql_realloc(void* ptr, size_t oldSize, size_t newSize); extern bool setQFout(const char* fname); diff --git a/src/bin/psql/input.cpp b/src/bin/psql/input.cpp index 08847b2b8..aac0ab555 100644 --- a/src/bin/psql/input.cpp +++ b/src/bin/psql/input.cpp @@ -31,8 +31,6 @@ bool useReadline = false; #define NL_IN_HISTORY 0x01 #endif -void setHistSize(const char* targetName, char* targetValue, bool setToDefault); - /* * gets_interactive() * @@ -250,41 +248,27 @@ bool SensitiveStrCheck(const char* target) } } -void setHistSize(const char* targetName, const char* targetValue, bool setToDefault) +/* + * Put any startup stuff related to input in here. It's good to maintain + * abstraction this way. + * + * The only "flag" right now is 1 for use readline & history. + */ +void initializeInput(int flags) { -#ifndef ENABLE_LLT - char* end = NULL; - long int result; -#define MAXHISTSIZE 500 -#define DEFHISTSIZE 32 - if (targetName == NULL) { - return; - } - if (strcmp(targetName, "HISTSIZE") == 0) { - if (!setToDefault) { - if (targetValue == NULL || strlen(targetValue) == 0) { - fprintf(stderr, "warning:\"HISTSIZE\" is not changed,because its value can not be null\n"); - return; - } else { - errno = 0; - result = strtol(targetValue, &end, 0); - if ((errno == ERANGE && (result == LONG_MAX || result == LONG_MIN)) || (errno != 0 && result == 0)) { - fprintf(stderr, "warning:\"HISTSIZE\" is not changed,because its value overflows\n"); - return; - } - if (*end || result < 0) { - fprintf(stderr, "warning:\"HISTSIZE\" is not changed,because its value must be positive integer\n"); - return; - } - } - if (result > MAXHISTSIZE) { - fprintf(stderr, "warning:\"HISTSIZE\" is set to 500,because its value can not be greater than 500\n"); - result = MAXHISTSIZE; - } - } else { - result = DEFHISTSIZE; - } - stifle_history((int)result); +#ifdef USE_READLINE + +#ifndef ENABLE_MULTIPLE_NODES + flags &= useReadline; +#endif + + if (flags & 1) { + useReadline = true; + + /* these two things must be done in this order: */ + initialize_readline(); + rl_variable_bind ("enable-meta-key", "off"); + rl_initialize(); } #endif } diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h index 37aa0e218..b184f0217 100644 --- a/src/bin/psql/input.h +++ b/src/bin/psql/input.h @@ -8,6 +8,7 @@ #ifndef INPUT_H #define INPUT_H +#ifdef HAVE_LIBREADLINE /* * If some other file needs to have access to readline/history, include this * file and save yourself all this work. @@ -16,19 +17,28 @@ */ #define USE_READLINE 1 +#if defined(HAVE_READLINE_READLINE_H) +#include +#include +#elif defined(HAVE_EDITLINE_READLINE_H) #include +#elif defined(HAVE_READLINE_H) +#include +#endif /* HAVE_READLINE_READLINE_H, etc */ +#else +#include +#endif /* HAVE_LIBREADLINE */ #include "libpq/pqexpbuffer.h" char* gets_interactive(const char* prompt); char* gets_fromFile(FILE* source); -bool saveHistory(const char* fname, int max_lines, bool appendFlag, bool encodeFlag); - void pg_append_history(const char* s, PQExpBuffer history_buf); void pg_send_history(PQExpBuffer history_buf); -void setHistSize(const char* targetName, const char* targetValue, bool setToDefault); extern bool useReadline; extern bool SensitiveStrCheck(const char* target); +void initializeInput(int flags); + #endif /* INPUT_H */ diff --git a/src/bin/psql/mainloop.cpp b/src/bin/psql/mainloop.cpp index e174642bb..1b80ae644 100644 --- a/src/bin/psql/mainloop.cpp +++ b/src/bin/psql/mainloop.cpp @@ -148,11 +148,6 @@ int MainLoop(FILE* source, char* querystring) } pset.lineno = 0; - if (pset.cur_cmd_interactive) { - const char* val = GetVariable(pset.vars, "HISTSIZE"); - setHistSize("HISTSIZE", val, val == NULL); - } - /* Create working state */ scan_state = psql_scan_create(); diff --git a/src/bin/psql/startup.cpp b/src/bin/psql/startup.cpp index 1176d7011..acbe73e81 100644 --- a/src/bin/psql/startup.cpp +++ b/src/bin/psql/startup.cpp @@ -506,7 +506,7 @@ int main(int argc, char* argv[]) printf(_("Type \"help\" for help.\n\n")); canAddHist = true; - + initializeInput(options.no_readline ? 0 : 1); successResult = MainLoop(stdin); } @@ -614,7 +614,9 @@ static void parse_psql_options(int argc, char* const argv[], struct adhoc_opts* /* Database Security: Data importing/dumping support AES128. */ char* dencrypt_key = NULL; errno_t rc = EOK; +#ifdef USE_READLINE useReadline = false; +#endif rc = memset_s(options, sizeof(*options), 0, sizeof(*options)); check_memset_s(rc); @@ -732,7 +734,9 @@ static void parse_psql_options(int argc, char* const argv[], struct adhoc_opts* pset.enable_client_encryption = true; break; case 'r': +#ifdef USE_READLINE useReadline = true; +#endif break; case 'R': if (pset.popt.topt.recordSep.separator != NULL) @@ -778,7 +782,6 @@ static void parse_psql_options(int argc, char* const argv[], struct adhoc_opts* fprintf(stderr, _("%s: could not set variable \"%s\"\n"), pset.progname, value); exit(EXIT_FAILURE); } - setHistSize(value, equal_loc + 1, false); } free(value); diff --git a/src/bin/psql/tab-complete.cpp b/src/bin/psql/tab-complete.cpp index 67ba16218..5942fdd98 100644 --- a/src/bin/psql/tab-complete.cpp +++ b/src/bin/psql/tab-complete.cpp @@ -20,9 +20,9 @@ * you can turn off tab completion in your ~/.inputrc (or else * ${INPUTRC}) file so: * - * $if psql - * set disable-completion on - * $endif + * $if psql + * set disable-completion on + * $endif * * See `man 3 readline' or `info readline' for the full details. Also, * hence the @@ -30,32 +30,32 @@ * BUGS: * * - If you split your queries across lines, this whole thing gets - * confused. (To fix this, one would have to read psql's query - * buffer rather than readline's line buffer, which would require - * some major revisions of things.) + * confused. (To fix this, one would have to read psql's query + * buffer rather than readline's line buffer, which would require + * some major revisions of things.) * * - Table or attribute names with spaces in it may confuse it. * * - Quotes, parenthesis, and other funny characters are not handled - * all that gracefully. + * all that gracefully. * ---------------------------------------------------------------------- */ #include "settings.h" #include "postgres_fe.h" -#include "tab-complete.h" #include "input.h" +#include "tab-complete.h" /* If we don't have this, we might as well forget about the whole thing: */ #ifdef USE_READLINE #include -#include "libpq-fe.h" -#include "pqexpbuffer.h" +#include "libpq/libpq-fe.h" +#include "libpq/pqexpbuffer.h" #include "common.h" #include "stringutils.h" #ifndef WIN32 -#include "libpq-int.h" +#include "libpq/libpq-int.h" #endif #define filename_completion_function rl_filename_completion_function @@ -70,14 +70,14 @@ * to obtain possibly-schema-qualified names of database objects. There is * enough similarity in the structure that we don't want to repeat it each * time. So we put the components of each query into this struct and - * assemble them with the common boilerplate in _complete_from_query(). + * assemble them with the common boilerplate in _CompleteFromQuery(). */ typedef struct SchemaQuery { /* * Name of catalog or catalogs to be queried, with alias, eg. * "pg_catalog.pg_class c". Note that "pg_namespace n" will be added. */ - const char* catname; + const char *catname; /* * Selection condition --- only rows meeting this condition are candidates @@ -85,31 +85,31 @@ typedef struct SchemaQuery { * join condition here. For example, "c.relkind = 'r'". Write NULL (not * an empty string) if not needed. */ - const char* selcondition; + const char *selcondition; /* * Visibility condition --- which rows are visible without schema * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)". */ - const char* viscondition; + const char *viscondition; /* * Namespace --- name of field to join to pg_namespace.oid. For example, * "c.relnamespace". */ - const char* nameSpace; + const char *nameSpace; /* * Result --- the appropriately-quoted name to return, in the case of an * unqualified name. For example, "pg_catalog.quote_ident(c.relname)". */ - const char* result; + const char *result; /* * In some cases a different result must be used for qualified names. * Enter that here, or write NULL if result can be used. */ - const char* qualresult; + const char *qualresult; } SchemaQuery; /* Store maximum number of records we want from database queries @@ -137,44 +137,39 @@ static bool completion_case_sensitive; /* completion is case sensitive */ * 4) A string constant. * 5) The list of attributes of the given table (possibly schema-qualified). */ -#define COMPLETE_WITH_QUERY(query) \ - do { \ +#define COMPLETE_WITH_QUERY(query) do { \ completion_charp = query; \ - matches = completion_matches(text, complete_from_query); \ + matches = completion_matches(text, CompleteFromQuery); \ } while (0) -#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \ - do { \ +#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) do { \ completion_squery = &(query); \ completion_charp = addon; \ - matches = completion_matches(text, complete_from_schema_query); \ + matches = completion_matches(text, CompleteFromSchemaQuery); \ } while (0) -#define COMPLETE_WITH_LIST_CS(list) \ - do { \ +#define COMPLETE_WITH_LIST_CS(list) do { \ completion_charpp = list; \ completion_case_sensitive = true; \ - matches = completion_matches(text, complete_from_list); \ + matches = completion_matches(text, CompleteFromList); \ } while (0) -#define COMPLETE_WITH_LIST(list) \ - do { \ +#define COMPLETE_WITH_LIST(list) do { \ completion_charpp = list; \ completion_case_sensitive = false; \ - matches = completion_matches(text, complete_from_list); \ + matches = completion_matches(text, CompleteFromList); \ } while (0) -#define COMPLETE_WITH_CONST(string) \ - do { \ +#define COMPLETE_WITH_CONST(string) do { \ completion_charp = string; \ completion_case_sensitive = false; \ - matches = completion_matches(text, complete_from_const); \ + matches = completion_matches(text, CompleteFromConst); \ } while (0) #define COMPLETE_WITH_ATTR(relation, addon) \ do { \ - char* _completion_schema; \ - char* _completion_table; \ + char *_completion_schema; \ + char *_completion_table; \ _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, false, false, pset.encoding); \ (void)strtokx(NULL, " \t\n\r", ".", "\"", 0, false, false, pset.encoding); \ _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, false, false, pset.encoding); \ @@ -186,7 +181,7 @@ static bool completion_case_sensitive; /* completion is case sensitive */ completion_info_charp = _completion_table; \ completion_info_charp2 = _completion_schema; \ } \ - matches = completion_matches(text, complete_from_query); \ + matches = completion_matches(text, CompleteFromQuery); \ } while (0) /* @@ -204,7 +199,8 @@ static const SchemaQuery Query_for_list_of_aggregates = { /* result */ "pg_catalog.quote_ident(p.proname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_datatypes = { /* catname */ @@ -220,7 +216,8 @@ static const SchemaQuery Query_for_list_of_datatypes = { /* result */ "pg_catalog.format_type(t.oid, NULL)", /* qualresult */ - "pg_catalog.quote_ident(t.typname)"}; + "pg_catalog.quote_ident(t.typname)" +}; static const SchemaQuery Query_for_list_of_domains = { /* catname */ @@ -234,13 +231,14 @@ static const SchemaQuery Query_for_list_of_domains = { /* result */ "pg_catalog.quote_ident(t.typname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_functions = { /* catname */ "pg_catalog.pg_proc p", /* selcondition */ - NULL, + "p.prokind != 'p'", /* viscondition */ "pg_catalog.pg_function_is_visible(p.oid)", /* namespace */ @@ -248,7 +246,23 @@ static const SchemaQuery Query_for_list_of_functions = { /* result */ "pg_catalog.quote_ident(p.proname)", /* qualresult */ - NULL}; + NULL +}; + +static const SchemaQuery Query_for_list_of_procedures[] = { + /* catname */ + "pg_catalog.pg_proc p", + /* selcondition */ + "p.prokind = 'p'", + /* viscondition */ + "pg_catalog.pg_function_is_visible(p.oid)", + /* namespace */ + "p.pronamespace", + /* result */ + "pg_catalog.quote_ident(p.proname)", + /* qualresult */ + NULL +}; static const SchemaQuery Query_for_list_of_indexes = { /* catname */ @@ -262,7 +276,8 @@ static const SchemaQuery Query_for_list_of_indexes = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_sequences = { /* catname */ @@ -276,7 +291,8 @@ static const SchemaQuery Query_for_list_of_sequences = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_foreign_tables = { /* catname */ @@ -290,7 +306,8 @@ static const SchemaQuery Query_for_list_of_foreign_tables = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_tables = { /* catname */ @@ -304,7 +321,8 @@ static const SchemaQuery Query_for_list_of_tables = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; /* The bit masks for the following three functions come from * src/include/catalog/pg_trigger.h. @@ -322,7 +340,8 @@ static const SchemaQuery Query_for_list_of_insertables = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_deletables = { /* catname */ @@ -337,7 +356,8 @@ static const SchemaQuery Query_for_list_of_deletables = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_updatables = { /* catname */ @@ -352,7 +372,8 @@ static const SchemaQuery Query_for_list_of_updatables = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; static const SchemaQuery Query_for_list_of_relations = { /* catname */ @@ -366,13 +387,14 @@ static const SchemaQuery Query_for_list_of_relations = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; -static const SchemaQuery Query_for_list_of_tsvf = { +static const SchemaQuery Query_for_list_of_tsvmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'S', 'v', 'f')", + "c.relkind IN ('r', 'S', 'v', 'm', 'f')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -380,13 +402,14 @@ static const SchemaQuery Query_for_list_of_tsvf = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; -static const SchemaQuery Query_for_list_of_tf = { +static const SchemaQuery Query_for_list_of_tmf = { /* catname */ "pg_catalog.pg_class c", /* selcondition */ - "c.relkind IN ('r', 'f')", + "c.relkind IN ('r', 'm', 'f')", /* viscondition */ "pg_catalog.pg_table_is_visible(c.oid)", /* namespace */ @@ -394,7 +417,23 @@ static const SchemaQuery Query_for_list_of_tf = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; + +static const SchemaQuery Query_for_list_of_tm = { + /* catname */ + "pg_catalog.pg_class c", + /* selcondition */ + "c.relkind IN ('r', 'm')", + /* viscondition */ + "pg_catalog.pg_table_is_visible(c.oid)", + /* namespace */ + "c.relnamespace", + /* result */ + "pg_catalog.quote_ident(c.relname)", + /* qualresult */ + NULL +}; static const SchemaQuery Query_for_list_of_views = { /* catname */ @@ -408,7 +447,23 @@ static const SchemaQuery Query_for_list_of_views = { /* result */ "pg_catalog.quote_ident(c.relname)", /* qualresult */ - NULL}; + NULL +}; + +static const SchemaQuery Query_for_list_of_matviews = { + /* catname */ + "pg_catalog.pg_class c", + /* selcondition */ + "c.relkind IN ('m')", + /* viscondition */ + "pg_catalog.pg_table_is_visible(c.oid)", + /* namespace */ + "c.relnamespace", + /* result */ + "pg_catalog.quote_ident(c.relname)", + /* qualresult */ + NULL +}; /* * Queries to get lists of names of various kinds of things, possibly @@ -421,187 +476,158 @@ static const SchemaQuery Query_for_list_of_views = { * completion_info_charp2. * * Beware that the allowed sequences of %s and %d are determined by - * _complete_from_query(). + * _CompleteFromQuery(). */ -#define Query_for_list_of_attributes \ - "SELECT pg_catalog.quote_ident(attname) " \ - " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c " \ - " WHERE c.oid = a.attrelid " \ - " AND a.attnum > 0 " \ - " AND NOT a.attisdropped " \ - " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' " \ - " AND (pg_catalog.quote_ident(relname)='%s' " \ - " OR '\"' || relname || '\"'='%s') " \ - " AND pg_catalog.pg_table_is_visible(c.oid)" +#define Query_for_list_of_attributes "SELECT pg_catalog.quote_ident(attname) " \ + " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c " \ + " WHERE c.oid = a.attrelid " \ + " AND a.attnum > 0 " \ + " AND NOT a.attisdropped " \ + " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' " \ + " AND (pg_catalog.quote_ident(relname)='%s' " \ + " OR '\"' || relname || '\"'='%s') " \ + " AND pg_catalog.pg_table_is_visible(c.oid)" -#define Query_for_list_of_attributes_with_schema \ - "SELECT pg_catalog.quote_ident(attname) " \ - " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n " \ - " WHERE c.oid = a.attrelid " \ - " AND n.oid = c.relnamespace " \ - " AND a.attnum > 0 " \ - " AND NOT a.attisdropped " \ - " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' " \ - " AND (pg_catalog.quote_ident(relname)='%s' " \ - " OR '\"' || relname || '\"' ='%s') " \ - " AND (pg_catalog.quote_ident(nspname)='%s' " \ - " OR '\"' || nspname || '\"' ='%s') " +#define Query_for_list_of_attributes_with_schema \ + "SELECT pg_catalog.quote_ident(attname) " \ + " FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n " \ + " WHERE c.oid = a.attrelid " \ + " AND n.oid = c.relnamespace " \ + " AND a.attnum > 0 " \ + " AND NOT a.attisdropped " \ + " AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' " \ + " AND (pg_catalog.quote_ident(relname)='%s' " \ + " OR '\"' || relname || '\"' ='%s') " \ + " AND (pg_catalog.quote_ident(nspname)='%s' " \ + " OR '\"' || nspname || '\"' ='%s') " -#define Query_for_list_of_template_databases \ - "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database " \ - " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' AND datistemplate" +#define Query_for_list_of_template_databases "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database " \ + " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s' AND datistemplate" -#define Query_for_list_of_databases \ - "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database " \ - " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'" +#define Query_for_list_of_databases "SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database " \ + " WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'" -#define Query_for_list_of_tablespaces \ - "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace " \ - " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'" +#define Query_for_list_of_tablespaces "SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace " \ + " WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'" -#define Query_for_list_of_encodings \ - " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) " \ - " FROM pg_catalog.pg_conversion " \ - " WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')" +#define Query_for_list_of_encodings " SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) " \ + " FROM pg_catalog.pg_conversion " \ + " WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')" -#define Query_for_list_of_languages \ - "SELECT pg_catalog.quote_ident(lanname) " \ - " FROM pg_catalog.pg_language " \ - " WHERE lanname != 'internal' " \ - " AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'" +#define Query_for_list_of_languages "SELECT pg_catalog.quote_ident(lanname) " \ + " FROM pg_catalog.pg_language " \ + " WHERE lanname != 'internal' " \ + " AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'" -#define Query_for_list_of_schemas \ - "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace " \ - " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'" +#define Query_for_list_of_schemas "SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace " \ + " WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'" -#define Query_for_list_of_set_vars \ - "SELECT name FROM " \ - " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings " \ - " WHERE context IN ('user', 'superuser') " \ - " UNION ALL SELECT 'constraints' " \ - " UNION ALL SELECT 'transaction' " \ - " UNION ALL SELECT 'session' " \ - " UNION ALL SELECT 'role' " \ - " UNION ALL SELECT 'tablespace' " \ - " UNION ALL SELECT 'all') ss " \ - " WHERE substring(name,1,%d)='%s'" +#define Query_for_list_of_set_vars "SELECT name FROM " \ + " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings " \ + " WHERE context IN ('user', 'superuser') " \ + " UNION ALL SELECT 'constraints' " \ + " UNION ALL SELECT 'transaction' " \ + " UNION ALL SELECT 'session' " \ + " UNION ALL SELECT 'role' " \ + " UNION ALL SELECT 'tablespace' " \ + " UNION ALL SELECT 'all') ss " \ + " WHERE substring(name,1,%d)='%s'" -#define Query_for_list_of_show_vars \ - "SELECT name FROM " \ - " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings " \ - " UNION ALL SELECT 'session authorization' " \ - " UNION ALL SELECT 'all') ss " \ - " WHERE substring(name,1,%d)='%s'" +#define Query_for_list_of_show_vars "SELECT name FROM " \ + " (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings " \ + " UNION ALL SELECT 'session authorization' " \ + " UNION ALL SELECT 'all') ss " \ + " WHERE substring(name,1,%d)='%s'" -#define Query_for_list_of_roles \ - " SELECT pg_catalog.quote_ident(rolname) " \ - " FROM pg_catalog.pg_roles " \ - " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" +#define Query_for_list_of_roles " SELECT pg_catalog.quote_ident(rolname) " \ + " FROM pg_catalog.pg_roles " \ + " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" -#define Query_for_list_of_grant_roles \ - " SELECT pg_catalog.quote_ident(rolname) " \ - " FROM pg_catalog.pg_roles " \ - " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" \ - " UNION ALL SELECT 'PUBLIC'" +#define Query_for_list_of_grant_roles " SELECT pg_catalog.quote_ident(rolname) " \ + " FROM pg_catalog.pg_roles " \ + " WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'" \ + " UNION ALL SELECT 'PUBLIC'" /* the silly-looking length condition is just to eat up the current word */ -#define Query_for_table_owning_index \ - "SELECT pg_catalog.quote_ident(c1.relname) " \ - " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i" \ - " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid" \ - " and (%d = pg_catalog.length('%s'))" \ - " and pg_catalog.quote_ident(c2.relname)='%s'" \ - " and pg_catalog.pg_table_is_visible(c2.oid)" +#define Query_for_table_owning_index \ + "SELECT pg_catalog.quote_ident(c1.relname) " \ + " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i" \ + " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid" \ + " and (%d = pg_catalog.length('%s'))" \ + " and pg_catalog.quote_ident(c2.relname)='%s'" \ + " and pg_catalog.pg_table_is_visible(c2.oid)" /* the silly-looking length condition is just to eat up the current word */ -#define Query_for_index_of_table \ - "SELECT pg_catalog.quote_ident(c2.relname) " \ - " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i" \ - " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid" \ - " and (%d = pg_catalog.length('%s'))" \ - " and pg_catalog.quote_ident(c1.relname)='%s'" \ - " and pg_catalog.pg_table_is_visible(c2.oid)" +#define Query_for_index_of_table "SELECT pg_catalog.quote_ident(c2.relname) " \ + " FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i" \ + " WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid" \ + " and (%d = pg_catalog.length('%s'))" \ + " and pg_catalog.quote_ident(c1.relname)='%s'" \ + " and pg_catalog.pg_table_is_visible(c2.oid)" /* the silly-looking length condition is just to eat up the current word */ -#define Query_for_list_of_tables_for_trigger \ - "SELECT pg_catalog.quote_ident(relname) " \ - " FROM pg_catalog.pg_class" \ - " WHERE (%d = pg_catalog.length('%s'))" \ - " AND oid IN " \ - " (SELECT tgrelid FROM pg_catalog.pg_trigger " \ - " WHERE pg_catalog.quote_ident(tgname)='%s')" +#define Query_for_list_of_tables_for_trigger "SELECT pg_catalog.quote_ident(relname) " \ + " FROM pg_catalog.pg_class" \ + " WHERE (%d = pg_catalog.length('%s'))" \ + " AND oid IN " \ + " (SELECT tgrelid FROM pg_catalog.pg_trigger " \ + " WHERE pg_catalog.quote_ident(tgname)='%s')" -#define Query_for_list_of_ts_configurations \ - "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config " \ - " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'" +#define Query_for_list_of_ts_configurations "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config " \ + " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'" -#define Query_for_list_of_ts_dictionaries \ - "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict " \ - " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'" +#define Query_for_list_of_ts_dictionaries "SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict " \ + " WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'" -#define Query_for_list_of_ts_parsers \ - "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser " \ - " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'" +#define Query_for_list_of_ts_parsers "SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser " \ + " WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'" -#define Query_for_list_of_ts_templates \ - "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template " \ - " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'" +#define Query_for_list_of_ts_templates "SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template " \ + " WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'" -#define Query_for_list_of_fdws \ - " SELECT pg_catalog.quote_ident(fdwname) " \ - " FROM pg_catalog.pg_foreign_data_wrapper " \ - " WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'" +#define Query_for_list_of_fdws " SELECT pg_catalog.quote_ident(fdwname) " \ + " FROM pg_catalog.pg_foreign_data_wrapper " \ + " WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'" -#define Query_for_list_of_servers \ - " SELECT pg_catalog.quote_ident(srvname) " \ - " FROM pg_catalog.pg_foreign_server " \ - " WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'" +#define Query_for_list_of_servers " SELECT pg_catalog.quote_ident(srvname) " \ + " FROM pg_catalog.pg_foreign_server " \ + " WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'" -#define Query_for_list_of_user_mappings \ - " SELECT pg_catalog.quote_ident(usename) " \ - " FROM pg_catalog.pg_user_mappings " \ - " WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" +#define Query_for_list_of_user_mappings " SELECT pg_catalog.quote_ident(usename) " \ + " FROM pg_catalog.pg_user_mappings " \ + " WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'" -#define Query_for_list_of_access_methods \ - " SELECT pg_catalog.quote_ident(amname) " \ - " FROM pg_catalog.pg_am " \ - " WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'" +#define Query_for_list_of_access_methods " SELECT pg_catalog.quote_ident(amname) " \ + " FROM pg_catalog.pg_am " \ + " WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'" -#define Query_for_list_of_arguments \ - " SELECT pg_catalog.oidvectortypes(proargtypes)||')' " \ - " FROM pg_catalog.pg_proc " \ - " WHERE proname='%s'" +#define Query_for_list_of_arguments " SELECT pg_catalog.oidvectortypes(proargtypes)||')' " \ + " FROM pg_catalog.pg_proc " \ + " WHERE proname='%s'" -#define Query_for_list_of_extensions \ - " SELECT pg_catalog.quote_ident(extname) " \ - " FROM pg_catalog.pg_extension " \ - " WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'" +#define Query_for_list_of_extensions " SELECT pg_catalog.quote_ident(extname) " \ + " FROM pg_catalog.pg_extension " \ + " WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'" -#define Query_for_list_of_available_extensions \ - " SELECT pg_catalog.quote_ident(name) " \ - " FROM pg_catalog.pg_available_extensions " \ - " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL" +#define Query_for_list_of_available_extensions " SELECT pg_catalog.quote_ident(name) " \ + " FROM pg_catalog.pg_available_extensions " \ + " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL" -#define Query_for_list_of_prepared_statements \ - " SELECT pg_catalog.quote_ident(name) " \ - " FROM pg_catalog.pg_prepared_statements " \ - " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'" +#define Query_for_list_of_prepared_statements " SELECT pg_catalog.quote_ident(name) " \ + " FROM pg_catalog.pg_prepared_statements " \ + " WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'" #ifdef PGXC -#define Query_for_list_of_available_nodenames \ - " SELECT NODE_NAME " \ - " FROM PGXC_NODE" -#define Query_for_list_of_available_coordinators \ - " SELECT NODE_NAME " \ - " FROM PGXC_NODE" \ - " WHERE NODE_TYPE = 'C'" -#define Query_for_list_of_available_datanodes \ - " SELECT NODE_NAME " \ - " FROM PGXC_NODE" \ - " WHERE NODE_TYPE = 'D'" -#define Query_for_list_of_available_nodegroup_names \ - " SELECT GROUP_NAME " \ - " FROM PGXC_GROUP" +#define Query_for_list_of_available_nodenames " SELECT NODE_NAME " \ + " FROM PGXC_NODE" +#define Query_for_list_of_available_coordinators " SELECT NODE_NAME " \ + " FROM PGXC_NODE" \ + " WHERE NODE_TYPE = 'C'" +#define Query_for_list_of_available_datanodes " SELECT NODE_NAME " \ + " FROM PGXC_NODE" \ + " WHERE NODE_TYPE = 'D'" +#define Query_for_list_of_available_nodegroup_names " SELECT GROUP_NAME " \ + " FROM PGXC_GROUP" #endif /* @@ -609,9 +635,9 @@ static const SchemaQuery Query_for_list_of_views = { * DROP; and there is also a query to get a list of them. */ typedef struct { - const char* name; - const char* query; /* simple query, or NULL */ - const SchemaQuery* squery; /* schema query, or NULL */ + const char *name; + const char *query; /* simple query, or NULL */ + const SchemaQuery *squery; /* schema query, or NULL */ const bits32 flags; /* visibility flags, see below */ } pgsql_thing_t; @@ -625,13 +651,13 @@ static const pgsql_thing_t words_after_create[] = { {"BARRIER", NULL, NULL, 0}, /* Comes barrier name next, so skip it */ #endif {"CAST", NULL, NULL, 0}, /* Casts have complex structures for names, so - * skip it */ + * skip it */ {"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'", - NULL, - 0}, + "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'", + NULL, + 0}, /* * CREATE CONSTRAINT TRIGGER is not supported here because it is designed @@ -639,10 +665,10 @@ static const pgsql_thing_t words_after_create[] = { */ {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW}, {"CONVERSION", - "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE " + "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE " "substring(pg_catalog.quote_ident(conname),1,%d)='%s'", - NULL, - 0}, + NULL, + 0}, {"DATABASE", Query_for_list_of_databases, NULL, 0}, {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW}, {"DOMAIN", NULL, &Query_for_list_of_domains, 0}, @@ -653,8 +679,9 @@ static const pgsql_thing_t words_after_create[] = { #endif {"FUNCTION", NULL, &Query_for_list_of_functions, 0}, {"GROUP", Query_for_list_of_roles, NULL, 0}, - {"LANGUAGE", Query_for_list_of_languages, NULL, 0}, {"INDEX", NULL, &Query_for_list_of_indexes, 0}, + {"LANGUAGE", Query_for_list_of_languages, NULL, 0}, + {"MATERIALIZED VIEW", NULL, &Query_for_list_of_matviews, 0}, #ifdef PGXC {"NODE", Query_for_list_of_available_nodenames, NULL, 0}, {"NODE GROUP", Query_for_list_of_available_nodegroup_names, NULL, 0}, @@ -663,6 +690,8 @@ static const pgsql_thing_t words_after_create[] = { * good idea. */ {"OWNED", NULL, NULL, THING_NO_CREATE}, /* for DROP OWNED BY ... */ {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW}, + {"POLICY", NULL, NULL, 0}, + {"PROCEDURE", NULL, Query_for_list_of_procedures, 0}, {"ROLE", Query_for_list_of_roles, NULL, 0}, {"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE " @@ -678,6 +707,7 @@ static const pgsql_thing_t words_after_create[] = { {"TABLESPACE", Query_for_list_of_tablespaces, NULL, 0}, {"TEMP", NULL, NULL, THING_NO_DROP}, /* for CREATE TEMP TABLE ... */ {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW}, + {"TEMPORARY", NULL, NULL, THING_NO_DROP}, {"TEXT SEARCH", NULL, NULL, 0}, {"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE " @@ -697,20 +727,20 @@ static const pgsql_thing_t words_after_create[] = { }; /* Forward declaration of functions */ -/*psql_completion is deleted because it's too complex and not be used at all. */ -static char* create_command_generator(const char* text, int state); -static char* drop_command_generator(const char* text, int state); -static char* complete_from_query(const char* text, int state); -static char* complete_from_schema_query(const char* text, int state); -static char* _complete_from_query(int is_schema_query, const char* text, int state); -static char* complete_from_list(const char* text, int state); -static char* complete_from_const(const char* text, int state); -static char** complete_from_variables(char* text, const char* prefix, const char* suffix); -static char* complete_from_files(const char* text, int state); +static char** PsqlCompletion(const char *text, int start, int end); +static char* CreateCommandGenerator(const char* text, int state); +static char* DropCommandGenerator(const char* text, int state); +static char* CompleteFromQuery(const char* text, int state); +static char* CompleteFromSchemaQuery(const char* text, int state); +static char* _CompleteFromQuery(int isSchemaQuery, const char* text, int state); +static char* CompleteFromList(const char* text, int state); +static char* CompleteFromConst(const char* text, int state); +static char** CompleteFromVariables(const char* text, const char* prefix, const char* suffix); +static char* CompleteFromFiles(const char* text, int state); static char* pg_strdup_keyword_case(const char* s, const char* ref); -static PGresult* exec_query(const char* query); -static void get_previous_words(int point, char** previous_words, int nwords); +static PGresult* ExecQuery(const char* query); +static void GetPreviousWords(int point, char** previousWords, int nwords); #ifdef NOT_USED static char* quote_file_name(char* text, int match_type, char* quote_pointer); @@ -722,10 +752,10 @@ static char* dequote_file_name(char* text, char quote_char); */ void initialize_readline(void) { - rl_readline_name = (char*)pset.progname; + rl_readline_name = (char *)pset.progname; - /* psql_completion is deleted because it's too complex and not be used at all. */ - rl_attempted_completion_function = NULL; + /* PsqlCompletion is deleted because it's too complex and not be used at all. */ + rl_attempted_completion_function = PsqlCompletion; rl_basic_word_break_characters = WORD_BREAKS; @@ -751,23 +781,23 @@ void initialize_readline(void) * something of that sort. */ /* - * Common routine for create_command_generator and drop_command_generator. + * Common routine for CreateCommandGenerator and DropCommandGenerator. * Entries that have 'excluded' flags are not returned. */ -static char* create_or_drop_command_generator(const char* text, int state, bits32 excluded) +static char *create_or_DropCommandGenerator(const char *text, int state, bits32 excluded) { - static int list_index, string_length; - const char* name = NULL; + static int listIndex, stringLength; + const char *name = NULL; /* If this is the first time for this completion, init some values */ if (state == 0) { - list_index = 0; - string_length = strlen(text); + listIndex = 0; + stringLength = strlen(text); } /* find something that matches */ - while ((name = words_after_create[list_index++].name)) { - if ((pg_strncasecmp(name, text, string_length) == 0) && !(words_after_create[list_index - 1].flags & excluded)) + while ((name = words_after_create[listIndex++].name)) { + if ((pg_strncasecmp(name, text, stringLength) == 0) && !(words_after_create[listIndex - 1].flags & excluded)) return pg_strdup_keyword_case(name, text); } /* if nothing matches, return NULL */ @@ -778,28 +808,1953 @@ static char* create_or_drop_command_generator(const char* text, int state, bits3 * This one gives you one from a list of things you can put after CREATE * as defined above. */ -static char* create_command_generator(const char* text, int state) +static char *CreateCommandGenerator(const char *text, int state) { - return create_or_drop_command_generator(text, state, THING_NO_CREATE); + return create_or_DropCommandGenerator(text, state, THING_NO_CREATE); } /* * This function gives you a list of things you can put after a DROP command. */ -static char* drop_command_generator(const char* text, int state) +static char *DropCommandGenerator(const char *text, int state) { - return create_or_drop_command_generator(text, state, THING_NO_DROP); + return create_or_DropCommandGenerator(text, state, THING_NO_DROP); } -/* The following two functions are wrappers for _complete_from_query */ -static char* complete_from_query(const char* text, int state) +/* The following two functions are wrappers for _CompleteFromQuery */ +static char *CompleteFromQuery(const char *text, int state) { - return _complete_from_query(0, text, state); + return _CompleteFromQuery(0, text, state); } -static char* complete_from_schema_query(const char* text, int state) +static char *CompleteFromSchemaQuery(const char *text, int state) { - return _complete_from_query(1, text, state); + return _CompleteFromQuery(1, text, state); +} +/* + * The completion function. + * + * According to readline spec this gets passed the text entered so far and its + * start and end positions in the readline buffer. The return value is some + * partially obscure list format that can be generated by readline's + * completion_matches() function, so we don't have to worry about it. + */ +static char** PsqlCompletion(const char *text, int start, int end) +{ + /* This is the variable we'll return. */ + char **matches = NULL; + + /* This array will contain some scannage of the input line. */ + char *previousWords[7]; + + /* For compactness, we use these macros to reference previous_words[]. */ +#define PREV_WD (previousWords[0]) +#define PREV2_WD (previousWords[1]) +#define PREV3_WD (previousWords[2]) +#define PREV4_WD (previousWords[3]) +#define PREV5_WD (previousWords[4]) +#define PREV6_WD (previousWords[5]) +#define PREV7_WD (previousWords[6]) + + static const char* const sqlCommands[] = { + "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER", + "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", + "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH", + "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE", + "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", + "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START", + "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH", + NULL + }; + + static const char* const backslashCommands[] = { + "\\a", + "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright", + "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\ddp", "\\dD", "\\ded", + "\\des", "\\det", "\\deu", "\\dew", "\\dE", "\\df", "\\dF", "\\dFd", + "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL", "\\dn", "\\do", "\\dO", + "\\dp", "\\drds", "\\ds", "\\dt", "\\dT", "\\dv", "\\du", "\\dx", + "\\e", "\\echo", "\\ef", "\\encoding", + "\\f", + "\\g", + "\\h", "\\help", "\\H", + "\\i", "\\i+", "\\ir", "\\ir+", + "\\l", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", + "\\o", + "\\p", "\\parallel", "\\prompt", "\\pset", + "\\q", "\\qecho", + "\\r", + "\\set", "\\setenv", "\\sf", + "\\t", "\\T", "\\timing", + "\\unset", + "\\x", + "\\w", + "\\z", + "\\!", "\\?", + NULL + }; + + (void)end; /* not used */ + +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER + rl_completion_append_character = ' '; +#endif + + /* Clear a few things. */ + completion_charp = NULL; + completion_charpp = NULL; + completion_info_charp = NULL; + completion_info_charp2 = NULL; + + /* + * Scan the input line before our current position for the last few words. + * According to those we'll make some smart decisions on what the user is + * probably intending to type. + */ + GetPreviousWords(start, previousWords, lengthof(previousWords)); + + /* If a backslash command was started, continue */ + if (text[0] == '\\') { + COMPLETE_WITH_LIST_CS(backslashCommands); + } + + /* Variable interpolation */ + else if (text[0] == ':' && text[1] != ':') { + if (text[1] == '\'') { + matches = CompleteFromVariables(text, ":'", "'"); + } + else if (text[1] == '"') { + matches = CompleteFromVariables(text, ":\"", "\""); + } + else { + matches = CompleteFromVariables(text, ":", ""); + } + } + + /* If no previous word, suggest one of the basic sql commands */ + else if (PREV_WD[0] == '\0') { + COMPLETE_WITH_LIST(sqlCommands); + } + + /* CREATE */ + /* complete with something you can create */ + else if (pg_strcasecmp(PREV_WD, "CREATE") == 0) + matches = completion_matches(text, CreateCommandGenerator); + + /* DROP, but not DROP embedded in other commands */ + /* complete with something you can drop */ + else if (pg_strcasecmp(PREV_WD, "DROP") == 0 && PREV2_WD[0] == '\0') + matches = completion_matches(text, DropCommandGenerator); + + /* ALTER */ + + /* + * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're + * in ALTER TABLE sth ALTER + */ + else if (pg_strcasecmp(PREV_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TABLE") != 0) { + static const char* const listAlter[] = { + "AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", + "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION", + "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR", + "POLICY", "PROCEDURE", "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", + "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", + "USER", "USER MAPPING FOR", "VIEW", NULL + }; + + COMPLETE_WITH_LIST(listAlter); + } + /* ALTER AGGREGATE,FUNCTION,PROCEDURE */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && (pg_strcasecmp(PREV2_WD, "AGGREGATE") == 0 || + pg_strcasecmp(PREV2_WD, "FUNCTION") == 0 || pg_strcasecmp(PREV2_WD, "PROCEDURE") == 0)) + COMPLETE_WITH_CONST("("); + /* ALTER AGGREGATE,FUNCTION (...) */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && (pg_strcasecmp(PREV3_WD, "AGGREGATE") == 0 || + pg_strcasecmp(PREV3_WD, "FUNCTION") == 0 || pg_strcasecmp(PREV2_WD, "PROCEDURE") == 0)) { + if (PREV_WD[strlen(PREV_WD) - 1] == ')') { + static const char* const listAlterAgg[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterAgg); + } else { + size_t tmpLength = strlen(Query_for_list_of_arguments) + strlen(PREV2_WD); + char *tmpBuf = (char *)pg_malloc(tmpLength); + + int rc = sprintf_s(tmpBuf, tmpLength, Query_for_list_of_arguments, PREV2_WD); + securec_check_ss_c(rc, "", ""); + COMPLETE_WITH_QUERY(tmpBuf); + free(tmpBuf); + } + } + + /* ALTER SCHEMA */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "SCHEMA") == 0) { + static const char* const listAlterGen[] = {"OWNER TO", "RENAME TO", NULL}; + + COMPLETE_WITH_LIST(listAlterGen); + } + + /* ALTER COLLATION */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "COLLATION") == 0) { + static const char* const listAlterGen[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterGen); + } + + /* ALTER CONVERSION */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "CONVERSION") == 0) { + static const char* const listAlterGen[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterGen); + } + + /* ALTER DATABASE */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "DATABASE") == 0) { + static const char* const listAlterDatabase[] = { + "RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL + }; + + COMPLETE_WITH_LIST(listAlterDatabase); + } + + /* ALTER EXTENSION */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "EXTENSION") == 0) { + static const char* const listAlterExtension[] = {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterExtension); + } + + /* ALTER FOREIGN */ + else if (pg_strcasecmp(PREV2_WD, "ALTER") == 0 && pg_strcasecmp(PREV_WD, "FOREIGN") == 0) { + static const char* const listAlterForeign[] = {"DATA WRAPPER", "TABLE", NULL}; + + COMPLETE_WITH_LIST(listAlterForeign); + } + + /* ALTER FOREIGN DATA WRAPPER */ + else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "FOREIGN") == 0 && + pg_strcasecmp(PREV3_WD, "DATA") == 0 && pg_strcasecmp(PREV2_WD, "WRAPPER") == 0) { + static const char* const listAlterFdw[] = {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL}; + + COMPLETE_WITH_LIST(listAlterFdw); + } + + /* ALTER FOREIGN TABLE */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "FOREIGN") == 0 && + pg_strcasecmp(PREV2_WD, "TABLE") == 0) { + static const char* const listAlterForeignTable[] = {"ALTER", "DROP", "RENAME", "OWNER TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterForeignTable); + } + + /* ALTER INDEX */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "INDEX") == 0) { + static const char* const listAlterindex[] = {"OWNER TO", "RENAME TO", "SET", "RESET", NULL}; + + COMPLETE_WITH_LIST(listAlterindex); + } + /* ALTER INDEX SET */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "INDEX") == 0 && + pg_strcasecmp(PREV_WD, "SET") == 0) { + static const char* const listAlterindexset[] = {"(", "TABLESPACE", NULL}; + + COMPLETE_WITH_LIST(listAlterindexset); + } + /* ALTER INDEX RESET */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "INDEX") == 0 && + pg_strcasecmp(PREV_WD, "RESET") == 0) + COMPLETE_WITH_CONST("("); + /* ALTER INDEX SET|RESET ( */ + else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "INDEX") == 0 && + (pg_strcasecmp(PREV2_WD, "SET") == 0 || pg_strcasecmp(PREV2_WD, "RESET") == 0) && + pg_strcasecmp(PREV_WD, "(") == 0) { + static const char* const listIndexOptions[] = {"fillfactor", "fastupdate", NULL}; + + COMPLETE_WITH_LIST(listIndexOptions); + } + + /* ALTER LANGUAGE */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "LANGUAGE") == 0) { + static const char* const listAlterLanguage[] = {"OWNER TO", "RENAME TO", NULL}; + + COMPLETE_WITH_LIST(listAlterLanguage); + } + + /* ALTER LARGE OBJECT */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "LARGE") == 0 && + pg_strcasecmp(PREV2_WD, "OBJECT") == 0) { + static const char* const listAlterLargeObject[] = {"OWNER TO", NULL}; + + COMPLETE_WITH_LIST(listAlterLargeObject); + } + + /* ALTER MATERIALIZED VIEW */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV2_WD, "VIEW") == 0) { + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + } + + /* ALTER USER,ROLE */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && + !(pg_strcasecmp(PREV2_WD, "USER") == 0 && pg_strcasecmp(PREV_WD, "MAPPING") == 0) && + (pg_strcasecmp(PREV2_WD, "USER") == 0 || pg_strcasecmp(PREV2_WD, "ROLE") == 0)) { + static const char* const listAlterUser[] = { + "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER", + "ENCRYPTED", "IDENT", "IDENTIFIED BY", "INHERIT", "LOGIN", + "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", + "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", "RENAME TO", + "REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED", + "VALID UNTIL", "WITH", NULL + }; + + COMPLETE_WITH_LIST(listAlterUser); + } + + /* ALTER USER,ROLE WITH */ + else if ((pg_strcasecmp(PREV4_WD, "ALTER") == 0 && + (pg_strcasecmp(PREV3_WD, "USER") == 0 || pg_strcasecmp(PREV3_WD, "ROLE") == 0) && + pg_strcasecmp(PREV_WD, "WITH") == 0)) { + /* Similar to the above, but don't complete "WITH" again. */ + static const char* const listAlterUserWith[] = { + "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER", + "ENCRYPTED", "IDENT", "IDENTIFIED BY", "INHERIT", "LOGIN", + "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", + "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD", "RENAME TO", + "REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED", + "VALID UNTIL", NULL + }; + + COMPLETE_WITH_LIST(listAlterUserWith); + } + + /* ALTER USER,ROLE ENCRYPTED,UNENCRYPTED */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && + (pg_strcasecmp(PREV3_WD, "ROLE") == 0 || pg_strcasecmp(PREV3_WD, "USER") == 0) && + (pg_strcasecmp(PREV_WD, "ENCRYPTED") == 0 || pg_strcasecmp(PREV_WD, "UNENCRYPTED") == 0)) { + static const char* const listAlterUserEncrypted[] = {"IDENTIFIED BY", "PASSWORD", NULL}; + COMPLETE_WITH_LIST(listAlterUserEncrypted); + } + + /* ALTER USER,ROLE WITH ENCRYPTED,UNENCRYPTED */ + else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && (pg_strcasecmp(PREV4_WD, "ROLE") == 0 || + pg_strcasecmp(PREV4_WD, "USER") == 0) && pg_strcasecmp(PREV2_WD, "WITH") && + (pg_strcasecmp(PREV_WD, "ENCRYPTED") == 0 || pg_strcasecmp(PREV_WD, "UNENCRYPTED") == 0)) { + static const char* const listAlterUserWithEncrypted[] = {"IDENTIFIED BY", "PASSWORD", NULL}; + COMPLETE_WITH_LIST(listAlterUserWithEncrypted); + } + + /* ALTER DEFAULT PRIVILEGES */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "DEFAULT") == 0 && + pg_strcasecmp(PREV_WD, "PRIVILEGES") == 0) { + static const char* const listAlterDefaultPrivileges[] = {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterDefaultPrivileges); + } + /* ALTER DEFAULT PRIVILEGES FOR */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "DEFAULT") == 0 && + pg_strcasecmp(PREV2_WD, "PRIVILEGES") == 0 && pg_strcasecmp(PREV_WD, "FOR") == 0) { + static const char* const listAlterDefaultPrivileges_FOR[] = {"ROLE", "USER", NULL}; + + COMPLETE_WITH_LIST(listAlterDefaultPrivileges_FOR); + } + /* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */ + else if (pg_strcasecmp(PREV5_WD, "DEFAULT") == 0 && pg_strcasecmp(PREV4_WD, "PRIVILEGES") == 0 && + (pg_strcasecmp(PREV3_WD, "FOR") == 0 || pg_strcasecmp(PREV3_WD, "IN") == 0)) { + static const char* const listAlterDefaultPrivilegesRest[] = {"GRANT", "REVOKE", NULL}; + + COMPLETE_WITH_LIST(listAlterDefaultPrivilegesRest); + } + /* ALTER DOMAIN */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "DOMAIN") == 0) { + static const char* const listAlterDomain[] = { + "ADD", "DROP", "OWNER TO", "RENAME", "SET", "VALIDATE CONSTRAINT", NULL + }; + + COMPLETE_WITH_LIST(listAlterDomain); + } + /* ALTER DOMAIN DROP */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "DOMAIN") == 0 && + pg_strcasecmp(PREV_WD, "DROP") == 0) { + static const char* const listAlterDomain2[] = {"CONSTRAINT", "DEFAULT", "NOT NULL", NULL}; + + COMPLETE_WITH_LIST(listAlterDomain2); + } + /* ALTER DOMAIN RENAME */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "DOMAIN") == 0 && + pg_strcasecmp(PREV_WD, "RENAME") == 0) { + static const char* const listAlterDomain[] = {"CONSTRAINT", "TO", NULL}; + + COMPLETE_WITH_LIST(listAlterDomain); + } + /* ALTER DOMAIN RENAME CONSTRAINT */ + else if (pg_strcasecmp(PREV5_WD, "DOMAIN") == 0 && pg_strcasecmp(PREV3_WD, "RENAME") == 0 && + pg_strcasecmp(PREV2_WD, "CONSTRAINT") == 0) + COMPLETE_WITH_CONST("TO"); + + /* ALTER DOMAIN SET */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "DOMAIN") == 0 && + pg_strcasecmp(PREV_WD, "SET") == 0) { + static const char* const listAlterDomain3[] = {"DEFAULT", "NOT NULL", "SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterDomain3); + } + /* ALTER SEQUENCE */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "SEQUENCE") == 0) { + static const char* const listAlterSequence[] = { + "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", + "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL + }; + + COMPLETE_WITH_LIST(listAlterSequence); + } + /* ALTER SEQUENCE NO */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "SEQUENCE") == 0 && + pg_strcasecmp(PREV_WD, "NO") == 0) { + static const char* const listAlterSequencE2[] = {"MINVALUE", "MAXVALUE", "CYCLE", NULL}; + + COMPLETE_WITH_LIST(listAlterSequencE2); + } + /* ALTER SERVER */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "SERVER") == 0) { + static const char* const listAlterServer[] = { + "VERSION", "OPTIONS", "OWNER TO", NULL + }; + + COMPLETE_WITH_LIST(listAlterServer); + } + + /* ALTER MATERIALIZED VIEW */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV2_WD, "VIEW") == 0) { + static const char *const listAlterMatview[] = { + "ALTER COLUMN", "OWNER TO", "RENAME", "RESET (", "SET", NULL}; + + COMPLETE_WITH_LIST(listAlterMatview); + } + /* ALTER MATERIALIZED VIEW xxx RENAME */ + else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV3_WD, "VIEW") == 0 && pg_strcasecmp(PREV_WD, "RENAME") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, " UNION SELECT 'COLUMN' UNION SELECT 'TO'"); + else if (pg_strcasecmp(PREV6_WD, "ALTER") == 0 && pg_strcasecmp(PREV5_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV4_WD, "VIEW") == 0 && (pg_strcasecmp(PREV2_WD, "ALTER") == 0 || + pg_strcasecmp(PREV2_WD, "RENAME") == 0) && pg_strcasecmp(PREV_WD, "COLUMN") == 0) + COMPLETE_WITH_ATTR(PREV3_WD, ""); + /* ALTER MATERIALIZED VIEW xxx RENAME yyy */ + else if (pg_strcasecmp(PREV6_WD, "ALTER") == 0 && pg_strcasecmp(PREV5_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV4_WD, "VIEW") == 0 && pg_strcasecmp(PREV2_WD, "RENAME") == 0 && + pg_strcasecmp(PREV_WD, "TO") != 0) + COMPLETE_WITH_CONST("TO"); + /* ALTER MATERIALIZED VIEW xxx RENAME COLUMN yyy */ + else if (pg_strcasecmp(PREV7_WD, "ALTER") == 0 && pg_strcasecmp(PREV6_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV5_WD, "VIEW") == 0 && pg_strcasecmp(PREV3_WD, "RENAME") == 0 && + pg_strcasecmp(PREV2_WD, "COLUMN") == 0 && pg_strcasecmp(PREV_WD, "TO") != 0) + COMPLETE_WITH_CONST("TO"); + /* ALTER MATERIALIZED VIEW xxx SET */ + else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV3_WD, "VIEW") == 0 && pg_strcasecmp(PREV_WD, "SET") == 0) { + static const char *const listAlterS[] = { + "(", "SCHEMA", "TABLESPACE", "WITHOUT CLUSTER", NULL}; + + COMPLETE_WITH_LIST(listAlterS); + } + + /* ALTER VIEW */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "VIEW") == 0) { + static const char* const listAlterview[] = {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterview); + } + /* ALTER TRIGGER , add ON */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "TRIGGER") == 0) + COMPLETE_WITH_CONST("ON"); + + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TRIGGER") == 0) { + completion_info_charp = PREV2_WD; + COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger); + } + + /* + * If we have ALTER TRIGGER ON, then add the correct tablename + */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TRIGGER") == 0 && + pg_strcasecmp(PREV_WD, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + + /* ALTER TRIGGER ON */ + else if (pg_strcasecmp(PREV4_WD, "TRIGGER") == 0 && pg_strcasecmp(PREV2_WD, "ON") == 0) + COMPLETE_WITH_CONST("RENAME TO"); + + /* + * If we detect ALTER TABLE , suggest sub commands + */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "TABLE") == 0) { + static const char* const listAlteR2[] = { + "ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT", + "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", + "VALIDATE CONSTRAINT", NULL + }; + + COMPLETE_WITH_LIST(listAlteR2); + } + /* ALTER TABLE xxx ENABLE */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TABLE") == 0 && + pg_strcasecmp(PREV_WD, "ENABLE") == 0) { + static const char* const listAlterEnable[] = {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL}; + + COMPLETE_WITH_LIST(listAlterEnable); + } else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "ENABLE") == 0 && + (pg_strcasecmp(PREV_WD, "REPLICA") == 0 || pg_strcasecmp(PREV_WD, "ALWAYS") == 0)) { + static const char* const listAlterEnablE2[] = {"RULE", "TRIGGER", NULL}; + + COMPLETE_WITH_LIST(listAlterEnablE2); + } else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TABLE") == 0 && + pg_strcasecmp(PREV_WD, "DISABLE") == 0) { + static const char* const listAlterDisable[] = {"RULE", "TRIGGER", NULL}; + + COMPLETE_WITH_LIST(listAlterDisable); + } + + /* ALTER TABLE xxx ALTER */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TABLE") == 0 && + pg_strcasecmp(PREV_WD, "ALTER") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, " UNION SELECT 'COLUMN'"); + + /* ALTER TABLE xxx RENAME */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TABLE") == 0 && + pg_strcasecmp(PREV_WD, "RENAME") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'"); + + /* + * If we have TABLE ALTER COLUMN|RENAME COLUMN, provide list of + * columns + */ + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && + (pg_strcasecmp(PREV2_WD, "ALTER") == 0 || pg_strcasecmp(PREV2_WD, "RENAME") == 0) && + pg_strcasecmp(PREV_WD, "COLUMN") == 0) + COMPLETE_WITH_ATTR(PREV3_WD, ""); + + /* ALTER TABLE xxx RENAME yyy */ + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "RENAME") == 0 && + pg_strcasecmp(PREV_WD, "CONSTRAINT") != 0 && pg_strcasecmp(PREV_WD, "TO") != 0) + COMPLETE_WITH_CONST("TO"); + + /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */ + else if (pg_strcasecmp(PREV5_WD, "TABLE") == 0 && pg_strcasecmp(PREV3_WD, "RENAME") == 0 && + (pg_strcasecmp(PREV2_WD, "COLUMN") == 0 || pg_strcasecmp(PREV2_WD, "CONSTRAINT") == 0) && + pg_strcasecmp(PREV_WD, "TO") != 0) + COMPLETE_WITH_CONST("TO"); + + /* If we have TABLE DROP, provide COLUMN or CONSTRAINT */ + else if (pg_strcasecmp(PREV3_WD, "TABLE") == 0 && pg_strcasecmp(PREV_WD, "DROP") == 0) { + static const char* const listTableDrop[] = {"COLUMN", "CONSTRAINT", NULL}; + + COMPLETE_WITH_LIST(listTableDrop); + } + /* If we have TABLE DROP COLUMN, provide list of columns */ + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "DROP") == 0 && + pg_strcasecmp(PREV_WD, "COLUMN") == 0) + COMPLETE_WITH_ATTR(PREV3_WD, ""); + /* ALTER TABLE ALTER [COLUMN] */ + else if ((pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "COLUMN") == 0) || + (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "ALTER") == 0)) { + static const char* const listColumnAlter[] = {"TYPE", "SET", "RESET", "DROP", NULL}; + + COMPLETE_WITH_LIST(listColumnAlter); + } + /* ALTER TABLE ALTER [COLUMN] SET */ + else if (((pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "COLUMN") == 0) || + (pg_strcasecmp(PREV5_WD, "TABLE") == 0 && pg_strcasecmp(PREV3_WD, "ALTER") == 0)) && + pg_strcasecmp(PREV_WD, "SET") == 0) { + static const char* const listColumnSet[] = {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL}; + + COMPLETE_WITH_LIST(listColumnSet); + } + /* ALTER TABLE ALTER [COLUMN] SET ( */ + else if (((pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "COLUMN") == 0) || + pg_strcasecmp(PREV4_WD, "ALTER") == 0) && + pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV_WD, "(") == 0) { + static const char* const listColumnOptions[] = {"n_distinct", "n_distinct_inherited", NULL}; + + COMPLETE_WITH_LIST(listColumnOptions); + } + /* ALTER TABLE ALTER [COLUMN] SET STORAGE */ + else if (((pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "COLUMN") == 0) || + pg_strcasecmp(PREV4_WD, "ALTER") == 0) && + pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV_WD, "STORAGE") == 0) { + static const char* const listColumnStorage[] = {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL}; + + COMPLETE_WITH_LIST(listColumnStorage); + } + /* ALTER TABLE ALTER [COLUMN] DROP */ + else if (((pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "COLUMN") == 0) || + (pg_strcasecmp(PREV5_WD, "TABLE") == 0 && pg_strcasecmp(PREV3_WD, "ALTER") == 0)) && + pg_strcasecmp(PREV_WD, "DROP") == 0) { + static const char* const listColumnDrop[] = {"DEFAULT", "NOT NULL", NULL}; + + COMPLETE_WITH_LIST(listColumnDrop); + } else if (pg_strcasecmp(PREV3_WD, "TABLE") == 0 && pg_strcasecmp(PREV_WD, "CLUSTER") == 0) + COMPLETE_WITH_CONST("ON"); + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "CLUSTER") == 0 && + pg_strcasecmp(PREV_WD, "ON") == 0) { + completion_info_charp = PREV3_WD; + COMPLETE_WITH_QUERY(Query_for_index_of_table); + } + /* If we have TABLE SET, provide WITHOUT,TABLESPACE and SCHEMA */ + else if (pg_strcasecmp(PREV3_WD, "TABLE") == 0 && pg_strcasecmp(PREV_WD, "SET") == 0) { + static const char* const listTableSet[] = {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listTableSet); + } + /* If we have TABLE SET TABLESPACE provide a list of tablespaces */ + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "SET") == 0 && + pg_strcasecmp(PREV_WD, "TABLESPACE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); + /* If we have TABLE SET WITHOUT provide CLUSTER or OIDS */ + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "SET") == 0 && + pg_strcasecmp(PREV_WD, "WITHOUT") == 0) { + static const char* const listTableSet2[] = {"CLUSTER", "OIDS", NULL}; + + COMPLETE_WITH_LIST(listTableSet2); + } + /* ALTER TABLE RESET */ + else if (pg_strcasecmp(PREV3_WD, "TABLE") == 0 && pg_strcasecmp(PREV_WD, "RESET") == 0) + COMPLETE_WITH_CONST("("); + /* ALTER TABLE SET|RESET ( */ + else if (pg_strcasecmp(PREV4_WD, "TABLE") == 0 && + (pg_strcasecmp(PREV2_WD, "SET") == 0 || pg_strcasecmp(PREV2_WD, "RESET") == 0) && + pg_strcasecmp(PREV_WD, "(") == 0) { + static const char* const listTableOptions[] = { + "autovacuum_analyze_scale_factor", + "autovacuum_analyze_threshold", + "autovacuum_enabled", + "autovacuum_freeze_max_age", + "autovacuum_freeze_min_age", + "autovacuum_freeze_table_age", + "autovacuum_vacuum_cost_delay", + "autovacuum_vacuum_cost_limit", + "autovacuum_vacuum_scale_factor", + "autovacuum_vacuum_threshold", + "fillfactor", + "toast.autovacuum_enabled", + "toast.autovacuum_freeze_max_age", + "toast.autovacuum_freeze_min_age", + "toast.autovacuum_freeze_table_age", + "toast.autovacuum_vacuum_cost_delay", + "toast.autovacuum_vacuum_cost_limit", + "toast.autovacuum_vacuum_scale_factor", + "toast.autovacuum_vacuum_threshold", + NULL + }; + + COMPLETE_WITH_LIST(listTableOptions); + } + + /* ALTER TABLESPACE with RENAME TO, OWNER TO, SET, RESET */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "TABLESPACE") == 0) { + static const char* const listAlterTspc[] = {"RENAME TO", "OWNER TO", "SET", "RESET", NULL}; + + COMPLETE_WITH_LIST(listAlterTspc); + } + /* ALTER TABLESPACE SET|RESET */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TABLESPACE") == 0 && + (pg_strcasecmp(PREV_WD, "SET") == 0 || pg_strcasecmp(PREV_WD, "RESET") == 0)) + COMPLETE_WITH_CONST("("); + /* ALTER TABLESPACE SET|RESET ( */ + else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "TABLESPACE") == 0 && + (pg_strcasecmp(PREV2_WD, "SET") == 0 || pg_strcasecmp(PREV2_WD, "RESET") == 0) && + pg_strcasecmp(PREV_WD, "(") == 0) { + static const char* const listTablespaceOptions[] = {"seq_page_cost", "random_page_cost", NULL}; + + COMPLETE_WITH_LIST(listTablespaceOptions); + } + + /* ALTER TEXT SEARCH */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "TEXT") == 0 && + pg_strcasecmp(PREV_WD, "SEARCH") == 0) { + static const char* const listAlterTextSearch[] = {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(listAlterTextSearch); + } else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "TEXT") == 0 && + pg_strcasecmp(PREV3_WD, "SEARCH") == 0 && + (pg_strcasecmp(PREV2_WD, "TEMPLATE") == 0 || pg_strcasecmp(PREV2_WD, "PARSER") == 0)) { + static const char* const listAlterTextSearcH2[] = {"RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterTextSearcH2); + } else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "TEXT") == 0 && + pg_strcasecmp(PREV3_WD, "SEARCH") == 0 && pg_strcasecmp(PREV2_WD, "DICTIONARY") == 0) { + static const char* const listAlterTextSearch3[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(listAlterTextSearch3); + } else if (pg_strcasecmp(PREV5_WD, "ALTER") == 0 && pg_strcasecmp(PREV4_WD, "TEXT") == 0 && + pg_strcasecmp(PREV3_WD, "SEARCH") == 0 && pg_strcasecmp(PREV2_WD, "CONFIGURATION") == 0) { + static const char* const listAlterTextSearch4[] = { + "ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL + }; + + COMPLETE_WITH_LIST(listAlterTextSearch4); + } + + /* complete ALTER TYPE with actions */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "TYPE") == 0) { + static const char* const listAlterType[] = { + "ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE", + "OWNER TO", "RENAME", "SET SCHEMA", NULL + }; + + COMPLETE_WITH_LIST(listAlterType); + } + /* complete ALTER TYPE ADD with actions */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TYPE") == 0 && + pg_strcasecmp(PREV_WD, "ADD") == 0) { + static const char* const listAlterType[] = {"ATTRIBUTE", "VALUE", NULL}; + + COMPLETE_WITH_LIST(listAlterType); + } + /* ALTER TYPE RENAME */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "TYPE") == 0 && + pg_strcasecmp(PREV_WD, "RENAME") == 0) { + static const char* const listAlterType[] = {"ATTRIBUTE", "TO", NULL}; + + COMPLETE_WITH_LIST(listAlterType); + } + /* ALTER TYPE xxx RENAME ATTRIBUTE yyy */ + else if (pg_strcasecmp(PREV5_WD, "TYPE") == 0 && pg_strcasecmp(PREV3_WD, "RENAME") == 0 && + pg_strcasecmp(PREV2_WD, "ATTRIBUTE") == 0) + COMPLETE_WITH_CONST("TO"); + + /* + * If we have TYPE ALTER/DROP/RENAME ATTRIBUTE, provide list of + * attributes + */ + else if (pg_strcasecmp(PREV4_WD, "TYPE") == 0 && + (pg_strcasecmp(PREV2_WD, "ALTER") == 0 || pg_strcasecmp(PREV2_WD, "DROP") == 0 || + pg_strcasecmp(PREV2_WD, "RENAME") == 0) && + pg_strcasecmp(PREV_WD, "ATTRIBUTE") == 0) + COMPLETE_WITH_ATTR(PREV3_WD, ""); + /* ALTER TYPE ALTER ATTRIBUTE */ + else if ((pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "ATTRIBUTE") == 0)) { + COMPLETE_WITH_CONST("TYPE"); + } + /* complete ALTER GROUP */ + else if (pg_strcasecmp(PREV3_WD, "ALTER") == 0 && pg_strcasecmp(PREV2_WD, "GROUP") == 0) { + static const char* const listAlterGroup[] = {"ADD USER", "DROP USER", "RENAME TO", NULL}; + + COMPLETE_WITH_LIST(listAlterGroup); + } + /* complete ALTER GROUP ADD|DROP with USER */ + else if (pg_strcasecmp(PREV4_WD, "ALTER") == 0 && pg_strcasecmp(PREV3_WD, "GROUP") == 0 && + (pg_strcasecmp(PREV_WD, "ADD") == 0 || pg_strcasecmp(PREV_WD, "DROP") == 0)) + COMPLETE_WITH_CONST("USER"); + /* complete {ALTER} GROUP ADD|DROP USER with a user name */ + else if (pg_strcasecmp(PREV4_WD, "GROUP") == 0 && + (pg_strcasecmp(PREV2_WD, "ADD") == 0 || pg_strcasecmp(PREV2_WD, "DROP") == 0) && + pg_strcasecmp(PREV_WD, "USER") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + + /* BEGIN, END, ABORT */ + else if (pg_strcasecmp(PREV_WD, "BEGIN") == 0 || pg_strcasecmp(PREV_WD, "END") == 0 || + pg_strcasecmp(PREV_WD, "ABORT") == 0) { + static const char* const listTrans[] = {"WORK", "TRANSACTION", NULL}; + + COMPLETE_WITH_LIST(listTrans); + } + /* COMMIT */ + else if (pg_strcasecmp(PREV_WD, "COMMIT") == 0) { + static const char* const listCommit[] = {"WORK", "TRANSACTION", "PREPARED", NULL}; + + COMPLETE_WITH_LIST(listCommit); + } + /* RELEASE SAVEPOINT */ + else if (pg_strcasecmp(PREV_WD, "RELEASE") == 0) + COMPLETE_WITH_CONST("SAVEPOINT"); + /* ROLLBACK */ + else if (pg_strcasecmp(PREV_WD, "ROLLBACK") == 0) { + static const char* const listTrans[] = {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL}; + + COMPLETE_WITH_LIST(listTrans); + } + /* CLUSTER */ + + /* + * If the previous word is CLUSTER and not without produce list of tables + */ + else if (pg_strcasecmp(PREV_WD, "CLUSTER") == 0 && pg_strcasecmp(PREV2_WD, "WITHOUT") != 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + /* If we have CLUSTER , then add "USING" */ + else if (pg_strcasecmp(PREV2_WD, "CLUSTER") == 0 && pg_strcasecmp(PREV_WD, "ON") != 0) { + COMPLETE_WITH_CONST("USING"); + } + + /* + * If we have CLUSTER USING, then add the index as well. + */ + else if (pg_strcasecmp(PREV3_WD, "CLUSTER") == 0 && pg_strcasecmp(PREV_WD, "USING") == 0) { + completion_info_charp = PREV2_WD; + COMPLETE_WITH_QUERY(Query_for_index_of_table); + } + + /* COMMENT */ + else if (pg_strcasecmp(PREV_WD, "COMMENT") == 0) + COMPLETE_WITH_CONST("ON"); + else if (pg_strcasecmp(PREV2_WD, "COMMENT") == 0 && pg_strcasecmp(PREV_WD, "ON") == 0) { + static const char* const listComment[] = { + "CAST", "COLLATION", "CONVERSION", "DATABASE", "EXTENSION", + "FOREIGN DATA WRAPPER", "FOREIGN TABLE", + "SERVER", "INDEX", "LANGUAGE", "RULE", "SCHEMA", "SEQUENCE", + "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", + "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", + "TABLESPACE", "TEXT SEARCH", "ROLE", NULL + }; + + COMPLETE_WITH_LIST(listComment); + } else if (pg_strcasecmp(PREV3_WD, "COMMENT") == 0 && pg_strcasecmp(PREV2_WD, "ON") == 0 && + pg_strcasecmp(PREV_WD, "FOREIGN") == 0) { + static const char* const listTrans2[] = {"DATA WRAPPER", "TABLE", NULL}; + + COMPLETE_WITH_LIST(listTrans2); + } else if (pg_strcasecmp(PREV4_WD, "COMMENT") == 0 && pg_strcasecmp(PREV3_WD, "ON") == 0 && + pg_strcasecmp(PREV2_WD, "TEXT") == 0 && pg_strcasecmp(PREV_WD, "SEARCH") == 0) { + static const char* const listTrans2[] = {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(listTrans2); + } else if (pg_strcasecmp(PREV4_WD, "COMMENT") == 0 && pg_strcasecmp(PREV3_WD, "ON") == 0 && + pg_strcasecmp(PREV2_WD, "MATERIALIZED") == 0 && pg_strcasecmp(PREV_WD, "VIEW") == 0) { + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + } else if ((pg_strcasecmp(PREV4_WD, "COMMENT") == 0 && pg_strcasecmp(PREV3_WD, "ON") == 0) || + (pg_strcasecmp(PREV5_WD, "COMMENT") == 0 && pg_strcasecmp(PREV4_WD, "ON") == 0) || + (pg_strcasecmp(PREV6_WD, "COMMENT") == 0 && pg_strcasecmp(PREV5_WD, "ON") == 0)) + COMPLETE_WITH_CONST("IS"); + + /* COPY */ + + /* + * If we have COPY [BINARY] (which you'd have to type yourself), offer + * list of tables (Also cover the analogous backslash command) + */ + else if (pg_strcasecmp(PREV_WD, "COPY") == 0 || pg_strcasecmp(PREV_WD, "\\copy") == 0 || + (pg_strcasecmp(PREV2_WD, "COPY") == 0 && pg_strcasecmp(PREV_WD, "BINARY") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + /* If we have COPY|BINARY , complete it with "TO" or "FROM" */ + else if (pg_strcasecmp(PREV2_WD, "COPY") == 0 || pg_strcasecmp(PREV2_WD, "\\copy") == 0 || + pg_strcasecmp(PREV2_WD, "BINARY") == 0) { + static const char* const listFromTo[] = {"FROM", "TO", NULL}; + + COMPLETE_WITH_LIST(listFromTo); + } + /* If we have COPY|BINARY FROM|TO, complete with filename */ + else if ((pg_strcasecmp(PREV3_WD, "COPY") == 0 || pg_strcasecmp(PREV3_WD, "\\copy") == 0 || + pg_strcasecmp(PREV3_WD, "BINARY") == 0) && + (pg_strcasecmp(PREV_WD, "FROM") == 0 || pg_strcasecmp(PREV_WD, "TO") == 0)) { + completion_charp = ""; + matches = completion_matches(text, CompleteFromFiles); + } + + /* Handle COPY|BINARY FROM|TO filename */ + else if ((pg_strcasecmp(PREV4_WD, "COPY") == 0 || pg_strcasecmp(PREV4_WD, "\\copy") == 0 || + pg_strcasecmp(PREV4_WD, "BINARY") == 0) && + (pg_strcasecmp(PREV2_WD, "FROM") == 0 || pg_strcasecmp(PREV2_WD, "TO") == 0)) { + static const char* const listCopy[] = {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", "ENCODING", NULL}; + + COMPLETE_WITH_LIST(listCopy); + } + + /* Handle COPY|BINARY FROM|TO filename CSV */ + else if (pg_strcasecmp(PREV_WD, "CSV") == 0 && + (pg_strcasecmp(PREV3_WD, "FROM") == 0 || pg_strcasecmp(PREV3_WD, "TO") == 0)) { + static const char* const listCsv[] = {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "FORCE NOT NULL", NULL}; + + COMPLETE_WITH_LIST(listCsv); + } + + /* CREATE DATABASE */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "DATABASE") == 0) { + static const char* const listDatabase[] = { + "OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT", NULL + }; + + COMPLETE_WITH_LIST(listDatabase); + } else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && pg_strcasecmp(PREV3_WD, "DATABASE") == 0 && + pg_strcasecmp(PREV_WD, "TEMPLATE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); + + /* CREATE EXTENSION */ + /* Complete with available extensions rather than installed ones. */ + else if (pg_strcasecmp(PREV2_WD, "CREATE") == 0 && pg_strcasecmp(PREV_WD, "EXTENSION") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions); + /* CREATE EXTENSION */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "EXTENSION") == 0) + COMPLETE_WITH_CONST("WITH SCHEMA"); + + /* CREATE FOREIGN */ + else if (pg_strcasecmp(PREV2_WD, "CREATE") == 0 && pg_strcasecmp(PREV_WD, "FOREIGN") == 0) { + static const char* const listCreateForeign[] = {"DATA WRAPPER", "TABLE", NULL}; + + COMPLETE_WITH_LIST(listCreateForeign); + } + + /* CREATE FOREIGN DATA WRAPPER */ + else if (pg_strcasecmp(PREV5_WD, "CREATE") == 0 && pg_strcasecmp(PREV4_WD, "FOREIGN") == 0 && + pg_strcasecmp(PREV3_WD, "DATA") == 0 && pg_strcasecmp(PREV2_WD, "WRAPPER") == 0) { + static const char* const listCreateForeignDataWrapper[] = {"HANDLER", "VALIDATOR", NULL}; + + COMPLETE_WITH_LIST(listCreateForeignDataWrapper); + } + + /* CREATE INDEX */ + /* First off we complete CREATE UNIQUE with "INDEX" */ + else if (pg_strcasecmp(PREV2_WD, "CREATE") == 0 && pg_strcasecmp(PREV_WD, "UNIQUE") == 0) + COMPLETE_WITH_CONST("INDEX"); + /* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */ + else if (pg_strcasecmp(PREV_WD, "INDEX") == 0 && + (pg_strcasecmp(PREV2_WD, "CREATE") == 0 || pg_strcasecmp(PREV2_WD, "UNIQUE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, " UNION SELECT 'ON'" + " UNION SELECT 'CONCURRENTLY'"); + /* Complete ... INDEX [] ON with a list of tables */ + else if ((pg_strcasecmp(PREV3_WD, "INDEX") == 0 || pg_strcasecmp(PREV2_WD, "INDEX") == 0 || + pg_strcasecmp(PREV2_WD, "CONCURRENTLY") == 0) && + pg_strcasecmp(PREV_WD, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + /* If we have CREATE|UNIQUE INDEX CONCURRENTLY, then add "ON" */ + else if ((pg_strcasecmp(PREV3_WD, "INDEX") == 0 || pg_strcasecmp(PREV2_WD, "INDEX") == 0) && + pg_strcasecmp(PREV_WD, "CONCURRENTLY") == 0) + COMPLETE_WITH_CONST("ON"); + /* If we have CREATE|UNIQUE INDEX , then add "ON" or "CONCURRENTLY" */ + else if ((pg_strcasecmp(PREV3_WD, "CREATE") == 0 || pg_strcasecmp(PREV3_WD, "UNIQUE") == 0) && + pg_strcasecmp(PREV2_WD, "INDEX") == 0) { + static const char* const listCreateIndex[] = {"CONCURRENTLY", "ON", NULL}; + + COMPLETE_WITH_LIST(listCreateIndex); + } + + /* + * Complete INDEX ON with a list of table columns (which + * should really be in parens) + */ + else if ((pg_strcasecmp(PREV4_WD, "INDEX") == 0 || pg_strcasecmp(PREV3_WD, "INDEX") == 0 || + pg_strcasecmp(PREV3_WD, "CONCURRENTLY") == 0) && + pg_strcasecmp(PREV2_WD, "ON") == 0) { + static const char* const listCreateIndex2[] = {"(", "USING", NULL}; + + COMPLETE_WITH_LIST(listCreateIndex2); + } else if ((pg_strcasecmp(PREV5_WD, "INDEX") == 0 || pg_strcasecmp(PREV4_WD, "INDEX") == 0 || + pg_strcasecmp(PREV4_WD, "CONCURRENTLY") == 0) && + pg_strcasecmp(PREV3_WD, "ON") == 0 && pg_strcasecmp(PREV_WD, "(") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, ""); + /* same if you put in USING */ + else if (pg_strcasecmp(PREV5_WD, "ON") == 0 && pg_strcasecmp(PREV3_WD, "USING") == 0 && + pg_strcasecmp(PREV_WD, "(") == 0) + COMPLETE_WITH_ATTR(PREV4_WD, ""); + /* Complete USING with an index method */ + else if (pg_strcasecmp(PREV_WD, "USING") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_access_methods); + else if (pg_strcasecmp(PREV4_WD, "ON") == 0 && pg_strcasecmp(PREV2_WD, "USING") == 0) + COMPLETE_WITH_CONST("("); + + /* CREATE RULE */ + /* Complete "CREATE RULE " with "AS" */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "RULE") == 0) + COMPLETE_WITH_CONST("AS"); + /* Complete "CREATE RULE AS with "ON" */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && pg_strcasecmp(PREV3_WD, "RULE") == 0 && + pg_strcasecmp(PREV_WD, "AS") == 0) + COMPLETE_WITH_CONST("ON"); + /* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */ + else if (pg_strcasecmp(PREV4_WD, "RULE") == 0 && pg_strcasecmp(PREV2_WD, "AS") == 0 && + pg_strcasecmp(PREV_WD, "ON") == 0) { + static const char* const ruleEvents[] = {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; + + COMPLETE_WITH_LIST(ruleEvents); + } + /* Complete "AS ON " with a "TO" */ + else if (pg_strcasecmp(PREV3_WD, "AS") == 0 && pg_strcasecmp(PREV2_WD, "ON") == 0 && + (pg_toupper((unsigned char)PREV_WD[4]) == 'T' || pg_toupper((unsigned char)PREV_WD[5]) == 'T')) + COMPLETE_WITH_CONST("TO"); + /* Complete "AS ON TO" with a table name */ + else if (pg_strcasecmp(PREV4_WD, "AS") == 0 && pg_strcasecmp(PREV3_WD, "ON") == 0 && + pg_strcasecmp(PREV_WD, "TO") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + + /* CREATE SERVER */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "SERVER") == 0) { + static const char* const list_CREATE_SERVER[] = {"TYPE", "VERSION", "FOREIGN DATA WRAPPER", NULL}; + + COMPLETE_WITH_LIST(list_CREATE_SERVER); + } + + /* CREATE TABLE */ + /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */ + else if (pg_strcasecmp(PREV2_WD, "CREATE") == 0 && + (pg_strcasecmp(PREV_WD, "TEMP") == 0 || pg_strcasecmp(PREV_WD, "TEMPORARY") == 0)) { + static const char* const listTemp[] = {"SEQUENCE", "TABLE", "VIEW", NULL}; + + COMPLETE_WITH_LIST(listTemp); + } + /* Complete "CREATE UNLOGGED" with TABLE */ + else if (pg_strcasecmp(PREV2_WD, "CREATE") == 0 && pg_strcasecmp(PREV_WD, "UNLOGGED") == 0) { + COMPLETE_WITH_CONST("TABLE"); + } + + /* CREATE TABLESPACE */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "TABLESPACE") == 0) { + static const char* const listCreateTablespace[] = {"OWNER", "LOCATION", NULL}; + + COMPLETE_WITH_LIST(listCreateTablespace); + } + /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */ + else if (pg_strcasecmp(PREV5_WD, "CREATE") == 0 && pg_strcasecmp(PREV4_WD, "TABLESPACE") == 0 && + pg_strcasecmp(PREV2_WD, "OWNER") == 0) { + COMPLETE_WITH_CONST("LOCATION"); + } + + /* CREATE TEXT SEARCH */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "TEXT") == 0 && + pg_strcasecmp(PREV_WD, "SEARCH") == 0) { + static const char* const listCreateTextSearch[] = {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(listCreateTextSearch); + } else if (pg_strcasecmp(PREV4_WD, "TEXT") == 0 && pg_strcasecmp(PREV3_WD, "SEARCH") == 0 && + pg_strcasecmp(PREV2_WD, "CONFIGURATION") == 0) + COMPLETE_WITH_CONST("("); + + /* CREATE TRIGGER */ + /* complete CREATE TRIGGER with BEFORE,AFTER */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "TRIGGER") == 0) { + static const char* const listCreateTrigger[] = {"BEFORE", "AFTER", "INSTEAD OF", NULL}; + + COMPLETE_WITH_LIST(listCreateTrigger); + } + /* complete CREATE TRIGGER BEFORE,AFTER with an event */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && pg_strcasecmp(PREV3_WD, "TRIGGER") == 0 && + (pg_strcasecmp(PREV_WD, "BEFORE") == 0 || pg_strcasecmp(PREV_WD, "AFTER") == 0)) { + static const char* const listCreateTriggerEvents[] = {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL}; + + COMPLETE_WITH_LIST(listCreateTriggerEvents); + } + /* complete CREATE TRIGGER INSTEAD OF with an event */ + else if (pg_strcasecmp(PREV5_WD, "CREATE") == 0 && pg_strcasecmp(PREV4_WD, "TRIGGER") == 0 && + pg_strcasecmp(PREV2_WD, "INSTEAD") == 0 && pg_strcasecmp(PREV_WD, "OF") == 0) { + static const char* const listCreateTriggerEvents[] = {"INSERT", "DELETE", "UPDATE", NULL}; + + COMPLETE_WITH_LIST(listCreateTriggerEvents); + } + /* complete CREATE TRIGGER BEFORE,AFTER sth with OR,ON */ + else if ((pg_strcasecmp(PREV5_WD, "CREATE") == 0 && pg_strcasecmp(PREV4_WD, "TRIGGER") == 0 && + (pg_strcasecmp(PREV2_WD, "BEFORE") == 0 || pg_strcasecmp(PREV2_WD, "AFTER") == 0)) || + (pg_strcasecmp(PREV5_WD, "TRIGGER") == 0 && pg_strcasecmp(PREV3_WD, "INSTEAD") == 0 && + pg_strcasecmp(PREV2_WD, "OF") == 0)) { + static const char* const listCreateTrigger2[] = {"ON", "OR", NULL}; + + COMPLETE_WITH_LIST(listCreateTrigger2); + } + + /* + * complete CREATE TRIGGER BEFORE,AFTER event ON with a list of + * tables + */ + else if (pg_strcasecmp(PREV5_WD, "TRIGGER") == 0 && + (pg_strcasecmp(PREV3_WD, "BEFORE") == 0 || pg_strcasecmp(PREV3_WD, "AFTER") == 0) && + pg_strcasecmp(PREV_WD, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + /* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */ + else if (pg_strcasecmp(PREV4_WD, "INSTEAD") == 0 && pg_strcasecmp(PREV3_WD, "OF") == 0 && + pg_strcasecmp(PREV_WD, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + /* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */ + else if (pg_strcasecmp(PREV_WD, "EXECUTE") == 0 && PREV2_WD[0] != '\0') + COMPLETE_WITH_CONST("PROCEDURE"); + + /* CREATE ROLE,USER,GROUP */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && + !(pg_strcasecmp(PREV2_WD, "USER") == 0 && pg_strcasecmp(PREV_WD, "MAPPING") == 0) && + (pg_strcasecmp(PREV2_WD, "ROLE") == 0 || pg_strcasecmp(PREV2_WD, "GROUP") == 0 || + pg_strcasecmp(PREV2_WD, "USER") == 0)) { + static const char* const listCreateRole[] = { + "ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER", + "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB", + "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN", + "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE", + "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL + }; + + COMPLETE_WITH_LIST(listCreateRole); + } + + /* CREATE ROLE,USER,GROUP WITH */ + else if ((pg_strcasecmp(PREV4_WD, "CREATE") == 0 && + (pg_strcasecmp(PREV3_WD, "ROLE") == 0 || pg_strcasecmp(PREV3_WD, "GROUP") == 0 || + pg_strcasecmp(PREV3_WD, "USER") == 0) && + pg_strcasecmp(PREV_WD, "WITH") == 0)) { + /* Similar to the above, but don't complete "WITH" again. */ + static const char* const listCreateRoleWith[] = { + "ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER", + "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB", + "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN", + "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE", + "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", NULL + }; + + COMPLETE_WITH_LIST(listCreateRoleWith); + } + + /* + * complete CREATE ROLE,USER,GROUP ENCRYPTED,UNENCRYPTED with + * PASSWORD + */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && + (pg_strcasecmp(PREV3_WD, "ROLE") == 0 || pg_strcasecmp(PREV3_WD, "GROUP") == 0 || + pg_strcasecmp(PREV3_WD, "USER") == 0) && + (pg_strcasecmp(PREV_WD, "ENCRYPTED") == 0 || pg_strcasecmp(PREV_WD, "UNENCRYPTED") == 0)) { + COMPLETE_WITH_CONST("PASSWORD"); + } + /* complete CREATE ROLE,USER,GROUP IN with ROLE,GROUP */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && + (pg_strcasecmp(PREV3_WD, "ROLE") == 0 || pg_strcasecmp(PREV3_WD, "GROUP") == 0 || + pg_strcasecmp(PREV3_WD, "USER") == 0) && + pg_strcasecmp(PREV_WD, "IN") == 0) { + static const char* const listCreateRole3[] = {"GROUP", "ROLE", NULL}; + + COMPLETE_WITH_LIST(listCreateRole3); + } + + /* CREATE VIEW */ + /* Complete CREATE VIEW with AS */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") == 0 && pg_strcasecmp(PREV2_WD, "VIEW") == 0) + COMPLETE_WITH_CONST("AS"); + /* Complete "CREATE VIEW AS with "SELECT" */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && pg_strcasecmp(PREV3_WD, "VIEW") == 0 && + pg_strcasecmp(PREV_WD, "AS") == 0) + COMPLETE_WITH_CONST("SELECT"); + + /* CREATE MATERIALIZED VIEW */ + else if (pg_strcasecmp(PREV2_WD, "CREATE") == 0 && pg_strcasecmp(PREV_WD, "MATERIALIZED") == 0) + COMPLETE_WITH_CONST("VIEW"); + /* Complete CREATE MATERIALIZED VIEW with AS */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && pg_strcasecmp(PREV3_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV2_WD, "VIEW") == 0) + COMPLETE_WITH_CONST("AS"); + /* Complete "CREATE MATERIALIZED VIEW AS with "SELECT" */ + else if (pg_strcasecmp(PREV5_WD, "CREATE") == 0 && pg_strcasecmp(PREV4_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV3_WD, "VIEW") == 0 && pg_strcasecmp(PREV_WD, "AS") == 0) + COMPLETE_WITH_CONST("SELECT"); + + /* DECLARE */ + else if (pg_strcasecmp(PREV2_WD, "DECLARE") == 0) { + static const char* const listDeclare[] = {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL}; + + COMPLETE_WITH_LIST(listDeclare); + } + + /* CURSOR */ + else if (pg_strcasecmp(PREV_WD, "CURSOR") == 0) { + static const char* const listDeclareCursor[] = {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL}; + + COMPLETE_WITH_LIST(listDeclareCursor); + } + + /* DELETE */ + + /* + * Complete DELETE with FROM (only if the word before that is not "ON" + * (cf. rules) or "BEFORE" or "AFTER" (cf. triggers) or GRANT) + */ + else if (pg_strcasecmp(PREV_WD, "DELETE") == 0 && + !(pg_strcasecmp(PREV2_WD, "ON") == 0 || pg_strcasecmp(PREV2_WD, "GRANT") == 0 || + pg_strcasecmp(PREV2_WD, "BEFORE") == 0 || pg_strcasecmp(PREV2_WD, "AFTER") == 0)) + COMPLETE_WITH_CONST("FROM"); + /* Complete DELETE FROM with a list of tables */ + else if (pg_strcasecmp(PREV2_WD, "DELETE") == 0 && pg_strcasecmp(PREV_WD, "FROM") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_deletables, NULL); + /* Complete DELETE FROM
*/ + else if (pg_strcasecmp(PREV3_WD, "DELETE") == 0 && pg_strcasecmp(PREV2_WD, "FROM") == 0) { + static const char* const listDelete[] = {"USING", "WHERE", "SET", NULL}; + + COMPLETE_WITH_LIST(listDelete); + } + /* XXX: implement tab completion for DELETE ... USING */ + + /* DISCARD */ + else if (pg_strcasecmp(PREV_WD, "DISCARD") == 0) { + static const char* const listDiscard[] = {"ALL", "PLANS", "TEMP", NULL}; + + COMPLETE_WITH_LIST(listDiscard); + } + + /* DO */ + + /* + * Complete DO with LANGUAGE. + */ + else if (pg_strcasecmp(PREV_WD, "DO") == 0) { + static const char* const listDo[] = {"LANGUAGE", NULL}; + + COMPLETE_WITH_LIST(listDo); + } + + /* DROP (when not the previous word) */ + /* DROP AGGREGATE */ + else if (pg_strcasecmp(PREV3_WD, "DROP") == 0 && pg_strcasecmp(PREV2_WD, "AGGREGATE") == 0) + COMPLETE_WITH_CONST("("); + + /* DROP object with CASCADE / RESTRICT */ + else if ((pg_strcasecmp(PREV3_WD, "DROP") == 0 && (pg_strcasecmp(PREV2_WD, "COLLATION") == 0 || + pg_strcasecmp(PREV2_WD, "CONVERSION") == 0 || pg_strcasecmp(PREV2_WD, "DOMAIN") == 0 || + pg_strcasecmp(PREV2_WD, "EXTENSION") == 0 || pg_strcasecmp(PREV2_WD, "FUNCTION") == 0 || + pg_strcasecmp(PREV2_WD, "INDEX") == 0 || pg_strcasecmp(PREV2_WD, "LANGUAGE") == 0 || + pg_strcasecmp(PREV2_WD, "SCHEMA") == 0 || pg_strcasecmp(PREV2_WD, "SEQUENCE") == 0 || + pg_strcasecmp(PREV2_WD, "SERVER") == 0 || pg_strcasecmp(PREV2_WD, "TABLE") == 0 || + pg_strcasecmp(PREV2_WD, "TYPE") == 0 || pg_strcasecmp(PREV2_WD, "VIEW") == 0)) || + (pg_strcasecmp(PREV4_WD, "DROP") == 0 && pg_strcasecmp(PREV3_WD, "AGGREGATE") == 0 && + PREV_WD[strlen(PREV_WD) - 1] == ')') || + (pg_strcasecmp(PREV5_WD, "DROP") == 0 && pg_strcasecmp(PREV4_WD, "FOREIGN") == 0 && + pg_strcasecmp(PREV3_WD, "DATA") == 0 && pg_strcasecmp(PREV2_WD, "WRAPPER") == 0) || + (pg_strcasecmp(PREV5_WD, "DROP") == 0 && pg_strcasecmp(PREV4_WD, "TEXT") == 0 && + pg_strcasecmp(PREV3_WD, "SEARCH") == 0 && + (pg_strcasecmp(PREV2_WD, "CONFIGURATION") == 0 || pg_strcasecmp(PREV2_WD, "DICTIONARY") == 0 || + pg_strcasecmp(PREV2_WD, "PARSER") == 0 || pg_strcasecmp(PREV2_WD, "TEMPLATE") == 0))) { + if (pg_strcasecmp(PREV3_WD, "DROP") == 0 && pg_strcasecmp(PREV2_WD, "FUNCTION") == 0) { + COMPLETE_WITH_CONST("("); + } else { + static const char* const listDropCr[] = {"CASCADE", "RESTRICT", NULL}; + + COMPLETE_WITH_LIST(listDropCr); + } + } else if (pg_strcasecmp(PREV2_WD, "DROP") == 0 && pg_strcasecmp(PREV_WD, "FOREIGN") == 0) { + static const char* const dropCreateForeign[] = {"DATA WRAPPER", "TABLE", NULL}; + + COMPLETE_WITH_LIST(dropCreateForeign); + /* DROP MATERIALIZED VIEW */ + } else if (pg_strcasecmp(PREV2_WD, "DROP") == 0 && pg_strcasecmp(PREV_WD, "MATERIALIZED") == 0) { + COMPLETE_WITH_CONST("VIEW"); + } else if (pg_strcasecmp(PREV3_WD, "DROP") == 0 && pg_strcasecmp(PREV2_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV_WD, "VIEW") == 0) { + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + } else if (pg_strcasecmp(PREV4_WD, "DROP") == 0 && + (pg_strcasecmp(PREV3_WD, "AGGREGATE") == 0 || pg_strcasecmp(PREV3_WD, "FUNCTION") == 0) && + pg_strcasecmp(PREV_WD, "(") == 0) { + size_t tmpLength = strlen(Query_for_list_of_arguments) + strlen(PREV2_WD); + char *tmpBuf = (char *)pg_malloc(tmpLength); + + int rc = sprintf_s(tmpBuf, tmpLength, Query_for_list_of_arguments, PREV2_WD); + securec_check_ss_c(rc,"",""); + COMPLETE_WITH_QUERY(tmpBuf); + free(tmpBuf); + } + /* DROP OWNED BY */ + else if (pg_strcasecmp(PREV2_WD, "DROP") == 0 && pg_strcasecmp(PREV_WD, "OWNED") == 0) + COMPLETE_WITH_CONST("BY"); + else if (pg_strcasecmp(PREV3_WD, "DROP") == 0 && pg_strcasecmp(PREV2_WD, "OWNED") == 0 && + pg_strcasecmp(PREV_WD, "BY") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if (pg_strcasecmp(PREV3_WD, "DROP") == 0 && pg_strcasecmp(PREV2_WD, "TEXT") == 0 && + pg_strcasecmp(PREV_WD, "SEARCH") == 0) { + static const char* const listAlterTextSearch[] = {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL}; + + COMPLETE_WITH_LIST(listAlterTextSearch); + } + + /* EXECUTE, but not EXECUTE embedded in other commands */ + else if (pg_strcasecmp(PREV_WD, "EXECUTE") == 0 && PREV2_WD[0] == '\0') + COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements); + + /* EXPLAIN */ + + /* + * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands + */ + else if (pg_strcasecmp(PREV_WD, "EXPLAIN") == 0) { + static const char* const listExplain[] = { + "SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL + }; + + COMPLETE_WITH_LIST(listExplain); + } else if (pg_strcasecmp(PREV2_WD, "EXPLAIN") == 0 && pg_strcasecmp(PREV_WD, "ANALYZE") == 0) { + static const char* const listExplain[] = {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL}; + + COMPLETE_WITH_LIST(listExplain); + } else if ((pg_strcasecmp(PREV2_WD, "EXPLAIN") == 0 && pg_strcasecmp(PREV_WD, "VERBOSE") == 0) || + (pg_strcasecmp(PREV3_WD, "EXPLAIN") == 0 && pg_strcasecmp(PREV2_WD, "ANALYZE") == 0 && + pg_strcasecmp(PREV_WD, "VERBOSE") == 0)) { + static const char* const listExplain[] = {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL}; + + COMPLETE_WITH_LIST(listExplain); + } + + /* FETCH && MOVE */ + /* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */ + else if (pg_strcasecmp(PREV_WD, "FETCH") == 0 || pg_strcasecmp(PREV_WD, "MOVE") == 0) { + static const char* const listFetch1[] = {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL}; + + COMPLETE_WITH_LIST(listFetch1); + } + /* Complete FETCH with one of ALL, NEXT, PRIOR */ + else if (pg_strcasecmp(PREV2_WD, "FETCH") == 0 || pg_strcasecmp(PREV2_WD, "MOVE") == 0) { + static const char* const list_FETCH2[] = {"ALL", "NEXT", "PRIOR", NULL}; + + COMPLETE_WITH_LIST(list_FETCH2); + } + + /* + * Complete FETCH with "FROM" or "IN". These are equivalent, + * but we may as well tab-complete both: perhaps some users prefer one + * variant or the other. + */ + else if (pg_strcasecmp(PREV3_WD, "FETCH") == 0 || pg_strcasecmp(PREV3_WD, "MOVE") == 0) { + static const char* const listFromin[] = {"FROM", "IN", NULL}; + + COMPLETE_WITH_LIST(listFromin); + } + + /* FOREIGN DATA WRAPPER */ + /* applies in ALTER/DROP FDW and in CREATE SERVER */ + else if (pg_strcasecmp(PREV4_WD, "CREATE") != 0 && pg_strcasecmp(PREV3_WD, "FOREIGN") == 0 && + pg_strcasecmp(PREV2_WD, "DATA") == 0 && pg_strcasecmp(PREV_WD, "WRAPPER") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_fdws); + + /* FOREIGN TABLE */ + else if (pg_strcasecmp(PREV3_WD, "CREATE") != 0 && pg_strcasecmp(PREV2_WD, "FOREIGN") == 0 && + pg_strcasecmp(PREV_WD, "TABLE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL); + + /* GRANT && REVOKE */ + /* Complete GRANT/REVOKE with a list of roles and privileges */ + else if (pg_strcasecmp(PREV_WD, "GRANT") == 0 || pg_strcasecmp(PREV_WD, "REVOKE") == 0) { + COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'SELECT'" + " UNION SELECT 'INSERT'" + " UNION SELECT 'UPDATE'" + " UNION SELECT 'DELETE'" + " UNION SELECT 'TRUNCATE'" + " UNION SELECT 'REFERENCES'" + " UNION SELECT 'TRIGGER'" + " UNION SELECT 'CREATE'" + " UNION SELECT 'CONNECT'" + " UNION SELECT 'TEMPORARY'" + " UNION SELECT 'EXECUTE'" + " UNION SELECT 'USAGE'" + " UNION SELECT 'ALL'"); + } + + /* + * Complete GRANT/REVOKE with "ON", GRANT/REVOKE with + * TO/FROM + */ + else if (pg_strcasecmp(PREV2_WD, "GRANT") == 0 || pg_strcasecmp(PREV2_WD, "REVOKE") == 0) { + if (pg_strcasecmp(PREV_WD, "SELECT") == 0 || pg_strcasecmp(PREV_WD, "INSERT") == 0 || + pg_strcasecmp(PREV_WD, "UPDATE") == 0 || pg_strcasecmp(PREV_WD, "DELETE") == 0 || + pg_strcasecmp(PREV_WD, "TRUNCATE") == 0 || pg_strcasecmp(PREV_WD, "REFERENCES") == 0 || + pg_strcasecmp(PREV_WD, "TRIGGER") == 0 || pg_strcasecmp(PREV_WD, "CREATE") == 0 || + pg_strcasecmp(PREV_WD, "CONNECT") == 0 || pg_strcasecmp(PREV_WD, "TEMPORARY") == 0 || + pg_strcasecmp(PREV_WD, "TEMP") == 0 || pg_strcasecmp(PREV_WD, "EXECUTE") == 0 || + pg_strcasecmp(PREV_WD, "USAGE") == 0 || pg_strcasecmp(PREV_WD, "ALL") == 0) + COMPLETE_WITH_CONST("ON"); + else { + if (pg_strcasecmp(PREV2_WD, "GRANT") == 0) + COMPLETE_WITH_CONST("TO"); + else + COMPLETE_WITH_CONST("FROM"); + } + } + + /* + * Complete GRANT/REVOKE ON with a list of tables, views, sequences, + * and indexes + * + * keywords DATABASE, FUNCTION, LANGUAGE, SCHEMA added to query result via + * UNION; seems to work intuitively + * + * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented + * here will only work if the privilege list contains exactly one + * privilege + */ + else if ((pg_strcasecmp(PREV3_WD, "GRANT") == 0 || pg_strcasecmp(PREV3_WD, "REVOKE") == 0) && + pg_strcasecmp(PREV_WD, "ON") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, " UNION SELECT 'DATABASE'" + " UNION SELECT 'DOMAIN'" + " UNION SELECT 'FOREIGN DATA WRAPPER'" + " UNION SELECT 'FOREIGN SERVER'" + " UNION SELECT 'FUNCTION'" + " UNION SELECT 'LANGUAGE'" + " UNION SELECT 'LARGE OBJECT'" + " UNION SELECT 'SCHEMA'" + " UNION SELECT 'TABLESPACE'" + " UNION SELECT 'TYPE'"); + else if ((pg_strcasecmp(PREV4_WD, "GRANT") == 0 || pg_strcasecmp(PREV4_WD, "REVOKE") == 0) && + pg_strcasecmp(PREV2_WD, "ON") == 0 && pg_strcasecmp(PREV_WD, "FOREIGN") == 0) { + static const char* const listPrivilegeForeign[] = {"DATA WRAPPER", "SERVER", NULL}; + + COMPLETE_WITH_LIST(listPrivilegeForeign); + } + + /* Complete "GRANT/REVOKE * ON * " with "TO/FROM" */ + else if ((pg_strcasecmp(PREV4_WD, "GRANT") == 0 || pg_strcasecmp(PREV4_WD, "REVOKE") == 0) && + pg_strcasecmp(PREV2_WD, "ON") == 0) { + if (pg_strcasecmp(PREV_WD, "DATABASE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + else if (pg_strcasecmp(PREV_WD, "DOMAIN") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + else if (pg_strcasecmp(PREV_WD, "FUNCTION") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + else if (pg_strcasecmp(PREV_WD, "LANGUAGE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_languages); + else if (pg_strcasecmp(PREV_WD, "SCHEMA") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_schemas); + else if (pg_strcasecmp(PREV_WD, "TABLESPACE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); + else if (pg_strcasecmp(PREV_WD, "TYPE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + else if (pg_strcasecmp(PREV4_WD, "GRANT") == 0) + COMPLETE_WITH_CONST("TO"); + else + COMPLETE_WITH_CONST("FROM"); + } + + /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */ + else if (pg_strcasecmp(PREV5_WD, "GRANT") == 0 && pg_strcasecmp(PREV3_WD, "ON") == 0) { + if (pg_strcasecmp(PREV_WD, "TO") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + else + COMPLETE_WITH_CONST("TO"); + } else if (pg_strcasecmp(PREV5_WD, "REVOKE") == 0 && pg_strcasecmp(PREV3_WD, "ON") == 0) { + if (pg_strcasecmp(PREV_WD, "FROM") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + else + COMPLETE_WITH_CONST("FROM"); + } + + /* Complete "GRANT/REVOKE * TO/FROM" with username, GROUP, or PUBLIC */ + else if (pg_strcasecmp(PREV3_WD, "GRANT") == 0 && pg_strcasecmp(PREV_WD, "TO") == 0) { + COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + } else if (pg_strcasecmp(PREV3_WD, "REVOKE") == 0 && pg_strcasecmp(PREV_WD, "FROM") == 0) { + COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles); + } + + /* GROUP BY */ + else if (pg_strcasecmp(PREV3_WD, "FROM") == 0 && pg_strcasecmp(PREV_WD, "GROUP") == 0) + COMPLETE_WITH_CONST("BY"); + + /* INSERT */ + /* Complete INSERT with "INTO" */ + else if (pg_strcasecmp(PREV_WD, "INSERT") == 0) + COMPLETE_WITH_CONST("INTO"); + /* Complete INSERT INTO with table names */ + else if (pg_strcasecmp(PREV2_WD, "INSERT") == 0 && pg_strcasecmp(PREV_WD, "INTO") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_insertables, NULL); + /* Complete "INSERT INTO
(" with attribute names */ + else if (pg_strcasecmp(PREV4_WD, "INSERT") == 0 && pg_strcasecmp(PREV3_WD, "INTO") == 0 && + pg_strcasecmp(PREV_WD, "(") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, ""); + + /* + * Complete INSERT INTO
with "(" or "VALUES" or "SELECT" or + * "TABLE" or "DEFAULT VALUES" + */ + else if (pg_strcasecmp(PREV3_WD, "INSERT") == 0 && pg_strcasecmp(PREV2_WD, "INTO") == 0) { + static const char* const listInsert[] = {"(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL}; + + COMPLETE_WITH_LIST(listInsert); + } + + /* + * Complete INSERT INTO
(attribs) with "VALUES" or "SELECT" or + * "TABLE" + */ + else if (pg_strcasecmp(PREV4_WD, "INSERT") == 0 && pg_strcasecmp(PREV3_WD, "INTO") == 0 && + PREV_WD[strlen(PREV_WD) - 1] == ')') { + static const char* const listInsert[] = {"SELECT", "TABLE", "VALUES", NULL}; + + COMPLETE_WITH_LIST(listInsert); + } + + /* Insert an open parenthesis after "VALUES" */ + else if (pg_strcasecmp(PREV_WD, "VALUES") == 0 && pg_strcasecmp(PREV2_WD, "DEFAULT") != 0) + COMPLETE_WITH_CONST("("); + + /* LOCK */ + /* Complete LOCK [TABLE] with a list of tables */ + else if (pg_strcasecmp(PREV_WD, "LOCK") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, " UNION SELECT 'TABLE'"); + else if (pg_strcasecmp(PREV_WD, "TABLE") == 0 && pg_strcasecmp(PREV2_WD, "LOCK") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, ""); + + /* For the following, handle the case of a single table only for now */ + + /* Complete LOCK [TABLE]
with "IN" */ + else if ((pg_strcasecmp(PREV2_WD, "LOCK") == 0 && pg_strcasecmp(PREV_WD, "TABLE") != 0) || + (pg_strcasecmp(PREV2_WD, "TABLE") == 0 && pg_strcasecmp(PREV3_WD, "LOCK") == 0)) + COMPLETE_WITH_CONST("IN"); + + /* Complete LOCK [TABLE]
IN with a lock mode */ + else if (pg_strcasecmp(PREV_WD, "IN") == 0 && (pg_strcasecmp(PREV3_WD, "LOCK") == 0 || + (pg_strcasecmp(PREV3_WD, "TABLE") == 0 && pg_strcasecmp(PREV4_WD, "LOCK") == 0))) { + static const char* const lockModes[] = { + "ACCESS SHARE MODE", + "ROW SHARE MODE", "ROW EXCLUSIVE MODE", + "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE", + "SHARE ROW EXCLUSIVE MODE", + "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE", NULL + }; + + COMPLETE_WITH_LIST(lockModes); + } + + /* NOTIFY */ + else if (pg_strcasecmp(PREV_WD, "NOTIFY") == 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'"); + + /* OPTIONS */ + else if (pg_strcasecmp(PREV_WD, "OPTIONS") == 0) + COMPLETE_WITH_CONST("("); + + /* OWNER TO - complete with available roles */ + else if (pg_strcasecmp(PREV2_WD, "OWNER") == 0 && pg_strcasecmp(PREV_WD, "TO") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + + /* ORDER BY */ + else if (pg_strcasecmp(PREV3_WD, "FROM") == 0 && pg_strcasecmp(PREV_WD, "ORDER") == 0) + COMPLETE_WITH_CONST("BY"); + else if (pg_strcasecmp(PREV4_WD, "FROM") == 0 && pg_strcasecmp(PREV2_WD, "ORDER") == 0 && + pg_strcasecmp(PREV_WD, "BY") == 0) + COMPLETE_WITH_ATTR(PREV3_WD, ""); + + /* PREPARE xx AS */ + else if (pg_strcasecmp(PREV_WD, "AS") == 0 && pg_strcasecmp(PREV3_WD, "PREPARE") == 0) { + static const char* const listPrepare[] = {"SELECT", "UPDATE", "INSERT", "DELETE", NULL}; + + COMPLETE_WITH_LIST(listPrepare); + } + + /* + * PREPARE TRANSACTION is missing on purpose. It's intended for transaction + * managers, not for manual use in interactive sessions. + */ + + /* REASSIGN OWNED BY xxx TO yyy */ + else if (pg_strcasecmp(PREV_WD, "REASSIGN") == 0) + COMPLETE_WITH_CONST("OWNED"); + else if (pg_strcasecmp(PREV_WD, "OWNED") == 0 && pg_strcasecmp(PREV2_WD, "REASSIGN") == 0) + COMPLETE_WITH_CONST("BY"); + else if (pg_strcasecmp(PREV_WD, "BY") == 0 && pg_strcasecmp(PREV2_WD, "OWNED") == 0 && + pg_strcasecmp(PREV3_WD, "REASSIGN") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if (pg_strcasecmp(PREV2_WD, "BY") == 0 && pg_strcasecmp(PREV3_WD, "OWNED") == 0 && + pg_strcasecmp(PREV4_WD, "REASSIGN") == 0) + COMPLETE_WITH_CONST("TO"); + else if (pg_strcasecmp(PREV_WD, "TO") == 0 && pg_strcasecmp(PREV3_WD, "BY") == 0 && + pg_strcasecmp(PREV4_WD, "OWNED") == 0 && pg_strcasecmp(PREV5_WD, "REASSIGN") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + + /* REFRESH MATERIALIZED VIEW */ + else if (pg_strcasecmp(PREV_WD, "REFRESH") == 0) + COMPLETE_WITH_CONST("MATERIALIZED VIEW"); + else if (pg_strcasecmp(PREV2_WD, "REFRESH") == 0 && pg_strcasecmp(PREV_WD, "MATERIALIZED") == 0) + COMPLETE_WITH_CONST("VIEW"); + else if (pg_strcasecmp(PREV3_WD, "REFRESH") == 0 && pg_strcasecmp(PREV2_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV_WD, "VIEW") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + else if (pg_strcasecmp(PREV4_WD, "REFRESH") == 0 && pg_strcasecmp(PREV3_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV2_WD, "VIEW") == 0) + COMPLETE_WITH_CONST("WITH"); + else if (pg_strcasecmp(PREV5_WD, "REFRESH") == 0 && pg_strcasecmp(PREV4_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV3_WD, "VIEW") == 0 && pg_strcasecmp(PREV_WD, "WITH") == 0) { + static const char *const listWithData[] = { + "NO DATA", "DATA", NULL}; + + COMPLETE_WITH_LIST(listWithData); + } else if (pg_strcasecmp(PREV6_WD, "REFRESH") == 0 && pg_strcasecmp(PREV5_WD, "MATERIALIZED") == 0 && + pg_strcasecmp(PREV4_WD, "VIEW") == 0 && pg_strcasecmp(PREV2_WD, "WITH") == 0 && + pg_strcasecmp(PREV_WD, "NO") == 0) + COMPLETE_WITH_CONST("DATA"); + + /* REINDEX */ + else if (pg_strcasecmp(PREV_WD, "REINDEX") == 0) { + static const char* const listReindex[] = {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL}; + + COMPLETE_WITH_LIST(listReindex); + } else if (pg_strcasecmp(PREV2_WD, "REINDEX") == 0) { + if (pg_strcasecmp(PREV_WD, "TABLE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + else if (pg_strcasecmp(PREV_WD, "INDEX") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + else if (pg_strcasecmp(PREV_WD, "SYSTEM") == 0 || pg_strcasecmp(PREV_WD, "DATABASE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } + + /* SECURITY LABEL */ + else if (pg_strcasecmp(PREV_WD, "SECURITY") == 0) + COMPLETE_WITH_CONST("LABEL"); + else if (pg_strcasecmp(PREV2_WD, "SECURITY") == 0 && pg_strcasecmp(PREV_WD, "LABEL") == 0) { + static const char* const listSecurityLabelPreposition[] = {"ON", "FOR"}; + + COMPLETE_WITH_LIST(listSecurityLabelPreposition); + } else if (pg_strcasecmp(PREV4_WD, "SECURITY") == 0 && pg_strcasecmp(PREV3_WD, "LABEL") == 0 && + pg_strcasecmp(PREV2_WD, "FOR") == 0) + COMPLETE_WITH_CONST("ON"); + else if ((pg_strcasecmp(PREV3_WD, "SECURITY") == 0 && pg_strcasecmp(PREV2_WD, "LABEL") == 0 && + pg_strcasecmp(PREV_WD, "ON") == 0) || + (pg_strcasecmp(PREV5_WD, "SECURITY") == 0 && pg_strcasecmp(PREV4_WD, "LABEL") == 0 && + pg_strcasecmp(PREV3_WD, "FOR") == 0 && pg_strcasecmp(PREV_WD, "ON") == 0)) { + static const char* const listSecurityLabel[] = { + "LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", + "AGGREGATE", "FUNCTION", "DOMAIN", "LARGE OBJECT", NULL + }; + + COMPLETE_WITH_LIST(listSecurityLabel); + } else if (pg_strcasecmp(PREV5_WD, "SECURITY") == 0 && pg_strcasecmp(PREV4_WD, "LABEL") == 0 && + pg_strcasecmp(PREV3_WD, "ON") == 0) + COMPLETE_WITH_CONST("IS"); + + /* SELECT */ + /* naah . . . */ + + /* SET, RESET, SHOW */ + /* Complete with a variable name */ + else if ((pg_strcasecmp(PREV_WD, "SET") == 0 && pg_strcasecmp(PREV3_WD, "UPDATE") != 0) || + pg_strcasecmp(PREV_WD, "RESET") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_set_vars); + else if (pg_strcasecmp(PREV_WD, "SHOW") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_show_vars); + /* Complete "SET TRANSACTION" */ + else if ((pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV_WD, "TRANSACTION") == 0) || + (pg_strcasecmp(PREV2_WD, "START") == 0 && pg_strcasecmp(PREV_WD, "TRANSACTION") == 0) || + (pg_strcasecmp(PREV2_WD, "BEGIN") == 0 && pg_strcasecmp(PREV_WD, "WORK") == 0) || + (pg_strcasecmp(PREV2_WD, "BEGIN") == 0 && pg_strcasecmp(PREV_WD, "TRANSACTION") == 0) || + (pg_strcasecmp(PREV4_WD, "SESSION") == 0 && pg_strcasecmp(PREV3_WD, "CHARACTERISTICS") == 0 && + pg_strcasecmp(PREV2_WD, "AS") == 0 && pg_strcasecmp(PREV_WD, "TRANSACTION") == 0)) { + static const char* const myList[] = {"ISOLATION LEVEL", "READ", NULL}; + + COMPLETE_WITH_LIST(myList); + } else if ((pg_strcasecmp(PREV3_WD, "SET") == 0 || pg_strcasecmp(PREV3_WD, "BEGIN") == 0 || + pg_strcasecmp(PREV3_WD, "START") == 0 || + (pg_strcasecmp(PREV4_WD, "CHARACTERISTICS") == 0 && pg_strcasecmp(PREV3_WD, "AS") == 0)) && + (pg_strcasecmp(PREV2_WD, "TRANSACTION") == 0 || pg_strcasecmp(PREV2_WD, "WORK") == 0) && + pg_strcasecmp(PREV_WD, "ISOLATION") == 0) + COMPLETE_WITH_CONST("LEVEL"); + else if ((pg_strcasecmp(PREV4_WD, "SET") == 0 || pg_strcasecmp(PREV4_WD, "BEGIN") == 0 || + pg_strcasecmp(PREV4_WD, "START") == 0 || pg_strcasecmp(PREV4_WD, "AS") == 0) && + (pg_strcasecmp(PREV3_WD, "TRANSACTION") == 0 || pg_strcasecmp(PREV3_WD, "WORK") == 0) && + pg_strcasecmp(PREV2_WD, "ISOLATION") == 0 && pg_strcasecmp(PREV_WD, "LEVEL") == 0) { + static const char* const myList[] = {"READ", "REPEATABLE", "SERIALIZABLE", NULL}; + + COMPLETE_WITH_LIST(myList); + } else if ((pg_strcasecmp(PREV4_WD, "TRANSACTION") == 0 || pg_strcasecmp(PREV4_WD, "WORK") == 0) && + pg_strcasecmp(PREV3_WD, "ISOLATION") == 0 && pg_strcasecmp(PREV2_WD, "LEVEL") == 0 && + pg_strcasecmp(PREV_WD, "READ") == 0) { + static const char* const myList[] = {"UNCOMMITTED", "COMMITTED", NULL}; + + COMPLETE_WITH_LIST(myList); + } else if ((pg_strcasecmp(PREV4_WD, "TRANSACTION") == 0 || pg_strcasecmp(PREV4_WD, "WORK") == 0) && + pg_strcasecmp(PREV3_WD, "ISOLATION") == 0 && pg_strcasecmp(PREV2_WD, "LEVEL") == 0 && + pg_strcasecmp(PREV_WD, "REPEATABLE") == 0) + COMPLETE_WITH_CONST("READ"); + else if ((pg_strcasecmp(PREV3_WD, "SET") == 0 || pg_strcasecmp(PREV3_WD, "BEGIN") == 0 || + pg_strcasecmp(PREV3_WD, "START") == 0 || pg_strcasecmp(PREV3_WD, "AS") == 0) && + (pg_strcasecmp(PREV2_WD, "TRANSACTION") == 0 || pg_strcasecmp(PREV2_WD, "WORK") == 0) && + pg_strcasecmp(PREV_WD, "READ") == 0) { + static const char* const myList[] = {"ONLY", "WRITE", NULL}; + + COMPLETE_WITH_LIST(myList); + } + /* Complete SET CONSTRAINTS with DEFERRED|IMMEDIATE */ + else if (pg_strcasecmp(PREV3_WD, "SET") == 0 && pg_strcasecmp(PREV2_WD, "CONSTRAINTS") == 0) { + static const char* const constraintList[] = {"DEFERRED", "IMMEDIATE", NULL}; + + COMPLETE_WITH_LIST(constraintList); + } + /* Complete SET ROLE */ + else if (pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV_WD, "ROLE") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */ + else if (pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV_WD, "SESSION") == 0) { + static const char* const myList[] = {"AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION", NULL}; + + COMPLETE_WITH_LIST(myList); + } + /* Complete SET SESSION AUTHORIZATION with username */ + else if (pg_strcasecmp(PREV3_WD, "SET") == 0 && pg_strcasecmp(PREV2_WD, "SESSION") == 0 && + pg_strcasecmp(PREV_WD, "AUTHORIZATION") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'"); + /* Complete RESET SESSION with AUTHORIZATION */ + else if (pg_strcasecmp(PREV2_WD, "RESET") == 0 && pg_strcasecmp(PREV_WD, "SESSION") == 0) + COMPLETE_WITH_CONST("AUTHORIZATION"); + /* Complete SET with "TO" */ + else if (pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV4_WD, "UPDATE") != 0 && + pg_strcasecmp(PREV_WD, "TABLESPACE") != 0 && pg_strcasecmp(PREV_WD, "SCHEMA") != 0 && + PREV_WD[strlen(PREV_WD) - 1] != ')' && pg_strcasecmp(PREV4_WD, "DOMAIN") != 0) + COMPLETE_WITH_CONST("TO"); + /* Suggest possible variable values */ + else if (pg_strcasecmp(PREV3_WD, "SET") == 0 && (pg_strcasecmp(PREV_WD, "TO") == 0 || strcmp(PREV_WD, "=") == 0)) { + if (pg_strcasecmp(PREV2_WD, "DateStyle") == 0) { + static const char* const myList[] = { + "ISO", "SQL", "Postgres", "German", + "YMD", "DMY", "MDY", + "US", "European", "NonEuropean", + "DEFAULT", NULL + }; + + COMPLETE_WITH_LIST(myList); + } else if (pg_strcasecmp(PREV2_WD, "IntervalStyle") == 0) { + static const char* const myList[] = {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL}; + + COMPLETE_WITH_LIST(myList); + } else if (pg_strcasecmp(PREV2_WD, "GEQO") == 0) { + static const char* const myList[] = {"ON", "OFF", "DEFAULT", NULL}; + + COMPLETE_WITH_LIST(myList); + } else { + static const char* const myList[] = {"DEFAULT", NULL}; + + COMPLETE_WITH_LIST(myList); + } + } + + /* START TRANSACTION */ + else if (pg_strcasecmp(PREV_WD, "START") == 0) + COMPLETE_WITH_CONST("TRANSACTION"); + + /* TABLE, but not TABLE embedded in other commands */ + else if (pg_strcasecmp(PREV_WD, "TABLE") == 0 && PREV2_WD[0] == '\0') + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + + /* TRUNCATE */ + else if (pg_strcasecmp(PREV_WD, "TRUNCATE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + + /* UNLISTEN */ + else if (pg_strcasecmp(PREV_WD, "UNLISTEN") == 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 '*'"); + + /* UPDATE */ + /* If prev. word is UPDATE suggest a list of tables */ + else if (pg_strcasecmp(PREV_WD, "UPDATE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL); + /* Complete UPDATE
with "SET" */ + else if (pg_strcasecmp(PREV2_WD, "UPDATE") == 0) + COMPLETE_WITH_CONST("SET"); + + /* + * If the previous word is SET (and it wasn't caught above as the _first_ + * word) the word before it was (hopefully) a table name and we'll now + * make a list of attributes. + */ + else if (pg_strcasecmp(PREV_WD, "SET") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, ""); + + /* UPDATE xx SET yy = */ + else if (pg_strcasecmp(PREV2_WD, "SET") == 0 && pg_strcasecmp(PREV4_WD, "UPDATE") == 0) + COMPLETE_WITH_CONST("="); + + /* USER MAPPING */ + else if ((pg_strcasecmp(PREV3_WD, "ALTER") == 0 || pg_strcasecmp(PREV3_WD, "CREATE") == 0 || + pg_strcasecmp(PREV3_WD, "DROP") == 0) && + pg_strcasecmp(PREV2_WD, "USER") == 0 && pg_strcasecmp(PREV_WD, "MAPPING") == 0) + COMPLETE_WITH_CONST("FOR"); + else if (pg_strcasecmp(PREV4_WD, "CREATE") == 0 && pg_strcasecmp(PREV3_WD, "USER") == 0 && + pg_strcasecmp(PREV2_WD, "MAPPING") == 0 && pg_strcasecmp(PREV_WD, "FOR") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'CURRENT_USER'" + " UNION SELECT 'PUBLIC'" + " UNION SELECT 'USER'"); + else if ((pg_strcasecmp(PREV4_WD, "ALTER") == 0 || pg_strcasecmp(PREV4_WD, "DROP") == 0) && + pg_strcasecmp(PREV3_WD, "USER") == 0 && pg_strcasecmp(PREV2_WD, "MAPPING") == 0 && + pg_strcasecmp(PREV_WD, "FOR") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); + else if ((pg_strcasecmp(PREV5_WD, "CREATE") == 0 || pg_strcasecmp(PREV5_WD, "ALTER") == 0 || + pg_strcasecmp(PREV5_WD, "DROP") == 0) && + pg_strcasecmp(PREV4_WD, "USER") == 0 && pg_strcasecmp(PREV3_WD, "MAPPING") == 0 && + pg_strcasecmp(PREV2_WD, "FOR") == 0) + COMPLETE_WITH_CONST("SERVER"); + + /* + * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ] + * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ] + */ + else if (pg_strcasecmp(PREV_WD, "VACUUM") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'FULL'" + " UNION SELECT 'FREEZE'" + " UNION SELECT 'ANALYZE'" + " UNION SELECT 'VERBOSE'"); + else if (pg_strcasecmp(PREV2_WD, "VACUUM") == 0 && + (pg_strcasecmp(PREV_WD, "FULL") == 0 || pg_strcasecmp(PREV_WD, "FREEZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'ANALYZE'" + " UNION SELECT 'VERBOSE'"); + else if (pg_strcasecmp(PREV3_WD, "VACUUM") == 0 && pg_strcasecmp(PREV_WD, "ANALYZE") == 0 && + (pg_strcasecmp(PREV2_WD, "FULL") == 0 || pg_strcasecmp(PREV2_WD, "FREEZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'VERBOSE'"); + else if (pg_strcasecmp(PREV3_WD, "VACUUM") == 0 && pg_strcasecmp(PREV_WD, "VERBOSE") == 0 && + (pg_strcasecmp(PREV2_WD, "FULL") == 0 || pg_strcasecmp(PREV2_WD, "FREEZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'ANALYZE'"); + else if (pg_strcasecmp(PREV2_WD, "VACUUM") == 0 && pg_strcasecmp(PREV_WD, "VERBOSE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'ANALYZE'"); + else if (pg_strcasecmp(PREV2_WD, "VACUUM") == 0 && pg_strcasecmp(PREV_WD, "ANALYZE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, " UNION SELECT 'VERBOSE'"); + else if ((pg_strcasecmp(PREV_WD, "ANALYZE") == 0 && pg_strcasecmp(PREV2_WD, "VERBOSE") == 0) || + (pg_strcasecmp(PREV_WD, "VERBOSE") == 0 && pg_strcasecmp(PREV2_WD, "ANALYZE") == 0)) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL); + + /* WITH [RECURSIVE] */ + + /* + * Only match when WITH is the first word, as WITH may appear in many + * other contexts. + */ + else if (pg_strcasecmp(PREV_WD, "WITH") == 0 && PREV2_WD[0] == '\0') + COMPLETE_WITH_CONST("RECURSIVE"); + + /* ANALYZE */ + /* If the previous word is ANALYZE, produce list of tables */ + else if (pg_strcasecmp(PREV_WD, "ANALYZE") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL); + + /* WHERE */ + /* Simple case of the word before the where being the table name */ + else if (pg_strcasecmp(PREV_WD, "WHERE") == 0) + COMPLETE_WITH_ATTR(PREV2_WD, ""); + + /* ... FROM ... */ + else if (pg_strcasecmp(PREV_WD, "FROM") == 0 && pg_strcasecmp(PREV3_WD, "COPY") != 0 && + pg_strcasecmp(PREV3_WD, "\\copy") != 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + + /* ... JOIN ... */ + else if (pg_strcasecmp(PREV_WD, "JOIN") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + + /* Backslash commands */ + else if (strcmp(PREV_WD, "\\connect") == 0 || strcmp(PREV_WD, "\\c") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + + else if (strncmp(PREV_WD, "\\da", strlen("\\da")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); + else if (strncmp(PREV_WD, "\\db", strlen("\\db")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); + else if (strncmp(PREV_WD, "\\dD", strlen("\\dD")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL); + else if (strncmp(PREV_WD, "\\des", strlen("\\des")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_servers); + else if (strncmp(PREV_WD, "\\deu", strlen("\\deu")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings); + else if (strncmp(PREV_WD, "\\dew", strlen("\\dew")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_fdws); + + else if (strncmp(PREV_WD, "\\df", strlen("\\df")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + else if (strncmp(PREV_WD, "\\dFd", strlen("\\dFd")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries); + else if (strncmp(PREV_WD, "\\dFp", strlen("\\dFp")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers); + else if (strncmp(PREV_WD, "\\dFt", strlen("\\dFt")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates); + /* must be at end of \dF */ + else if (strncmp(PREV_WD, "\\dF", strlen("\\dF")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations); + + else if (strncmp(PREV_WD, "\\di", strlen("\\di")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL); + else if (strncmp(PREV_WD, "\\dL", strlen("\\dL")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_languages); + else if (strncmp(PREV_WD, "\\dn", strlen("\\dn")) == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_schemas); + else if (strncmp(PREV_WD, "\\dp", strlen("\\dp")) == 0 || strncmp(PREV_WD, "\\z", strlen("\\z")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL); + else if (strncmp(PREV_WD, "\\ds", strlen("\\ds")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL); + else if (strncmp(PREV_WD, "\\dt", strlen("\\dt")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + else if (strncmp(PREV_WD, "\\dT", strlen("\\dT")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL); + else if (strncmp(PREV_WD, "\\du", strlen("\\du")) == 0 || (strncmp(PREV_WD, "\\dg", strlen("\\dg")) == 0)) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if (strncmp(PREV_WD, "\\dv", strlen("\\dv")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL); + else if (strncmp(PREV_WD, "\\dm", strlen("\\dm")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL); + + /* must be at end of \d list */ + else if (strncmp(PREV_WD, "\\d", strlen("\\d")) == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL); + + else if (strcmp(PREV_WD, "\\ef") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + + else if (strcmp(PREV_WD, "\\encoding") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_encodings); + else if (strcmp(PREV_WD, "\\h") == 0 || strcmp(PREV_WD, "\\help") == 0) + COMPLETE_WITH_LIST(sqlCommands); + else if (strcmp(PREV_WD, "\\password") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + else if (strcmp(PREV_WD, "\\pset") == 0) { + static const char* const myList[] = { + "format", "border", "expanded", + "null", "fieldsep", "tuples_only", "title", "tableattr", + "linestyle", "pager", "recordsep", NULL + }; + + COMPLETE_WITH_LIST_CS(myList); + } else if (strcmp(PREV2_WD, "\\pset") == 0) { + if (strcmp(PREV_WD, "format") == 0) { + static const char* const myList[] = {"unaligned", "aligned", "wrapped", "html", "latex", "troff-ms", NULL}; + + COMPLETE_WITH_LIST_CS(myList); + } else if (strcmp(PREV_WD, "linestyle") == 0) { + static const char* const myList[] = {"ascii", "old-ascii", "unicode", NULL}; + + COMPLETE_WITH_LIST_CS(myList); + } + } else if (strcmp(PREV_WD, "\\set") == 0) { + matches = CompleteFromVariables(text, "", ""); + } else if (strcmp(PREV_WD, "\\sf") == 0 || strcmp(PREV_WD, "\\sf+") == 0) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL); + else if (strcmp(PREV_WD, "\\cd") == 0 || strcmp(PREV_WD, "\\e") == 0 || strcmp(PREV_WD, "\\edit") == 0 || + strcmp(PREV_WD, "\\g") == 0 || strcmp(PREV_WD, "\\i") == 0 || strcmp(PREV_WD, "\\include") == 0 || + strcmp(PREV_WD, "\\ir") == 0 || strcmp(PREV_WD, "\\include_relative") == 0 || strcmp(PREV_WD, "\\o") == 0 || + strcmp(PREV_WD, "\\out") == 0 || strcmp(PREV_WD, "\\s") == 0 || strcmp(PREV_WD, "\\w") == 0 || + strcmp(PREV_WD, "\\write") == 0) { + completion_charp = "\\"; + matches = completion_matches(text, CompleteFromFiles); + } + + /* + * Finally, we look through the list of "things", such as TABLE, INDEX and + * check if that was the previous word. If so, execute the query to get a + * list of them. + */ + else { + int i; + + for (i = 0; words_after_create[i].name; i++) { + if (pg_strcasecmp(PREV_WD, words_after_create[i].name) == 0) { + if (words_after_create[i].query) { + COMPLETE_WITH_QUERY(words_after_create[i].query); + } + else if (words_after_create[i].squery) { + COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery, NULL); + } + break; + } + } + } + + /* + * If we still don't have anything to match we have to fabricate some sort + * of default list. If we were to just return NULL, readline automatically + * attempts filename completion, and that's usually no good. + */ + if (matches == NULL) { + COMPLETE_WITH_CONST(""); +#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER + rl_completion_append_character = '\0'; +#endif + } + + /* free storage */ + { + int i; + + for (i = 0; i < (int)lengthof(previousWords); i++) + free(previousWords[i]); + } + + /* Return our Grand List O' Matches */ + return matches; } /* @@ -823,67 +2778,67 @@ static char* complete_from_schema_query(const char* text, int state) * * See top of file for examples of both kinds of query. */ -static char* _complete_from_query(int is_schema_query, const char* text, int state) +static char *_CompleteFromQuery(int isSchemaQuery, const char *text, int state) { - static int list_index, string_length; - static PGresult* result = NULL; + static int listIndex, stringLength; + static PGresult *result = NULL; /* * If this is the first time for this completion, we fetch a list of our * "things" from the backend. */ if (state == 0) { - PQExpBufferData query_buffer; - char* e_text = NULL; - char* e_info_charp = NULL; - char* e_info_charp2 = NULL; + PQExpBufferData queryBuffer; + char *eText = NULL; + char *eInfoCharp = NULL; + char *eInfoCharp2 = NULL; - list_index = 0; - string_length = strlen(text); + listIndex = 0; + stringLength = strlen(text); /* Free any prior result */ PQclear(result); result = NULL; /* Set up suitably-escaped copies of textual inputs */ - e_text = (char*)pg_malloc(string_length * 2 + 1); - PQescapeString(e_text, text, string_length); + eText = (char *)pg_malloc(stringLength * 2 + 1); + PQescapeString(eText, text, stringLength); if (NULL != completion_info_charp) { - size_t charp_len; + size_t charpLen; - charp_len = strlen(completion_info_charp); - e_info_charp = (char*)pg_malloc(charp_len * 2 + 1); - PQescapeString(e_info_charp, completion_info_charp, charp_len); + charpLen = strlen(completion_info_charp); + eInfoCharp = (char *)pg_malloc(charpLen * 2 + 1); + PQescapeString(eInfoCharp, completion_info_charp, charpLen); } else - e_info_charp = NULL; + eInfoCharp = NULL; if (NULL != completion_info_charp2) { - size_t charp_len; + size_t charpLen; - charp_len = strlen(completion_info_charp2); - e_info_charp2 = (char*)pg_malloc(charp_len * 2 + 1); - PQescapeString(e_info_charp2, completion_info_charp2, charp_len); + charpLen = strlen(completion_info_charp2); + eInfoCharp2 = (char *)pg_malloc(charpLen * 2 + 1); + PQescapeString(eInfoCharp2, completion_info_charp2, charpLen); } else - e_info_charp2 = NULL; + eInfoCharp2 = NULL; - initPQExpBuffer(&query_buffer); + initPQExpBuffer(&queryBuffer); - if (is_schema_query) { + if (isSchemaQuery) { /* completion_squery gives us the pieces to assemble */ - const char* qualresult = completion_squery->qualresult; + const char *qualresult = completion_squery->qualresult; if (qualresult == NULL) qualresult = completion_squery->result; /* Get unqualified names matching the input-so-far */ - appendPQExpBuffer( - &query_buffer, "SELECT %s FROM %s WHERE ", completion_squery->result, completion_squery->catname); + appendPQExpBuffer(&queryBuffer, "SELECT %s FROM %s WHERE ", completion_squery->result, + completion_squery->catname); if (NULL != completion_squery->selcondition) - appendPQExpBuffer(&query_buffer, "%s AND ", completion_squery->selcondition); - appendPQExpBuffer( - &query_buffer, "substring(%s,1,%d)='%s'", completion_squery->result, string_length, e_text); - appendPQExpBuffer(&query_buffer, " AND %s", completion_squery->viscondition); + appendPQExpBuffer(&queryBuffer, "%s AND ", completion_squery->selcondition); + appendPQExpBuffer(&queryBuffer, "substring(%s,1,%d)='%s'", completion_squery->result, stringLength, + eText); + appendPQExpBuffer(&queryBuffer, " AND %s", completion_squery->viscondition); /* * When fetching relation names, suppress system catalogs unless @@ -892,8 +2847,7 @@ static char* _complete_from_query(int is_schema_query, const char* text, int sta * having them swamp the result when the input is just "p". */ if (strcmp(completion_squery->catname, "pg_catalog.pg_class c") == 0 && strncmp(text, "pg_", 3) != 0) { - appendPQExpBuffer(&query_buffer, - " AND c.relnamespace <> (SELECT oid FROM" + appendPQExpBuffer(&queryBuffer, " AND c.relnamespace <> (SELECT oid FROM" " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')"); } @@ -901,97 +2855,82 @@ static char* _complete_from_query(int is_schema_query, const char* text, int sta * Add in matching schema names, but only if there is more than * one potential match among schema names. */ - appendPQExpBuffer(&query_buffer, + appendPQExpBuffer(&queryBuffer, "\nUNION\n" "SELECT pg_catalog.quote_ident(n.nspname) || '.' " "FROM pg_catalog.pg_namespace n " "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'", - string_length, - e_text); - appendPQExpBuffer(&query_buffer, + stringLength, eText); + appendPQExpBuffer(&queryBuffer, " AND (SELECT pg_catalog.count(*)" " FROM pg_catalog.pg_namespace" " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1", - string_length, - e_text); + stringLength, eText); /* * Add in matching qualified names, but only if there is exactly * one schema matching the input-so-far. */ - appendPQExpBuffer(&query_buffer, + appendPQExpBuffer(&queryBuffer, "\nUNION\n" "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s " "FROM %s, pg_catalog.pg_namespace n " "WHERE %s = n.oid AND ", - qualresult, - completion_squery->catname, - completion_squery->nameSpace); + qualresult, completion_squery->catname, completion_squery->nameSpace); if (completion_squery->selcondition != NULL) - appendPQExpBuffer(&query_buffer, "%s AND ", completion_squery->selcondition); - appendPQExpBuffer(&query_buffer, - "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", - qualresult, - string_length, - e_text); + appendPQExpBuffer(&queryBuffer, "%s AND ", completion_squery->selcondition); + appendPQExpBuffer(&queryBuffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'", + qualresult, stringLength, eText); /* * This condition exploits the single-matching-schema rule to * speed up the query */ - appendPQExpBuffer(&query_buffer, + appendPQExpBuffer(&queryBuffer, " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) =" " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)", - string_length, - e_text); - appendPQExpBuffer(&query_buffer, + stringLength, eText); + appendPQExpBuffer(&queryBuffer, " AND (SELECT pg_catalog.count(*)" " FROM pg_catalog.pg_namespace" " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) =" " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1", - string_length, - e_text); + stringLength, eText); /* If an addon query was provided, use it */ if (NULL != completion_charp) - appendPQExpBuffer(&query_buffer, "\n%s", completion_charp); + appendPQExpBuffer(&queryBuffer, "\n%s", completion_charp); } else { /* completion_charp is an sprintf-style format string */ - appendPQExpBuffer(&query_buffer, - completion_charp, - string_length, - e_text, - e_info_charp, - e_info_charp, - e_info_charp2, - e_info_charp2); + appendPQExpBuffer(&queryBuffer, completion_charp, stringLength, eText, eInfoCharp, eInfoCharp, + eInfoCharp2, eInfoCharp2); } /* Limit the number of records in the result */ - appendPQExpBuffer(&query_buffer, "\nLIMIT %d", completion_max_records); + appendPQExpBuffer(&queryBuffer, "\nLIMIT %d", completion_max_records); - result = exec_query(query_buffer.data); + result = ExecQuery(queryBuffer.data); - termPQExpBuffer(&query_buffer); - free(e_text); - e_text = NULL; - if (e_info_charp != NULL) { - free(e_info_charp); - e_info_charp = NULL; + termPQExpBuffer(&queryBuffer); + free(eText); + eText = NULL; + if (eInfoCharp != NULL) { + free(eInfoCharp); + eInfoCharp = NULL; } - if (e_info_charp2 != NULL) { - free(e_info_charp2); - e_info_charp2 = NULL; + if (eInfoCharp2 != NULL) { + free(eInfoCharp2); + eInfoCharp2 = NULL; } } /* Find something that matches */ if (result && PQresultStatus(result) == PGRES_TUPLES_OK) { - const char* item = NULL; + const char *item = NULL; - while (list_index < PQntuples(result) && (item = PQgetvalue(result, list_index++, 0))) - if (pg_strncasecmp(text, item, string_length) == 0) + while (listIndex < PQntuples(result) && (item = PQgetvalue(result, listIndex++, 0))) + if (pg_strncasecmp(text, item, stringLength) == 0) return pg_strdup(item); } @@ -1006,32 +2945,32 @@ static char* _complete_from_query(int is_schema_query, const char* text, int sta * of strings (if matching). This can be used if there are only a fixed number * SQL words that can appear at certain spot. */ -static char* complete_from_list(const char* text, int state) +static char *CompleteFromList(const char *text, int state) { - static int string_length, list_index, matches; + static int stringLength, listIndex, matches; static bool casesensitive = false; - const char* item = NULL; + const char *item = NULL; /* need to have a list */ psql_assert(completion_charpp); /* Initialization */ if (state == 0) { - list_index = 0; - string_length = strlen(text); + listIndex = 0; + stringLength = strlen(text); casesensitive = completion_case_sensitive; matches = 0; } - while ((item = completion_charpp[list_index++])) { + while ((item = completion_charpp[listIndex++])) { /* First pass is case sensitive */ - if (casesensitive && strncmp(text, item, string_length) == 0) { + if (casesensitive && strncmp(text, item, stringLength) == 0) { matches++; return pg_strdup(item); } /* Second pass is case insensitive, don't bother counting matches */ - if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0) { + if (!casesensitive && pg_strncasecmp(text, item, stringLength) == 0) { if (completion_case_sensitive) return pg_strdup(item); else @@ -1050,9 +2989,9 @@ static char* complete_from_list(const char* text, int state) */ if (casesensitive && matches == 0) { casesensitive = false; - list_index = 0; + listIndex = 0; state++; - return complete_from_list(text, state); + return CompleteFromList(text, state); } /* If no more matches, return null. */ @@ -1066,7 +3005,7 @@ static char* complete_from_list(const char* text, int state) * misspellings will be overwritten. The string to be passed must be in * completion_charp. */ -static char* complete_from_const(const char* text, int state) +static char *CompleteFromConst(const char *text, int state) { psql_assert(completion_charp); if (state == 0) { @@ -1088,27 +3027,27 @@ static char* complete_from_const(const char* text, int state) * The variable names can be prefixed and suffixed with additional text * to support quoting usages. */ -static char** complete_from_variables(char* text, const char* prefix, const char* suffix) +static char **CompleteFromVariables(const char *text, const char *prefix, const char *suffix) { - char** matches = NULL; + char **matches = NULL; int overhead = strlen(prefix) + strlen(suffix) + 1; - char** varnames = NULL; + char **varnames = NULL; int nvars = 0; int maxvars = 100; int i; - struct _variable* ptr = NULL; + struct _variable *ptr = NULL; int rc; size_t sz = 0; - varnames = (char**)pg_malloc((maxvars + 1) * sizeof(char*)); + varnames = (char **)pg_malloc((maxvars + 1) * sizeof(char *)); ptr = (pset.vars != NULL) ? pset.vars->next : NULL; - for (;ptr != NULL; ptr = ptr->next) { - char* buffer = NULL; + for (; ptr != NULL; ptr = ptr->next) { + char *buffer = NULL; if (nvars >= maxvars) { maxvars *= 2; - varnames = (char**)pg_realloc(varnames, (maxvars + 1) * sizeof(char*)); + varnames = (char **)psql_realloc(varnames, (maxvars + 1) * sizeof(char *), (maxvars + 1) * sizeof(char *)); if (varnames == NULL) { psql_error("out of memory\n"); exit(EXIT_FAILURE); @@ -1116,14 +3055,14 @@ static char** complete_from_variables(char* text, const char* prefix, const char } sz = strlen(ptr->name) + overhead; - buffer = (char*)pg_malloc(sz); + buffer = (char *)pg_malloc(sz); rc = sprintf_s(buffer, sz, "%s%s%s", prefix, ptr->name, suffix); check_sprintf_s(rc); varnames[nvars++] = buffer; } varnames[nvars] = NULL; - COMPLETE_WITH_LIST_CS((const char* const*)varnames); + COMPLETE_WITH_LIST_CS((const char * const *)varnames); for (i = 0; i < nvars; i++) { free(varnames[i]); @@ -1140,24 +3079,24 @@ static char** complete_from_variables(char* text, const char* prefix, const char * the input before searching for matches and to quote any matches for which * the consuming command will require it. */ -static char* complete_from_files(const char* text, int state) +static char *CompleteFromFiles(const char *text, int state) { - static const char* unquoted_text = NULL; - char* unquoted_match = NULL; - char* ret = NULL; + static const char *unquotedText = NULL; + char *unquotedMatch = NULL; + char *ret = NULL; if (state == 0) { /* Initialization: stash the unquoted input. */ - unquoted_text = strtokx(text, "", NULL, "'", *completion_charp, false, true, pset.encoding); + unquotedText = strtokx(text, "", NULL, "'", *completion_charp, false, true, pset.encoding); /* expect a NULL return for the empty string only */ - if (NULL == unquoted_text) { + if (NULL == unquotedText) { psql_assert(!*text); - unquoted_text = text; + unquotedText = text; } } - unquoted_match = filename_completion_function(unquoted_text, state); - if (unquoted_match != NULL) { + unquotedMatch = filename_completion_function(unquotedText, state); + if (unquotedMatch != NULL) { /* * Caller sets completion_charp to a zero- or one-character string * containing the escape character. This is necessary since \copy has @@ -1165,14 +3104,13 @@ static char* complete_from_files(const char* text, int state) * "\" as an escape character. Since we have only two callers, don't * bother providing a macro to simplify this. */ - ret = quote_if_needed(unquoted_match, " \t\r\n\"`", '\'', *completion_charp, pset.encoding); + ret = quote_if_needed(unquotedMatch, " \t\r\n\"`", '\'', *completion_charp, pset.encoding); if (ret != NULL) { - free(unquoted_match); - unquoted_match = NULL; + free(unquotedMatch); + unquotedMatch = NULL; } else - ret = unquoted_match; + ret = unquotedMatch; } - return ret; } @@ -1181,13 +3119,13 @@ static char* complete_from_files(const char* text, int state) * Make a pg_strdup copy of s and convert the case according to * COMP_KEYWORD_CASE variable, using ref as the text that was already entered. */ -static char* pg_strdup_keyword_case(const char* s, const char* ref) +static char *pg_strdup_keyword_case(const char *s, const char *ref) { char *ret = NULL; char *p = NULL; unsigned char first = ref[0]; int tocase; - const char* varval = NULL; + const char *varval = NULL; varval = GetVariable(pset.vars, "COMP_KEYWORD_CASE"); if (varval == NULL) @@ -1223,18 +3161,16 @@ static char* pg_strdup_keyword_case(const char* s, const char* ref) * Execute a query and report any errors. This should be the preferred way of * talking to the database in this file. */ -static PGresult* exec_query(const char* query) +static PGresult *ExecQuery(const char *query) { - PGresult* result = NULL; + PGresult *result = NULL; if (query == NULL || pset.db == NULL || PQstatus(pset.db) != CONNECTION_OK) return NULL; result = PQexec(pset.db, query); if (PQresultStatus(result) != PGRES_TUPLES_OK) { -#ifdef NOT_USED psql_error("tab completion query failed: %s\nQuery was:\n%s\n", PQerrorMessage(pset.db), query); -#endif PQclear(result); result = NULL; } @@ -1244,13 +3180,13 @@ static PGresult* exec_query(const char* query) /* * Return the nwords word(s) before point. Words are returned right to left, - * that is, previous_words[0] gets the last word before point. + * that is, previousWords[0] gets the last word before point. * If we run out of words, remaining array elements are set to empty strings. * Each array element is filled with a malloc'd string. */ -static void get_previous_words(int point, char** previous_words, int nwords) +static void GetPreviousWords(int point, char **previousWords, int nwords) { - const char* buf = rl_line_buffer; /* alias */ + const char *buf = rl_line_buffer; /* alias */ int i; errno_t rc = EOK; @@ -1262,7 +3198,7 @@ static void get_previous_words(int point, char** previous_words, int nwords) while (nwords-- > 0) { int start, end; - char* s = NULL; + char *s = NULL; /* now find the first non-space which then constitutes the end */ end = -1; @@ -1307,12 +3243,13 @@ static void get_previous_words(int point, char** previous_words, int nwords) point = start - 1; /* make a copy of chars from start to end inclusive */ - s = (char*)pg_malloc(end - start + 2); - rc = strncpy_s(s, end - start + 2, &buf[start], end - start + 2); + s = (char *)pg_malloc(end - start + 2); + rc = strncpy_s(s, end - start + 2, &buf[start], end - start + 1); + s[end - start + 1] = '\0'; securec_check_c(rc, "\0", "\0"); } - *previous_words++ = s; + *previousWords++ = s; } } @@ -1323,9 +3260,9 @@ static void get_previous_words(int point, char** previous_words, int nwords) * psql internal. Currently disabled because it is reported not to * cooperate with certain versions of readline. */ -static char* quote_file_name(char* text, int match_type, char* quote_pointer) +static char *quote_file_name(char *text, int match_type, char *quote_pointer) { - char* s = NULL; + char *s = NULL; size_t length; int rc; @@ -1342,9 +3279,9 @@ static char* quote_file_name(char* text, int match_type, char* quote_pointer) return s; } -static char* dequote_file_name(char* text, char quote_char) +static char *dequote_file_name(char *text, char quote_char) { - char* s = NULL; + char *s = NULL; size_t length; errno_t rc = EOK; diff --git a/src/test/regress/expected/readline.out b/src/test/regress/expected/readline.out index abe5c7650..048f3b4b6 100644 --- a/src/test/regress/expected/readline.out +++ b/src/test/regress/expected/readline.out @@ -1,17 +1,9 @@ --include chinese -create table 华为(f int); -insert into 华为 values (1); -select * from 华为; +create table 数据库(f int); +insert into 数据库 values (1); +select * from 数据库; f --- 1 (1 row) -\set HISTSIZE -1 -\set HISTSIZE 30.5 -\set HISTSIZE 0 -\set HISTSIZE 6 -\set HISTSIZE 500 -\set HISTSIZE 600 -\set HISTSIZE 66666666666666666666666600 -\set HISTSIZE -666666666666666666666666666 diff --git a/src/test/regress/sql/readline.sql b/src/test/regress/sql/readline.sql index 5b168c5e9..de975aab3 100644 --- a/src/test/regress/sql/readline.sql +++ b/src/test/regress/sql/readline.sql @@ -1,12 +1,4 @@ --include chinese -create table 华为(f int); -insert into 华为 values (1); -select * from 华为; -\set HISTSIZE -1 -\set HISTSIZE 30.5 -\set HISTSIZE 0 -\set HISTSIZE 6 -\set HISTSIZE 500 -\set HISTSIZE 600 -\set HISTSIZE 66666666666666666666666600 -\set HISTSIZE -666666666666666666666666666 +create table 数据库(f int); +insert into 数据库 values (1); +select * from 数据库;