/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright (c) 2000-2011, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2013, PostgreSQL Global Development Group
*
* src/bin/psql/tab-complete.c
*/
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_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.
*/
NULL
};
-static const SchemaQuery Query_for_list_of_tisv = {
+static const SchemaQuery Query_for_list_of_relations = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ NULL,
+ /* 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_tsvmf = {
+ /* catname */
+ "pg_catalog.pg_class c",
+ /* selcondition */
+ "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', 'i', 'S', 'v')",
+ "c.relkind IN ('r', 'm', 'f')",
/* 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_tm = {
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
- "c.relkind IN ('r', 'S', 'v')",
+ "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
" 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) "\
" 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 proname='%s'"
+"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
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},
{"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_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);
+ 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 void get_previous_words(int point, char **previous_words, int nwords);
#ifdef NOT_USED
static char *quote_file_name(char *text, int match_type, char *quote_pointer);
/* 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", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
"GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
- "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
+ "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", "\\conninfo", "\\C", "\\cd", "\\copy", "\\copyright",
- "\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\deu", "\\dew", "\\df",
- "\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl",
- "\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du",
+ "\\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", "\\h", "\\help", "\\H", "\\i", "\\l",
+ "\\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", "\\sf", "\\t", "\\T",
- "\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
+ "\\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, but watch out for DROP embedded in other commands */
+/* DROP, but not DROP embedded in other commands */
/* complete with something you can drop */
else if (pg_strcasecmp(prev_wd, "DROP") == 0 &&
- pg_strcasecmp(prev2_wd, "DROP") == 0)
+ 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", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION",
- "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
- "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL};
+ {"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);
}
COMPLETE_WITH_LIST(list_ALTERAGG);
}
else
- {
- char *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
-
- sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
- COMPLETE_WITH_QUERY(tmp_buf);
- free(tmp_buf);
- }
+ COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
}
/* ALTER SCHEMA <name> */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 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)
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(prev2_wd, "WRAPPER") == 0)
{
static const char *const list_ALTER_FDW[] =
- {"VALIDATOR", "OPTIONS", "OWNER TO", NULL};
+ {"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_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, "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) &&
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 &&
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");
+
/* 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(prev2_wd, "TYPE") == 0)
{
static const char *const list_ALTERTYPE[] =
- {"ADD ATTRIBUTE", "ALTER ATTRIBUTE", "DROP ATTRIBUTE",
- "OWNER TO", "RENAME", "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 */
+ /* ALTER TYPE <foo> RENAME */
else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
pg_strcasecmp(prev3_wd, "TYPE") == 0 &&
pg_strcasecmp(prev_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 */
+ /*
+ * 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 ||
/* 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)
- COMPLETE_WITH_CONST("VALIDATOR");
+ {
+ 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" */
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) &&
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 &&
COMPLETE_WITH_LIST(list_CREATETRIGGER_EVENTS);
}
- /* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
+ /* 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};
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)
+ else if (pg_strcasecmp(prev_wd, "EXECUTE") == 0 &&
+ prev2_wd[0] != '\0')
COMPLETE_WITH_CONST("PROCEDURE");
-/* CREATE ROLE,USER,GROUP */
+/* 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 ||
{
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)
{
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);
}
/* 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 ||
COMPLETE_WITH_LIST(list_DROPCR);
}
}
+ else if (pg_strcasecmp(prev2_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ {
+ static const char *const drop_CREATE_FOREIGN[] =
+ {"DATA WRAPPER", "TABLE", NULL};
+
+ COMPLETE_WITH_LIST(drop_CREATE_FOREIGN);
+ }
+
+ /* 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)
- {
- char *tmp_buf = malloc(strlen(Query_for_list_of_arguments) + strlen(prev2_wd));
-
- sprintf(tmp_buf, Query_for_list_of_arguments, prev2_wd);
- COMPLETE_WITH_QUERY(tmp_buf);
- free(tmp_buf);
- }
+ 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 */
/*
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 privileges */
+ /* 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_privilege[] =
- {"SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES",
- "TRIGGER", "CREATE", "CONNECT", "TEMPORARY", "EXECUTE", "USAGE",
- "ALL", NULL};
-
- COMPLETE_WITH_LIST(list_privilege);
+ 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 &&
{
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_WITH_CONST("FROM");
}
+ /* Complete "GRANT/REVOKE * TO/FROM" with username, GROUP, or PUBLIC */
+ else if (pg_strcasecmp(prev3_wd, "GRANT") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") == 0)
+ {
+ COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
+ }
+ else if (pg_strcasecmp(prev3_wd, "REVOKE") == 0 &&
+ pg_strcasecmp(prev_wd, "FROM") == 0)
+ {
+ COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
+ }
+
/* GROUP BY */
else if (pg_strcasecmp(prev3_wd, "FROM") == 0 &&
pg_strcasecmp(prev_wd, "GROUP") == 0)
/* Complete 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");
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 ||
pg_strcasecmp(prev_wd, "LABEL") == 0)
{
static const char *const list_SECURITY_LABEL_preposition[] =
- {"ON", "FOR"};
+ {"ON", "FOR"};
+
COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition);
}
else if (pg_strcasecmp(prev4_wd, "SECURITY") == 0 &&
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(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", "COLUMN",
- "AGGREGATE", "FUNCTION", "DOMAIN", "LARGE OBJECT",
- NULL};
+ {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW",
+ "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "DOMAIN",
+ "LARGE OBJECT", NULL};
COMPLETE_WITH_LIST(list_SECURITY_LABEL);
}
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)
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 */
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);
* 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 (strncmp(prev_wd, "\\di", strlen("\\di")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
+ else if (strncmp(prev_wd, "\\dL", strlen("\\dL")) == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
|| strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsv, NULL);
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+ else if (strncmp(prev_wd, "\\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_tisv, NULL);
+ 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);
"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)
{
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);
-
-/* 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, ":", "");
+ completion_charp = "\\";
+ matches = completion_matches(text, complete_from_files);
}
/*
}
/* 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;
*/
/*
- * 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;
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);
+ !(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 */
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);
+ }
}
/*
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;
}
complete_from_variables(char *text, const char *prefix, const char *suffix)
{
char **matches;
- int overhead = strlen(prefix) + strlen(suffix) + 1;
- const char **varnames;
+ char **varnames;
int nvars = 0;
int maxvars = 100;
int i;
struct _variable *ptr;
- varnames = (const char **) pg_malloc((maxvars + 1) * sizeof(char *));
+ varnames = (char **) pg_malloc((maxvars + 1) * sizeof(char *));
for (ptr = pset.vars->next; ptr; ptr = ptr->next)
{
- char *buffer;
-
if (nvars >= maxvars)
{
maxvars *= 2;
- varnames = (const char **) realloc(varnames,
- (maxvars + 1) * sizeof(char *));
+ varnames = (char **) realloc(varnames,
+ (maxvars + 1) * sizeof(char *));
if (!varnames)
{
psql_error("out of memory\n");
}
}
- buffer = (char *) pg_malloc(strlen(ptr->name) + overhead);
- sprintf(buffer, "%s%s%s", prefix, ptr->name, suffix);
- varnames[nvars++] = buffer;
+ varnames[nvars++] = psprintf("%s%s%s", prefix, ptr->name, suffix);
}
varnames[nvars] = NULL;
- COMPLETE_WITH_LIST(varnames);
+ COMPLETE_WITH_LIST_CS((const char *const *) varnames);
for (i = 0; i < nvars; i++)
- free((void *) varnames[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.
/*
- * 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;
- /* first we look for a space or a parenthesis before the current word */
+ /* 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 (skip-- >= 0)
+ while (nwords-- > 0)
{
- int parentheses = 0;
+ int start,
+ end;
+ char *s;
/* now find the first non-space which then constitutes the end */
+ end = -1;
for (i = point; i >= 0; i--)
- if (buf[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. We also handle open quotes and parentheses.
+ * 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 (buf[start] == '"')
- inquotes = !inquotes;
- if (inquotes == 0)
+ 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;
+
+ for (start = end; start > 0; start--)
{
- if (buf[start] == ')')
- parentheses++;
- else if (buf[start] == '(')
+ if (buf[start] == '"')
+ inquotes = !inquotes;
+ if (!inquotes)
{
- if (--parentheses <= 0)
+ if (buf[start] == ')')
+ parentheses++;
+ else if (buf[start] == '(')
+ {
+ if (--parentheses <= 0)
+ break;
+ }
+ else if (parentheses == 0 &&
+ strchr(WORD_BREAKS, buf[start - 1]))
break;
}
- else if (parentheses == 0 &&
- strchr(WORD_BREAKS, buf[start - 1]))
- break;
}
- }
- point = start - 1;
- }
+ point = start - 1;
- /* make a copy */
- s = pg_malloc(end - start + 2);
- strlcpy(s, &buf[start], end - start + 2);
+ /* make a copy of chars from start to end inclusive */
+ s = pg_malloc(end - start + 2);
+ strlcpy(s, &buf[start], end - start + 2);
+ }
- return s;
+ *previous_words++ = s;
+ }
}
#ifdef NOT_USED