/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright (c) 2000-2004, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2010, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.116 2004/09/22 04:25:16 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.189 2010/01/02 16:58:00 momjian Exp $
*/
/*----------------------------------------------------------------------
#include "pqexpbuffer.h"
#include "common.h"
#include "settings.h"
-
+#include "stringutils.h"
#ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION
#define filename_completion_function rl_filename_completion_function
const char *catname;
/*
- * Selection condition --- only rows meeting this condition are
- * candidates to display. If catname mentions multiple tables,
- * include the necessary join condition here. For example, "c.relkind
- * = 'r'". Write NULL (not an empty string) if not needed.
+ * Selection condition --- only rows meeting this condition are candidates
+ * to display. If catname mentions multiple tables, include the necessary
+ * join condition here. For example, "c.relkind = 'r'". Write NULL (not
+ * an empty string) if not needed.
*/
const char *selcondition;
/*
* Visibility condition --- which rows are visible without schema
- * qualification? For example,
- * "pg_catalog.pg_table_is_visible(c.oid)".
+ * qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)".
*/
const char *viscondition;
/*
- * Namespace --- name of field to join to pg_namespace.oid. For
- * example, "c.relnamespace".
+ * Namespace --- name of field to join to pg_namespace.oid. For example,
+ * "c.relnamespace".
*/
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)".
+ * 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;
* the completion callback functions. Ugly but there is no better way.
*/
static const char *completion_charp; /* to pass a string */
-static const char *const * completion_charpp; /* to pass a list of
- * strings */
+static const char *const * completion_charpp; /* to pass a list of strings */
static const char *completion_info_charp; /* to pass a second string */
+static const char *completion_info_charp2; /* to pass a third string */
static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */
-/* A couple of macros to ease typing. You can use these to complete the given
- string with
- 1) The results from a query you pass it. (Perhaps one of those below?)
- 2) The results from a schema query you pass it.
- 3) The items from a null-pointer-terminated list.
- 4) A string constant
- 5) The list of attributes to the given table.
-*/
+/*
+ * A few macros to ease typing. You can use these to complete the given
+ * string with
+ * 1) The results from a query you pass it. (Perhaps one of those below?)
+ * 2) The results from a schema query you pass it.
+ * 3) The items from a null-pointer-terminated list.
+ * 4) A string constant.
+ * 5) The list of attributes of the given table (possibly schema-qualified).
+ */
#define COMPLETE_WITH_QUERY(query) \
-do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
-#define COMPLETE_WITH_SCHEMA_QUERY(query,addon) \
-do { completion_squery = &(query); completion_charp = addon; matches = completion_matches(text, complete_from_schema_query); } while(0)
+do { \
+ completion_charp = query; \
+ matches = completion_matches(text, complete_from_query); \
+} while (0)
+
+#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
+do { \
+ completion_squery = &(query); \
+ completion_charp = addon; \
+ matches = completion_matches(text, complete_from_schema_query); \
+} while (0)
+
#define COMPLETE_WITH_LIST(list) \
-do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
+do { \
+ completion_charpp = list; \
+ matches = completion_matches(text, complete_from_list); \
+} while (0)
+
#define COMPLETE_WITH_CONST(string) \
-do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0)
-#define COMPLETE_WITH_ATTR(table) \
-do {completion_charp = Query_for_list_of_attributes; completion_info_charp = table; matches = completion_matches(text, complete_from_query); } while(0)
+do { \
+ completion_charp = string; \
+ matches = completion_matches(text, complete_from_const); \
+} while (0)
+
+#define COMPLETE_WITH_ATTR(relation, addon) \
+do { \
+ 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); \
+ if (_completion_table == NULL) \
+ { \
+ completion_charp = Query_for_list_of_attributes addon; \
+ completion_info_charp = relation; \
+ } \
+ else \
+ { \
+ completion_charp = Query_for_list_of_attributes_with_schema addon; \
+ completion_info_charp = _completion_table; \
+ completion_info_charp2 = _completion_schema; \
+ } \
+ matches = completion_matches(text, complete_from_query); \
+} while (0)
/*
* Assembly instructions for schema queries
/*
* Queries to get lists of names of various kinds of things, possibly
* restricted to names matching a partially entered name. In these queries,
- * %s will be replaced by the text entered so far (suitably escaped to
- * become a SQL literal string). %d will be replaced by the length of the
- * string (in unescaped form). A second %s, if present, will be replaced
- * by a suitably-escaped version of the string provided in
- * completion_info_charp.
+ * the first %s will be replaced by the text entered so far (suitably escaped
+ * to become a SQL literal string). %d will be replaced by the length of the
+ * string (in unescaped form). A second and third %s, if present, will be
+ * replaced by a suitably-escaped version of the string provided in
+ * completion_info_charp. A fourth and fifth %s are similarly replaced by
+ * completion_info_charp2.
*
* Beware that the allowed sequences of %s and %d are determined by
* _complete_from_query().
" 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' "\
+" 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_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 IS TRUE"
+
#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'"
"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_system_relations \
-"SELECT pg_catalog.quote_ident(relname) "\
-" FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
-" WHERE c.relkind IN ('r', 'v', 's', 'S') "\
-" AND substring(pg_catalog.quote_ident(relname),1,%d)='%s' "\
-" AND c.relnamespace = n.oid "\
-" AND n.nspname = 'pg_catalog'"
-
-#define Query_for_list_of_users \
-" SELECT pg_catalog.quote_ident(usename) "\
-" FROM pg_catalog.pg_user "\
-" WHERE substring(pg_catalog.quote_ident(usename),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_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'"
/* 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 = length('%s'))"\
+" and (%d = pg_catalog.length('%s'))"\
" and pg_catalog.quote_ident(c2.relname)='%s'"\
" and pg_catalog.pg_table_is_visible(c2.oid)"
"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 = length('%s'))"\
+" 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_list_of_tables_for_trigger \
"SELECT pg_catalog.quote_ident(relname) "\
" FROM pg_catalog.pg_class"\
-" WHERE (%d = length('%s'))"\
+" 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_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_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_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'"
+
/*
* This is a list of all "things" in Pgsql, which can show up after CREATE or
* DROP; and there is also a query to get a list of them.
const char *name;
const char *query; /* simple query, or NULL */
const SchemaQuery *squery; /* schema query, or NULL */
+ const bool noshow; /* NULL or true if this word should not show
+ * up after CREATE or DROP */
} pgsql_thing_t;
static const pgsql_thing_t words_after_create[] = {
{"AGGREGATE", NULL, &Query_for_list_of_aggregates},
- {"CAST", NULL, NULL}, /* Casts have complex structures for
- * names, so skip it */
+ {"CAST", NULL, NULL}, /* Casts have complex structures for names, so
+ * skip it */
+
+ /*
+ * CREATE CONSTRAINT TRIGGER is not supported here because it is designed
+ * to be used only by pg_dump.
+ */
+ {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, true},
{"CONVERSION", "SELECT pg_catalog.quote_ident(conname) FROM pg_catalog.pg_conversion WHERE substring(pg_catalog.quote_ident(conname),1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
+ {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true},
{"DOMAIN", NULL, &Query_for_list_of_domains},
+ {"FOREIGN DATA WRAPPER", NULL, NULL},
{"FUNCTION", NULL, &Query_for_list_of_functions},
- {"GROUP", "SELECT pg_catalog.quote_ident(groname) FROM pg_catalog.pg_group WHERE substring(pg_catalog.quote_ident(groname),1,%d)='%s'"},
+ {"GROUP", Query_for_list_of_roles},
{"LANGUAGE", Query_for_list_of_languages},
{"INDEX", NULL, &Query_for_list_of_indexes},
- {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such
- * a good idea. */
+ {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a
+ * good idea. */
+ {"PARSER", Query_for_list_of_ts_parsers, NULL, true},
+ {"ROLE", Query_for_list_of_roles},
{"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
{"SCHEMA", Query_for_list_of_schemas},
{"SEQUENCE", NULL, &Query_for_list_of_sequences},
+ {"SERVER", Query_for_list_of_servers},
{"TABLE", NULL, &Query_for_list_of_tables},
{"TABLESPACE", Query_for_list_of_tablespaces},
{"TEMP", NULL, NULL}, /* for CREATE TEMP TABLE ... */
+ {"TEMPLATE", Query_for_list_of_ts_templates, NULL, true},
+ {"TEXT SEARCH", NULL, NULL},
{"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s'"},
{"TYPE", NULL, &Query_for_list_of_datatypes},
{"UNIQUE", NULL, NULL}, /* for CREATE UNIQUE INDEX ... */
- {"USER", Query_for_list_of_users},
+ {"USER", Query_for_list_of_roles},
+ {"USER MAPPING FOR", NULL, NULL},
{"VIEW", NULL, &Query_for_list_of_views},
- {NULL, NULL, NULL} /* end of list */
+ {NULL, NULL, NULL, false} /* end of list */
};
/* Forward declaration of functions */
static char **psql_completion(char *text, int start, int end);
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,
static char *previous_word(int point, int skip);
+static int find_open_parenthesis(int end);
+
#if 0
static char *quote_file_name(char *text, int match_type, char *quote_pointer);
static char *dequote_file_name(char *text, char quote_char);
completion_max_records = 1000;
/*
- * There is a variable rl_completion_query_items for this but
- * apparently it's not defined everywhere.
+ * There is a variable rl_completion_query_items for this but apparently
+ * it's not defined everywhere.
*/
}
char *prev_wd,
*prev2_wd,
*prev3_wd,
- *prev4_wd;
+ *prev4_wd,
+ *prev5_wd;
static const char *const sql_commands[] = {
- "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", "COMMENT",
- "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE", "DELETE", "DROP", "END", "EXECUTE",
- "EXPLAIN", "FETCH", "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY",
- "PREPARE", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK", "SAVEPOINT",
- "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", NULL
- };
-
- static const char *const pgsql_variables[] = {
- /* these SET arguments are known in gram.y */
- "CONSTRAINTS",
- "NAMES",
- "SESSION",
- "TRANSACTION",
-
- /*
- * the rest should match USERSET and possibly SUSET entries in
- * backend/utils/misc/guc.c.
- */
- "add_missing_from",
- "australian_timezones",
- "client_encoding",
- "client_min_messages",
- "commit_delay",
- "commit_siblings",
- "cpu_index_tuple_cost",
- "cpu_operator_cost",
- "cpu_tuple_cost",
- "DateStyle",
- "deadlock_timeout",
- "debug_pretty_print",
- "debug_print_parse",
- "debug_print_plan",
- "debug_print_rewritten",
- "default_statistics_target",
- "default_transaction_isolation",
- "default_transaction_read_only",
- "default_with_oids",
- "dynamic_library_path",
- "effective_cache_size",
- "enable_hashagg",
- "enable_hashjoin",
- "enable_indexscan",
- "enable_mergejoin",
- "enable_nestloop",
- "enable_seqscan",
- "enable_sort",
- "enable_tidscan",
- "explain_pretty_print",
- "extra_float_digits",
- "from_collapse_limit",
- "fsync",
- "geqo",
- "geqo_effort",
- "geqo_generations",
- "geqo_pool_size",
- "geqo_selection_bias",
- "geqo_threshold",
- "join_collapse_limit",
- "krb_server_keyfile",
- "lc_messages",
- "lc_monetary",
- "lc_numeric",
- "lc_time",
- "log_destination",
- "log_duration",
- "log_error_verbosity",
- "log_executor_stats",
- "log_min_duration_statement",
- "log_min_error_statement",
- "log_min_messages",
- "log_parser_stats",
- "log_planner_stats",
- "log_statement",
- "log_statement_stats",
- "maintenance_work_mem",
- "max_connections",
- "max_files_per_process",
- "max_fsm_pages",
- "max_fsm_relations",
- "max_locks_per_transaction",
- "max_stack_depth",
- "password_encryption",
- "port",
- "random_page_cost",
- "regex_flavor",
- "search_path",
- "shared_buffers",
- "seed",
- "server_encoding",
- "sql_inheritance",
- "ssl",
- "statement_timeout",
- "stats_block_level",
- "stats_command_string",
- "stats_reset_on_server_start",
- "stats_row_level",
- "stats_start_collector",
- "superuser_reserved_connections",
- "syslog_facility",
- "syslog_ident",
- "TimeZone",
- "trace_notify",
- "transform_null_equals",
- "unix_socket_directory",
- "unix_socket_group",
- "unix_socket_permissions",
- "wal_buffers",
- "wal_debug",
- "wal_sync_method",
- "work_mem",
- NULL
+ "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
+ "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
+ "DELETE FROM", "DISCARD", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
+ "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
+ "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
+ "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TABLE", "TRUNCATE", "UNLISTEN",
+ "UPDATE", "VACUUM", "VALUES", "WITH", NULL
};
static const char *const backslash_commands[] = {
"\\a", "\\connect", "\\C", "\\cd", "\\copy", "\\copyright",
- "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\df",
- "\\dg", "\\di", "\\dl", "\\dn", "\\do", "\\dp", "\\ds", "\\dS",
- "\\dt", "\\dT", "\\dv", "\\du",
- "\\e", "\\echo", "\\encoding",
+ "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
+ "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
+ "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
+ "\\e", "\\echo", "\\ef", "\\encoding",
"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
- "\\o", "\\p", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\T",
+ "\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
+ "\\set", "\\t", "\\T",
"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
};
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 four
- * words. According to those we'll make some smart decisions on what
- * the user is probably intending to type. TODO: Use strtokx() to do
- * this.
+ * Scan the input line before our current position for the last five
+ * words. According to those we'll make some smart decisions on what the
+ * user is probably intending to type. TODO: Use strtokx() to do this.
*/
prev_wd = previous_word(start, 0);
prev2_wd = previous_word(start, 1);
prev3_wd = previous_word(start, 2);
prev4_wd = previous_word(start, 3);
+ prev5_wd = previous_word(start, 4);
/* If a backslash command was started, continue */
if (text[0] == '\\')
else if (!prev_wd)
COMPLETE_WITH_LIST(sql_commands);
-/* CREATE or DROP but not ALTER (TABLE|DOMAIN|GROUP) sth DROP */
- /* complete with something you can create or drop */
- else if (pg_strcasecmp(prev_wd, "CREATE") == 0 ||
- (pg_strcasecmp(prev_wd, "DROP") == 0 &&
- pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
- pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
- pg_strcasecmp(prev3_wd, "GROUP") != 0))
+/* CREATE */
+ /* complete with something you can create */
+ else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
matches = completion_matches(text, create_command_generator);
+/* DROP, but watch out for DROP embedded in other commands */
+ /* complete with something you can drop */
+ else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev2_wd, "DROP") == 0)
+ matches = completion_matches(text, drop_command_generator);
+
/* ALTER */
/*
- * complete with what you can alter (TABLE, GROUP, USER, ...) unless
- * we're in ALTER TABLE sth 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 list_ALTER[] =
- {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION",
- "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "SCHEMA", "SEQUENCE", "TABLE",
- "TABLESPACE", "TRIGGER", "TYPE", "USER", NULL};
+ {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
+ "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
+ "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
-
- /* ALTER AGGREGATE,CONVERSION,FUNCTION,SCHEMA <name> */
+ /* ALTER AGGREGATE,FUNCTION <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
(pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
- pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
- pg_strcasecmp(prev2_wd, "FUNCTION") == 0 ||
+ pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
+ {
+ static const char *const list_ALTERAGG[] =
+ {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERAGG);
+ }
+
+ /* ALTER CONVERSION,SCHEMA <name> */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
pg_strcasecmp(prev2_wd, "SCHEMA") == 0))
{
static const char *const list_ALTERGEN[] =
pg_strcasecmp(prev2_wd, "DATABASE") == 0)
{
static const char *const list_ALTERDATABASE[] =
- {"RESET", "SET", "OWNER TO", "RENAME TO", NULL};
+ {"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};
COMPLETE_WITH_LIST(list_ALTERDATABASE);
}
+ /* ALTER FOREIGN DATA WRAPPER <name> */
+ 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 list_ALTER_FDW[] =
+ {"VALIDATOR", "OPTIONS", "OWNER TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_FDW);
+ }
+
/* ALTER INDEX <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "INDEX") == 0)
{
static const char *const list_ALTERINDEX[] =
- {"SET TABLESPACE", "OWNER TO", "RENAME TO", NULL};
+ {"OWNER TO", "RENAME TO", "SET", "RESET", NULL};
COMPLETE_WITH_LIST(list_ALTERINDEX);
}
/* ALTER LANGUAGE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "LANGUAGE") == 0)
- COMPLETE_WITH_CONST("RENAME TO");
+ {
+ static const char *const list_ALTERLANGUAGE[] =
+ {"OWNER TO", "RENAME TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERLANGUAGE);
+ }
+
+ /* ALTER LARGE OBJECT <oid> */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "LARGE") == 0 &&
+ pg_strcasecmp(prev2_wd, "OBJECT") == 0)
+ {
+ static const char *const list_ALTERLARGEOBJECT[] =
+ {"OWNER TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT);
+ }
- /* ALTER USER <name> */
+ /* ALTER USER,ROLE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
- pg_strcasecmp(prev2_wd, "USER") == 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 list_ALTERUSER[] =
{"ENCRYPTED", "UNENCRYPTED", "CREATEDB", "NOCREATEDB", "CREATEUSER",
- "NOCREATEUSER", "VALID UNTIL", "RENAME TO", "SET", "RESET", NULL};
+ "NOCREATEUSER", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT",
+ "LOGIN", "NOLOGIN", "CONNECTION LIMIT", "VALID UNTIL", "RENAME TO",
+ "SUPERUSER", "NOSUPERUSER", "SET", "RESET", NULL};
COMPLETE_WITH_LIST(list_ALTERUSER);
}
+ /* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
+ 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))
+ {
+ COMPLETE_WITH_CONST("PASSWORD");
+ }
/* ALTER DOMAIN <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_ALTERDOMAIN3[] =
- {"DEFAULT", "NOT NULL", NULL};
+ {"DEFAULT", "NOT NULL", "SCHEMA", NULL};
COMPLETE_WITH_LIST(list_ALTERDOMAIN3);
}
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "SEQUENCE") == 0)
{
- static const char *const list_ALTERSCHEMA[] =
- {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE", NULL};
+ static const char *const list_ALTERSEQUENCE[] =
+ {"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
+ "SET SCHEMA", "OWNED BY", "RENAME TO", NULL};
- COMPLETE_WITH_LIST(list_ALTERSCHEMA);
+ COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
}
/* ALTER SEQUENCE <name> 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 list_ALTERSCHEMA2[] =
- {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
-
- COMPLETE_WITH_LIST(list_ALTERSCHEMA2);
+ static const char *const list_ALTERSEQUENCE2[] =
+ {"MINVALUE", "MAXVALUE", "CYCLE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERSEQUENCE2);
+ }
+ /* ALTER SERVER <name> */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "SERVER") == 0)
+ {
+ static const char *const list_ALTER_SERVER[] =
+ {"VERSION", "OPTIONS", "OWNER TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_SERVER);
+ }
+ /* ALTER VIEW <name> */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "VIEW") == 0)
+ {
+ static const char *const list_ALTERVIEW[] = {"RENAME TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERVIEW);
}
/* ALTER TRIGGER <name>, 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)
{
pg_strcasecmp(prev2_wd, "TABLE") == 0)
{
static const char *const list_ALTER2[] =
- {"ADD", "ALTER", "CLUSTER ON", "DROP", "RENAME", "OWNER TO",
- "SET", NULL};
+ {"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
+ "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", NULL};
COMPLETE_WITH_LIST(list_ALTER2);
}
+ /* 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 list_ALTERENABLE[] =
+ {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERENABLE);
+ }
+ 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 list_ALTERENABLE2[] =
+ {"RULE", "TRIGGER", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERENABLE2);
+ }
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev_wd, "DISABLE") == 0)
+ {
+ static const char *const list_ALTERDISABLE[] =
+ {"RULE", "TRIGGER", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERDISABLE);
+ }
+
/* If we have TABLE <sth> ALTER|RENAME, provide list of columns */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
(pg_strcasecmp(prev_wd, "ALTER") == 0 ||
pg_strcasecmp(prev_wd, "RENAME") == 0))
- COMPLETE_WITH_ATTR(prev2_wd);
+ COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN'");
+
+ /*
+ * If we have TABLE <sth> 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(prev2_wd, "RENAME") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") != 0)
+ COMPLETE_WITH_CONST("TO");
+
+ /* ALTER TABLE xxx RENAME COLUMN yyy */
+ else if (pg_strcasecmp(prev5_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev3_wd, "RENAME") == 0 &&
+ pg_strcasecmp(prev2_wd, "COLUMN") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") != 0)
COMPLETE_WITH_CONST("TO");
/* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
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);
+ COMPLETE_WITH_ATTR(prev3_wd, "");
/* ALTER TABLE ALTER [COLUMN] <foo> */
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))
{
- /* DROP ... does not work well yet */
- static const char *const list_COLUMNALTER[] =
- {"TYPE", "SET DEFAULT", "DROP DEFAULT", "SET NOT NULL",
- "DROP NOT NULL", "SET STATISTICS", "SET STORAGE", NULL};
+ static const char *const list_COLUMNALTER[] =
+ {"TYPE", "SET", "DROP", NULL};
COMPLETE_WITH_LIST(list_COLUMNALTER);
}
+ 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 list_COLUMNSET[] =
+ {"DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
+
+ COMPLETE_WITH_LIST(list_COLUMNSET);
+ }
+ 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, "STATISTICS") == 0)
+ {
+ static const char *const list_COLUMNSETSTATS[] =
+ {"DISTINCT", NULL};
+
+ COMPLETE_WITH_LIST(list_COLUMNSETSTATS);
+ }
+ 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 list_COLUMNDROP[] =
+ {"DEFAULT", "NOT NULL", NULL};
+
+ COMPLETE_WITH_LIST(list_COLUMNDROP);
+ }
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "CLUSTER") == 0)
COMPLETE_WITH_CONST("ON");
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
- /* If we have TABLE <sth> SET, provide WITHOUT or TABLESPACE */
+ /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"WITHOUT", "TABLESPACE", NULL};
+ {"WITHOUT", "TABLESPACE", "SCHEMA", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
COMPLETE_WITH_LIST(list_ALTERTSPC);
}
- /* complete ALTER TYPE <foo> with OWNER TO */
+ /* 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 list_ALTERTEXTSEARCH[] =
+ {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
+ }
+ 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))
+ COMPLETE_WITH_CONST("RENAME TO");
+
+ 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 list_ALTERTEXTSEARCH2[] =
+ {"OWNER TO", "RENAME TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2);
+ }
+
+ 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 list_ALTERTEXTSEARCH3[] =
+ {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
+ }
+
+ /* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "TYPE") == 0)
- COMPLETE_WITH_CONST("OWNER TO");
+ {
+ static const char *const list_ALTERTYPE[] =
+ {"OWNER TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTYPE);
+ }
/* complete ALTER GROUP <foo> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_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_users);
-
-/* 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_tables, NULL);
- /* If we have ANALYZE <table>, complete with semicolon. */
- else if (pg_strcasecmp(prev2_wd, "ANALYZE") == 0)
- COMPLETE_WITH_CONST(";");
+ COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-/* BEGIN, END, COMMIT, ABORT */
+/* BEGIN, END, ABORT */
else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
pg_strcasecmp(prev_wd, "END") == 0 ||
- pg_strcasecmp(prev_wd, "COMMIT") == 0 ||
pg_strcasecmp(prev_wd, "ABORT") == 0)
{
static const char *const list_TRANS[] =
COMPLETE_WITH_LIST(list_TRANS);
}
+/* COMMIT */
+ else if (pg_strcasecmp(prev_wd, "COMMIT") == 0)
+ {
+ static const char *const list_COMMIT[] =
+ {"WORK", "TRANSACTION", "PREPARED", NULL};
+
+ COMPLETE_WITH_LIST(list_COMMIT);
+ }
/* RELEASE SAVEPOINT */
else if (pg_strcasecmp(prev_wd, "RELEASE") == 0)
COMPLETE_WITH_CONST("SAVEPOINT");
else if (pg_strcasecmp(prev_wd, "ROLLBACK") == 0)
{
static const char *const list_TRANS[] =
- {"WORK", "TRANSACTION", "TO SAVEPOINT", NULL};
+ {"WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED", NULL};
COMPLETE_WITH_LIST(list_TRANS);
}
/* CLUSTER */
/*
- * If the previous word is CLUSTER and not without produce list of
- * indexes.
+ * 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_indexes, NULL);
- /* If we have CLUSTER <sth>, then add "ON" */
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ /* If we have CLUSTER <sth>, then add "USING" */
else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
pg_strcasecmp(prev_wd, "ON") != 0)
- COMPLETE_WITH_CONST("ON");
+ {
+ COMPLETE_WITH_CONST("USING");
+ }
/*
- * If we have CLUSTER <sth> ON, then add the correct tablename as
- * well.
+ * If we have CLUSTER <sth> USING, then add the index as well.
*/
else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
- pg_strcasecmp(prev_wd, "ON") == 0)
+ pg_strcasecmp(prev_wd, "USING") == 0)
{
completion_info_charp = prev2_wd;
- COMPLETE_WITH_QUERY(Query_for_table_owning_index);
+ COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
/* COMMENT */
static const char *const list_COMMENT[] =
{"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
"SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
- "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", NULL};
+ "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT",
+ "TABLESPACE", "TEXT SEARCH", "ROLE", NULL};
COMPLETE_WITH_LIST(list_COMMENT);
}
else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
- pg_strcasecmp(prev3_wd, "ON") == 0)
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
+ pg_strcasecmp(prev_wd, "SEARCH") == 0)
+ {
+ static const char *const list_TRANS2[] =
+ {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
+
+ COMPLETE_WITH_LIST(list_TRANS2);
+ }
+ else if ((pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0) ||
+ (pg_strcasecmp(prev5_wd, "ON") == 0 &&
+ pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
+ pg_strcasecmp(prev3_wd, "SEARCH") == 0))
COMPLETE_WITH_CONST("IS");
/* COPY */
else if (pg_strcasecmp(prev2_wd, "COPY") == 0 ||
pg_strcasecmp(prev2_wd, "\\copy") == 0 ||
pg_strcasecmp(prev2_wd, "BINARY") == 0)
- {
- static const char *const list_FROMTO[] =
- {"FROM", "TO", NULL};
-
- COMPLETE_WITH_LIST(list_FROMTO);
- }
+ {
+ static const char *const list_FROMTO[] =
+ {"FROM", "TO", NULL};
+
+ COMPLETE_WITH_LIST(list_FROMTO);
+ }
/* If we have COPY|BINARY <sth> FROM|TO, complete with filename */
else if ((pg_strcasecmp(prev3_wd, "COPY") == 0 ||
pg_strcasecmp(prev3_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 list_COPY[] =
- {"BINARY", "OIDS", "DELIMETER", "NULL", "CSV", NULL};
+ {
+ static const char *const list_COPY[] =
+ {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL};
- COMPLETE_WITH_LIST(list_COPY);
- }
+ COMPLETE_WITH_LIST(list_COPY);
+ }
/* Handle COPY|BINARY <sth> FROM|TO filename CSV */
- else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
+ else if (pg_strcasecmp(prev_wd, "CSV") == 0 &&
(pg_strcasecmp(prev3_wd, "FROM") == 0 ||
pg_strcasecmp(prev3_wd, "TO") == 0))
- {
- static const char *const list_CSV[] =
- {"QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
+ {
+ static const char *const list_CSV[] =
+ {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
- COMPLETE_WITH_LIST(list_CSV);
- }
+ COMPLETE_WITH_LIST(list_CSV);
+ }
+
+ /* CREATE DATABASE */
+ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev2_wd, "DATABASE") == 0)
+ {
+ static const char *const list_DATABASE[] =
+ {"OWNER", "TEMPLATE", "ENCODING", "TABLESPACE", "CONNECTION LIMIT",
+ NULL};
+
+ COMPLETE_WITH_LIST(list_DATABASE);
+ }
-/* CREATE INDEX */
+ 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 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)
+ COMPLETE_WITH_CONST("VALIDATOR");
+
+ /* 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_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
/*
- * Complete INDEX <name> ON <table> with a list of table columns
- * (which should really be in parens)
+ * Complete INDEX <name> ON <table> with a list of table columns (which
+ * should really be in parens)
*/
else if (pg_strcasecmp(prev4_wd, "INDEX") == 0 &&
pg_strcasecmp(prev2_wd, "ON") == 0)
- COMPLETE_WITH_ATTR(prev_wd);
+ {
+ if (find_open_parenthesis(end))
+ COMPLETE_WITH_ATTR(prev_wd, "");
+ else
+ COMPLETE_WITH_CONST("(");
+ }
+ else if (pg_strcasecmp(prev5_wd, "INDEX") == 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(prev4_wd, "ON") == 0 &&
pg_strcasecmp(prev2_wd, "USING") == 0)
- COMPLETE_WITH_ATTR(prev3_wd);
+ COMPLETE_WITH_ATTR(prev3_wd, "");
/* Complete USING with an index method */
else if (pg_strcasecmp(prev_wd, "USING") == 0)
{
static const char *const index_mth[] =
- {"BTREE", "RTREE", "HASH", "GIST", NULL};
+ {"BTREE", "HASH", "GIN", "GIST", NULL};
COMPLETE_WITH_LIST(index_mth);
}
/* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
else if (pg_strcasecmp(prev3_wd, "AS") == 0 &&
pg_strcasecmp(prev2_wd, "ON") == 0 &&
- (toupper((unsigned char) prev_wd[4]) == 'T' ||
- toupper((unsigned char) prev_wd[5]) == 'T'))
+ (pg_toupper((unsigned char) prev_wd[4]) == 'T' ||
+ pg_toupper((unsigned char) prev_wd[5]) == 'T'))
COMPLETE_WITH_CONST("TO");
/* Complete "AS ON <sth> TO" with a table name */
else if (pg_strcasecmp(prev4_wd, "AS") == 0 &&
pg_strcasecmp(prev_wd, "TO") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+/* CREATE SERVER <name> */
+ 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 with "TABLE" */
+ /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
- pg_strcasecmp(prev_wd, "TEMP") == 0)
- COMPLETE_WITH_CONST("TABLE");
+ (pg_strcasecmp(prev_wd, "TEMP") == 0 ||
+ pg_strcasecmp(prev_wd, "TEMPORARY") == 0))
+ {
+ static const char *const list_TEMP[] =
+ {"SEQUENCE", "TABLE", "VIEW", NULL};
+
+ COMPLETE_WITH_LIST(list_TEMP);
+ }
+
+/* CREATE TABLESPACE */
+ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev2_wd, "TABLESPACE") == 0)
+ {
+ static const char *const list_CREATETABLESPACE[] =
+ {"OWNER", "LOCATION", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATETABLESPACE);
+ }
+ /* 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 list_CREATETEXTSEARCH[] =
+ {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATETEXTSEARCH);
+ }
+ 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 */
- /* is on the agenda . . . */
+ /* complete CREATE TRIGGER <name> with BEFORE,AFTER */
+ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
+ {
+ static const char *const list_CREATETRIGGER[] =
+ {"BEFORE", "AFTER", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATETRIGGER);
+ }
+ /* complete CREATE TRIGGER <name> 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))
+ {
+ static const char *const list_CREATETRIGGER2[] =
+ {"ON", "OR", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATETRIGGER2);
+ }
+
+/* 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 list_CREATEROLE[] =
+ {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
+ "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOINHERIT", "NOLOGIN", "NOCREATEDB",
+ "NOCREATEROLE", "NOCREATEUSER", "NOSUPERUSER", "ROLE", "SUPERUSER", "SYSID",
+ "UNENCRYPTED", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATEROLE);
+ }
+
+ /*
+ * complete CREATE ROLE,USER,GROUP <name> 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 <name> 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 list_CREATEROLE3[] =
+ {"GROUP", "ROLE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATEROLE3);
+ }
/* CREATE VIEW */
- /* Complete "CREATE VIEW <name>" with "AS" */
+ /* Complete CREATE VIEW <name> with AS */
else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
pg_strcasecmp(prev2_wd, "VIEW") == 0)
COMPLETE_WITH_CONST("AS");
pg_strcasecmp(prev_wd, "AS") == 0)
COMPLETE_WITH_CONST("SELECT");
+/* DECLARE */
+ else if (pg_strcasecmp(prev2_wd, "DECLARE") == 0)
+ {
+ static const char *const list_DECLARE[] =
+ {"BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR", NULL};
+
+ COMPLETE_WITH_LIST(list_DECLARE);
+ }
+
+ else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
+ {
+ static const char *const list_DECLARECURSOR[] =
+ {"WITH HOLD", "WITHOUT HOLD", "FOR", NULL};
+
+ COMPLETE_WITH_LIST(list_DECLARECURSOR);
+ }
+
+
/* DELETE */
/*
else if (pg_strcasecmp(prev2_wd, "DELETE") == 0 &&
pg_strcasecmp(prev_wd, "FROM") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
- /* Complete DELETE FROM <table> with "WHERE" (perhaps a safe idea?) */
+ /* Complete DELETE FROM <table> */
else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
pg_strcasecmp(prev2_wd, "FROM") == 0)
- COMPLETE_WITH_CONST("WHERE");
+ {
+ static const char *const list_DELETE[] =
+ {"USING", "WHERE", "SET", NULL};
+
+ COMPLETE_WITH_LIST(list_DELETE);
+ }
+ /* XXX: implement tab completion for DELETE ... USING */
+
+/* DISCARD */
+ else if (pg_strcasecmp(prev_wd, "DISCARD") == 0)
+ {
+ static const char *const list_DISCARD[] =
+ {"ALL", "PLANS", "TEMP", NULL};
+
+ COMPLETE_WITH_LIST(list_DISCARD);
+ }
+
+/* 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, "CONVERSION") == 0 ||
+ pg_strcasecmp(prev2_wd, "DOMAIN") == 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))
+ {
+ if (find_open_parenthesis(end))
+ {
+ static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
+ char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev_wd));
+
+ sprintf(tmp_buf, func_args_query, prev_wd);
+ COMPLETE_WITH_QUERY(tmp_buf);
+ free(tmp_buf);
+ }
+ else
+ {
+ COMPLETE_WITH_CONST("(");
+ }
+ }
+ else
+ {
+ static const char *const list_DROPCR[] =
+ {"CASCADE", "RESTRICT", NULL};
+
+ COMPLETE_WITH_LIST(list_DROPCR);
+ }
+ }
+ else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev3_wd, "FUNCTION") == 0 &&
+ pg_strcasecmp(prev_wd, "(") == 0)
+ {
+ static const char func_args_query[] = "select pg_catalog.oidvectortypes(proargtypes)||')' from pg_proc where proname='%s'";
+ char *tmp_buf = malloc(strlen(func_args_query) + strlen(prev2_wd));
+
+ sprintf(tmp_buf, func_args_query, prev2_wd);
+ COMPLETE_WITH_QUERY(tmp_buf);
+ free(tmp_buf);
+ }
+ /* 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 list_ALTERTEXTSEARCH[] =
+ {"CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
+ }
/* EXPLAIN */
/*
- * Complete EXPLAIN [VERBOSE] (which you'd have to type yourself) with
- * the list of SQL commands
+ * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
*/
- else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0 ||
- (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
+ else if (pg_strcasecmp(prev_wd, "EXPLAIN") == 0)
+ {
+ static const char *const list_EXPLAIN[] =
+ {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "ANALYZE", "VERBOSE", NULL};
+
+ COMPLETE_WITH_LIST(list_EXPLAIN);
+ }
+ else if (pg_strcasecmp(prev2_wd, "EXPLAIN") == 0 &&
+ pg_strcasecmp(prev_wd, "ANALYZE") == 0)
+ {
+ static const char *const list_EXPLAIN[] =
+ {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", "VERBOSE", NULL};
+
+ COMPLETE_WITH_LIST(list_EXPLAIN);
+ }
+ 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))
- COMPLETE_WITH_LIST(sql_commands);
+ {
+ static const char *const list_EXPLAIN[] =
+ {"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
+
+ COMPLETE_WITH_LIST(list_EXPLAIN);
+ }
/* FETCH && MOVE */
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
pg_strcasecmp(prev_wd, "MOVE") == 0)
{
static const char *const list_FETCH1[] =
- {"ABSOLUT", "BACKWARD", "FORWARD", "RELATIVE", NULL};
+ {"ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE", NULL};
COMPLETE_WITH_LIST(list_FETCH1);
}
}
/*
- * Complete FETCH <sth1> <sth2> with "FROM" or "TO". (Is there a
- * difference? If not, remove one.)
+ * Complete FETCH <sth1> <sth2> 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 list_FROMTO[] =
- {"FROM", "TO", NULL};
+ static const char *const list_FROMIN[] =
+ {"FROM", "IN", NULL};
- COMPLETE_WITH_LIST(list_FROMTO);
+ COMPLETE_WITH_LIST(list_FROMIN);
}
+/* 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);
+
/* GRANT && REVOKE*/
/* Complete GRANT/REVOKE with a list of privileges */
else if (pg_strcasecmp(prev_wd, "GRANT") == 0 ||
pg_strcasecmp(prev_wd, "REVOKE") == 0)
{
static const char *const list_privileg[] =
- {"SELECT", "INSERT", "UPDATE", "DELETE", "RULE", "REFERENCES",
- "TRIGGER", "CREATE", "TEMPORARY", "EXECUTE", "USAGE", "ALL", NULL};
+ {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
+ "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
+ "ALL", NULL};
COMPLETE_WITH_LIST(list_privileg);
}
COMPLETE_WITH_CONST("ON");
/*
- * Complete GRANT/REVOKE <sth> ON with a list of tables, views,
- * sequences, and indexes
+ * Complete GRANT/REVOKE <sth> 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
+ * 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
+ * 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_tsv,
" UNION SELECT 'DATABASE'"
+ " 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'");
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
+ else if (pg_strcasecmp(prev4_wd, "GRANT") == 0)
COMPLETE_WITH_CONST("TO");
+ else
+ COMPLETE_WITH_CONST("FROM");
}
- /*
- * TODO: to complete with user name we need prev5_wd -- wait for a
- * more general solution there same for GRANT <sth> ON { DATABASE |
- * FUNCTION | LANGUAGE | SCHEMA | TABLESPACE } xxx TO
- */
+ /* Complete "GRANT/REVOKE * ON * TO/FROM" with username, GROUP, or PUBLIC */
+ else if (pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ ((pg_strcasecmp(prev5_wd, "GRANT") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") == 0) ||
+ (pg_strcasecmp(prev5_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 (rl_line_buffer[start - 1] == '(' &&
pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
pg_strcasecmp(prev2_wd, "INTO") == 0)
- COMPLETE_WITH_ATTR(prev_wd);
+ COMPLETE_WITH_ATTR(prev_wd, "");
/*
- * Complete INSERT INTO <table> with "VALUES" or "SELECT" or "DEFAULT
- * VALUES"
+ * Complete INSERT INTO <table> with "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 list_INSERT[] =
- {"DEFAULT VALUES", "SELECT", "VALUES", NULL};
+ {"DEFAULT VALUES", "SELECT", "TABLE", "VALUES", NULL};
COMPLETE_WITH_LIST(list_INSERT);
}
- /* Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" */
+
+ /*
+ * Complete INSERT INTO <table> (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 list_INSERT[] =
- {"SELECT", "VALUES", NULL};
+ {"SELECT", "TABLE", "VALUES", NULL};
COMPLETE_WITH_LIST(list_INSERT);
}
/* LOCK */
/* Complete LOCK [TABLE] with a list of tables */
- else if (pg_strcasecmp(prev_wd, "LOCK") == 0 ||
- (pg_strcasecmp(prev_wd, "TABLE") == 0 &&
- pg_strcasecmp(prev2_wd, "LOCK") == 0))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ 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 */
/* NOTIFY */
else if (pg_strcasecmp(prev_wd, "NOTIFY") == 0)
COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s'");
-/* OWNER TO - complete with available users*/
+
+/* 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_users);
+ 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 list_PREPARE[] =
+ {"SELECT", "UPDATE", "INSERT", "DELETE", NULL};
+
+ COMPLETE_WITH_LIST(list_PREPARE);
+ }
+
+/* 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);
+
/* REINDEX */
else if (pg_strcasecmp(prev_wd, "REINDEX") == 0)
{
static const char *const list_REINDEX[] =
- {"TABLE", "DATABASE", "INDEX", NULL};
+ {"TABLE", "INDEX", "SYSTEM", "DATABASE", NULL};
COMPLETE_WITH_LIST(list_REINDEX);
}
{
if (pg_strcasecmp(prev_wd, "TABLE") == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
- else if (pg_strcasecmp(prev_wd, "DATABASE") == 0)
- COMPLETE_WITH_QUERY(Query_for_list_of_databases);
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);
}
/* SELECT */
/* 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 ||
- pg_strcasecmp(prev_wd, "SHOW") == 0)
- COMPLETE_WITH_LIST(pgsql_variables);
+ 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(prev_wd, "TRANSACTION") == 0))
{
static const char *const my_list[] =
- {"ISOLATION", "READ", NULL};
+ {"ISOLATION LEVEL", "READ", NULL};
COMPLETE_WITH_LIST(my_list);
}
COMPLETE_WITH_LIST(constraint_list);
}
+ /* 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)
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_users);
+ 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 <var> with "TO" */
else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
pg_strcasecmp(prev4_wd, "UPDATE") != 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))
+ (pg_strcasecmp(prev_wd, "TO") == 0 || strcmp(prev_wd, "=") == 0))
{
if (pg_strcasecmp(prev2_wd, "DateStyle") == 0)
{
COMPLETE_WITH_LIST(my_list);
}
+ else if (pg_strcasecmp(prev2_wd, "IntervalStyle") == 0)
+ {
+ static const char *const my_list[] =
+ {"postgres", "postgres_verbose", "sql_standard", "iso_8601", NULL};
+
+ COMPLETE_WITH_LIST(my_list);
+ }
else if (pg_strcasecmp(prev2_wd, "GEQO") == 0)
{
static const char *const my_list[] =
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.
+ * 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);
+ 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);
+ 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 */
+/*
+ * 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_tables,
" 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, "ANALYZE") == 0 ||
- pg_strcasecmp(prev_wd, "VERBOSE") == 0))
+ pg_strcasecmp(prev_wd, "FREEZE") == 0))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
+ " 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_tables,
+ " 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_tables,
+ " 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_tables,
+ " 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_tables,
+ " 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_tables, NULL);
+
+/* WITH [RECURSIVE] */
+ else if (pg_strcasecmp(prev_wd, "WITH") == 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_tables, 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);
+ COMPLETE_WITH_ATTR(prev2_wd, "");
/* ... FROM ... */
/* TODO: also include SRF ? */
/* TODO: \dc \dd \dl */
else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
- else if (strcmp(prev_wd, "\\d") == 0 || strcmp(prev_wd, "\\d+") == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, NULL);
- else if (strcmp(prev_wd, "\\da") == 0)
+
+ else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
- else if (strcmp(prev_wd, "\\db") == 0)
+ else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
- else if (strcmp(prev_wd, "\\dD") == 0)
+ else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
- else if (strcmp(prev_wd, "\\df") == 0 || strcmp(prev_wd, "\\df+") == 0)
+ 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 (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0)
+ 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 (strcmp(prev_wd, "\\dn") == 0)
+ else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
- else if (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 0)
+ else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
- else if (strcmp(prev_wd, "\\ds") == 0 || strcmp(prev_wd, "\\ds+") == 0)
+ else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
- else if (strcmp(prev_wd, "\\dS") == 0 || strcmp(prev_wd, "\\dS+") == 0)
- COMPLETE_WITH_QUERY(Query_for_list_of_system_relations);
- else if (strcmp(prev_wd, "\\dt") == 0 || strcmp(prev_wd, "\\dt+") == 0)
+ else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
- else if (strcmp(prev_wd, "\\dT") == 0 || strcmp(prev_wd, "\\dT+") == 0)
+ else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
- else if (strcmp(prev_wd, "\\du") == 0)
- COMPLETE_WITH_QUERY(Query_for_list_of_users);
- else if (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
+ else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 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);
+
+ /* must be at end of \d list */
+ else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tisv, 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(sql_commands);
+ 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 my_list[] =
{"format", "border", "expanded",
- "null", "fieldsep", "tuples_only", "title", "tableattr", "pager",
- "recordsep", NULL};
+ "null", "fieldsep", "tuples_only", "title", "tableattr",
+ "linestyle", "pager", "recordsep", NULL};
COMPLETE_WITH_LIST(my_list);
}
else if (strcmp(prev_wd, "\\cd") == 0 ||
- strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 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, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
+ strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 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
+ strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
)
matches = completion_matches(text, filename_completion_function);
/*
- * 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.
+ * 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
{
}
/*
- * 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 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)
{
free(prev2_wd);
free(prev3_wd);
free(prev4_wd);
+ free(prev5_wd);
/* Return our Grand List O' Matches */
return matches;
something of that sort.
*/
-/* This one gives you one from a list of things you can put after CREATE or DROP
+/* This one gives you one from a list of things you can put after CREATE
as defined above.
*/
static char *
/* find something that matches */
while ((name = words_after_create[list_index++].name))
- if (pg_strncasecmp(name, text, string_length) == 0)
+ {
+ if ((pg_strncasecmp(name, text, string_length) == 0) && !words_after_create[list_index - 1].noshow)
return pg_strdup(name);
-
+ }
/* if nothing matches, return NULL */
return NULL;
}
+/*
+ * This function gives you a list of things you can put after a DROP command.
+ * Very similar to create_command_generator, but has an additional entry for
+ * OWNED BY. (We do it this way in order not to duplicate the
+ * words_after_create list.)
+ */
+static char *
+drop_command_generator(const char *text, int state)
+{
+ static int list_index,
+ string_length;
+ const char *name;
+
+ if (state == 0)
+ {
+ /* If this is the first time for this completion, init some values */
+ list_index = 0;
+ string_length = strlen(text);
+
+ /*
+ * DROP can be followed by "OWNED BY", which is not found in the list
+ * for CREATE matches, so make it the first state. (We do not make it
+ * the last state because it would be more difficult to detect when we
+ * have to return NULL instead.)
+ *
+ * Make sure we advance to the next state.
+ */
+ list_index++;
+ if (pg_strncasecmp("OWNED", text, string_length) == 0)
+ return pg_strdup("OWNED");
+ }
+
+ /*
+ * In subsequent attempts, try to complete with the same items we use for
+ * CREATE
+ */
+ while ((name = words_after_create[list_index++ - 1].name))
+ {
+ if ((pg_strncasecmp(name, text, string_length) == 0) && (!words_after_create[list_index - 2].noshow))
+ return pg_strdup(name);
+ }
+
+ /* if nothing matches, return NULL */
+ return NULL;
+}
/* The following two functions are wrappers for _complete_from_query */
The query can be one of two kinds:
- A simple query which must contain a %d and a %s, which will be replaced
by the string length of the text and the text itself. The query may also
- have another %s in it, which will be replaced by the value of
- completion_info_charp.
+ have up to four more %s in it; the first two such will be replaced by the
+ value of completion_info_charp, the next two by the value of
+ completion_info_charp2.
or:
- A schema query used for completion of both schema and relation names;
these are more complex and must contain in the following order:
static PGresult *result = NULL;
/*
- * If this is the first time for this completion, we fetch a list of
- * our "things" from the backend.
+ * 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;
char *e_info_charp;
+ char *e_info_charp2;
list_index = 0;
string_length = strlen(text);
result = NULL;
/* Set up suitably-escaped copies of textual inputs */
- if (text)
- {
- e_text = pg_malloc(strlen(text) *2 + 1);
- PQescapeString(e_text, text, strlen(text));
- }
- else
- e_text = NULL;
+ e_text = pg_malloc(string_length * 2 + 1);
+ PQescapeString(e_text, text, string_length);
if (completion_info_charp)
{
else
e_info_charp = NULL;
+ if (completion_info_charp2)
+ {
+ size_t charp_len;
+
+ charp_len = strlen(completion_info_charp2);
+ e_info_charp2 = pg_malloc(charp_len * 2 + 1);
+ PQescapeString(e_info_charp2, completion_info_charp2,
+ charp_len);
+ }
+ else
+ e_info_charp2 = NULL;
+
initPQExpBuffer(&query_buffer);
if (is_schema_query)
if (completion_squery->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
completion_squery->selcondition);
- appendPQExpBuffer(&query_buffer, "%s AND ",
- completion_squery->viscondition);
appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
completion_squery->result,
string_length, e_text);
+ appendPQExpBuffer(&query_buffer, " AND %s",
+ completion_squery->viscondition);
/*
- * When fetching relation names, suppress system catalogs
- * unless the input-so-far begins with "pg_". This is a
- * compromise between not offering system catalogs for
- * completion at all, and having them swamp the result when
- * the input is just "p".
+ * When fetching relation names, suppress system catalogs unless
+ * the input-so-far begins with "pg_". This is a compromise
+ * between not offering system catalogs for completion at all, and
+ * 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"
- " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
+ " AND c.relnamespace <> (SELECT oid FROM"
+ " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
}
/*
- * Add in matching schema names, but only if there is more
- * than one potential match among schema names.
+ * Add in matching schema names, but only if there is more than
+ * one potential match among schema names.
*/
appendPQExpBuffer(&query_buffer, "\nUNION\n"
- "SELECT pg_catalog.quote_ident(n.nspname) || '.' "
+ "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,
" AND (SELECT pg_catalog.count(*)"
" FROM pg_catalog.pg_namespace"
- " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
+ " 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);
/*
- * Add in matching qualified names, but only if there is
- * exactly one schema matching the input-so-far.
+ * Add in matching qualified names, but only if there is exactly
+ * one schema matching the input-so-far.
*/
appendPQExpBuffer(&query_buffer, "\nUNION\n"
- "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
+ "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "
"FROM %s, pg_catalog.pg_namespace n "
"WHERE %s = n.oid AND ",
qualresult,
* speed up the query
*/
appendPQExpBuffer(&query_buffer,
- " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="
+ " 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,
" AND (SELECT pg_catalog.count(*)"
" FROM pg_catalog.pg_namespace"
- " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="
+ " 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);
{
/* completion_charp is an sprintf-style format string */
appendPQExpBuffer(&query_buffer, completion_charp,
- string_length, e_text, e_info_charp);
+ string_length, e_text,
+ e_info_charp, e_info_charp,
+ e_info_charp2, e_info_charp2);
}
/* Limit the number of records in the result */
result = exec_query(query_buffer.data);
termPQExpBuffer(&query_buffer);
-
- if (e_text)
- free(e_text);
+ free(e_text);
if (e_info_charp)
free(e_info_charp);
+ if (e_info_charp2)
+ free(e_info_charp2);
}
/* Find something that matches */
}
/*
- * No matches found. If we're not case insensitive already, lets
- * switch to being case insensitive and try again
+ * No matches found. If we're not case insensitive already, lets switch to
+ * being case insensitive and try again
*/
if (casesensitive && matches == 0)
{
casesensitive = false;
list_index = 0;
state++;
- return (complete_from_list(text, state));
+ return complete_from_list(text, state);
}
/* If no more matches, return null. */
result = PQexec(pset.db, query);
- if (result != NULL && PQresultStatus(result) != PGRES_TUPLES_OK)
+ if (PQresultStatus(result) != PGRES_TUPLES_OK)
{
#if 0
- psql_error("tab completion: %s failed - %s\n",
- query, PQresStatus(PQresultStatus(result)));
+ psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
+ PQerrorMessage(pset.db), query);
#endif
PQclear(result);
result = NULL;
-/* Return the word (space delimited) before point. Set skip > 0 to skip that
- many words; e.g. skip=1 finds the word before the previous one.
-*/
+/*
+ * Return the word (space delimited) before point. Set skip > 0 to
+ * skip that many words; e.g. skip=1 finds the word before the
+ * previous one. Return value is NULL or a malloc'ed string.
+ */
static char *
previous_word(int point, int skip)
{
}
/*
- * If no end found we return null, because there is no word before
- * the point
+ * If no end found we return null, because there is no word before the
+ * point
*/
if (end == -1)
return NULL;
/*
- * Otherwise we now look for the start. The start is either the
- * last character before any space going backwards from the end,
- * or it's simply character 0
+ * Otherwise we now look for the start. The start is either the last
+ * character before any space going backwards from the end, or it's
+ * simply character 0
*/
for (start = end; start > 0; start--)
{
/* make a copy */
s = pg_malloc(end - start + 2);
-
- strncpy(s, &rl_line_buffer[start], end - start + 1);
- s[end - start + 1] = '\0';
+ strlcpy(s, &rl_line_buffer[start], end - start + 2);
return s;
}
+/* Find the parenthesis after the last word */
+static int
+find_open_parenthesis(int end)
+{
+ int i = end - 1;
+
+ while ((rl_line_buffer[i] != ' ') && (i >= 0))
+ {
+ if (rl_line_buffer[i] == '(')
+ return 1;
+ i--;
+ }
+ while ((rl_line_buffer[i] == ' ') && (i >= 0))
+ {
+ i--;
+ }
+ if (rl_line_buffer[i] == '(')
+ {
+ return 1;
+ }
+ return 0;
+
+}
+
#if 0
/*
length = strlen(text);
s = pg_malloc(length - 2 + 1);
- strncpy(s, text +1, length - 2);
- s[length] = '\0';
+ strlcpy(s, text +1, length - 2 + 1);
return s;
}