*/
PQExpBuffer tab_completion_query_buf = NULL;
+/*
+ * In some situations, the query to find out what names are available to
+ * complete with must vary depending on server version. We handle this by
+ * storing a list of queries, each tagged with the minimum server version
+ * it will work for. Each list must be stored in descending server version
+ * order, so that the first satisfactory query is the one to use.
+ *
+ * When the query string is otherwise constant, an array of VersionedQuery
+ * suffices. Terminate the array with an entry having min_server_version = 0.
+ * That entry's query string can be a query that works in all supported older
+ * server versions, or NULL to give up and do no completion.
+ */
+typedef struct VersionedQuery
+{
+ int min_server_version;
+ const char *query;
+} VersionedQuery;
+
/*
* This struct is used to define "schema queries", which are custom-built
* to obtain possibly-schema-qualified names of database objects. There is
* enough similarity in the structure that we don't want to repeat it each
* time. So we put the components of each query into this struct and
* assemble them with the common boilerplate in _complete_from_query().
+ *
+ * As with VersionedQuery, we can use an array of these if the query details
+ * must vary across versions.
*/
typedef struct SchemaQuery
{
+ /*
+ * If not zero, minimum server version this struct applies to. If not
+ * zero, there should be a following struct with a smaller minimum server
+ * version; use catname == NULL in the last entry if we should do nothing.
+ */
+ int min_server_version;
+
/*
* Name of catalog or catalogs to be queried, with alias, eg.
* "pg_catalog.pg_class c". Note that "pg_namespace n" will be added.
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 VersionedQuery *completion_vquery; /* to pass a VersionedQuery */
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
* string with
* 1) The results from a query you pass it. (Perhaps one of those below?)
+ * We support both simple and versioned queries.
* 2) The results from a schema query you pass it.
+ * We support both simple and versioned schema queries.
* 3) The items from a null-pointer-terminated list (with or without
* case-sensitive comparison; see also COMPLETE_WITH_LISTn, below).
* 4) A string constant.
matches = completion_matches(text, complete_from_query); \
} while (0)
+#define COMPLETE_WITH_VERSIONED_QUERY(query) \
+do { \
+ completion_vquery = query; \
+ matches = completion_matches(text, complete_from_versioned_query); \
+} while (0)
+
#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
do { \
completion_squery = &(query); \
matches = completion_matches(text, complete_from_schema_query); \
} while (0)
+#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \
+do { \
+ completion_squery = query; \
+ completion_vquery = addon; \
+ matches = completion_matches(text, complete_from_versioned_schema_query); \
+} while (0)
+
#define COMPLETE_WITH_LIST_CS(list) \
do { \
completion_charpp = list; \
* Assembly instructions for schema queries
*/
-static const SchemaQuery Query_for_list_of_aggregates = {
- /* catname */
- "pg_catalog.pg_proc p",
- /* selcondition */
- "p.prokind = 'a'",
- /* viscondition */
- "pg_catalog.pg_function_is_visible(p.oid)",
- /* namespace */
- "p.pronamespace",
- /* result */
- "pg_catalog.quote_ident(p.proname)",
- /* qualresult */
- NULL
+static const SchemaQuery Query_for_list_of_aggregates[] = {
+ {
+ /* min_server_version */
+ 110000,
+ /* catname */
+ "pg_catalog.pg_proc p",
+ /* selcondition */
+ "p.prokind = 'a'",
+ /* viscondition */
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ /* namespace */
+ "p.pronamespace",
+ /* result */
+ "pg_catalog.quote_ident(p.proname)",
+ /* qualresult */
+ NULL
+ },
+ {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_proc p",
+ /* selcondition */
+ "p.proisagg",
+ /* viscondition */
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ /* namespace */
+ "p.pronamespace",
+ /* result */
+ "pg_catalog.quote_ident(p.proname)",
+ /* qualresult */
+ NULL
+ }
};
static const SchemaQuery Query_for_list_of_datatypes = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_type t",
/* selcondition --- ignore table rowtypes and array types */
};
static const SchemaQuery Query_for_list_of_domains = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_type t",
/* selcondition */
NULL
};
-static const SchemaQuery Query_for_list_of_functions = {
- /* catname */
- "pg_catalog.pg_proc p",
- /* selcondition */
- "p.prokind IN ('f', 'w')",
- /* viscondition */
- "pg_catalog.pg_function_is_visible(p.oid)",
- /* namespace */
- "p.pronamespace",
- /* result */
- "pg_catalog.quote_ident(p.proname)",
- /* qualresult */
- NULL
+static const SchemaQuery Query_for_list_of_functions[] = {
+ {
+ /* min_server_version */
+ 110000,
+ /* catname */
+ "pg_catalog.pg_proc p",
+ /* selcondition */
+ "p.prokind IN ('f', 'w')",
+ /* viscondition */
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ /* namespace */
+ "p.pronamespace",
+ /* result */
+ "pg_catalog.quote_ident(p.proname)",
+ /* qualresult */
+ NULL
+ },
+ {
+ /* min_server_version */
+ 0,
+ /* catname */
+ "pg_catalog.pg_proc p",
+ /* selcondition */
+ NULL,
+ /* viscondition */
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ /* namespace */
+ "p.pronamespace",
+ /* result */
+ "pg_catalog.quote_ident(p.proname)",
+ /* qualresult */
+ NULL
+ }
};
static const SchemaQuery Query_for_list_of_indexes = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
NULL
};
-static const SchemaQuery Query_for_list_of_procedures = {
- /* catname */
- "pg_catalog.pg_proc p",
- /* selcondition */
- "p.prokind = 'p'",
- /* viscondition */
- "pg_catalog.pg_function_is_visible(p.oid)",
- /* namespace */
- "p.pronamespace",
- /* result */
- "pg_catalog.quote_ident(p.proname)",
- /* qualresult */
- NULL
+static const SchemaQuery Query_for_list_of_procedures[] = {
+ {
+ /* min_server_version */
+ 110000,
+ /* catname */
+ "pg_catalog.pg_proc p",
+ /* selcondition */
+ "p.prokind = 'p'",
+ /* viscondition */
+ "pg_catalog.pg_function_is_visible(p.oid)",
+ /* namespace */
+ "p.pronamespace",
+ /* result */
+ "pg_catalog.quote_ident(p.proname)",
+ /* qualresult */
+ NULL
+ },
+ {0, NULL}
};
static const SchemaQuery Query_for_list_of_routines = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_proc p",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_sequences = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_foreign_tables = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_tables = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_partitioned_tables = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_constraints_with_schema = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_constraint c",
/* selcondition */
/* Relations supporting INSERT, UPDATE or DELETE */
static const SchemaQuery Query_for_list_of_updatables = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_relations = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_tsvmf = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_tmf = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_tpm = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_tm = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_views = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_matviews = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_class c",
/* selcondition */
};
static const SchemaQuery Query_for_list_of_statistics = {
+ /* min_server_version */
+ 0,
/* catname */
"pg_catalog.pg_statistic_ext s",
/* selcondition */
" FROM pg_catalog.pg_am "\
" WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"
-#define Query_for_list_of_publications \
-" SELECT pg_catalog.quote_ident(pubname) "\
-" FROM pg_catalog.pg_publication "\
-" WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'"
-
-#define Query_for_list_of_subscriptions \
-" SELECT pg_catalog.quote_ident(s.subname) "\
-" FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "\
-" WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' "\
-" AND d.datname = pg_catalog.current_database() "\
-" AND s.subdbid = d.oid"
-
/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
" and pg_catalog.pg_table_is_visible(c2.oid)"\
" and c2.relispartition = 'true'"
+/*
+ * These object types were introduced later than our support cutoff of
+ * server version 7.4. We use the VersionedQuery infrastructure so that
+ * we don't send certain-to-fail queries to older servers.
+ */
+
+static const VersionedQuery Query_for_list_of_publications[] = {
+ {100000,
+ " SELECT pg_catalog.quote_ident(pubname) "
+ " FROM pg_catalog.pg_publication "
+ " WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'"
+ },
+ {0, NULL}
+};
+
+static const VersionedQuery Query_for_list_of_subscriptions[] = {
+ {100000,
+ " SELECT pg_catalog.quote_ident(s.subname) "
+ " FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "
+ " WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' "
+ " AND d.datname = pg_catalog.current_database() "
+ " AND s.subdbid = d.oid"
+ },
+ {0, NULL}
+};
+
/*
* 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 VersionedQuery *vquery; /* versioned query, or NULL */
const SchemaQuery *squery; /* schema query, or NULL */
const bits32 flags; /* visibility flags, see below */
} pgsql_thing_t;
#define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)
static const pgsql_thing_t words_after_create[] = {
- {"ACCESS METHOD", NULL, NULL, THING_NO_ALTER},
- {"AGGREGATE", NULL, &Query_for_list_of_aggregates},
- {"CAST", NULL, NULL}, /* Casts have complex structures for names, so
+ {"ACCESS METHOD", NULL, NULL, NULL, THING_NO_ALTER},
+ {"AGGREGATE", NULL, NULL, Query_for_list_of_aggregates},
+ {"CAST", NULL, 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, THING_NO_SHOW},
+ {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, 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},
- {"DEFAULT PRIVILEGES", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
- {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, THING_NO_SHOW},
- {"DOMAIN", NULL, &Query_for_list_of_domains},
- {"EVENT TRIGGER", NULL, NULL},
+ {"DEFAULT PRIVILEGES", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
+ {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, THING_NO_SHOW},
+ {"DOMAIN", NULL, NULL, &Query_for_list_of_domains},
+ {"EVENT TRIGGER", NULL, NULL, NULL},
{"EXTENSION", Query_for_list_of_extensions},
- {"FOREIGN DATA WRAPPER", NULL, NULL},
- {"FOREIGN TABLE", NULL, NULL},
- {"FUNCTION", NULL, &Query_for_list_of_functions},
+ {"FOREIGN DATA WRAPPER", NULL, NULL, NULL},
+ {"FOREIGN TABLE", NULL, NULL, NULL},
+ {"FUNCTION", NULL, NULL, Query_for_list_of_functions},
{"GROUP", Query_for_list_of_roles},
- {"INDEX", NULL, &Query_for_list_of_indexes},
+ {"INDEX", NULL, NULL, &Query_for_list_of_indexes},
{"LANGUAGE", Query_for_list_of_languages},
- {"LARGE OBJECT", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
- {"MATERIALIZED VIEW", NULL, &Query_for_list_of_matviews},
- {"OPERATOR", NULL, NULL}, /* Querying for this is probably not such a
- * good idea. */
- {"OWNED", NULL, NULL, THING_NO_CREATE | THING_NO_ALTER}, /* for DROP OWNED BY ... */
- {"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
- {"POLICY", NULL, NULL},
- {"PROCEDURE", NULL, &Query_for_list_of_procedures},
- {"PUBLICATION", Query_for_list_of_publications},
+ {"LARGE OBJECT", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
+ {"MATERIALIZED VIEW", NULL, NULL, &Query_for_list_of_matviews},
+ {"OPERATOR", NULL, NULL, NULL}, /* Querying for this is probably not such
+ * a good idea. */
+ {"OWNED", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_ALTER}, /* for DROP OWNED BY ... */
+ {"PARSER", Query_for_list_of_ts_parsers, NULL, NULL, THING_NO_SHOW},
+ {"POLICY", NULL, NULL, NULL},
+ {"PROCEDURE", NULL, NULL, Query_for_list_of_procedures},
+ {"PUBLICATION", NULL, Query_for_list_of_publications},
{"ROLE", Query_for_list_of_roles},
- {"ROUTINE", NULL, &Query_for_list_of_routines, THING_NO_CREATE},
+ {"ROUTINE", NULL, NULL, &Query_for_list_of_routines, THING_NO_CREATE},
{"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},
+ {"SEQUENCE", NULL, NULL, &Query_for_list_of_sequences},
{"SERVER", Query_for_list_of_servers},
- {"STATISTICS", NULL, &Query_for_list_of_statistics},
- {"SUBSCRIPTION", Query_for_list_of_subscriptions},
- {"SYSTEM", NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
- {"TABLE", NULL, &Query_for_list_of_tables},
+ {"STATISTICS", NULL, NULL, &Query_for_list_of_statistics},
+ {"SUBSCRIPTION", NULL, Query_for_list_of_subscriptions},
+ {"SYSTEM", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
+ {"TABLE", NULL, NULL, &Query_for_list_of_tables},
{"TABLESPACE", Query_for_list_of_tablespaces},
- {"TEMP", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE
- * ... */
- {"TEMPLATE", Query_for_list_of_ts_templates, NULL, THING_NO_SHOW},
- {"TEMPORARY", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMPORARY
- * TABLE ... */
- {"TEXT SEARCH", NULL, NULL},
- {"TRANSFORM", NULL, NULL},
+ {"TEMP", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE
+ * ... */
+ {"TEMPLATE", Query_for_list_of_ts_templates, NULL, NULL, THING_NO_SHOW},
+ {"TEMPORARY", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMPORARY
+ * TABLE ... */
+ {"TEXT SEARCH", NULL, NULL, NULL},
+ {"TRANSFORM", NULL, NULL, NULL},
{"TRIGGER", "SELECT pg_catalog.quote_ident(tgname) FROM pg_catalog.pg_trigger WHERE substring(pg_catalog.quote_ident(tgname),1,%d)='%s' AND NOT tgisinternal"},
- {"TYPE", NULL, &Query_for_list_of_datatypes},
- {"UNIQUE", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE
- * INDEX ... */
- {"UNLOGGED", NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED
- * TABLE ... */
+ {"TYPE", NULL, NULL, &Query_for_list_of_datatypes},
+ {"UNIQUE", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE
+ * INDEX ... */
+ {"UNLOGGED", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED
+ * TABLE ... */
{"USER", Query_for_list_of_roles " UNION SELECT 'MAPPING FOR'"},
- {"USER MAPPING FOR", NULL, NULL},
- {"VIEW", NULL, &Query_for_list_of_views},
+ {"USER MAPPING FOR", NULL, NULL, NULL},
+ {"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
};
static char *drop_command_generator(const char *text, int state);
static char *alter_command_generator(const char *text, int state);
static char *complete_from_query(const char *text, int state);
+static char *complete_from_versioned_query(const char *text, int state);
static char *complete_from_schema_query(const char *text, int state);
-static char *_complete_from_query(int is_schema_query,
+static char *complete_from_versioned_schema_query(const char *text, int state);
+static char *_complete_from_query(const char *simple_query,
+ const SchemaQuery *schema_query,
const char *text, int state);
static char *complete_from_list(const char *text, int state);
static char *complete_from_const(const char *text, int state);
COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
/* CALL */
else if (Matches1("CALL"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
else if (Matches2("CALL", MatchAny))
COMPLETE_WITH_CONST("(");
/* CLUSTER */
else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE"))
COMPLETE_WITH_CONST("PROCEDURE");
else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches2("EXECUTE", "PROCEDURE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
/* CREATE ROLE,USER,GROUP <name> */
else if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
else if (TailMatches1("DOMAIN"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
else if (TailMatches1("FUNCTION"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (TailMatches1("LANGUAGE"))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (TailMatches1("PROCEDURE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
else if (TailMatches1("ROUTINE"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);
else if (TailMatches1("SCHEMA"))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
}
else if (TailMatchesCS1("\\da*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
else if (TailMatchesCS1("\\dA*"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS1("\\db*"))
else if (TailMatchesCS1("\\dew*"))
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
else if (TailMatchesCS1("\\df*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (TailMatchesCS1("\\dFd*"))
COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
else if (TailMatchesCS1("\\ef"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (TailMatchesCS1("\\ev"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
COMPLETE_WITH_LIST_CS3("default", "verbose", "terse");
}
else if (TailMatchesCS1("\\sf*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (TailMatchesCS1("\\sv*"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
else if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|"
{
if (words_after_create[i].query)
COMPLETE_WITH_QUERY(words_after_create[i].query);
+ else if (words_after_create[i].vquery)
+ COMPLETE_WITH_VERSIONED_QUERY(words_after_create[i].vquery);
else if (words_after_create[i].squery)
- COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
- NULL);
+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(words_after_create[i].squery,
+ NULL);
break;
}
}
return create_or_drop_command_generator(text, state, THING_NO_ALTER);
}
-/* The following two functions are wrappers for _complete_from_query */
+/*
+ * These functions generate lists using server queries.
+ * They are all wrappers for _complete_from_query.
+ */
static char *
complete_from_query(const char *text, int state)
{
- return _complete_from_query(0, text, state);
+ /* query is assumed to work for any server version */
+ return _complete_from_query(completion_charp, NULL, text, state);
+}
+
+static char *
+complete_from_versioned_query(const char *text, int state)
+{
+ const VersionedQuery *vquery = completion_vquery;
+
+ /* Find appropriate array element */
+ while (pset.sversion < vquery->min_server_version)
+ vquery++;
+ /* Fail completion if server is too old */
+ if (vquery->query == NULL)
+ return NULL;
+
+ return _complete_from_query(vquery->query, NULL, text, state);
}
static char *
complete_from_schema_query(const char *text, int state)
{
- return _complete_from_query(1, text, state);
+ /* query is assumed to work for any server version */
+ return _complete_from_query(completion_charp, completion_squery,
+ text, state);
+}
+
+static char *
+complete_from_versioned_schema_query(const char *text, int state)
+{
+ const SchemaQuery *squery = completion_squery;
+ const VersionedQuery *vquery = completion_vquery;
+
+ /* Find appropriate array element */
+ while (pset.sversion < squery->min_server_version)
+ squery++;
+ /* Fail completion if server is too old */
+ if (squery->catname == NULL)
+ return NULL;
+
+ /* Likewise for the add-on text, if any */
+ if (vquery)
+ {
+ while (pset.sversion < vquery->min_server_version)
+ vquery++;
+ if (vquery->query == NULL)
+ return NULL;
+ }
+
+ return _complete_from_query(vquery ? vquery->query : NULL,
+ squery, text, state);
}
/*
- * This creates a list of matching things, according to a query pointed to
- * by completion_charp.
+ * This creates a list of matching things, according to a query described by
+ * the initial arguments. The caller has already done any work needed to
+ * select the appropriate query for the server's version.
+ *
* The query can be one of two kinds:
*
* 1. A simple query which must contain a %d and a %s, which will be replaced
* %d %s %d %s %d %s %s %d %s
* where %d is the string length of the text and %s the text itself.
*
+ * If both simple_query and schema_query are non-NULL, then we construct
+ * a schema query and append the (uninterpreted) string simple_query to it.
+ *
* 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.
+ *
+ * "text" and "state" are supplied by readline.
*/
static char *
-_complete_from_query(int is_schema_query, const char *text, int state)
+_complete_from_query(const char *simple_query,
+ const SchemaQuery *schema_query,
+ const char *text, int state)
{
static int list_index,
byte_length;
initPQExpBuffer(&query_buffer);
- if (is_schema_query)
+ if (schema_query)
{
- /* completion_squery gives us the pieces to assemble */
- const char *qualresult = completion_squery->qualresult;
+ /* schema_query gives us the pieces to assemble */
+ const char *qualresult = schema_query->qualresult;
if (qualresult == NULL)
- qualresult = completion_squery->result;
+ qualresult = schema_query->result;
/* Get unqualified names matching the input-so-far */
appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",
- completion_squery->result,
- completion_squery->catname);
- if (completion_squery->selcondition)
+ schema_query->result,
+ schema_query->catname);
+ if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
- completion_squery->selcondition);
+ schema_query->selcondition);
appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
- completion_squery->result,
+ schema_query->result,
char_length, e_text);
appendPQExpBuffer(&query_buffer, " AND %s",
- completion_squery->viscondition);
+ schema_query->viscondition);
/*
* When fetching relation names, suppress system catalogs unless
* between not offering system catalogs for completion at all, and
* having them swamp the result when the input is just "p".
*/
- if (strcmp(completion_squery->catname,
+ if (strcmp(schema_query->catname,
"pg_catalog.pg_class c") == 0 &&
strncmp(text, "pg_", 3) !=0)
{
"FROM %s, pg_catalog.pg_namespace n "
"WHERE %s = n.oid AND ",
qualresult,
- completion_squery->catname,
- completion_squery->namespace);
- if (completion_squery->selcondition)
+ schema_query->catname,
+ schema_query->namespace);
+ if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
- completion_squery->selcondition);
+ schema_query->selcondition);
appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",
qualresult,
char_length, e_text);
char_length, e_text);
/* If an addon query was provided, use it */
- if (completion_charp)
- appendPQExpBuffer(&query_buffer, "\n%s", completion_charp);
+ if (simple_query)
+ appendPQExpBuffer(&query_buffer, "\n%s", simple_query);
}
else
{
- /* completion_charp is an sprintf-style format string */
- appendPQExpBuffer(&query_buffer, completion_charp,
+ Assert(simple_query);
+ /* simple_query is an sprintf-style format string */
+ appendPQExpBuffer(&query_buffer, simple_query,
char_length, e_text,
e_info_charp, e_info_charp,
e_info_charp2, e_info_charp2);