/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright (c) 2000-2008, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2013, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.174 2008/11/07 18:25:07 tgl Exp $
+ * src/bin/psql/tab-complete.c
*/
/*----------------------------------------------------------------------
#define completion_matches rl_completion_matches
#endif
+/* word break characters */
+#define WORD_BREAKS "\t\n@$><=;|&{() "
/*
* This struct is used to define "schema queries", which are custom-built
* Communication variables set by COMPLETE_WITH_FOO macros and then used by
* the completion callback functions. Ugly but there is no better way.
*/
-static const char *completion_charp; /* to pass a string */
+static const char *completion_charp; /* to pass a string */
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 */
+static bool completion_case_sensitive; /* completion is case sensitive */
/*
* A few macros to ease typing. You can use these to complete the given
* 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).
+ * 6/ The list of arguments to the given function (possibly schema-qualified).
*/
#define COMPLETE_WITH_QUERY(query) \
do { \
matches = completion_matches(text, complete_from_schema_query); \
} while (0)
+#define COMPLETE_WITH_LIST_CS(list) \
+do { \
+ completion_charpp = list; \
+ completion_case_sensitive = true; \
+ matches = completion_matches(text, complete_from_list); \
+} while (0)
+
#define COMPLETE_WITH_LIST(list) \
do { \
completion_charpp = list; \
+ completion_case_sensitive = false; \
matches = completion_matches(text, complete_from_list); \
} while (0)
#define COMPLETE_WITH_CONST(string) \
do { \
completion_charp = string; \
+ completion_case_sensitive = false; \
matches = completion_matches(text, complete_from_const); \
} while (0)
matches = completion_matches(text, complete_from_query); \
} while (0)
+#define COMPLETE_WITH_FUNCTION_ARG(function) \
+do { \
+ char *_completion_schema; \
+ char *_completion_function; \
+\
+ _completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \
+ false, false, pset.encoding); \
+ (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+ false, false, pset.encoding); \
+ _completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+ false, false, pset.encoding); \
+ if (_completion_function == NULL) \
+ { \
+ completion_charp = Query_for_list_of_arguments; \
+ completion_info_charp = function; \
+ } \
+ else \
+ { \
+ completion_charp = Query_for_list_of_arguments_with_schema; \
+ completion_info_charp = _completion_function; \
+ completion_info_charp2 = _completion_schema; \
+ } \
+ matches = completion_matches(text, complete_from_query); \
+} while (0)
+
/*
* Assembly instructions for schema queries
*/
NULL
};
+static const SchemaQuery Query_for_list_of_foreign_tables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "c.relkind IN ('f')",
+ /* 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_tables = {
/* catname */
"pg_catalog.pg_class c",
NULL
};
-static const SchemaQuery Query_for_list_of_tisv = {
+static const SchemaQuery Query_for_list_of_constraints_with_schema = {
+ /* catname */
+ "pg_catalog.pg_constraint c",
+ /* selcondition */
+ "c.conrelid <> 0",
+ /* viscondition */
+ "true", /* there is no pg_constraint_is_visible */
+ /* namespace */
+ "c.connamespace",
+ /* result */
+ "pg_catalog.quote_ident(c.conname)",
+ /* qualresult */
+ NULL
+};
+
+/* The bit masks for the following three functions come from
+ * src/include/catalog/pg_trigger.h.
+ */
+static const SchemaQuery Query_for_list_of_insertables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
+ "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 2) <> 0)))",
+ /* 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_deletables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
+ "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 3) <> 0)))",
+ /* 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_updatables = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "(c.relkind = 'r' OR (c.relkind = 'v' AND c.relhastriggers AND EXISTS "
+ "(SELECT 1 FROM pg_catalog.pg_trigger t WHERE t.tgrelid = c.oid AND t.tgtype & (1 << 4) <> 0)))",
+ /* 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_relations = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
- "c.relkind IN ('r', 'i', 'S', 'v')",
+ NULL,
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
NULL
};
-static const SchemaQuery Query_for_list_of_tsv = {
+static const SchemaQuery Query_for_list_of_tsvmf = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
- "c.relkind IN ('r', 'S', 'v')",
+ "c.relkind IN ('r', 'S', 'v', 'm', 'f')",
+ /* 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_tmf = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "c.relkind IN ('r', 'm', 'f')",
+ /* 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_tm = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "c.relkind IN ('r', 'm')",
/* viscondition */
"pg_catalog.pg_table_is_visible(c.oid)",
/* namespace */
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
#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"
+" 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 "\
#define Query_for_list_of_languages \
"SELECT pg_catalog.quote_ident(lanname) "\
-" FROM pg_language "\
+" FROM pg_catalog.pg_language "\
" WHERE lanname != 'internal' "\
-" AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s' "
+" 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 "\
" UNION ALL SELECT 'all') ss "\
" WHERE substring(name,1,%d)='%s'"
-/*
- * Note: As of Pg 8.2, we no longer use relkind 's', but we keep it here
- * for compatibility with older servers
- */
-#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_roles \
" SELECT pg_catalog.quote_ident(rolname) "\
" FROM pg_catalog.pg_roles "\
" 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_constraint_of_table \
+"SELECT pg_catalog.quote_ident(conname) "\
+" FROM pg_catalog.pg_class c1, pg_catalog.pg_constraint con "\
+" WHERE c1.oid=conrelid and (%d = pg_catalog.length('%s'))"\
+" and pg_catalog.quote_ident(c1.relname)='%s'"\
+" and pg_catalog.pg_table_is_visible(c1.oid)"
+
+#define Query_for_all_table_constraints \
+"SELECT pg_catalog.quote_ident(conname) "\
+" FROM pg_catalog.pg_constraint c "\
+" WHERE c.conrelid <> 0 "
+
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_constraint_of_type \
+"SELECT pg_catalog.quote_ident(conname) "\
+" FROM pg_catalog.pg_type t, pg_catalog.pg_constraint con "\
+" WHERE t.oid=contypid and (%d = pg_catalog.length('%s'))"\
+" and pg_catalog.quote_ident(t.typname)='%s'"\
+" and pg_catalog.pg_type_is_visible(t.oid)"
+
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_list_of_tables_for_constraint \
+"SELECT pg_catalog.quote_ident(relname) "\
+" FROM pg_catalog.pg_class"\
+" WHERE (%d = pg_catalog.length('%s'))"\
+" AND oid IN "\
+" (SELECT conrelid FROM pg_catalog.pg_constraint "\
+" WHERE pg_catalog.quote_ident(conname)='%s')"
+
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_list_of_tables_for_rule \
+"SELECT pg_catalog.quote_ident(relname) "\
+" FROM pg_catalog.pg_class"\
+" WHERE (%d = pg_catalog.length('%s'))"\
+" AND oid IN "\
+" (SELECT ev_class FROM pg_catalog.pg_rewrite "\
+" WHERE pg_catalog.quote_ident(rulename)='%s')"
+
/* 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) "\
"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'"
+
+#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'"
+
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_list_of_arguments \
+"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
+" FROM pg_catalog.pg_proc "\
+" WHERE (%d = pg_catalog.length('%s'))"\
+" AND (pg_catalog.quote_ident(proname)='%s'"\
+" OR '\"' || proname || '\"'='%s') "\
+" AND (pg_catalog.pg_function_is_visible(pg_proc.oid))"
+
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_list_of_arguments_with_schema \
+"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
+" FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n "\
+" WHERE (%d = pg_catalog.length('%s'))"\
+" AND n.oid = p.pronamespace "\
+" AND (pg_catalog.quote_ident(proname)='%s' "\
+" OR '\"' || proname || '\"' ='%s') "\
+" AND (pg_catalog.quote_ident(nspname)='%s' "\
+" OR '\"' || nspname || '\"' ='%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_prepared_statements \
+" SELECT pg_catalog.quote_ident(name) "\
+" FROM pg_catalog.pg_prepared_statements "\
+" WHERE substring(pg_catalog.quote_ident(name),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 */
+ const bits32 flags; /* visibility flags, see below */
} pgsql_thing_t;
+#define THING_NO_CREATE (1 << 0) /* should not show up after CREATE */
+#define THING_NO_DROP (1 << 1) /* should not show up after DROP */
+#define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP)
+
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 */
+ {"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'"},
/*
* 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},
+ {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, THING_NO_SHOW},
{"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},
+ {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW},
{"DOMAIN", NULL, &Query_for_list_of_domains},
+ {"EXTENSION", Query_for_list_of_extensions},
+ {"FOREIGN DATA WRAPPER", NULL, NULL},
+ {"FOREIGN TABLE", NULL, NULL},
{"FUNCTION", NULL, &Query_for_list_of_functions},
{"GROUP", Query_for_list_of_roles},
{"LANGUAGE", Query_for_list_of_languages},
{"INDEX", NULL, &Query_for_list_of_indexes},
+ {"MATERIALIZED VIEW", NULL, NULL},
{"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a
* good idea. */
- {"PARSER", Query_for_list_of_ts_parsers, NULL, true},
+ {"OWNED", NULL, NULL, THING_NO_CREATE}, /* for DROP OWNED BY ... */
+ {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
{"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},
+ {"TEMP", NULL, NULL, THING_NO_DROP}, /* for CREATE TEMP TABLE ... */
+ {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
{"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 ... */
+ {"UNIQUE", NULL, NULL, THING_NO_DROP}, /* for CREATE UNIQUE INDEX ... */
+ {"UNLOGGED", NULL, NULL, THING_NO_DROP}, /* for CREATE UNLOGGED TABLE
+ * ... */
{"USER", Query_for_list_of_roles},
+ {"USER MAPPING FOR", NULL, NULL},
{"VIEW", NULL, &Query_for_list_of_views},
- {NULL, NULL, NULL, false} /* end of list */
+ {NULL} /* end of list */
};
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_const(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 *pg_strdup_keyword_case(const char *s, const char *ref);
static PGresult *exec_query(const char *query);
-static char *previous_word(int point, int skip);
-
-static int find_open_parenthesis(int end);
+static void get_previous_words(int point, char **previous_words, int nwords);
-#if 0
+#ifdef NOT_USED
static char *quote_file_name(char *text, int match_type, char *quote_pointer);
static char *dequote_file_name(char *text, char quote_char);
#endif
-/* Initialize the readline library for our purposes. */
+/*
+ * Initialize the readline library for our purposes.
+ */
void
initialize_readline(void)
{
rl_readline_name = (char *) pset.progname;
rl_attempted_completion_function = (void *) psql_completion;
- rl_basic_word_break_characters = "\t\n@$><=;|&{( ";
+ rl_basic_word_break_characters = WORD_BREAKS;
completion_max_records = 1000;
}
-/* The completion function. Acc. to readline spec this gets passed the text
- entered to far and its start and end in the readline buffer. The return value
- is some partially obscure list format that can be generated by the readline
- libraries completion_matches() function, so we don't have to worry about it.
-*/
+/*
+ * 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 **
psql_completion(char *text, int start, int end)
{
/* This is the variable we'll return. */
char **matches = NULL;
- /* These are going to contain some scannage of the input line. */
- char *prev_wd,
- *prev2_wd,
- *prev3_wd,
- *prev4_wd,
- *prev5_wd;
+ /* This array will contain some scannage of the input line. */
+ char *previous_words[6];
+
+ /* For compactness, we use these macros to reference previous_words[]. */
+#define prev_wd (previous_words[0])
+#define prev2_wd (previous_words[1])
+#define prev3_wd (previous_words[2])
+#define prev4_wd (previous_words[3])
+#define prev5_wd (previous_words[4])
+#define prev6_wd (previous_words[5])
static const char *const sql_commands[] = {
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
- "DELETE FROM", "DISCARD", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
+ "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
"GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
- "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
- "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TRUNCATE", "UNLISTEN",
- "UPDATE", "VACUUM", "VALUES", "WITH", NULL
+ "REASSIGN", "REFRESH", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
+ "SAVEPOINT", "SECURITY LABEL", "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",
- "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
- "\\dn", "\\do", "\\dp", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
- "\\e", "\\echo", "\\encoding",
- "\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
+ "\\a", "\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
+ "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
+ "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
+ "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\dx",
+ "\\e", "\\echo", "\\ef", "\\encoding",
+ "\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
- "\\set", "\\t", "\\T",
- "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
+ "\\set", "\\sf", "\\t", "\\T",
+ "\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
};
(void) end; /* not used */
completion_info_charp2 = NULL;
/*
- * 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.
+ * 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.
*/
- 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);
+ get_previous_words(start, previous_words, lengthof(previous_words));
/* If a backslash command was started, continue */
if (text[0] == '\\')
- COMPLETE_WITH_LIST(backslash_commands);
+ COMPLETE_WITH_LIST_CS(backslash_commands);
+
+ /* Variable interpolation */
+ else if (text[0] == ':' && text[1] != ':')
+ {
+ if (text[1] == '\'')
+ matches = complete_from_variables(text, ":'", "'");
+ else if (text[1] == '"')
+ matches = complete_from_variables(text, ":\"", "\"");
+ else
+ matches = complete_from_variables(text, ":", "");
+ }
/* If no previous word, suggest one of the basic sql commands */
- else if (!prev_wd)
+ else if (prev_wd[0] == '\0')
COMPLETE_WITH_LIST(sql_commands);
/* CREATE */
else if (pg_strcasecmp(prev_wd, "CREATE") == 0)
matches = completion_matches(text, create_command_generator);
-/* DROP, except ALTER (TABLE|DOMAIN|GROUP) sth DROP */
+/* DROP, but not DROP embedded in other commands */
/* complete with something you can drop */
else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
- pg_strcasecmp(prev3_wd, "TABLE") != 0 &&
- pg_strcasecmp(prev3_wd, "DOMAIN") != 0 &&
- pg_strcasecmp(prev3_wd, "GROUP") != 0)
+ prev2_wd[0] == '\0')
matches = completion_matches(text, drop_command_generator);
/* ALTER */
pg_strcasecmp(prev3_wd, "TABLE") != 0)
{
static const char *const list_ALTER[] =
- {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FUNCTION",
- "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SEQUENCE", "TABLE",
- "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "VIEW", NULL};
+ {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
+ "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
+ "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR",
+ "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
+ "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
+ "USER", "USER MAPPING FOR", "VIEW", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
(pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 ||
pg_strcasecmp(prev2_wd, "FUNCTION") == 0))
+ COMPLETE_WITH_CONST("(");
+ /* ALTER AGGREGATE,FUNCTION <name> (...) */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 ||
+ pg_strcasecmp(prev3_wd, "FUNCTION") == 0))
{
- static const char *const list_ALTERAGG[] =
- {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+ if (prev_wd[strlen(prev_wd) - 1] == ')')
+ {
+ static const char *const list_ALTERAGG[] =
+ {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
- COMPLETE_WITH_LIST(list_ALTERAGG);
+ COMPLETE_WITH_LIST(list_ALTERAGG);
+ }
+ else
+ COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
}
- /* ALTER CONVERSION,SCHEMA <name> */
+ /* ALTER SCHEMA <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
- (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 ||
- pg_strcasecmp(prev2_wd, "SCHEMA") == 0))
+ pg_strcasecmp(prev2_wd, "SCHEMA") == 0)
{
static const char *const list_ALTERGEN[] =
{"OWNER TO", "RENAME TO", NULL};
COMPLETE_WITH_LIST(list_ALTERGEN);
}
+ /* ALTER COLLATION <name> */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "COLLATION") == 0)
+ {
+ static const char *const list_ALTERGEN[] =
+ {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERGEN);
+ }
+
+ /* ALTER CONVERSION <name> */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "CONVERSION") == 0)
+ {
+ static const char *const list_ALTERGEN[] =
+ {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERGEN);
+ }
+
/* ALTER DATABASE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "DATABASE") == 0)
COMPLETE_WITH_LIST(list_ALTERDATABASE);
}
+ /* ALTER EXTENSION <name> */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "EXTENSION") == 0)
+ {
+ static const char *const list_ALTEREXTENSION[] =
+ {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTEREXTENSION);
+ }
+
+ /* ALTER FOREIGN */
+ else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ {
+ static const char *const list_ALTER_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_FOREIGN);
+ }
+
+ /* 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[] =
+ {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_FDW);
+ }
+
+ /* ALTER FOREIGN TABLE <name> */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "FOREIGN") == 0 &&
+ pg_strcasecmp(prev2_wd, "TABLE") == 0)
+ {
+ static const char *const list_ALTER_FOREIGN_TABLE[] =
+ {"ALTER", "DROP", "RENAME", "OWNER TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_FOREIGN_TABLE);
+ }
+
/* ALTER INDEX <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "INDEX") == 0)
COMPLETE_WITH_LIST(list_ALTERINDEX);
}
+ /* ALTER INDEX <name> 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 list_ALTERINDEXSET[] =
+ {"(", "TABLESPACE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERINDEXSET);
+ }
+ /* ALTER INDEX <name> 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 <foo> 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 list_INDEXOPTIONS[] =
+ {"fillfactor", "fastupdate", NULL};
+
+ COMPLETE_WITH_LIST(list_INDEXOPTIONS);
+ }
/* ALTER LANGUAGE <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
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 MATERIALIZED VIEW */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "MATERIALIZED") == 0 &&
+ pg_strcasecmp(prev_wd, "VIEW") == 0)
+ {
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
+ }
+
/* ALTER USER,ROLE <name> */
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 list_ALTERUSER[] =
- {"ENCRYPTED", "UNENCRYPTED", "CREATEDB", "NOCREATEDB", "CREATEUSER",
- "NOCREATEUSER", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT",
- "LOGIN", "NOLOGIN", "CONNECTION LIMIT", "VALID UNTIL", "RENAME TO",
- "SUPERUSER", "NOSUPERUSER", "SET", "RESET", NULL};
+ {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
+ "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
+ "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
+ "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
+ "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
COMPLETE_WITH_LIST(list_ALTERUSER);
}
+ /* ALTER USER,ROLE <name> 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 list_ALTERUSER_WITH[] =
+ {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
+ "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
+ "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
+ "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
+ "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERUSER_WITH);
+ }
+
/* 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) &&
{
COMPLETE_WITH_CONST("PASSWORD");
}
+ /* 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 list_ALTER_DEFAULT_PRIVILEGES[] =
+ {"FOR ROLE", "FOR USER", "IN SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES);
+ }
+ /* 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 list_ALTER_DEFAULT_PRIVILEGES_FOR[] =
+ {"ROLE", "USER", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_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 list_ALTER_DEFAULT_PRIVILEGES_REST[] =
+ {"GRANT", "REVOKE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTER_DEFAULT_PRIVILEGES_REST);
+ }
/* ALTER DOMAIN <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "DOMAIN") == 0)
{
static const char *const list_ALTERDOMAIN[] =
- {"ADD", "DROP", "OWNER TO", "SET", NULL};
+ {"ADD", "DROP", "OWNER TO", "RENAME", "SET", "VALIDATE CONSTRAINT", NULL};
COMPLETE_WITH_LIST(list_ALTERDOMAIN);
}
COMPLETE_WITH_LIST(list_ALTERDOMAIN2);
}
+ /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "DOMAIN") == 0 &&
+ (pg_strcasecmp(prev2_wd, "DROP") == 0 ||
+ pg_strcasecmp(prev2_wd, "RENAME") == 0 ||
+ pg_strcasecmp(prev2_wd, "VALIDATE") == 0) &&
+ pg_strcasecmp(prev_wd, "CONSTRAINT") == 0)
+ {
+ completion_info_charp = prev3_wd;
+ COMPLETE_WITH_QUERY(Query_for_constraint_of_type);
+ }
+ /* ALTER DOMAIN <sth> 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 list_ALTERDOMAIN[] =
+ {"CONSTRAINT", "TO", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERDOMAIN);
+ }
+ /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
+ 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 <sth> SET */
else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
pg_strcasecmp(prev3_wd, "DOMAIN") == 0 &&
{
static const char *const list_ALTERSEQUENCE[] =
{"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
- "SET SCHEMA", "OWNED BY", "RENAME TO", NULL};
+ "SET SCHEMA", "OWNED BY", "OWNER TO", "RENAME TO", NULL};
COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
}
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};
+ static const char *const list_ALTERVIEW[] =
+ {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERVIEW);
+ }
+ /* ALTER MATERIALIZED VIEW <name> */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 &&
+ pg_strcasecmp(prev2_wd, "VIEW") == 0)
+ {
+ static const char *const list_ALTERMATVIEW[] =
+ {"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERMATVIEW);
+ }
+
+ /* ALTER RULE <name>, add ON */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "RULE") == 0)
+ COMPLETE_WITH_CONST("ON");
+
+ /* If we have ALTER RULE <name> ON, then add the correct tablename */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "RULE") == 0 &&
+ pg_strcasecmp(prev_wd, "ON") == 0)
+ {
+ completion_info_charp = prev2_wd;
+ COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
+ }
+
+ /* ALTER RULE <name> ON <name> */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "RULE") == 0)
+ COMPLETE_WITH_CONST("RENAME TO");
- 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("RENAME TO");
/*
- * If we detect ALTER TABLE <name>, suggest either ADD, DROP, ALTER,
- * RENAME, CLUSTER ON or OWNER
+ * If we detect ALTER TABLE <name>, suggest sub commands
*/
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "TABLE") == 0)
{
static const char *const list_ALTER2[] =
{"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
- "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET", NULL};
+ "NO INHERIT", "RENAME", "RESET", "OWNER TO", "SET",
+ "VALIDATE CONSTRAINT", NULL};
COMPLETE_WITH_LIST(list_ALTER2);
}
COMPLETE_WITH_LIST(list_ALTERENABLE2);
}
+ /* ALTER TABLE xxx INHERIT */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev_wd, "INHERIT") == 0)
+ {
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
+ }
+ /* ALTER TABLE xxx NO INHERIT */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev2_wd, "NO") == 0 &&
+ pg_strcasecmp(prev_wd, "INHERIT") == 0)
+ {
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
+ }
else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "DISABLE") == 0)
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))
+ /* 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 <sth> ALTER COLUMN|RENAME COLUMN, provide list of
* columns
/* 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 yyy */
+ /* 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, "COLUMN") == 0 ||
+ pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0) &&
pg_strcasecmp(prev_wd, "TO") != 0)
COMPLETE_WITH_CONST("TO");
COMPLETE_WITH_LIST(list_TABLEDROP);
}
- /* If we have TABLE <sth> DROP COLUMN, provide list of columns */
- else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+ /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
pg_strcasecmp(prev2_wd, "DROP") == 0 &&
pg_strcasecmp(prev_wd, "COLUMN") == 0)
COMPLETE_WITH_ATTR(prev3_wd, "");
+
+ /*
+ * If we have ALTER TABLE <sth> DROP|RENAME|VALIDATE CONSTRAINT, provide
+ * list of constraints
+ */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+ (pg_strcasecmp(prev2_wd, "DROP") == 0 ||
+ pg_strcasecmp(prev2_wd, "RENAME") == 0 ||
+ pg_strcasecmp(prev2_wd, "VALIDATE") == 0) &&
+ pg_strcasecmp(prev_wd, "CONSTRAINT") == 0)
+ {
+ completion_info_charp = prev3_wd;
+ COMPLETE_WITH_QUERY(Query_for_constraint_of_table);
+ }
/* 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};
+ {"TYPE", "SET", "RESET", "DROP", NULL};
COMPLETE_WITH_LIST(list_COLUMNALTER);
}
+ /* ALTER TABLE ALTER [COLUMN] <foo> 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 list_COLUMNSET[] =
+ {"(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE", NULL};
+
+ COMPLETE_WITH_LIST(list_COLUMNSET);
+ }
+ /* ALTER TABLE ALTER [COLUMN] <foo> 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 list_COLUMNOPTIONS[] =
+ {"n_distinct", "n_distinct_inherited", NULL};
+
+ COMPLETE_WITH_LIST(list_COLUMNOPTIONS);
+ }
+ /* ALTER TABLE ALTER [COLUMN] <foo> 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 list_COLUMNSTORAGE[] =
+ {"PLAIN", "EXTERNAL", "EXTENDED", "MAIN", NULL};
+
+ COMPLETE_WITH_LIST(list_COLUMNSTORAGE);
+ }
+ /* ALTER TABLE ALTER [COLUMN] <foo> 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 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");
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"WITHOUT", "TABLESPACE", "SCHEMA", NULL};
+ {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
COMPLETE_WITH_LIST(list_TABLESET2);
}
- /* we have ALTER TABLESPACE, so suggest RENAME TO, OWNER TO */
+ /* ALTER TABLE <foo> RESET */
+ else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
+ pg_strcasecmp(prev_wd, "RESET") == 0)
+ COMPLETE_WITH_CONST("(");
+ /* ALTER TABLE <foo> 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 list_TABLEOPTIONS[] =
+ {
+ "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(list_TABLEOPTIONS);
+ }
+
+ /* ALTER TABLESPACE <foo> 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 list_ALTERTSPC[] =
- {"RENAME TO", "OWNER TO", NULL};
+ {"RENAME TO", "OWNER TO", "SET", "RESET", NULL};
COMPLETE_WITH_LIST(list_ALTERTSPC);
}
+ /* ALTER TABLESPACE <foo> 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 <foo> 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 list_TABLESPACEOPTIONS[] =
+ {"seq_page_cost", "random_page_cost", NULL};
+
+ COMPLETE_WITH_LIST(list_TABLESPACEOPTIONS);
+ }
+
/* ALTER TEXT SEARCH */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_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");
+ {
+ static const char *const list_ALTERTEXTSEARCH2[] =
+ {"RENAME TO", "SET SCHEMA", 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, "DICTIONARY") == 0)
{
- static const char *const list_ALTERTEXTSEARCH2[] =
- {"OWNER TO", "RENAME TO", NULL};
+ static const char *const list_ALTERTEXTSEARCH3[] =
+ {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
- COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH2);
+ COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
}
else if (pg_strcasecmp(prev5_wd, "ALTER") == 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};
+ static const char *const list_ALTERTEXTSEARCH4[] =
+ {"ADD MAPPING FOR", "ALTER MAPPING", "DROP MAPPING FOR", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
- COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH3);
+ COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH4);
}
- /* complete ALTER TYPE <foo> with OWNER TO, SET SCHEMA */
+ /* complete ALTER TYPE <foo> with actions */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "TYPE") == 0)
{
static const char *const list_ALTERTYPE[] =
- {"OWNER TO", "SET SCHEMA", NULL};
+ {"ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE",
+ "OWNER TO", "RENAME", "SET SCHEMA", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTYPE);
+ }
+ /* complete ALTER TYPE <foo> 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 list_ALTERTYPE[] =
+ {"ATTRIBUTE", "VALUE", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERTYPE);
+ }
+ /* ALTER TYPE <foo> 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 list_ALTERTYPE[] =
+ {"ATTRIBUTE", "TO", NULL};
COMPLETE_WITH_LIST(list_ALTERTYPE);
}
+ /* 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 <sth> 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 <foo> */
+ else if ((pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "ATTRIBUTE") == 0))
+ {
+ COMPLETE_WITH_CONST("TYPE");
+ }
/* complete ALTER GROUP <foo> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "GROUP") == 0)
/* CLUSTER */
/*
- * If the previous word is CLUSTER and not without produce list of tables
+ * 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_tables, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'");
+
+ /*
+ * If the previous words are CLUSTER VERBOSE produce list of tables
+ */
+ else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
+ pg_strcasecmp(prev2_wd, "CLUSTER") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+
/* If we have CLUSTER <sth>, then add "USING" */
else if (pg_strcasecmp(prev2_wd, "CLUSTER") == 0 &&
- pg_strcasecmp(prev_wd, "ON") != 0)
+ pg_strcasecmp(prev_wd, "ON") != 0 &&
+ pg_strcasecmp(prev_wd, "VERBOSE") != 0)
+ {
+ COMPLETE_WITH_CONST("USING");
+ }
+ /* If we have CLUSTER VERBOSE <sth>, then add "USING" */
+ else if (pg_strcasecmp(prev3_wd, "CLUSTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "VERBOSE") == 0)
{
COMPLETE_WITH_CONST("USING");
}
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
+ /*
+ * If we have CLUSTER VERBOSE <sth> USING, then add the index as well.
+ */
+ else if (pg_strcasecmp(prev4_wd, "CLUSTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "VERBOSE") == 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");
pg_strcasecmp(prev_wd, "ON") == 0)
{
static const char *const list_COMMENT[] =
- {"CAST", "CONVERSION", "DATABASE", "INDEX", "LANGUAGE", "RULE", "SCHEMA",
- "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION",
+ {"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(list_COMMENT);
}
+ else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 &&
+ pg_strcasecmp(prev2_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ {
+ static const char *const list_TRANS2[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(list_TRANS2);
+ }
else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
pg_strcasecmp(prev3_wd, "ON") == 0 &&
pg_strcasecmp(prev2_wd, "TEXT") == 0 &&
COMPLETE_WITH_LIST(list_TRANS2);
}
+ else if (pg_strcasecmp(prev3_wd, "COMMENT") == 0 &&
+ pg_strcasecmp(prev2_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "CONSTRAINT") == 0)
+ {
+ COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
+ }
+ else if (pg_strcasecmp(prev4_wd, "COMMENT") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev2_wd, "CONSTRAINT") == 0)
+ {
+ COMPLETE_WITH_CONST("ON");
+ }
+ else if (pg_strcasecmp(prev5_wd, "COMMENT") == 0 &&
+ pg_strcasecmp(prev4_wd, "ON") == 0 &&
+ pg_strcasecmp(prev3_wd, "CONSTRAINT") == 0 &&
+ pg_strcasecmp(prev_wd, "ON") == 0)
+ {
+ completion_info_charp = prev2_wd;
+ COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint);
+ }
+ 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, "ON") == 0 &&
- pg_strcasecmp(prev4_wd, "TEXT") == 0 &&
- pg_strcasecmp(prev3_wd, "SEARCH") == 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 */
pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
(pg_strcasecmp(prev_wd, "FROM") == 0 ||
pg_strcasecmp(prev_wd, "TO") == 0))
- matches = completion_matches(text, filename_completion_function);
+ {
+ completion_charp = "";
+ matches = completion_matches(text, complete_from_files);
+ }
/* Handle COPY|BINARY <sth> FROM|TO filename */
else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
pg_strcasecmp(prev2_wd, "TO") == 0))
{
static const char *const list_COPY[] =
- {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", NULL};
+ {"BINARY", "OIDS", "DELIMITER", "NULL", "CSV", "ENCODING", NULL};
COMPLETE_WITH_LIST(list_COPY);
}
pg_strcasecmp(prev3_wd, "TO") == 0))
{
static const char *const list_CSV[] =
- {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", NULL};
+ {"HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE", "FORCE NOT NULL", NULL};
COMPLETE_WITH_LIST(list_CSV);
}
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 <name> */
+ 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 list_CREATE_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATE_FOREIGN);
+ }
+
+ /* 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 list_CREATE_FOREIGN_DATA_WRAPPER[] =
+ {"HANDLER", "VALIDATOR", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATE_FOREIGN_DATA_WRAPPER);
+ }
+
/* 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 <sth>, then add "ON" */
- else if (pg_strcasecmp(prev2_wd, "INDEX") == 0 &&
- (pg_strcasecmp(prev3_wd, "CREATE") == 0 ||
- pg_strcasecmp(prev3_wd, "UNIQUE") == 0))
- COMPLETE_WITH_CONST("ON");
- /* Complete ... INDEX <name> ON with a list of tables */
- else if (pg_strcasecmp(prev3_wd, "INDEX") == 0 &&
+ /* 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 [<name>] 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_tables, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+ /* If we have CREATE|UNIQUE INDEX <sth> 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 <sth>, 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 list_CREATE_INDEX[] =
+ {"CONCURRENTLY", "ON", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATE_INDEX);
+ }
/*
* 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 &&
+ 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)
{
- if (find_open_parenthesis(end))
- COMPLETE_WITH_ATTR(prev_wd, "");
- else
- COMPLETE_WITH_CONST("(");
+ static const char *const list_CREATE_INDEX2[] =
+ {"(", "USING", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATE_INDEX2);
}
- else if (pg_strcasecmp(prev5_wd, "INDEX") == 0 &&
+ 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(prev4_wd, "ON") == 0 &&
- pg_strcasecmp(prev2_wd, "USING") == 0)
- COMPLETE_WITH_ATTR(prev3_wd, "");
+ 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)
- {
- static const char *const index_mth[] =
- {"BTREE", "HASH", "GIN", "GIST", NULL};
-
- COMPLETE_WITH_LIST(index_mth);
- }
+ 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 <sth>" with "AS" */
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/TEMPORARY" with the possible temp objects */
else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
COMPLETE_WITH_LIST(list_TEMP);
}
+ /* Complete "CREATE UNLOGGED" with TABLE */
+ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev_wd, "UNLOGGED") == 0)
+ {
+ static const char *const list_UNLOGGED[] =
+ {"TABLE", "MATERIALIZED VIEW", NULL};
+
+ COMPLETE_WITH_LIST(list_UNLOGGED);
+ }
/* CREATE TABLESPACE */
else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
pg_strcasecmp(prev2_wd, "TRIGGER") == 0)
{
static const char *const list_CREATETRIGGER[] =
- {"BEFORE", "AFTER", NULL};
+ {"BEFORE", "AFTER", "INSTEAD OF", NULL};
COMPLETE_WITH_LIST(list_CREATETRIGGER);
}
- /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
+ /* complete CREATE TRIGGER <name> 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 list_CREATETRIGGER_EVENTS[] =
+ {"INSERT", "DELETE", "UPDATE", "TRUNCATE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
+ }
+ /* complete CREATE TRIGGER <name> INSTEAD OF with an event */
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(prev2_wd, "INSTEAD") == 0 &&
+ pg_strcasecmp(prev_wd, "OF") == 0)
+ {
+ static const char *const list_CREATETRIGGER_EVENTS[] =
+ {"INSERT", "DELETE", "UPDATE", NULL};
+
+ COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
+ }
+ /* 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)) ||
+ (pg_strcasecmp(prev5_wd, "TRIGGER") == 0 &&
+ pg_strcasecmp(prev3_wd, "INSTEAD") == 0 &&
+ pg_strcasecmp(prev2_wd, "OF") == 0))
{
static const char *const list_CREATETRIGGER2[] =
{"ON", "OR", NULL};
COMPLETE_WITH_LIST(list_CREATETRIGGER2);
}
-/* CREATE ROLE,USER,GROUP */
+ /*
+ * complete CREATE TRIGGER <name> 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 <name> */
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};
+ "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
+ "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
+ "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
+ "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
COMPLETE_WITH_LIST(list_CREATEROLE);
}
+/* CREATE ROLE,USER,GROUP <name> 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 list_CREATEROLE_WITH[] =
+ {"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(list_CREATEROLE_WITH);
+ }
+
/*
* complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
* PASSWORD
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 <name> 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 <sth> 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)
{
COMPLETE_WITH_LIST(list_DECLARE);
}
+/* CURSOR */
else if (pg_strcasecmp(prev_wd, "CURSOR") == 0)
{
static const char *const list_DECLARECURSOR[] =
/* 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_tables, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_deletables, NULL);
/* Complete DELETE FROM <table> */
else if (pg_strcasecmp(prev3_wd, "DELETE") == 0 &&
pg_strcasecmp(prev2_wd, "FROM") == 0)
else if (pg_strcasecmp(prev_wd, "DISCARD") == 0)
{
static const char *const list_DISCARD[] =
- {"ALL", "PLANS", "TEMP", NULL};
+ {"ALL", "PLANS", "SEQUENCES", "TEMP", NULL};
COMPLETE_WITH_LIST(list_DISCARD);
}
+/* DO */
+
+ /*
+ * Complete DO with LANGUAGE.
+ */
+ else if (pg_strcasecmp(prev_wd, "DO") == 0)
+ {
+ static const char *const list_DO[] =
+ {"LANGUAGE", NULL};
+
+ COMPLETE_WITH_LIST(list_DO);
+ }
+
/* DROP (when not the previous word) */
/* DROP AGGREGATE */
else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
/* DROP object with CASCADE / RESTRICT */
else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 &&
- (pg_strcasecmp(prev2_wd, "CONVERSION") == 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, "TEMPLATE") == 0))
)
{
- if ((pg_strcasecmp(prev3_wd, "DROP") == 0) && (pg_strcasecmp(prev2_wd, "FUNCTION") == 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("(");
- }
+ COMPLETE_WITH_CONST("(");
}
else
{
COMPLETE_WITH_LIST(list_DROPCR);
}
}
- else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
- pg_strcasecmp(prev3_wd, "FUNCTION") == 0 &&
- pg_strcasecmp(prev_wd, "(") == 0)
+ else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 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));
+ static const char *const drop_CREATE_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
+ }
- sprintf(tmp_buf, func_args_query, prev2_wd);
- COMPLETE_WITH_QUERY(tmp_buf);
- free(tmp_buf);
+ /* 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)
+ COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
/* DROP OWNED BY */
else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
pg_strcasecmp(prev_wd, "OWNED") == 0)
COMPLETE_WITH_LIST(list_ALTERTEXTSEARCH);
}
+/* 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_WITH_LIST(list_EXPLAIN);
}
- else if (pg_strcasecmp(prev_wd, "VERBOSE") == 0 &&
- pg_strcasecmp(prev3_wd, "VACUUM") != 0 &&
- pg_strcasecmp(prev4_wd, "VACUUM") != 0 &&
- (pg_strcasecmp(prev2_wd, "ANALYZE") == 0 ||
- pg_strcasecmp(prev2_wd, "EXPLAIN") == 0))
+ 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 list_EXPLAIN[] =
{"SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE", NULL};
COMPLETE_WITH_LIST(list_FROMIN);
}
-/* GRANT && REVOKE*/
- /* Complete GRANT/REVOKE with a list of privileges */
+/* 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)
{
- static const char *const list_privileg[] =
- {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
- "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
- "ALL", NULL};
-
- COMPLETE_WITH_LIST(list_privileg);
+ 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 <sth> with "ON" */
+
+ /*
+ * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
+ * TO/FROM
+ */
else if (pg_strcasecmp(prev2_wd, "GRANT") == 0 ||
pg_strcasecmp(prev2_wd, "REVOKE") == 0)
- COMPLETE_WITH_CONST("ON");
+ {
+ 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 <sth> ON with a list of tables, views, sequences,
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,
+ 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 '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 list_privilege_foreign[] =
+ {"DATA WRAPPER", "SERVER", NULL};
- /* Complete "GRANT/REVOKE * ON * " with "TO" */
+ COMPLETE_WITH_LIST(list_privilege_foreign);
+ }
+
+ /* 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_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 "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)))
+ 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 &&
/* 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_tables, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_insertables, NULL);
/* Complete "INSERT INTO <table> (" with attribute names */
- else if (rl_line_buffer[start - 1] == '(' &&
- pg_strcasecmp(prev3_wd, "INSERT") == 0 &&
- pg_strcasecmp(prev2_wd, "INTO") == 0)
- COMPLETE_WITH_ATTR(prev_wd, "");
+ 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 <table> with "VALUES" or "SELECT" or "DEFAULT
- * VALUES"
+ * Complete INSERT INTO <table> 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 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);
}
/* Complete LOCK [TABLE] <table> with "IN" */
else if ((pg_strcasecmp(prev2_wd, "LOCK") == 0 &&
- pg_strcasecmp(prev_wd, "TABLE")) ||
+ pg_strcasecmp(prev_wd, "TABLE") != 0) ||
(pg_strcasecmp(prev2_wd, "TABLE") == 0 &&
pg_strcasecmp(prev3_wd, "LOCK") == 0))
COMPLETE_WITH_CONST("IN");
/* 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'");
+ 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 &&
COMPLETE_WITH_LIST(list_PREPARE);
}
+/*
+ * 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");
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,
+ " UNION SELECT 'CONCURRENTLY'");
+ else if (pg_strcasecmp(prev4_wd, "REFRESH") == 0 &&
+ pg_strcasecmp(prev3_wd, "MATERIALIZED") == 0 &&
+ pg_strcasecmp(prev2_wd, "VIEW") == 0 &&
+ pg_strcasecmp(prev_wd, "CONCURRENTLY") == 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(prev2_wd, "CONCURRENTLY") == 0)
+ COMPLETE_WITH_CONST("WITH DATA");
+ 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 list_WITH_DATA[] =
+ {"NO DATA", "DATA", NULL};
+
+ COMPLETE_WITH_LIST(list_WITH_DATA);
+ }
+ else if (pg_strcasecmp(prev6_wd, "REFRESH") == 0 &&
+ pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 &&
+ pg_strcasecmp(prev4_wd, "VIEW") == 0 &&
+ pg_strcasecmp(prev3_wd, "CONCURRENTLY") == 0 &&
+ pg_strcasecmp(prev_wd, "WITH") == 0)
+ COMPLETE_WITH_CONST("DATA");
+ 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)
{
else if (pg_strcasecmp(prev2_wd, "REINDEX") == 0)
{
if (pg_strcasecmp(prev_wd, "TABLE") == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ 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 ||
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 list_SECURITY_LABEL_preposition[] =
+ {"ON", "FOR"};
+
+ COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition);
+ }
+ 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 list_SECURITY_LABEL[] =
+ {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW",
+ "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "DOMAIN",
+ "LARGE OBJECT", NULL};
+
+ COMPLETE_WITH_LIST(list_SECURITY_LABEL);
+ }
+ 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 . . . */
COMPLETE_WITH_LIST(my_list);
}
+ /* SET CONSTRAINTS */
+ else if (pg_strcasecmp(prev2_wd, "SET") == 0 &&
+ pg_strcasecmp(prev_wd, "CONSTRAINTS") == 0)
+ {
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");
+ }
/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
else if (pg_strcasecmp(prev3_wd, "SET") == 0 &&
pg_strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
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] != ')' &&
+ prev_wd[strlen(prev_wd) - 1] != '=' &&
pg_strcasecmp(prev4_wd, "DOMAIN") != 0)
COMPLETE_WITH_CONST("TO");
/* Suggest possible variable values */
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[] =
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(relname) FROM pg_catalog.pg_listener WHERE substring(pg_catalog.quote_ident(relname),1,%d)='%s' UNION SELECT '*'");
+ 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_tables, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
/* Complete UPDATE <table> with "SET" */
else if (pg_strcasecmp(prev2_wd, "UPDATE") == 0)
COMPLETE_WITH_CONST("SET");
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_tables,
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
" UNION SELECT 'FULL'"
" UNION SELECT 'FREEZE'"
" UNION SELECT 'ANALYZE'"
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_tables,
+ 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_tables,
+ 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_tables,
+ 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_tables,
+ 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_tables,
+ 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_tables, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
/* WITH [RECURSIVE] */
- else if (pg_strcasecmp(prev_wd, "WITH") == 0)
+
+ /*
+ * 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_tables, NULL);
+ 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, "FROM") == 0 &&
pg_strcasecmp(prev3_wd, "COPY") != 0 &&
pg_strcasecmp(prev3_wd, "\\copy") != 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+ 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 */
/* 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, "\\dF") == 0 || strcmp(prev_wd, "\\dF+") == 0)
- COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
- else if (strcmp(prev_wd, "\\dFd") == 0 || strcmp(prev_wd, "\\dFd+") == 0)
+ else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
- else if (strcmp(prev_wd, "\\dFp") == 0 || strcmp(prev_wd, "\\dFp+") == 0)
+ else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
- else if (strcmp(prev_wd, "\\dFt") == 0 || strcmp(prev_wd, "\\dFt+") == 0)
+ else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
- else if (strcmp(prev_wd, "\\di") == 0 || strcmp(prev_wd, "\\di+") == 0)
+ /* 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, "\\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 (strcmp(prev_wd, "\\dp") == 0 || strcmp(prev_wd, "\\z") == 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, "\\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 (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)
+ 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 (strcmp(prev_wd, "\\dv") == 0 || strcmp(prev_wd, "\\dv+") == 0)
+ else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+ else if (strncmp(prev_wd, "\\dx", strlen("\\dx")) == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+ 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)
{
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);
+ COMPLETE_WITH_LIST_CS(my_list);
+ }
+ else if (strcmp(prev2_wd, "\\pset") == 0)
+ {
+ if (strcmp(prev_wd, "format") == 0)
+ {
+ static const char *const my_list[] =
+ {"unaligned", "aligned", "wrapped", "html", "latex",
+ "troff-ms", NULL};
+
+ COMPLETE_WITH_LIST_CS(my_list);
+ }
+ else if (strcmp(prev_wd, "linestyle") == 0)
+ {
+ static const char *const my_list[] =
+ {"ascii", "old-ascii", "unicode", NULL};
+
+ COMPLETE_WITH_LIST_CS(my_list);
+ }
}
+ else if (strcmp(prev_wd, "\\set") == 0)
+ {
+ matches = complete_from_variables(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
+ strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 ||
+ strcmp(prev_wd, "\\lo_import") == 0
)
- matches = completion_matches(text, filename_completion_function);
-
+ {
+ completion_charp = "\\";
+ matches = completion_matches(text, complete_from_files);
+ }
/*
* Finally, we look through the list of "things", such as TABLE, INDEX and
}
/* free storage */
- free(prev_wd);
- free(prev2_wd);
- free(prev3_wd);
- free(prev4_wd);
- free(prev5_wd);
+ {
+ int i;
+
+ for (i = 0; i < lengthof(previous_words); i++)
+ free(previous_words[i]);
+ }
/* Return our Grand List O' Matches */
return matches;
}
+/*
+ * GENERATOR FUNCTIONS
+ *
+ * These functions do all the actual work of completing the input. They get
+ * passed the text so far and the count how many times they have been called
+ * so far with the same text.
+ * If you read the above carefully, you'll see that these don't get called
+ * directly but through the readline interface.
+ * The return value is expected to be the full completion of the text, going
+ * through a list each time, or NULL if there are no more matches. The string
+ * will be free()'d by readline, so you must run it through strdup() or
+ * something of that sort.
+ */
-/* GENERATOR FUNCTIONS
-
- These functions do all the actual work of completing the input. They get
- passed the text so far and the count how many times they have been called so
- far with the same text.
- If you read the above carefully, you'll see that these don't get called
- directly but through the readline interface.
- The return value is expected to be the full completion of the text, going
- through a list each time, or NULL if there are no more matches. The string
- will be free()'d by readline, so you must run it through strdup() or
- something of that sort.
-*/
-
-/* This one gives you one from a list of things you can put after CREATE
- as defined above.
-*/
+/*
+ * Common routine for create_command_generator and drop_command_generator.
+ * Entries that have 'excluded' flags are not returned.
+ */
static char *
-create_command_generator(const char *text, int state)
+create_or_drop_command_generator(const char *text, int state, bits32 excluded)
{
static int list_index,
string_length;
/* 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].noshow)
- return pg_strdup(name);
+ if ((pg_strncasecmp(name, text, string_length) == 0) &&
+ !(words_after_create[list_index - 1].flags & excluded))
+ return pg_strdup_keyword_case(name, text);
}
/* if nothing matches, return NULL */
return NULL;
}
+/*
+ * 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)
+{
+ return create_or_drop_command_generator(text, state, THING_NO_CREATE);
+}
+
/*
* 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;
+ return create_or_drop_command_generator(text, state, THING_NO_DROP);
}
/* The following two functions are wrappers for _complete_from_query */
}
-/* This creates a list of matching things, according to a query pointed to
- by completion_charp.
- 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 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:
- %d %s %d %s %d %s %s %d %s
- where %d is the string length of the text and %s the text itself.
-
- It is assumed that strings should be escaped to become SQL literals
- (that is, what is in the query is actually ... '%s' ...)
-
- See top of file for examples of both kinds of query.
-*/
-
+/*
+ * This creates a list of matching things, according to a query pointed to
+ * by completion_charp.
+ * The query can be one of two kinds:
+ *
+ * 1. 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 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.
+ *
+ * 2. A schema query used for completion of both schema and relation names.
+ * These are more complex and must contain in the following order:
+ * %d %s %d %s %d %s %s %d %s
+ * where %d is the string length of the text and %s the text itself.
+ *
+ * It is assumed that strings should be escaped to become SQL literals
+ * (that is, what is in the query is actually ... '%s' ...)
+ *
+ * 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)
{
}
-/* This function returns in order one of a fixed, NULL pointer terminated list
- of strings (if matching). This can be used if there are only a fixed number
- SQL words that can appear at certain spot.
-*/
+/*
+ * This function returns in order one of a fixed, NULL pointer terminated list
+ * 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)
{
const char *item;
/* need to have a list */
- psql_assert(completion_charpp);
+ Assert(completion_charpp != NULL);
/* Initialization */
if (state == 0)
{
list_index = 0;
string_length = strlen(text);
- casesensitive = true;
+ casesensitive = completion_case_sensitive;
matches = 0;
}
/* Second pass is case insensitive, don't bother counting matches */
if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0)
- return pg_strdup(item);
+ {
+ if (completion_case_sensitive)
+ return pg_strdup(item);
+ else
+
+ /*
+ * If case insensitive matching was requested initially,
+ * adjust the case according to setting.
+ */
+ return pg_strdup_keyword_case(item, text);
+ }
}
/*
}
-/* This function returns one fixed string the first time even if it doesn't
- match what's there, and nothing the second time. This should be used if there
- is only one possibility that can appear at a certain spot, so misspellings
- will be overwritten.
- The string to be passed must be in completion_charp.
-*/
+/*
+ * This function returns one fixed string the first time even if it doesn't
+ * match what's there, and nothing the second time. This should be used if
+ * there is only one possibility that can appear at a certain spot, so
+ * misspellings will be overwritten. The string to be passed must be in
+ * completion_charp.
+ */
static char *
complete_from_const(const char *text, int state)
{
- (void) text; /* We don't care about what was entered
- * already. */
-
- psql_assert(completion_charp);
+ Assert(completion_charp != NULL);
if (state == 0)
- return pg_strdup(completion_charp);
+ {
+ if (completion_case_sensitive)
+ return pg_strdup(completion_charp);
+ else
+
+ /*
+ * If case insensitive matching was requested initially, adjust
+ * the case according to setting.
+ */
+ return pg_strdup_keyword_case(completion_charp, text);
+ }
else
return NULL;
}
+/*
+ * This function supports completion with the name of a psql variable.
+ * 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)
+{
+ char **matches;
+ char **varnames;
+ int nvars = 0;
+ int maxvars = 100;
+ int i;
+ struct _variable *ptr;
+
+ varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *));
+
+ for (ptr = pset.vars->next; ptr; ptr = ptr->next)
+ {
+ if (nvars >= maxvars)
+ {
+ maxvars *= 2;
+ varnames = (char **) realloc(varnames,
+ (maxvars + 1) * sizeof(char *));
+ if (!varnames)
+ {
+ psql_error("out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ varnames[nvars++] = psprintf("%s%s%s", prefix, ptr->name, suffix);
+ }
+
+ varnames[nvars] = NULL;
+ COMPLETE_WITH_LIST_CS((const char *const *) varnames);
+
+ for (i = 0; i < nvars; i++)
+ free(varnames[i]);
+ free(varnames);
+
+ return matches;
+}
+
+
+/*
+ * This function wraps rl_filename_completion_function() to strip quotes from
+ * 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 const char *unquoted_text;
+ char *unquoted_match;
+ char *ret = NULL;
+
+ if (state == 0)
+ {
+ /* Initialization: stash the unquoted input. */
+ unquoted_text = strtokx(text, "", NULL, "'", *completion_charp,
+ false, true, pset.encoding);
+ /* expect a NULL return for the empty string only */
+ if (!unquoted_text)
+ {
+ Assert(*text == '\0');
+ unquoted_text = text;
+ }
+ }
+
+ unquoted_match = filename_completion_function(unquoted_text, state);
+ if (unquoted_match)
+ {
+ /*
+ * Caller sets completion_charp to a zero- or one-character string
+ * containing the escape character. This is necessary since \copy has
+ * no escape character, but every other backslash command recognizes
+ * "\" 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);
+ if (ret)
+ free(unquoted_match);
+ else
+ ret = unquoted_match;
+ }
+
+ return ret;
+}
+
/* HELPER FUNCTIONS */
+/*
+ * 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)
+{
+ char *ret,
+ *p;
+ unsigned char first = ref[0];
+ int tocase;
+ const char *varval;
+
+ varval = GetVariable(pset.vars, "COMP_KEYWORD_CASE");
+ if (!varval)
+ tocase = 0;
+ else if (strcmp(varval, "lower") == 0)
+ tocase = -2;
+ else if (strcmp(varval, "preserve-lower") == 0)
+ tocase = -1;
+ else if (strcmp(varval, "preserve-upper") == 0)
+ tocase = +1;
+ else if (strcmp(varval, "upper") == 0)
+ tocase = +2;
+ else
+ tocase = 0;
+
+ /* default */
+ if (tocase == 0)
+ tocase = +1;
+
+ ret = pg_strdup(s);
+
+ if (tocase == -2
+ || ((tocase == -1 || tocase == +1) && islower(first))
+ || (tocase == -1 && !isalpha(first))
+ )
+ for (p = ret; *p; p++)
+ *p = pg_tolower((unsigned char) *p);
+ else
+ for (p = ret; *p; p++)
+ *p = pg_toupper((unsigned char) *p);
+
+ return ret;
+}
+
+
/*
* Execute a query and report any errors. This should be the preferred way of
* talking to the database in this file.
if (PQresultStatus(result) != PGRES_TUPLES_OK)
{
-#if 0
+#ifdef NOT_USED
psql_error("tab completion query failed: %s\nQuery was:\n%s\n",
PQerrorMessage(pset.db), query);
#endif
}
-
/*
- * 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.
+ * Return the nwords word(s) before point. Words are returned right to left,
+ * that is, previous_words[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 char *
-previous_word(int point, int skip)
+static void
+get_previous_words(int point, char **previous_words, int nwords)
{
- int i,
- start = 0,
- end = -1,
- inquotes = 0;
- char *s;
+ const char *buf = rl_line_buffer; /* alias */
+ int i;
- while (skip-- >= 0)
+ /* first we look for a non-word char before the current point */
+ for (i = point - 1; i >= 0; i--)
+ if (strchr(WORD_BREAKS, buf[i]))
+ break;
+ point = i;
+
+ while (nwords-- > 0)
{
- /* first we look for a space before the current word */
- for (i = point; i >= 0; i--)
- if (rl_line_buffer[i] == ' ')
- break;
+ int start,
+ end;
+ char *s;
/* now find the first non-space which then constitutes the end */
- for (; i >= 0; i--)
- if (rl_line_buffer[i] != ' ')
+ end = -1;
+ for (i = point; i >= 0; i--)
+ {
+ if (!isspace((unsigned char) buf[i]))
{
end = i;
break;
}
+ }
/*
- * 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
+ * If no end found we return an empty string, because there is no word
+ * before the point
*/
- for (start = end; start > 0; start--)
+ if (end < 0)
{
- if (rl_line_buffer[start] == '"')
- inquotes = !inquotes;
- if ((rl_line_buffer[start - 1] == ' ') && inquotes == 0)
- break;
+ point = end;
+ s = pg_strdup("");
}
+ else
+ {
+ /*
+ * Otherwise we now look for the start. The start is either the
+ * last character before any word-break character going backwards
+ * from the end, or it's simply character 0. We also handle open
+ * quotes and parentheses.
+ */
+ bool inquotes = false;
+ int parentheses = 0;
- point = start;
- }
-
- /* make a copy */
- s = pg_malloc(end - start + 2);
- strlcpy(s, &rl_line_buffer[start], end - start + 2);
-
- return s;
-}
-
-/* Find the parenthesis after the last word */
+ for (start = end; start > 0; start--)
+ {
+ if (buf[start] == '"')
+ inquotes = !inquotes;
+ if (!inquotes)
+ {
+ if (buf[start] == ')')
+ parentheses++;
+ else if (buf[start] == '(')
+ {
+ if (--parentheses <= 0)
+ break;
+ }
+ else if (parentheses == 0 &&
+ strchr(WORD_BREAKS, buf[start - 1]))
+ break;
+ }
+ }
+ point = start - 1;
-static int
-find_open_parenthesis(int end)
-{
- int i = end - 1;
+ /* make a copy of chars from start to end inclusive */
+ s = pg_malloc(end - start + 2);
+ strlcpy(s, &buf[start], end - start + 2);
+ }
- 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;
+ *previous_words++ = s;
}
- return 0;
-
}
-#if 0
+#ifdef NOT_USED
/*
* Surround a string with single quotes. This works for both SQL and
return s;
}
-
-
static char *
dequote_file_name(char *text, char quote_char)
{
return s;
}
-#endif /* 0 */
+#endif /* NOT_USED */
#endif /* USE_READLINE */