From: Tom Lane <tgl@sss.pgh.pa.us>
To: tanghy.fnst@fujitsu.com <tanghy.fnst@fujitsu.com>
Cc: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
Cc: Japin Li <japinli@hotmail.com>
Cc: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Cc: Laurenz Albe <laurenz.albe@cybertec.at>
Cc: smithpb2250@gmail.com <smithpb2250@gmail.com>
Cc: david.zhang@highgo.ca <david.zhang@highgo.ca>
Cc: pgsql-hackers@lists.postgresql.org <pgsql-hackers@lists.postgresql.org>
Subject: Re: Support tab completion for upper character inputs in psql
Date: Fri, 28 Jan 2022 17:16:32 -0500
Message-ID: <2818574.1643408192@sss.pgh.pa.us> (raw)
In-Reply-To: <2805502.1643405157@sss.pgh.pa.us>
References: <OS0PR01MB611303B647967B6949B759F1FB4E9@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<20210423.115812.900808736988307020.horikyota.ntt@gmail.com>
<OS0PR01MB6113CA04E06D5BF221BC4FE2FB429@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<OS0PR01MB61136721363BD08576725F22FB089@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<80b093ae-88bd-8d8a-68b9-b535a3ab11be@enterprisedb.com>
<OS0PR01MB61137C7B2B09C0431415B9DBFBD69@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<54bd7839-f463-ef03-f2d2-ccb8184b9b27@enterprisedb.com>
<1068530.1641480996@sss.pgh.pa.us>
<TYCPR01MB6128BD8EA1529F4270114C18FB4D9@TYCPR01MB6128.jpnprd01.prod.outlook.com>
<MEYP282MB1669EAF2183E67EAF86F7F61B64D9@MEYP282MB1669.AUSP282.PROD.OUTLOOK.COM>
<TYCPR01MB6128D4933189A74AAD326F98FB4D9@TYCPR01MB6128.jpnprd01.prod.outlook.com>
<c7fb9ad1-0b77-eda5-0055-02ee472e3a4a@enterprisedb.co! m>
<3547066.1642272686@sss.pgh.pa.us>
<OS0PR01MB6113C130461FB8A9DBA0DAEBFB5A9@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<17d022ce-2543-b4a3-2b55-fc04! ! ! ae87a220@enterprisedb.com>
<OS0PR01MB6113DB08D8CEA4E214DA37BBFB5F9@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<2304294.1643152287@sss.pgh.pa.us>
<2645164.1643315022@sss.pgh.pa.us>
<OS0PR01MB611360D58EA41FC6557D92C0FB229@OS0PR01MB6113.jpnprd01.prod.outlook.com>
<2771362.1643385796@sss.pgh.pa.us>
<2805502.1643405157@sss.pgh.pa.us>
I wrote:
> [ v15 patch set ]
Sigh ... per the cfbot, this was already blindsided by 95787e849.
As I said, I don't want to sit on this for very long.
regards, tom lane
Attachments:download all (5 files, .tar.gz)v16-0001-rethink-tab-completion-quoting.patch (text/x-diff, 121.9K) downloaddiff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.cindex e09221d63d..e2026275b0 100644--- a/src/bin/psql/tab-complete.c+++ b/src/bin/psql/tab-complete.c@@ -46,7 +46,9 @@
#include "catalog/pg_am_d.h"
#include "catalog/pg_class_d.h"
#include "common.h"
+#include "common/keywords.h"
#include "libpq-fe.h"
+#include "mb/pg_wchar.h"
#include "pqexpbuffer.h"
#include "settings.h"
#include "stringutils.h"
@@ -148,16 +150,16 @@ typedef struct SchemaQuery
const char *namespace;
/*
- * Result --- the appropriately-quoted name to return, in the case of an- * unqualified name. For example, "pg_catalog.quote_ident(c.relname)".+ * Result --- the base object name to return. For example, "c.relname".
*/
const char *result;
/*
- * In some cases a different result must be used for qualified names.- * Enter that here, or write NULL if result can be used.+ * Additional literal strings (usually keywords) to be offered along with+ * the query results. Provide a NULL-terminated array of constant+ * strings, or NULL if none.
*/
- const char *qualresult;+ const char *const *keywords;
} SchemaQuery;
@@ -179,6 +181,7 @@ 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 */
+static bool completion_verbatim; /* completion is verbatim */
static bool completion_force_quote; /* true to force-quote filenames */
/*
@@ -190,36 +193,87 @@ static bool completion_force_quote; /* true to force-quote filenames */
* We support both simple and versioned schema queries.
* 3) The items from a null-pointer-terminated list (with or without
* case-sensitive comparison); if the list is constant you can build it
- * with COMPLETE_WITH() or COMPLETE_WITH_CS().+ * with COMPLETE_WITH() or COMPLETE_WITH_CS(). The QUERY_LIST and+ * QUERY_PLUS forms combine such literal lists with a query result.
* 4) The list of attributes of the given table (possibly schema-qualified).
* 5) The list of arguments to the given function (possibly schema-qualified).
*/
#define COMPLETE_WITH_QUERY(query) \
+ COMPLETE_WITH_QUERY_LIST(query, NULL)++#define COMPLETE_WITH_QUERY_LIST(query, list) \+do { \+ completion_charp = query; \+ completion_charpp = list; \+ completion_verbatim = false; \+ matches = rl_completion_matches(text, complete_from_query); \+} while (0)++#define COMPLETE_WITH_QUERY_PLUS(query, ...) \+do { \+ static const char *const list[] = { __VA_ARGS__, NULL }; \+ COMPLETE_WITH_QUERY_LIST(query, list); \+} while (0)++#define COMPLETE_WITH_QUERY_VERBATIM(query) \
do { \
completion_charp = query; \
+ completion_charpp = NULL; \+ completion_verbatim = true; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0)
#define COMPLETE_WITH_VERSIONED_QUERY(query) \
+ COMPLETE_WITH_VERSIONED_QUERY_LIST(query, NULL)++#define COMPLETE_WITH_VERSIONED_QUERY_LIST(query, list) \
do { \
completion_vquery = query; \
+ completion_charpp = list; \+ completion_verbatim = false; \
matches = rl_completion_matches(text, complete_from_versioned_query); \
} while (0)
-#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \+#define COMPLETE_WITH_VERSIONED_QUERY_PLUS(query, ...) \+do { \+ static const char *const list[] = { __VA_ARGS__, NULL }; \+ COMPLETE_WITH_VERSIONED_QUERY_LIST(query, list); \+} while (0)++#define COMPLETE_WITH_SCHEMA_QUERY(query) \+ COMPLETE_WITH_SCHEMA_QUERY_LIST(query, NULL)++#define COMPLETE_WITH_SCHEMA_QUERY_LIST(query, list) \
do { \
completion_squery = &(query); \
- completion_charp = addon; \+ completion_charpp = list; \+ completion_verbatim = false; \
matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0)
-#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query, addon) \+#define COMPLETE_WITH_SCHEMA_QUERY_PLUS(query, ...) \+do { \+ static const char *const list[] = { __VA_ARGS__, NULL }; \+ COMPLETE_WITH_SCHEMA_QUERY_LIST(query, list); \+} while (0)++#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query) \+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, NULL)++#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, list) \
do { \
completion_squery = query; \
- completion_vquery = addon; \+ completion_charpp = list; \+ completion_verbatim = false; \
matches = rl_completion_matches(text, complete_from_versioned_schema_query); \
} while (0)
+#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_PLUS(query, ...) \+do { \+ static const char *const list[] = { __VA_ARGS__, NULL }; \+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, list); \+} while (0)+
/*
* Caution: COMPLETE_WITH_CONST is not for general-purpose use; you probably
* want COMPLETE_WITH() with one element, instead.
@@ -253,7 +307,10 @@ do { \
COMPLETE_WITH_LIST_CS(list); \
} while (0)
-#define COMPLETE_WITH_ATTR(relation, addon) \+#define COMPLETE_WITH_ATTR(relation) \+ COMPLETE_WITH_ATTR_LIST(relation, NULL)++#define COMPLETE_WITH_ATTR_LIST(relation, list) \
do { \
char *_completion_schema; \
char *_completion_table; \
@@ -266,18 +323,26 @@ do { \
false, false, pset.encoding); \
if (_completion_table == NULL) \
{ \
- completion_charp = Query_for_list_of_attributes addon; \+ completion_charp = Query_for_list_of_attributes; \
completion_info_charp = relation; \
} \
else \
{ \
- completion_charp = Query_for_list_of_attributes_with_schema addon; \+ completion_charp = Query_for_list_of_attributes_with_schema; \
completion_info_charp = _completion_table; \
completion_info_charp2 = _completion_schema; \
} \
+ completion_charpp = list; \+ completion_verbatim = false; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0)
+#define COMPLETE_WITH_ATTR_PLUS(relation, ...) \+do { \+ static const char *const list[] = { __VA_ARGS__, NULL }; \+ COMPLETE_WITH_ATTR_LIST(relation, list); \+} while (0)+
/*
* libedit will typically include the literal's leading single quote in
* "text", while readline will not. Adapt our offered strings to fit.
@@ -315,6 +380,8 @@ do { \
completion_info_charp = _completion_type; \
completion_info_charp2 = _completion_schema; \
} \
+ completion_charpp = NULL; \+ completion_verbatim = true; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0)
@@ -340,6 +407,8 @@ do { \
completion_info_charp = _completion_function; \
completion_info_charp2 = _completion_schema; \
} \
+ completion_charpp = NULL; \+ completion_verbatim = true; \
matches = rl_completion_matches(text, complete_from_query); \
} while (0)
@@ -357,17 +426,45 @@ static const SchemaQuery Query_for_list_of_aggregates[] = {
.selcondition = "p.prokind = 'a'",
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
- .result = "pg_catalog.quote_ident(p.proname)",+ .result = "p.proname",
},
{
.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)",+ .result = "p.proname",
}
};
+static const char *const Keywords_for_list_of_datatypes[] = {+ "bigint",+ "boolean",+ "character",+ "double precision",+ "integer",+ "real",+ "smallint",++ /*+ * Note: currently there's no value in offering the following multiword+ * type names, because tab completion cannot succeed for them: we can't+ * disambiguate until somewhere in the second word, at which point we+ * won't have the first word as context. ("double precision" does work,+ * as long as no other type name begins with "double".) Leave them out to+ * encourage users to use the PG-specific aliases, which we can complete.+ */+#ifdef NOT_USED+ "bit varying",+ "character varying",+ "time with time zone",+ "time without time zone",+ "timestamp with time zone",+ "timestamp without time zone",+#endif+ NULL+};+
static const SchemaQuery Query_for_list_of_datatypes = {
.catname = "pg_catalog.pg_type t",
/* selcondition --- ignore table rowtypes and array types */
@@ -377,8 +474,8 @@ static const SchemaQuery Query_for_list_of_datatypes = {
"AND t.typname !~ '^_'",
.viscondition = "pg_catalog.pg_type_is_visible(t.oid)",
.namespace = "t.typnamespace",
- .result = "pg_catalog.format_type(t.oid, NULL)",- .qualresult = "pg_catalog.quote_ident(t.typname)",+ .result = "t.typname",+ .keywords = Keywords_for_list_of_datatypes,
};
static const SchemaQuery Query_for_list_of_composite_datatypes = {
@@ -389,8 +486,7 @@ static const SchemaQuery Query_for_list_of_composite_datatypes = {
"AND t.typname !~ '^_'",
.viscondition = "pg_catalog.pg_type_is_visible(t.oid)",
.namespace = "t.typnamespace",
- .result = "pg_catalog.format_type(t.oid, NULL)",- .qualresult = "pg_catalog.quote_ident(t.typname)",+ .result = "t.typname",
};
static const SchemaQuery Query_for_list_of_domains = {
@@ -398,7 +494,7 @@ static const SchemaQuery Query_for_list_of_domains = {
.selcondition = "t.typtype = 'd'",
.viscondition = "pg_catalog.pg_type_is_visible(t.oid)",
.namespace = "t.typnamespace",
- .result = "pg_catalog.quote_ident(t.typname)",+ .result = "t.typname",
};
/* Note: this intentionally accepts aggregates as well as plain functions */
@@ -409,13 +505,13 @@ static const SchemaQuery Query_for_list_of_functions[] = {
.selcondition = "p.prokind != 'p'",
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
- .result = "pg_catalog.quote_ident(p.proname)",+ .result = "p.proname",
},
{
.catname = "pg_catalog.pg_proc p",
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
- .result = "pg_catalog.quote_ident(p.proname)",+ .result = "p.proname",
}
};
@@ -426,7 +522,7 @@ static const SchemaQuery Query_for_list_of_procedures[] = {
.selcondition = "p.prokind = 'p'",
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
- .result = "pg_catalog.quote_ident(p.proname)",+ .result = "p.proname",
},
{
/* not supported in older versions */
@@ -438,7 +534,7 @@ static const SchemaQuery Query_for_list_of_routines = {
.catname = "pg_catalog.pg_proc p",
.viscondition = "pg_catalog.pg_function_is_visible(p.oid)",
.namespace = "p.pronamespace",
- .result = "pg_catalog.quote_ident(p.proname)",+ .result = "p.proname",
};
static const SchemaQuery Query_for_list_of_sequences = {
@@ -446,7 +542,7 @@ static const SchemaQuery Query_for_list_of_sequences = {
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_SEQUENCE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_foreign_tables = {
@@ -454,7 +550,7 @@ static const SchemaQuery Query_for_list_of_foreign_tables = {
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_FOREIGN_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_tables = {
@@ -464,7 +560,7 @@ static const SchemaQuery Query_for_list_of_tables = {
CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_partitioned_tables = {
@@ -472,7 +568,7 @@ static const SchemaQuery Query_for_list_of_partitioned_tables = {
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_views = {
@@ -480,7 +576,7 @@ static const SchemaQuery Query_for_list_of_views = {
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_VIEW) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_matviews = {
@@ -488,7 +584,7 @@ static const SchemaQuery Query_for_list_of_matviews = {
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_MATVIEW) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_indexes = {
@@ -498,7 +594,7 @@ static const SchemaQuery Query_for_list_of_indexes = {
CppAsString2(RELKIND_PARTITIONED_INDEX) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_partitioned_indexes = {
@@ -506,7 +602,7 @@ static const SchemaQuery Query_for_list_of_partitioned_indexes = {
.selcondition = "c.relkind = " CppAsString2(RELKIND_PARTITIONED_INDEX),
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
@@ -515,7 +611,7 @@ static const SchemaQuery Query_for_list_of_relations = {
.catname = "pg_catalog.pg_class c",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
/* partitioned relations */
@@ -525,14 +621,14 @@ static const SchemaQuery Query_for_list_of_partitioned_relations = {
", " CppAsString2(RELKIND_PARTITIONED_INDEX) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_operator_families = {
.catname = "pg_catalog.pg_opfamily c",
.viscondition = "pg_catalog.pg_opfamily_is_visible(c.oid)",
.namespace = "c.opfnamespace",
- .result = "pg_catalog.quote_ident(c.opfname)",+ .result = "c.opfname",
};
/* Relations supporting INSERT, UPDATE or DELETE */
@@ -545,7 +641,7 @@ static const SchemaQuery Query_for_list_of_updatables = {
CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
/* Relations supporting SELECT */
@@ -560,7 +656,7 @@ static const SchemaQuery Query_for_list_of_selectables = {
CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
/* Relations supporting TRUNCATE */
@@ -572,7 +668,7 @@ static const SchemaQuery Query_for_list_of_truncatables = {
CppAsString2(RELKIND_PARTITIONED_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
/* Relations supporting GRANT are currently same as those supporting SELECT */
@@ -588,7 +684,7 @@ static const SchemaQuery Query_for_list_of_analyzables = {
CppAsString2(RELKIND_FOREIGN_TABLE) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
/* Relations supporting index creation */
@@ -600,7 +696,7 @@ static const SchemaQuery Query_for_list_of_indexables = {
CppAsString2(RELKIND_MATVIEW) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
/*
@@ -617,7 +713,7 @@ static const SchemaQuery Query_for_list_of_clusterables = {
CppAsString2(RELKIND_MATVIEW) ")",
.viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
.namespace = "c.relnamespace",
- .result = "pg_catalog.quote_ident(c.relname)",+ .result = "c.relname",
};
static const SchemaQuery Query_for_list_of_constraints_with_schema = {
@@ -625,14 +721,14 @@ static const SchemaQuery Query_for_list_of_constraints_with_schema = {
.selcondition = "c.conrelid <> 0",
.viscondition = "true", /* there is no pg_constraint_is_visible */
.namespace = "c.connamespace",
- .result = "pg_catalog.quote_ident(c.conname)",+ .result = "c.conname",
};
static const SchemaQuery Query_for_list_of_statistics = {
.catname = "pg_catalog.pg_statistic_ext s",
.viscondition = "pg_catalog.pg_statistics_obj_is_visible(s.oid)",
.namespace = "s.stxnamespace",
- .result = "pg_catalog.quote_ident(s.stxname)",+ .result = "s.stxname",
};
static const SchemaQuery Query_for_list_of_collations = {
@@ -640,7 +736,7 @@ static const SchemaQuery Query_for_list_of_collations = {
.selcondition = "c.collencoding IN (-1, pg_catalog.pg_char_to_encoding(pg_catalog.getdatabaseencoding()))",
.viscondition = "pg_catalog.pg_collation_is_visible(c.oid)",
.namespace = "c.collnamespace",
- .result = "pg_catalog.quote_ident(c.collname)",+ .result = "c.collname",
};
@@ -659,13 +755,13 @@ static const SchemaQuery Query_for_list_of_collations = {
*/
#define Query_for_list_of_attributes \
-"SELECT pg_catalog.quote_ident(attname) "\+"SELECT attname "\
" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\
" WHERE c.oid = a.attrelid "\
" AND a.attnum > 0 "\
" AND NOT a.attisdropped "\
-" AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\-" AND (pg_catalog.quote_ident(relname)='%s' "\+" AND substring(attname,1,%d)='%s' "\+" AND (relname='%s' "\
" OR '\"' || relname || '\"'='%s') "\
" AND pg_catalog.pg_table_is_visible(c.oid)"
@@ -676,21 +772,21 @@ static const SchemaQuery Query_for_list_of_collations = {
" AND a.attnum > 0 "\
" AND NOT a.attisdropped "\
" AND substring(attnum::pg_catalog.text,1,%d)='%s' "\
-" AND (pg_catalog.quote_ident(relname)='%s' "\+" AND (relname='%s' "\
" OR '\"' || relname || '\"'='%s') "\
" AND pg_catalog.pg_table_is_visible(c.oid)"
#define Query_for_list_of_attributes_with_schema \
-"SELECT pg_catalog.quote_ident(attname) "\+"SELECT attname "\
" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\
" WHERE c.oid = a.attrelid "\
" AND n.oid = c.relnamespace "\
" AND a.attnum > 0 "\
" AND NOT a.attisdropped "\
-" AND substring(pg_catalog.quote_ident(attname),1,%d)='%s' "\-" AND (pg_catalog.quote_ident(relname)='%s' "\+" AND substring(attname,1,%d)='%s' "\+" AND (relname='%s' "\
" OR '\"' || relname || '\"' ='%s') "\
-" AND (pg_catalog.quote_ident(nspname)='%s' "\+" AND (nspname='%s' "\
" OR '\"' || nspname || '\"' ='%s') "
#define Query_for_list_of_enum_values_quoted \
@@ -698,7 +794,7 @@ static const SchemaQuery Query_for_list_of_collations = {
" FROM pg_catalog.pg_enum e, pg_catalog.pg_type t "\
" WHERE t.oid = e.enumtypid "\
" AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\
-" AND (pg_catalog.quote_ident(typname)='%s' "\+" AND (typname='%s' "\
" OR '\"' || typname || '\"'='%s') "\
" AND pg_catalog.pg_type_is_visible(t.oid)"
@@ -707,7 +803,7 @@ static const SchemaQuery Query_for_list_of_collations = {
" FROM pg_catalog.pg_enum e, pg_catalog.pg_type t "\
" WHERE t.oid = e.enumtypid "\
" AND substring(enumlabel,1,%d)='%s' "\
-" AND (pg_catalog.quote_ident(typname)='%s' "\+" AND (typname='%s' "\
" OR '\"' || typname || '\"'='%s') "\
" AND pg_catalog.pg_type_is_visible(t.oid)"
@@ -717,9 +813,9 @@ static const SchemaQuery Query_for_list_of_collations = {
" WHERE t.oid = e.enumtypid "\
" AND n.oid = t.typnamespace "\
" AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\
-" AND (pg_catalog.quote_ident(typname)='%s' "\+" AND (typname='%s' "\
" OR '\"' || typname || '\"'='%s') "\
-" AND (pg_catalog.quote_ident(nspname)='%s' "\+" AND (nspname='%s' "\
" OR '\"' || nspname || '\"' ='%s') "
#define Query_for_list_of_enum_values_with_schema_unquoted \
@@ -728,24 +824,24 @@ static const SchemaQuery Query_for_list_of_collations = {
" WHERE t.oid = e.enumtypid "\
" AND n.oid = t.typnamespace "\
" AND substring(enumlabel,1,%d)='%s' "\
-" AND (pg_catalog.quote_ident(typname)='%s' "\+" AND (typname='%s' "\
" OR '\"' || typname || '\"'='%s') "\
-" AND (pg_catalog.quote_ident(nspname)='%s' "\+" AND (nspname='%s' "\
" OR '\"' || nspname || '\"' ='%s') "
#define Query_for_list_of_template_databases \
-"SELECT pg_catalog.quote_ident(d.datname) "\+"SELECT d.datname "\
" FROM pg_catalog.pg_database d "\
-" WHERE substring(pg_catalog.quote_ident(d.datname),1,%d)='%s' "\+" WHERE substring(d.datname,1,%d)='%s' "\
" AND (d.datistemplate OR pg_catalog.pg_has_role(d.datdba, 'USAGE'))"
#define Query_for_list_of_databases \
-"SELECT pg_catalog.quote_ident(datname) FROM pg_catalog.pg_database "\-" WHERE substring(pg_catalog.quote_ident(datname),1,%d)='%s'"+"SELECT datname FROM pg_catalog.pg_database "\+" WHERE substring(datname,1,%d)='%s'"
#define Query_for_list_of_tablespaces \
-"SELECT pg_catalog.quote_ident(spcname) FROM pg_catalog.pg_tablespace "\-" WHERE substring(pg_catalog.quote_ident(spcname),1,%d)='%s'"+"SELECT spcname FROM pg_catalog.pg_tablespace "\+" WHERE substring(spcname,1,%d)='%s'"
#define Query_for_list_of_encodings \
" SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
@@ -753,287 +849,260 @@ static const SchemaQuery Query_for_list_of_collations = {
" WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"
#define Query_for_list_of_languages \
-"SELECT pg_catalog.quote_ident(lanname) "\+"SELECT lanname "\
" FROM pg_catalog.pg_language "\
" WHERE lanname != 'internal' "\
-" AND substring(pg_catalog.quote_ident(lanname),1,%d)='%s'"+" AND substring(lanname,1,%d)='%s'"
#define Query_for_list_of_schemas \
-"SELECT pg_catalog.quote_ident(nspname) FROM pg_catalog.pg_namespace "\-" WHERE substring(pg_catalog.quote_ident(nspname),1,%d)='%s'"+"SELECT nspname FROM pg_catalog.pg_namespace "\+" WHERE substring(nspname,1,%d)='%s'"
#define Query_for_list_of_alter_system_set_vars \
"SELECT name FROM "\
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
" WHERE context != 'internal' "\
-" UNION ALL SELECT 'all') ss "\+" ) ss "\
" WHERE substring(name,1,%d)='%s'"
#define Query_for_list_of_set_vars \
"SELECT name FROM "\
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
" WHERE context IN ('user', 'superuser') "\
-" UNION ALL SELECT 'constraints' "\-" UNION ALL SELECT 'transaction' "\-" UNION ALL SELECT 'session' "\-" UNION ALL SELECT 'role' "\-" UNION ALL SELECT 'tablespace' "\-" UNION ALL SELECT 'all') ss "\+" ) ss "\
" WHERE substring(name,1,%d)='%s'"
#define Query_for_list_of_show_vars \
"SELECT name FROM "\
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
-" UNION ALL SELECT 'session authorization' "\-" UNION ALL SELECT 'all') ss "\+" ) ss "\
" WHERE substring(name,1,%d)='%s'"
#define Query_for_list_of_roles \
-" SELECT pg_catalog.quote_ident(rolname) "\+" SELECT rolname "\
" FROM pg_catalog.pg_roles "\
-" WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"+" WHERE substring(rolname,1,%d)='%s'"-#define Query_for_list_of_grant_roles \-" SELECT pg_catalog.quote_ident(rolname) "\-" FROM pg_catalog.pg_roles "\-" WHERE substring(pg_catalog.quote_ident(rolname),1,%d)='%s'"\-" UNION ALL SELECT 'PUBLIC'"\-" UNION ALL SELECT 'CURRENT_ROLE'"\-" UNION ALL SELECT 'CURRENT_USER'"\-" UNION ALL SELECT 'SESSION_USER'"+/* add these to Query_for_list_of_roles in GRANT contexts */+#define Keywords_for_list_of_grant_roles \+"PUBLIC", "CURRENT_ROLE", "CURRENT_USER", "SESSION_USER"-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_index_of_table \
-"SELECT pg_catalog.quote_ident(c2.relname) "\+"SELECT c2.relname "\
" FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\
" WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\
-" and (%d = pg_catalog.length('%s'))"\-" and pg_catalog.quote_ident(c1.relname)='%s'"\+" and substring(c2.relname,1,%d)='%s'"\+" and c1.relname='%s'"\
" and pg_catalog.pg_table_is_visible(c2.oid)"
#define Query_for_unique_index_of_table \
Query_for_index_of_table \
" and i.indisunique"
-/* 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) "\+"SELECT 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'"\+" WHERE c1.oid=conrelid and substring(conname,1,%d)='%s'"\+" and c1.relname='%s'"\
" and pg_catalog.pg_table_is_visible(c1.oid)"
-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_constraint_of_table_not_validated \
-"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)" \+Query_for_constraint_of_table \
" and not con.convalidated"
#define Query_for_all_table_constraints \
-"SELECT pg_catalog.quote_ident(conname) "\+"SELECT conname "\
" FROM pg_catalog.pg_constraint c "\
-" WHERE c.conrelid <> 0 "+" WHERE c.conrelid <> 0 "\+" and substring(conname,1,%d)='%s'"-/* 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) "\+"SELECT 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'"\+" WHERE t.oid=contypid and substring(conname,1,%d)='%s'"\+" and 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) "\+"SELECT relname "\
" FROM pg_catalog.pg_class"\
-" WHERE (%d = pg_catalog.length('%s'))"\+" WHERE substring(relname,1,%d)='%s'"\
" AND oid IN "\
" (SELECT conrelid FROM pg_catalog.pg_constraint "\
-" WHERE pg_catalog.quote_ident(conname)='%s')"+" WHERE conname='%s')"-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_rule_of_table \
-"SELECT pg_catalog.quote_ident(rulename) "\+"SELECT rulename "\
" FROM pg_catalog.pg_class c1, pg_catalog.pg_rewrite "\
-" WHERE c1.oid=ev_class and (%d = pg_catalog.length('%s'))"\-" and pg_catalog.quote_ident(c1.relname)='%s'"\+" WHERE c1.oid=ev_class and substring(rulename,1,%d)='%s'"\+" and c1.relname='%s'"\
" and pg_catalog.pg_table_is_visible(c1.oid)"
-/* 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) "\+"SELECT relname "\
" FROM pg_catalog.pg_class"\
-" WHERE (%d = pg_catalog.length('%s'))"\+" WHERE substring(relname,1,%d)='%s'"\
" AND oid IN "\
" (SELECT ev_class FROM pg_catalog.pg_rewrite "\
-" WHERE pg_catalog.quote_ident(rulename)='%s')"+" WHERE rulename='%s')"-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_trigger_of_table \
-"SELECT pg_catalog.quote_ident(tgname) "\+"SELECT tgname "\
" FROM pg_catalog.pg_class c1, pg_catalog.pg_trigger "\
-" WHERE c1.oid=tgrelid and (%d = pg_catalog.length('%s'))"\-" and pg_catalog.quote_ident(c1.relname)='%s'"\+" WHERE c1.oid=tgrelid and substring(tgname,1,%d)='%s'"\+" and c1.relname='%s'"\
" and pg_catalog.pg_table_is_visible(c1.oid)"\
" and not tgisinternal"
-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_tables_for_trigger \
-"SELECT pg_catalog.quote_ident(relname) "\+"SELECT relname "\
" FROM pg_catalog.pg_class"\
-" WHERE (%d = pg_catalog.length('%s'))"\+" WHERE substring(relname,1,%d)='%s'"\
" AND oid IN "\
" (SELECT tgrelid FROM pg_catalog.pg_trigger "\
-" WHERE pg_catalog.quote_ident(tgname)='%s')"+" WHERE tgname='%s')"
#define Query_for_list_of_ts_configurations \
-"SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\-" WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"+"SELECT cfgname FROM pg_catalog.pg_ts_config "\+" WHERE substring(cfgname,1,%d)='%s'"
#define Query_for_list_of_ts_dictionaries \
-"SELECT pg_catalog.quote_ident(dictname) FROM pg_catalog.pg_ts_dict "\-" WHERE substring(pg_catalog.quote_ident(dictname),1,%d)='%s'"+"SELECT dictname FROM pg_catalog.pg_ts_dict "\+" WHERE substring(dictname,1,%d)='%s'"
#define Query_for_list_of_ts_parsers \
-"SELECT pg_catalog.quote_ident(prsname) FROM pg_catalog.pg_ts_parser "\-" WHERE substring(pg_catalog.quote_ident(prsname),1,%d)='%s'"+"SELECT prsname FROM pg_catalog.pg_ts_parser "\+" WHERE substring(prsname,1,%d)='%s'"
#define Query_for_list_of_ts_templates \
-"SELECT pg_catalog.quote_ident(tmplname) FROM pg_catalog.pg_ts_template "\-" WHERE substring(pg_catalog.quote_ident(tmplname),1,%d)='%s'"+"SELECT tmplname FROM pg_catalog.pg_ts_template "\+" WHERE substring(tmplname,1,%d)='%s'"
#define Query_for_list_of_fdws \
-" SELECT pg_catalog.quote_ident(fdwname) "\+" SELECT fdwname "\
" FROM pg_catalog.pg_foreign_data_wrapper "\
-" WHERE substring(pg_catalog.quote_ident(fdwname),1,%d)='%s'"+" WHERE substring(fdwname,1,%d)='%s'"
#define Query_for_list_of_servers \
-" SELECT pg_catalog.quote_ident(srvname) "\+" SELECT srvname "\
" FROM pg_catalog.pg_foreign_server "\
-" WHERE substring(pg_catalog.quote_ident(srvname),1,%d)='%s'"+" WHERE substring(srvname,1,%d)='%s'"
#define Query_for_list_of_user_mappings \
-" SELECT pg_catalog.quote_ident(usename) "\+" SELECT usename "\
" FROM pg_catalog.pg_user_mappings "\
-" WHERE substring(pg_catalog.quote_ident(usename),1,%d)='%s'"+" WHERE substring(usename,1,%d)='%s'"
#define Query_for_list_of_access_methods \
-" SELECT pg_catalog.quote_ident(amname) "\+" SELECT amname "\
" FROM pg_catalog.pg_am "\
-" WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s'"+" WHERE substring(amname,1,%d)='%s'"
#define Query_for_list_of_index_access_methods \
-" SELECT pg_catalog.quote_ident(amname) "\+" SELECT amname "\
" FROM pg_catalog.pg_am "\
-" WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s' AND "\+" WHERE substring(amname,1,%d)='%s' AND "\
" amtype=" CppAsString2(AMTYPE_INDEX)
#define Query_for_list_of_table_access_methods \
-" SELECT pg_catalog.quote_ident(amname) "\+" SELECT amname "\
" FROM pg_catalog.pg_am "\
-" WHERE substring(pg_catalog.quote_ident(amname),1,%d)='%s' AND "\+" WHERE substring(amname,1,%d)='%s' AND "\
" amtype=" CppAsString2(AMTYPE_TABLE)
-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_list_of_arguments \
"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\
" FROM pg_catalog.pg_proc "\
-" WHERE (%d = pg_catalog.length('%s'))"\-" AND (pg_catalog.quote_ident(proname)='%s'"\+" WHERE substring(pg_catalog.oidvectortypes(proargtypes)||')',1,%d)='%s'"\+" AND (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'))"\+" WHERE substring(pg_catalog.oidvectortypes(proargtypes)||')',1,%d)='%s'"\
" AND n.oid = p.pronamespace "\
-" AND (pg_catalog.quote_ident(proname)='%s' "\+" AND (proname='%s' "\
" OR '\"' || proname || '\"' ='%s') "\
-" AND (pg_catalog.quote_ident(nspname)='%s' "\+" AND (nspname='%s' "\
" OR '\"' || nspname || '\"' ='%s') "
#define Query_for_list_of_extensions \
-" SELECT pg_catalog.quote_ident(extname) "\+" SELECT extname "\
" FROM pg_catalog.pg_extension "\
-" WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'"+" WHERE substring(extname,1,%d)='%s'"
#define Query_for_list_of_available_extensions \
-" SELECT pg_catalog.quote_ident(name) "\+" SELECT name "\
" FROM pg_catalog.pg_available_extensions "\
-" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL"+" WHERE substring(name,1,%d)='%s' AND installed_version IS NULL"-/* the silly-looking length condition is just to eat up the current word */+/* the result of this query is not an identifier, so use VERBATIM */
#define Query_for_list_of_available_extension_versions \
-" SELECT pg_catalog.quote_ident(version) "\+" SELECT version "\
" FROM pg_catalog.pg_available_extension_versions "\
-" WHERE (%d = pg_catalog.length('%s'))"\-" AND pg_catalog.quote_ident(name)='%s'"+" WHERE substring(version,1,%d)='%s'"\+" AND name='%s'"-/* the silly-looking length condition is just to eat up the current word */+/* the result of this query is not an identifier, so use VERBATIM */
#define Query_for_list_of_available_extension_versions_with_TO \
-" SELECT 'TO ' || pg_catalog.quote_ident(version) "\+" SELECT 'TO ' || version "\
" FROM pg_catalog.pg_available_extension_versions "\
-" WHERE (%d = pg_catalog.length('%s'))"\-" AND pg_catalog.quote_ident(name)='%s'"+" WHERE substring('TO ' || version,1,%d)='%s'"\+" AND name='%s'"
#define Query_for_list_of_prepared_statements \
-" SELECT pg_catalog.quote_ident(name) "\+" SELECT name "\
" FROM pg_catalog.pg_prepared_statements "\
-" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"+" WHERE substring(name,1,%d)='%s'"
#define Query_for_list_of_event_triggers \
-" SELECT pg_catalog.quote_ident(evtname) "\+" SELECT evtname "\
" FROM pg_catalog.pg_event_trigger "\
-" WHERE substring(pg_catalog.quote_ident(evtname),1,%d)='%s'"+" WHERE substring(evtname,1,%d)='%s'"
#define Query_for_list_of_tablesample_methods \
-" SELECT pg_catalog.quote_ident(proname) "\+" SELECT proname "\
" FROM pg_catalog.pg_proc "\
" WHERE prorettype = 'pg_catalog.tsm_handler'::pg_catalog.regtype AND "\
" proargtypes[0] = 'pg_catalog.internal'::pg_catalog.regtype AND "\
-" substring(pg_catalog.quote_ident(proname),1,%d)='%s'"+" substring(proname,1,%d)='%s'"
#define Query_for_list_of_policies \
-" SELECT pg_catalog.quote_ident(polname) "\+" SELECT polname "\
" FROM pg_catalog.pg_policy "\
-" WHERE substring(pg_catalog.quote_ident(polname),1,%d)='%s'"+" WHERE substring(polname,1,%d)='%s'"
#define Query_for_list_of_tables_for_policy \
-"SELECT pg_catalog.quote_ident(relname) "\+"SELECT relname "\
" FROM pg_catalog.pg_class"\
-" WHERE (%d = pg_catalog.length('%s'))"\+" WHERE substring(relname,1,%d)='%s'"\
" AND oid IN "\
" (SELECT polrelid FROM pg_catalog.pg_policy "\
-" WHERE pg_catalog.quote_ident(polname)='%s')"+" WHERE polname='%s')"
#define Query_for_enum \
" SELECT name FROM ( "\
-" SELECT pg_catalog.quote_ident(pg_catalog.unnest(enumvals)) AS name "\+" SELECT pg_catalog.unnest(enumvals) AS name "\
" FROM pg_catalog.pg_settings "\
" WHERE pg_catalog.lower(name)=pg_catalog.lower('%s') "\
-" UNION ALL " \-" SELECT 'DEFAULT' ) ss "\+" ) ss "\
" WHERE pg_catalog.substring(name,1,%%d)='%%s'"
-/* the silly-looking length condition is just to eat up the current word */
#define Query_for_partition_of_table \
-"SELECT pg_catalog.quote_ident(c2.relname) "\+"SELECT c2.relname "\
" FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i"\
" WHERE c1.oid=i.inhparent and i.inhrelid=c2.oid"\
-" and (%d = pg_catalog.length('%s'))"\-" and pg_catalog.quote_ident(c1.relname)='%s'"\+" and substring(c2.relname,1,%d)='%s'"\+" and c1.relname='%s'"\
" and pg_catalog.pg_table_is_visible(c2.oid)"\
" and c2.relispartition = 'true'"
#define Query_for_list_of_cursors \
-" SELECT pg_catalog.quote_ident(name) "\+" SELECT name "\
" FROM pg_catalog.pg_cursors "\
-" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"+" WHERE substring(name,1,%d)='%s'"
/*
* These object types were introduced later than our support cutoff of
@@ -1043,18 +1112,18 @@ Query_for_index_of_table \
static const VersionedQuery Query_for_list_of_publications[] = {
{100000,
- " SELECT pg_catalog.quote_ident(pubname) "+ " SELECT pubname "
" FROM pg_catalog.pg_publication "
- " WHERE substring(pg_catalog.quote_ident(pubname),1,%d)='%s'"+ " WHERE substring(pubname,1,%d)='%s'"
},
{0, NULL}
};
static const VersionedQuery Query_for_list_of_subscriptions[] = {
{100000,
- " SELECT pg_catalog.quote_ident(s.subname) "+ " SELECT s.subname "
" FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "
- " WHERE substring(pg_catalog.quote_ident(s.subname),1,%d)='%s' "+ " WHERE substring(s.subname,1,%d)='%s' "
" AND d.datname = pg_catalog.current_database() "
" AND s.subdbid = d.oid"
},
@@ -1069,9 +1138,11 @@ static const VersionedQuery Query_for_list_of_subscriptions[] = {
typedef struct
{
const char *name;
+ /* Provide at most one of these three types of query: */
const char *query; /* simple query, or NULL */
const VersionedQuery *vquery; /* versioned query, or NULL */
const SchemaQuery *squery; /* schema query, or NULL */
+ const char *const *keywords; /* keywords to be offered as well */
const bits32 flags; /* visibility flags, see below */
} pgsql_thing_t;
@@ -1080,8 +1151,14 @@ typedef struct
#define THING_NO_ALTER (1 << 2) /* should not show up after ALTER */
#define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP | THING_NO_ALTER)
+/* When we have DROP USER etc, also offer MAPPING FOR */+static const char *const Keywords_for_user_thing[] = {+ "MAPPING FOR",+ NULL+};+
static const pgsql_thing_t words_after_create[] = {
- {"ACCESS METHOD", NULL, NULL, NULL, THING_NO_ALTER},+ {"ACCESS METHOD", NULL, 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 */
@@ -1091,11 +1168,11 @@ static const pgsql_thing_t words_after_create[] = {
* 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, 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'"},+ {"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, NULL, NULL, THING_NO_SHOW},+ {"CONVERSION", "SELECT conname FROM pg_catalog.pg_conversion WHERE substring(conname,1,%d)='%s'"},
{"DATABASE", Query_for_list_of_databases},
- {"DEFAULT PRIVILEGES", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},- {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, THING_NO_SHOW},+ {"DEFAULT PRIVILEGES", NULL, NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},+ {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, NULL, THING_NO_SHOW},
{"DOMAIN", NULL, NULL, &Query_for_list_of_domains},
{"EVENT TRIGGER", NULL, NULL, NULL},
{"EXTENSION", Query_for_list_of_extensions},
@@ -1105,41 +1182,41 @@ static const pgsql_thing_t words_after_create[] = {
{"GROUP", Query_for_list_of_roles},
{"INDEX", NULL, NULL, &Query_for_list_of_indexes},
{"LANGUAGE", Query_for_list_of_languages},
- {"LARGE OBJECT", NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},+ {"LARGE OBJECT", NULL, 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. */
- {"OR REPLACE", NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},- {"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},+ {"OR REPLACE", NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER},+ {"OWNED", NULL, NULL, NULL, NULL, THING_NO_CREATE | THING_NO_ALTER}, /* for DROP OWNED BY ... */+ {"PARSER", Query_for_list_of_ts_parsers, NULL, 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, 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'"},+ {"ROUTINE", NULL, NULL, &Query_for_list_of_routines, NULL, THING_NO_CREATE},+ {"RULE", "SELECT rulename FROM pg_catalog.pg_rules WHERE substring(rulename,1,%d)='%s'"},
{"SCHEMA", Query_for_list_of_schemas},
{"SEQUENCE", NULL, NULL, &Query_for_list_of_sequences},
{"SERVER", Query_for_list_of_servers},
{"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},+ {"SYSTEM", NULL, 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, 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 ... */+ {"TEMP", NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMP TABLE+ * ... */+ {"TEMPLATE", Query_for_list_of_ts_templates, NULL, NULL, NULL, THING_NO_SHOW},+ {"TEMPORARY", NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE TEMPORARY+ * TABLE ... */
{"TEXT SEARCH", NULL, NULL, NULL},
- {"TRANSFORM", NULL, NULL, NULL, THING_NO_ALTER},- {"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"},+ {"TRANSFORM", NULL, NULL, NULL, NULL, THING_NO_ALTER},+ {"TRIGGER", "SELECT tgname FROM pg_catalog.pg_trigger WHERE substring(tgname,1,%d)='%s' AND NOT tgisinternal"},
{"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'"},+ {"UNIQUE", NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE+ * INDEX ... */+ {"UNLOGGED", NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNLOGGED+ * TABLE ... */+ {"USER", Query_for_list_of_roles, NULL, NULL, Keywords_for_user_thing},
{"USER MAPPING FOR", NULL, NULL, NULL},
{"VIEW", NULL, NULL, &Query_for_list_of_views},
{NULL} /* end of list */
@@ -1200,6 +1277,8 @@ static char *complete_from_schema_query(const char *text, int state);
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 *const *keywords,+ bool verbatim,
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);
@@ -1212,6 +1291,12 @@ static char *complete_from_files(const char *text, int state);
static char *pg_strdup_keyword_case(const char *s, const char *ref);
static char *escape_string(const char *text);
+static void parse_identifier(const char *ident,+ char **schemaname, char **objectname,+ bool *schemaquoted, bool *objectquoted);+static char *requote_identifier(const char *schemaname, const char *objectname,+ bool quote_schema, bool quote_object);+static bool identifier_needs_quotes(const char *ident);
static PGresult *exec_query(const char *query);
static char **get_previous_words(int point, char **buffer, int *nwords);
@@ -1650,8 +1735,8 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE */
else if (Matches("ALTER", "TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,- "UNION SELECT 'ALL IN TABLESPACE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,+ "ALL IN TABLESPACE");
/* ALTER something */
else if (Matches("ALTER"))
@@ -1695,7 +1780,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
(HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
ends_with(prev_wd, ',')))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"))
COMPLETE_WITH(",");
/* ALTER PUBLICATION <name> DROP */
@@ -1705,11 +1790,11 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
- COMPLETE_WITH_QUERY(Query_for_list_of_schemas- " AND nspname != 'pg_catalog' "- " AND nspname not like 'pg\\_toast%%' "- " AND nspname not like 'pg\\_temp%%' "- " UNION SELECT 'CURRENT_SCHEMA'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas+ " AND nspname != 'pg_catalog' "+ " AND nspname not like 'pg\\_toast%%' "+ " AND nspname not like 'pg\\_temp%%' ",+ "CURRENT_SCHEMA");
/* ALTER PUBLICATION <name> SET ( */
else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("SET", "("))
COMPLETE_WITH("publish", "publish_via_partition_root");
@@ -1788,14 +1873,14 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE"))
{
completion_info_charp = prev2_wd;
- COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions_with_TO);+ COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_available_extension_versions_with_TO);
}
/* ALTER EXTENSION <name> UPDATE TO */
else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"))
{
completion_info_charp = prev3_wd;
- COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);+ COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_available_extension_versions);
}
/* ALTER FOREIGN */
@@ -1817,8 +1902,8 @@ psql_completion(const char *text, int start, int end)
/* ALTER INDEX */
else if (Matches("ALTER", "INDEX"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,- "UNION SELECT 'ALL IN TABLESPACE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,+ "ALL IN TABLESPACE");
/* ALTER INDEX <name> */
else if (Matches("ALTER", "INDEX", MatchAny))
COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET",
@@ -1827,7 +1912,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH"))
COMPLETE_WITH("PARTITION");
else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
/* ALTER INDEX <name> ALTER */
else if (Matches("ALTER", "INDEX", MatchAny, "ALTER"))
COMPLETE_WITH("COLUMN");
@@ -1884,8 +1969,8 @@ psql_completion(const char *text, int start, int end)
/* ALTER MATERIALIZED VIEW */
else if (Matches("ALTER", "MATERIALIZED", "VIEW"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,- "UNION SELECT 'ALL IN TABLESPACE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,+ "ALL IN TABLESPACE");
/* ALTER USER,ROLE <name> */
else if (Matches("ALTER", "USER|ROLE", MatchAny) &&
@@ -1979,7 +2064,8 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "SYSTEM"))
COMPLETE_WITH("SET", "RESET");
else if (Matches("ALTER", "SYSTEM", "SET|RESET"))
- COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_alter_system_set_vars,+ "all");
else if (Matches("ALTER", "SYSTEM", "SET", MatchAny))
COMPLETE_WITH("TO");
/* ALTER VIEW <name> */
@@ -1988,9 +2074,9 @@ psql_completion(const char *text, int start, int end)
"SET SCHEMA");
/* ALTER VIEW xxx RENAME */
else if (Matches("ALTER", "VIEW", MatchAny, "RENAME"))
- COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'TO'");+ COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
else if (Matches("ALTER", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"))
- COMPLETE_WITH_ATTR(prev3_wd, "");+ COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER VIEW xxx ALTER [ COLUMN ] yyy */
else if (Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny) ||
Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny))
@@ -2009,9 +2095,9 @@ psql_completion(const char *text, int start, int end)
"RESET (", "SET");
/* ALTER MATERIALIZED VIEW xxx RENAME */
else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME"))
- COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'TO'");+ COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"))
- COMPLETE_WITH_ATTR(prev3_wd, "");+ COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER MATERIALIZED VIEW xxx RENAME yyy */
else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")))
COMPLETE_WITH("TO");
@@ -2038,7 +2124,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("RENAME TO", "TO", "USING (", "WITH CHECK (");
/* ALTER POLICY <name> ON <table> TO <role> */
else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"))
- COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ Keywords_for_list_of_grant_roles);
/* ALTER POLICY <name> ON <table> USING ( */
else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
COMPLETE_WITH("(");
@@ -2079,7 +2166,7 @@ psql_completion(const char *text, int start, int end)
* If we have ALTER TRIGGER <sth> ON, then add the correct tablename
*/
else if (Matches("ALTER", "TRIGGER", MatchAny, "ON"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* ALTER TRIGGER <name> ON <name> */
else if (Matches("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
@@ -2106,7 +2193,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny) ||
(Matches("ALTER", "TABLE", MatchAny, "ADD", MatchAny) &&
!Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
/* ALTER TABLE xxx ADD CONSTRAINT yyy */
else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny))
COMPLETE_WITH("CHECK", "UNIQUE", "PRIMARY KEY", "EXCLUDE", "FOREIGN KEY");
@@ -2170,13 +2257,13 @@ psql_completion(const char *text, int start, int end)
}
/* ALTER TABLE xxx INHERIT */
else if (Matches("ALTER", "TABLE", MatchAny, "INHERIT"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* ALTER TABLE xxx NO */
else if (Matches("ALTER", "TABLE", MatchAny, "NO"))
COMPLETE_WITH("FORCE ROW LEVEL SECURITY", "INHERIT");
/* ALTER TABLE xxx NO INHERIT */
else if (Matches("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* ALTER TABLE xxx DISABLE */
else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE"))
COMPLETE_WITH("ROW LEVEL SECURITY", "RULE", "TRIGGER");
@@ -2193,13 +2280,13 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE xxx ALTER */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER"))
- COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'");+ COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT");
/* ALTER TABLE xxx RENAME */
else if (Matches("ALTER", "TABLE", MatchAny, "RENAME"))
- COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'");+ COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT", "TO");
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
- COMPLETE_WITH_ATTR(prev3_wd, "");+ COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER TABLE xxx RENAME yyy */
else if (Matches("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")))
@@ -2214,7 +2301,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("COLUMN", "CONSTRAINT");
/* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
else if (Matches("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
- COMPLETE_WITH_ATTR(prev3_wd, "");+ COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER TABLE <sth> ALTER|DROP|RENAME CONSTRAINT <constraint> */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME", "CONSTRAINT"))
{
@@ -2304,7 +2391,7 @@ psql_completion(const char *text, int start, int end)
* tables.
*/
else if (Matches("ALTER", "TABLE", MatchAny, "ATTACH", "PARTITION"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* Limited completion support for partition bound specification */
else if (TailMatches("ATTACH", "PARTITION", MatchAny))
COMPLETE_WITH("FOR VALUES", "DEFAULT");
@@ -2366,7 +2453,7 @@ psql_completion(const char *text, int start, int end)
* of attributes
*/
else if (Matches("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"))
- COMPLETE_WITH_ATTR(prev3_wd, "");+ COMPLETE_WITH_ATTR(prev3_wd);
/* ALTER TYPE ALTER ATTRIBUTE <foo> */
else if (Matches("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny))
COMPLETE_WITH("TYPE");
@@ -2391,8 +2478,8 @@ psql_completion(const char *text, int start, int end)
* ANALYZE [ VERBOSE ] [ table_and_columns [, ...] ]
*/
else if (Matches("ANALYZE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables,- " UNION SELECT 'VERBOSE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,+ "VERBOSE");
else if (HeadMatches("ANALYZE", "(*") &&
!HeadMatches("ANALYZE", "(*)"))
{
@@ -2408,9 +2495,9 @@ psql_completion(const char *text, int start, int end)
}
else if (HeadMatches("ANALYZE") && TailMatches("("))
/* "ANALYZE (" should be caught above, so assume we want columns */
- COMPLETE_WITH_ATTR(prev2_wd, "");+ COMPLETE_WITH_ATTR(prev2_wd);
else if (HeadMatches("ANALYZE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables);
/* BEGIN */
else if (Matches("BEGIN"))
@@ -2431,19 +2518,20 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("CHAIN");
/* CALL */
else if (Matches("CALL"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
else if (Matches("CALL", MatchAny))
COMPLETE_WITH("(");
/* CLOSE */
else if (Matches("CLOSE"))
- COMPLETE_WITH_QUERY(Query_for_list_of_cursors- " UNION SELECT 'ALL'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,+ "ALL");
/* CLUSTER */
else if (Matches("CLUSTER"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, "UNION SELECT 'VERBOSE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,+ "VERBOSE");
else if (Matches("CLUSTER", "VERBOSE") ||
Matches("CLUSTER", "(*)"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables);
/* If we have CLUSTER <sth>, then add "USING" */
else if (Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON|(|(*)")))
COMPLETE_WITH("USING");
@@ -2493,19 +2581,19 @@ psql_completion(const char *text, int start, int end)
else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
{
completion_info_charp = prev2_wd;
- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint- " UNION SELECT 'DOMAIN'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_tables_for_constraint,+ "DOMAIN");
}
else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON", "DOMAIN"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
else if (Matches("COMMENT", "ON", "EVENT", "TRIGGER"))
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
else if (Matches("COMMENT", "ON", "FOREIGN"))
COMPLETE_WITH("DATA WRAPPER", "TABLE");
else if (Matches("COMMENT", "ON", "FOREIGN", "TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
else if (Matches("COMMENT", "ON", "MATERIALIZED", "VIEW"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (Matches("COMMENT", "ON", "POLICY"))
COMPLETE_WITH_QUERY(Query_for_list_of_policies);
else if (Matches("COMMENT", "ON", "POLICY", MatchAny))
@@ -2535,7 +2623,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "TEMPLATE"))
COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny))
COMPLETE_WITH("LANGUAGE");
else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
@@ -2563,8 +2651,7 @@ psql_completion(const char *text, int start, int end)
* backslash command).
*/
else if (Matches("COPY|\\copy"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,- " UNION ALL SELECT '('");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables, "(");
/* Complete COPY ( with legal query commands */
else if (Matches("COPY|\\copy", "("))
COMPLETE_WITH("SELECT", "TABLE", "VALUES", "INSERT INTO", "UPDATE", "DELETE FROM", "WITH");
@@ -2622,7 +2709,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("CREATE", "COLLATION", MatchAny))
COMPLETE_WITH("(", "FROM");
else if (Matches("CREATE", "COLLATION", MatchAny, "FROM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
else if (HeadMatches("CREATE", "COLLATION", MatchAny, "(*"))
{
if (TailMatches("(|*,"))
@@ -2648,12 +2735,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches("CREATE", "DOMAIN", MatchAny))
COMPLETE_WITH("AS");
else if (Matches("CREATE", "DOMAIN", MatchAny, "AS"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny))
COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
"NOT NULL", "NULL", "CHECK (");
else if (Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
/* CREATE EXTENSION */
/* Complete with available extensions rather than installed ones. */
@@ -2666,7 +2753,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("CREATE", "EXTENSION", MatchAny, "VERSION"))
{
completion_info_charp = prev2_wd;
- COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);+ COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_available_extension_versions);
}
/* CREATE FOREIGN */
@@ -2691,9 +2778,8 @@ psql_completion(const char *text, int start, int end)
* existing indexes
*/
else if (TailMatches("CREATE|UNIQUE", "INDEX"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,- " UNION SELECT 'ON'"- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,+ "ON", "CONCURRENTLY");
/*
* Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of relations
@@ -2701,15 +2787,15 @@ psql_completion(const char *text, int start, int end)
*/
else if (TailMatches("INDEX|CONCURRENTLY", MatchAny, "ON") ||
TailMatches("INDEX|CONCURRENTLY", "ON"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
/*
* Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing
* indexes
*/
else if (TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,- " UNION SELECT 'ON'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,+ "ON");
/* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
else if (TailMatches("CREATE|UNIQUE", "INDEX", MatchAny) ||
TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny))
@@ -2724,10 +2810,10 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("(", "USING");
else if (TailMatches("INDEX", MatchAny, "ON", MatchAny, "(") ||
TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny, "("))
- COMPLETE_WITH_ATTR(prev2_wd, "");+ COMPLETE_WITH_ATTR(prev2_wd);
/* same if you put in USING */
else if (TailMatches("ON", MatchAny, "USING", MatchAny, "("))
- COMPLETE_WITH_ATTR(prev4_wd, "");+ COMPLETE_WITH_ATTR(prev4_wd);
/* Complete USING with an index method */
else if (TailMatches("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") ||
TailMatches("INDEX", MatchAny, "ON", MatchAny, "USING") ||
@@ -2748,7 +2834,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
/* Complete "CREATE POLICY <name> ON <table>" */
else if (Matches("CREATE", "POLICY", MatchAny, "ON"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */
else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny))
COMPLETE_WITH("AS", "FOR", "TO", "USING (", "WITH CHECK (");
@@ -2776,7 +2862,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
/* Complete "CREATE POLICY <name> ON <table> TO <role>" */
else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"))
- COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ Keywords_for_list_of_grant_roles);
/* Complete "CREATE POLICY <name> ON <table> USING (" */
else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
COMPLETE_WITH("(");
@@ -2814,7 +2901,8 @@ psql_completion(const char *text, int start, int end)
* <role>"
*/
else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "TO"))
- COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ Keywords_for_list_of_grant_roles);
/*
* Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
@@ -2837,18 +2925,18 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("WITH (");
/* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/*
* Complete "CREATE PUBLICATION <name> FOR ALL TABLES IN SCHEMA <schema>,
* ..."
*/
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA"))
- COMPLETE_WITH_QUERY(Query_for_list_of_schemas- " AND nspname != 'pg_catalog' "- " AND nspname not like 'pg\\_toast%%' "- " AND nspname not like 'pg\\_temp%%' "- " UNION SELECT 'CURRENT_SCHEMA' ");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas+ " AND nspname != 'pg_catalog' "+ " AND nspname not like 'pg\\_toast%%' "+ " AND nspname not like 'pg\\_temp%%' ",+ "CURRENT_SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ',')))
COMPLETE_WITH("WITH (");
/* Complete "CREATE PUBLICATION <name> [...] WITH" */
@@ -2877,7 +2965,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("TO");
/* Complete "AS ON <sth> TO" with a table name */
else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
else if (TailMatches("CREATE", "SEQUENCE", MatchAny) ||
@@ -2904,7 +2992,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
TailMatches("FROM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
/* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
@@ -2918,7 +3006,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("RANGE (", "LIST (", "HASH (");
/* If we have xxx PARTITION OF, provide a list of partitioned tables */
else if (TailMatches("PARTITION", "OF"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, "");+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
/* Limited completion support for partition bound specification */
else if (TailMatches("PARTITION", "OF", MatchAny))
COMPLETE_WITH("FOR VALUES", "DEFAULT");
@@ -2929,7 +3017,7 @@ psql_completion(const char *text, int start, int end)
/* Complete CREATE TABLE <name> OF with list of composite types */
else if (TailMatches("CREATE", "TABLE", MatchAny, "OF") ||
TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "OF"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
/* Complete CREATE TABLE name (...) with supported options */
else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)") ||
TailMatches("CREATE", "UNLOGGED", "TABLE", MatchAny, "(*)"))
@@ -2968,7 +3056,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("FOR");
else if (Matches("CREATE", "TRANSFORM", "FOR") ||
Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny) ||
Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny))
COMPLETE_WITH("LANGUAGE");
@@ -3036,7 +3124,7 @@ psql_completion(const char *text, int start, int end)
*/
else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON") ||
TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/*
* Complete CREATE [ OR REPLACE ] TRIGGER ... INSTEAD OF event ON with a
@@ -3044,7 +3132,7 @@ psql_completion(const char *text, int start, int end)
*/
else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON") ||
TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if ((HeadMatches("CREATE", "TRIGGER") ||
HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
TailMatches("ON", MatchAny))
@@ -3149,7 +3237,7 @@ psql_completion(const char *text, int start, int end)
else if ((HeadMatches("CREATE", "TRIGGER") ||
HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
/* CREATE ROLE,USER,GROUP <name> */
else if (Matches("CREATE", "ROLE|GROUP|USER", MatchAny) &&
@@ -3185,7 +3273,7 @@ psql_completion(const char *text, int start, int end)
else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "("))
{
if (TailMatches("(|*,", MatchAny))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatches("(|*,", MatchAny, MatchAnyExcept("*)")))
COMPLETE_WITH("COLLATE", ",", ")");
}
@@ -3269,12 +3357,12 @@ psql_completion(const char *text, int start, int end)
}
else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
/* DEALLOCATE */
else if (Matches("DEALLOCATE"))
- COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements- " UNION SELECT 'ALL'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_prepared_statements,+ "ALL");
/* DECLARE */
@@ -3322,7 +3410,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("FROM");
/* Complete DELETE FROM with a list of tables */
else if (TailMatches("DELETE", "FROM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
/* Complete DELETE FROM <table> */
else if (TailMatches("DELETE", "FROM", MatchAny))
COMPLETE_WITH("USING", "WHERE");
@@ -3364,10 +3452,10 @@ psql_completion(const char *text, int start, int end)
/* DROP INDEX */
else if (Matches("DROP", "INDEX"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,+ "CONCURRENTLY");
else if (Matches("DROP", "INDEX", "CONCURRENTLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
else if (Matches("DROP", "INDEX", MatchAny))
COMPLETE_WITH("CASCADE", "RESTRICT");
else if (Matches("DROP", "INDEX", "CONCURRENTLY", MatchAny))
@@ -3377,7 +3465,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("DROP", "MATERIALIZED"))
COMPLETE_WITH("VIEW");
else if (Matches("DROP", "MATERIALIZED", "VIEW"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (Matches("DROP", "MATERIALIZED", "VIEW", MatchAny))
COMPLETE_WITH("CASCADE", "RESTRICT");
@@ -3446,7 +3534,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("DROP", "TRANSFORM"))
COMPLETE_WITH("FOR");
else if (Matches("DROP", "TRANSFORM", "FOR"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny))
COMPLETE_WITH("LANGUAGE");
else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
@@ -3500,28 +3588,28 @@ psql_completion(const char *text, int start, int end)
* NEXT, PRIOR, FIRST, LAST, FROM, IN, and a list of cursors
*/
else if (Matches("FETCH|MOVE"))
- COMPLETE_WITH_QUERY(Query_for_list_of_cursors- " UNION SELECT 'ABSOLUTE'"- " UNION SELECT 'BACKWARD'"- " UNION SELECT 'FORWARD'"- " UNION SELECT 'RELATIVE'"- " UNION SELECT 'ALL'"- " UNION SELECT 'NEXT'"- " UNION SELECT 'PRIOR'"- " UNION SELECT 'FIRST'"- " UNION SELECT 'LAST'"- " UNION SELECT 'FROM'"- " UNION SELECT 'IN'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,+ "ABSOLUTE",+ "BACKWARD",+ "FORWARD",+ "RELATIVE",+ "ALL",+ "NEXT",+ "PRIOR",+ "FIRST",+ "LAST",+ "FROM",+ "IN");
/*
* Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN, and a
* list of cursors
*/
else if (Matches("FETCH|MOVE", "BACKWARD|FORWARD"))
- COMPLETE_WITH_QUERY(Query_for_list_of_cursors- " UNION SELECT 'ALL'"- " UNION SELECT 'FROM'"- " UNION SELECT 'IN'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,+ "ALL",+ "FROM",+ "IN");
/*
* Complete FETCH <direction> with "FROM" or "IN". These are equivalent,
@@ -3531,9 +3619,9 @@ psql_completion(const char *text, int start, int end)
else if (Matches("FETCH|MOVE", "ABSOLUTE|BACKWARD|FORWARD|RELATIVE",
MatchAnyExcept("FROM|IN")) ||
Matches("FETCH|MOVE", "ALL|NEXT|PRIOR|FIRST|LAST"))
- COMPLETE_WITH_QUERY(Query_for_list_of_cursors- " UNION SELECT 'FROM'"- " UNION SELECT 'IN'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,+ "FROM",+ "IN");
/* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
else if (HeadMatches("FETCH|MOVE") &&
TailMatches("FROM|IN"))
@@ -3552,7 +3640,7 @@ psql_completion(const char *text, int start, int end)
/* FOREIGN TABLE */
else if (TailMatches("FOREIGN", "TABLE") &&
!TailMatches("CREATE", MatchAny, MatchAny))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
/* FOREIGN SERVER */
else if (TailMatches("FOREIGN", "SERVER"))
@@ -3574,20 +3662,20 @@ psql_completion(const char *text, int start, int end)
"DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
"EXECUTE", "USAGE", "ALL");
else
- 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_WITH_QUERY_PLUS(Query_for_list_of_roles,+ "SELECT",+ "INSERT",+ "UPDATE",+ "DELETE",+ "TRUNCATE",+ "REFERENCES",+ "TRIGGER",+ "CREATE",+ "CONNECT",+ "TEMPORARY",+ "EXECUTE",+ "USAGE",+ "ALL");
}
/*
@@ -3607,9 +3695,6 @@ psql_completion(const char *text, int start, int end)
/*
* Complete GRANT/REVOKE <sth> ON with a list of appropriate relations.
*
- * Keywords like DATABASE, FUNCTION, LANGUAGE and SCHEMA added to query- * result via UNION; seems to work intuitively.- *
* Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
* here will only work if the privilege list contains exactly one
* privilege.
@@ -3623,26 +3708,26 @@ psql_completion(const char *text, int start, int end)
if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
COMPLETE_WITH("TABLES", "SEQUENCES", "FUNCTIONS", "PROCEDURES", "ROUTINES", "TYPES", "SCHEMAS");
else
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables,- " UNION SELECT 'ALL FUNCTIONS IN SCHEMA'"- " UNION SELECT 'ALL PROCEDURES IN SCHEMA'"- " UNION SELECT 'ALL ROUTINES IN SCHEMA'"- " UNION SELECT 'ALL SEQUENCES IN SCHEMA'"- " UNION SELECT 'ALL TABLES IN SCHEMA'"- " 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 'PROCEDURE'"- " UNION SELECT 'ROUTINE'"- " UNION SELECT 'SCHEMA'"- " UNION SELECT 'SEQUENCE'"- " UNION SELECT 'TABLE'"- " UNION SELECT 'TABLESPACE'"- " UNION SELECT 'TYPE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_grantables,+ "ALL FUNCTIONS IN SCHEMA",+ "ALL PROCEDURES IN SCHEMA",+ "ALL ROUTINES IN SCHEMA",+ "ALL SEQUENCES IN SCHEMA",+ "ALL TABLES IN SCHEMA",+ "DATABASE",+ "DOMAIN",+ "FOREIGN DATA WRAPPER",+ "FOREIGN SERVER",+ "FUNCTION",+ "LANGUAGE",+ "LARGE OBJECT",+ "PROCEDURE",+ "ROUTINE",+ "SCHEMA",+ "SEQUENCE",+ "TABLE",+ "TABLESPACE",+ "TYPE");
}
else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL"))
COMPLETE_WITH("FUNCTIONS IN SCHEMA",
@@ -3664,25 +3749,25 @@ psql_completion(const char *text, int start, int end)
if (TailMatches("DATABASE"))
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
else if (TailMatches("DOMAIN"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
else if (TailMatches("FUNCTION"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
else if (TailMatches("LANGUAGE"))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (TailMatches("PROCEDURE"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
else if (TailMatches("ROUTINE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
else if (TailMatches("SCHEMA"))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
else if (TailMatches("SEQUENCE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
else if (TailMatches("TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
else if (TailMatches("TABLESPACE"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatches("TYPE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny))
COMPLETE_WITH("TO");
else
@@ -3695,10 +3780,12 @@ psql_completion(const char *text, int start, int end)
*/
else if ((HeadMatches("GRANT") && TailMatches("TO")) ||
(HeadMatches("REVOKE") && TailMatches("FROM")))
- COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ Keywords_for_list_of_grant_roles);
/* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
- COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ Keywords_for_list_of_grant_roles);
/* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
else if (HeadMatches("GRANT") && TailMatches("ON", MatchAny, MatchAny))
COMPLETE_WITH("TO");
@@ -3759,10 +3846,10 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("INTO");
/* Complete INSERT INTO with table names */
else if (TailMatches("INSERT", "INTO"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
/* Complete "INSERT INTO <table> (" with attribute names */
else if (TailMatches("INSERT", "INTO", MatchAny, "("))
- COMPLETE_WITH_ATTR(prev2_wd, "");+ COMPLETE_WITH_ATTR(prev2_wd);
/*
* Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
@@ -3794,14 +3881,13 @@ psql_completion(const char *text, int start, int end)
/* LOCK */
/* Complete LOCK [TABLE] [ONLY] with a list of tables */
else if (Matches("LOCK"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,- " UNION SELECT 'TABLE'"- " UNION SELECT 'ONLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,+ "TABLE", "ONLY");
else if (Matches("LOCK", "TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,- " UNION SELECT 'ONLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,+ "ONLY");
else if (Matches("LOCK", "TABLE", "ONLY") || Matches("LOCK", "ONLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* For the following, handle the case of a single table only for now */
/* Complete LOCK [TABLE] [ONLY] <table> with IN or NOWAIT */
@@ -3837,7 +3923,7 @@ psql_completion(const char *text, int start, int end)
/* NOTIFY --- can be inside EXPLAIN, RULE, etc */
else if (TailMatches("NOTIFY"))
- COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'");+ COMPLETE_WITH_QUERY("SELECT channel FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(channel,1,%d)='%s'");
/* OPTIONS */
else if (TailMatches("OPTIONS"))
@@ -3851,7 +3937,7 @@ psql_completion(const char *text, int start, int end)
else if (TailMatches("FROM", MatchAny, "ORDER"))
COMPLETE_WITH("BY");
else if (TailMatches("FROM", MatchAny, "ORDER", "BY"))
- COMPLETE_WITH_ATTR(prev3_wd, "");+ COMPLETE_WITH_ATTR(prev3_wd);
/* PREPARE xx AS */
else if (Matches("PREPARE", MatchAny, "AS"))
@@ -3880,10 +3966,10 @@ psql_completion(const char *text, int start, int end)
else if (Matches("REFRESH", "MATERIALIZED"))
COMPLETE_WITH("VIEW");
else if (Matches("REFRESH", "MATERIALIZED", "VIEW"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,+ "CONCURRENTLY");
else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
COMPLETE_WITH("WITH");
else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
@@ -3903,26 +3989,26 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
else if (Matches("REINDEX", "TABLE") ||
Matches("REINDEX", "(*)", "TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables,- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexables,+ "CONCURRENTLY");
else if (Matches("REINDEX", "INDEX") ||
Matches("REINDEX", "(*)", "INDEX"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,+ "CONCURRENTLY");
else if (Matches("REINDEX", "SCHEMA") ||
Matches("REINDEX", "(*)", "SCHEMA"))
- COMPLETE_WITH_QUERY(Query_for_list_of_schemas- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,+ "CONCURRENTLY");
else if (Matches("REINDEX", "SYSTEM|DATABASE") ||
Matches("REINDEX", "(*)", "SYSTEM|DATABASE"))
- COMPLETE_WITH_QUERY(Query_for_list_of_databases- " UNION SELECT 'CONCURRENTLY'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_databases,+ "CONCURRENTLY");
else if (Matches("REINDEX", "TABLE", "CONCURRENTLY") ||
Matches("REINDEX", "(*)", "TABLE", "CONCURRENTLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
else if (Matches("REINDEX", "INDEX", "CONCURRENTLY") ||
Matches("REINDEX", "(*)", "INDEX", "CONCURRENTLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
else if (Matches("REINDEX", "SCHEMA", "CONCURRENTLY") ||
Matches("REINDEX", "(*)", "SCHEMA", "CONCURRENTLY"))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
@@ -3966,9 +4052,17 @@ psql_completion(const char *text, int start, int end)
/* SET, RESET, SHOW */
/* Complete with a variable name */
else if (TailMatches("SET|RESET") && !TailMatches("UPDATE", MatchAny, "SET"))
- COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_set_vars,+ "constraints",+ "transaction",+ "session",+ "role",+ "tablespace",+ "all");
else if (Matches("SHOW"))
- COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_show_vars,+ "session authorization",+ "all");
/* Complete "SET TRANSACTION" */
else if (Matches("SET", "TRANSACTION"))
COMPLETE_WITH("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
@@ -4003,7 +4097,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ONLY", "WRITE");
/* SET CONSTRAINTS */
else if (Matches("SET", "CONSTRAINTS"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_constraints_with_schema,+ "ALL");
/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
else if (Matches("SET", "CONSTRAINTS", MatchAny))
COMPLETE_WITH("DEFERRED", "IMMEDIATE");
@@ -4015,7 +4110,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
/* Complete SET SESSION AUTHORIZATION with username */
else if (Matches("SET", "SESSION", "AUTHORIZATION"))
- COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ "DEFAULT");
/* Complete RESET SESSION with AUTHORIZATION */
else if (Matches("RESET", "SESSION"))
COMPLETE_WITH("AUTHORIZATION");
@@ -4045,10 +4141,10 @@ psql_completion(const char *text, int start, int end)
"US", "European", "NonEuropean",
"DEFAULT");
else if (TailMatches("search_path", "TO|="))
- COMPLETE_WITH_QUERY(Query_for_list_of_schemas- " AND nspname not like 'pg\\_toast%%' "- " AND nspname not like 'pg\\_temp%%' "- " UNION SELECT 'DEFAULT' ");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas+ " AND nspname not like 'pg\\_toast%%' "+ " AND nspname not like 'pg\\_temp%%' ",+ "DEFAULT");
else
{
/* generic, type based, GUC support */
@@ -4067,7 +4163,7 @@ psql_completion(const char *text, int start, int end)
snprintf(querybuf, sizeof(querybuf),
Query_for_enum, prev2_wd);
- COMPLETE_WITH_QUERY(querybuf);+ COMPLETE_WITH_QUERY_PLUS(querybuf, "DEFAULT");
}
else if (strcmp(guctype, "bool") == 0)
COMPLETE_WITH("on", "off", "true", "false", "yes", "no",
@@ -4086,7 +4182,7 @@ psql_completion(const char *text, int start, int end)
/* TABLE, but not TABLE embedded in other commands */
else if (Matches("TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
/* TABLESAMPLE */
else if (TailMatches("TABLESAMPLE"))
@@ -4096,14 +4192,13 @@ psql_completion(const char *text, int start, int end)
/* TRUNCATE */
else if (Matches("TRUNCATE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables,- " UNION SELECT 'TABLE'"- " UNION SELECT 'ONLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,+ "TABLE", "ONLY");
else if (Matches("TRUNCATE", "TABLE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables,- " UNION SELECT 'ONLY'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,+ "ONLY");
else if (HeadMatches("TRUNCATE") && TailMatches("ONLY"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
else if (Matches("TRUNCATE", MatchAny) ||
Matches("TRUNCATE", "TABLE|ONLY", MatchAny) ||
Matches("TRUNCATE", "TABLE", "ONLY", MatchAny))
@@ -4113,18 +4208,20 @@ psql_completion(const char *text, int start, int end)
/* UNLISTEN */
else if (Matches("UNLISTEN"))
- COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s' UNION SELECT '*'");+ COMPLETE_WITH_QUERY_PLUS("SELECT channel FROM pg_catalog.pg_listening_channels() AS channel"+ " WHERE substring(channel,1,%d)='%s'",+ "*");
/* UPDATE --- can be inside EXPLAIN, RULE, etc */
/* If prev. word is UPDATE suggest a list of tables */
else if (TailMatches("UPDATE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
/* Complete UPDATE <table> with "SET" */
else if (TailMatches("UPDATE", MatchAny))
COMPLETE_WITH("SET");
/* Complete UPDATE <table> SET with list of attributes */
else if (TailMatches("UPDATE", MatchAny, "SET"))
- COMPLETE_WITH_ATTR(prev2_wd, "");+ COMPLETE_WITH_ATTR(prev2_wd);
/* UPDATE <table> SET <attr> = */
else if (TailMatches("UPDATE", MatchAny, "SET", MatchAnyExcept("*=")))
COMPLETE_WITH("=");
@@ -4133,11 +4230,11 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER|CREATE|DROP", "USER", "MAPPING"))
COMPLETE_WITH("FOR");
else if (Matches("CREATE", "USER", "MAPPING", "FOR"))
- COMPLETE_WITH_QUERY(Query_for_list_of_roles- " UNION SELECT 'CURRENT_ROLE'"- " UNION SELECT 'CURRENT_USER'"- " UNION SELECT 'PUBLIC'"- " UNION SELECT 'USER'");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,+ "CURRENT_ROLE",+ "CURRENT_USER",+ "PUBLIC",+ "USER");
else if (Matches("ALTER|DROP", "USER", "MAPPING", "FOR"))
COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
else if (Matches("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
@@ -4150,26 +4247,26 @@ psql_completion(const char *text, int start, int end)
* VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]
*/
else if (Matches("VACUUM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,- " UNION SELECT 'FULL'"- " UNION SELECT 'FREEZE'"- " UNION SELECT 'ANALYZE'"- " UNION SELECT 'VERBOSE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,+ "FULL",+ "FREEZE",+ "ANALYZE",+ "VERBOSE");
else if (Matches("VACUUM", "FULL"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,- " UNION SELECT 'FREEZE'"- " UNION SELECT 'ANALYZE'"- " UNION SELECT 'VERBOSE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,+ "FREEZE",+ "ANALYZE",+ "VERBOSE");
else if (Matches("VACUUM", "FREEZE") ||
Matches("VACUUM", "FULL", "FREEZE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,- " UNION SELECT 'VERBOSE'"- " UNION SELECT 'ANALYZE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,+ "VERBOSE",+ "ANALYZE");
else if (Matches("VACUUM", "VERBOSE") ||
Matches("VACUUM", "FULL|FREEZE", "VERBOSE") ||
Matches("VACUUM", "FULL", "FREEZE", "VERBOSE"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables,- " UNION SELECT 'ANALYZE'");+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,+ "ANALYZE");
else if (HeadMatches("VACUUM", "(*") &&
!HeadMatches("VACUUM", "(*)"))
{
@@ -4190,9 +4287,9 @@ psql_completion(const char *text, int start, int end)
}
else if (HeadMatches("VACUUM") && TailMatches("("))
/* "VACUUM (" should be caught above, so assume we want columns */
- COMPLETE_WITH_ATTR(prev2_wd, "");+ COMPLETE_WITH_ATTR(prev2_wd);
else if (HeadMatches("VACUUM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables);
/* WITH [RECURSIVE] */
@@ -4206,16 +4303,16 @@ psql_completion(const char *text, int start, int end)
/* WHERE */
/* Simple case of the word before the where being the table name */
else if (TailMatches(MatchAny, "WHERE"))
- COMPLETE_WITH_ATTR(prev2_wd, "");+ COMPLETE_WITH_ATTR(prev2_wd);
/* ... FROM ... */
/* TODO: also include SRF ? */
else if (TailMatches("FROM") && !Matches("COPY|\\copy", MatchAny, "FROM"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
/* ... JOIN ... */
else if (TailMatches("JOIN"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
/* Backslash commands */
/* TODO: \dc \dd \dl */
@@ -4232,19 +4329,19 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
}
else if (TailMatchesCS("\\da*"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates);
else if (TailMatchesCS("\\dAc*", MatchAny) ||
TailMatchesCS("\\dAf*", MatchAny))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\dAo*", MatchAny) ||
TailMatchesCS("\\dAp*", MatchAny))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families);
else if (TailMatchesCS("\\dA*"))
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (TailMatchesCS("\\db*"))
COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
else if (TailMatchesCS("\\dD*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
else if (TailMatchesCS("\\des*"))
COMPLETE_WITH_QUERY(Query_for_list_of_servers);
else if (TailMatchesCS("\\deu*"))
@@ -4252,9 +4349,9 @@ psql_completion(const char *text, int start, int end)
else if (TailMatchesCS("\\dew*"))
COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
else if (TailMatchesCS("\\df*"))
- COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
else if (HeadMatchesCS("\\df*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\dFd*"))
COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
@@ -4267,51 +4364,51 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
else if (TailMatchesCS("\\di*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
else if (TailMatchesCS("\\dL*"))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
else if (TailMatchesCS("\\dn*"))
COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
/* no support for completing operators, but we can complete types: */
else if (HeadMatchesCS("\\do*", MatchAny))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
else if (TailMatchesCS("\\dPi*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes);
else if (TailMatchesCS("\\dPt*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
else if (TailMatchesCS("\\dP*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations);
else if (TailMatchesCS("\\ds*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
else if (TailMatchesCS("\\dt*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
else if (TailMatchesCS("\\dT*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
else if (TailMatchesCS("\\du*") || TailMatchesCS("\\dg*"))
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
else if (TailMatchesCS("\\dv*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\dx*"))
COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
else if (TailMatchesCS("\\dX*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics);
else if (TailMatchesCS("\\dm*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
else if (TailMatchesCS("\\dE*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
else if (TailMatchesCS("\\dy*"))
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
/* must be at end of \d alternatives: */
else if (TailMatchesCS("\\d*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
else if (TailMatchesCS("\\ef"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
else if (TailMatchesCS("\\ev"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\encoding"))
COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
@@ -4413,9 +4510,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_CS("default", "verbose", "terse", "sqlstate");
}
else if (TailMatchesCS("\\sf*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
else if (TailMatchesCS("\\sv*"))
- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|"
"\\ir|\\include_relative|\\o|\\out|"
"\\s|\\w|\\write|\\lo_import"))
@@ -4432,19 +4529,21 @@ psql_completion(const char *text, int start, int end)
*/
else
{
- int i;+ const pgsql_thing_t *wac;- for (i = 0; words_after_create[i].name; i++)+ for (wac = words_after_create; wac->name != NULL; wac++)
{
- if (pg_strcasecmp(prev_wd, words_after_create[i].name) == 0)+ if (pg_strcasecmp(prev_wd, wac->name) == 0)
{
- 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_VERSIONED_SCHEMA_QUERY(words_after_create[i].squery,- NULL);+ if (wac->query)+ COMPLETE_WITH_QUERY_LIST(wac->query,+ wac->keywords);+ else if (wac->vquery)+ COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,+ wac->keywords);+ else if (wac->squery)+ COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,+ wac->keywords);
break;
}
}
@@ -4557,7 +4656,8 @@ static char *
complete_from_query(const char *text, int state)
{
/* query is assumed to work for any server version */
- return _complete_from_query(completion_charp, NULL, text, state);+ return _complete_from_query(completion_charp, NULL, completion_charpp,+ completion_verbatim, text, state);
}
static char *
@@ -4572,22 +4672,22 @@ complete_from_versioned_query(const char *text, int state)
if (vquery->query == NULL)
return NULL;
- return _complete_from_query(vquery->query, NULL, text, state);+ return _complete_from_query(vquery->query, NULL, completion_charpp,+ completion_verbatim, text, state);
}
static char *
complete_from_schema_query(const char *text, int state)
{
/* query is assumed to work for any server version */
- return _complete_from_query(completion_charp, completion_squery,- text, state);+ return _complete_from_query(NULL, completion_squery, completion_charpp,+ completion_verbatim, 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)
@@ -4596,17 +4696,8 @@ complete_from_versioned_schema_query(const char *text, int state)
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);+ return _complete_from_query(NULL, squery, completion_charpp,+ completion_verbatim, text, state);
}
@@ -4617,35 +4708,54 @@ complete_from_versioned_schema_query(const char *text, int state)
*
* The query can be one of two kinds:
*
- * 1. A simple query which must contain a %d and a %s, which will be replaced- * by the string length of the text and the text itself. The query may also- * have up to four more %s in it; the first two such will be replaced by the- * value of completion_info_charp, the next two by the value of- * completion_info_charp2.+ * 1. A simple query, which must contain a restriction clause of the form+ * substring(OUTPUT,1,%d)='%s'+ * where "OUTPUT" is the same string that the query returns. The %d and %s+ * will be replaced by the string length of the text and the text itself,+ * causing the results to be limited to those matching the already-typed text.+ * The query may also have up to four more %s in it; the first two such will+ * be replaced by the value of completion_info_charp, the next two by the+ * value of completion_info_charp2. (These strings will be escaped to+ * become SQL literals, so what is actually in the query should be '%s'.)+ * Simple queries should return a single column of matches. If "verbatim"+ * is true, the matches are returned as-is; otherwise, they are taken to+ * be SQL identifiers and quoted if necessary.
*
* 2. A schema query used for completion of both schema and relation names.
- * These are more complex and must contain in the following order:- * %d %s %d %s %d %s %s %d %s- * where %d is the string length of the text and %s the text itself.+ * This is represented by a SchemaQuery object; see that typedef for details.+ *+ * See top of file for examples of both kinds of query.
*
- * 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.+ * In addition to the query itself, we accept a null-terminated array of+ * literal keywords, which will be returned if they match the input-so-far+ * (case insensitively). (These are in addition to keywords specified+ * within the schema_query, if any.)
*
- * It is assumed that strings should be escaped to become SQL literals- * (that is, what is in the query is actually ... '%s' ...)+ * If "verbatim" is true, then we use the given text as-is to match the+ * query results; otherwise we parse it as a possibly-qualified identifier,+ * and reconstruct suitable quoting afterward.
*
- * See top of file for examples of both kinds of query.+ * "text" and "state" are supplied by Readline. "text" is the word we are+ * trying to complete. "state" is zero on first call, nonzero later.
*
- * "text" and "state" are supplied by readline.+ * readline will call this repeatedly with the same text and varying+ * state. On each call, we are supposed to return a malloc'd string+ * that is a candidate completion. Return NULL when done.
*/
static char *
_complete_from_query(const char *simple_query,
const SchemaQuery *schema_query,
+ const char *const *keywords,+ bool verbatim,
const char *text, int state)
{
static int list_index,
- byte_length;+ num_schema_only,+ num_other;
static PGresult *result = NULL;
+ static bool non_empty_object;+ static bool schemaquoted;+ static bool objectquoted;
/*
* If this is the first time for this completion, we fetch a list of our
@@ -4654,31 +4764,53 @@ _complete_from_query(const char *simple_query,
if (state == 0)
{
PQExpBufferData query_buffer;
- char *e_text;+ char *schemaname;+ char *objectname;+ int object_length = 0;+ char *e_schemaname;+ char *e_objectname;
char *e_info_charp;
char *e_info_charp2;
- const char *pstr = text;- int char_length = 0;+ /* Reset static state, ensuring no memory leaks */
list_index = 0;
- byte_length = strlen(text);+ num_schema_only = 0;+ num_other = 0;+ PQclear(result);+ result = NULL;++ /* Parse text, splitting into schema and object name if needed */+ if (verbatim)+ {+ objectname = pg_strdup(text);+ schemaname = NULL;+ }+ else+ {+ parse_identifier(text,+ &schemaname, &objectname,+ &schemaquoted, &objectquoted);+ }++ /* Remember whether the user has typed anything in the object part */+ non_empty_object = (*objectname != '\0');
/*
* Count length as number of characters (not bytes), for passing to
* substring
*/
- while (*pstr)- {- char_length++;- pstr += PQmblenBounded(pstr, pset.encoding);- }-- /* Free any prior result */- PQclear(result);- result = NULL;+ for (const char *p = objectname;+ *p;+ p += PQmblenBounded(p, pset.encoding))+ object_length++;
/* Set up suitably-escaped copies of textual inputs */
- e_text = escape_string(text);+ if (schemaname)+ e_schemaname = escape_string(schemaname);+ else+ e_schemaname = NULL;++ e_objectname = escape_string(objectname);
if (completion_info_charp)
e_info_charp = escape_string(completion_info_charp);
@@ -4694,14 +4826,17 @@ _complete_from_query(const char *simple_query,
if (schema_query)
{
- /* schema_query gives us the pieces to assemble */- const char *qualresult = schema_query->qualresult;-- if (qualresult == NULL)- qualresult = schema_query->result;+ Assert(simple_query == NULL);+ /*+ * We issue different queries depending on whether the input is+ * already qualified or not. schema_query gives us the pieces to+ * assemble.+ */+ if (schemaname == NULL)+ {
/* Get unqualified names matching the input-so-far */
- appendPQExpBuffer(&query_buffer, "SELECT %s FROM %s WHERE ",+ appendPQExpBuffer(&query_buffer, "SELECT %s, NULL::pg_catalog.text FROM %s WHERE ",
schema_query->result,
schema_query->catname);
if (schema_query->selcondition)
@@ -4709,7 +4844,7 @@ _complete_from_query(const char *simple_query,
schema_query->selcondition);
appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
schema_query->result,
- char_length, e_text);+ object_length, e_objectname);
appendPQExpBuffer(&query_buffer, " AND %s",
schema_query->viscondition);
@@ -4721,72 +4856,59 @@ _complete_from_query(const char *simple_query,
*/
if (strcmp(schema_query->catname,
"pg_catalog.pg_class c") == 0 &&
- strncmp(text, "pg_", 3) != 0)+ strncmp(objectname, "pg_", 3) != 0)
{
appendPQExpBufferStr(&query_buffer,
" AND c.relnamespace <> (SELECT oid FROM"
" pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
}
+ /* Add in schema names matching the input-so-far */+ appendPQExpBuffer(&query_buffer, "\nUNION ALL\n"+ "SELECT NULL::pg_catalog.text, n.nspname "+ "FROM pg_catalog.pg_namespace n "+ "WHERE substring(n.nspname,1,%d)='%s'",+ object_length, e_objectname);+
/*
- * Add in matching schema names, but only if there is more than- * one potential match among schema names.+ * Likewise, suppress system schemas unless the input-so-far+ * begins with "pg_".
*/
- appendPQExpBuffer(&query_buffer, "\nUNION\n"- "SELECT pg_catalog.quote_ident(n.nspname) || '.' "- "FROM pg_catalog.pg_namespace n "- "WHERE substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d)='%s'",- char_length, e_text);- appendPQExpBuffer(&query_buffer,- " AND (SELECT pg_catalog.count(*)"- " FROM pg_catalog.pg_namespace"- " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="- " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) > 1",- char_length, e_text);+ if (strncmp(objectname, "pg_", 3) != 0)+ appendPQExpBufferStr(&query_buffer,+ " AND n.nspname NOT LIKE 'pg\\_%'");
/*
- * Add in matching qualified names, but only if there is exactly- * one schema matching the input-so-far.+ * Since we're matching these schema names to the object name,+ * handle their quoting using the object name's quoting state.
*/
- appendPQExpBuffer(&query_buffer, "\nUNION\n"- "SELECT pg_catalog.quote_ident(n.nspname) || '.' || %s "+ schemaquoted = objectquoted;+ }+ else+ {+ /* Input is qualified, so produce only qualified names */+ appendPQExpBuffer(&query_buffer, "SELECT %s, n.nspname "
"FROM %s, pg_catalog.pg_namespace n "
"WHERE %s = n.oid AND ",
- qualresult,+ schema_query->result,
schema_query->catname,
schema_query->namespace);
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
schema_query->selcondition);
- appendPQExpBuffer(&query_buffer, "substring(pg_catalog.quote_ident(n.nspname) || '.' || %s,1,%d)='%s'",- qualresult,- char_length, e_text);-- /*- * This condition exploits the single-matching-schema rule to- * speed up the query- */- appendPQExpBuffer(&query_buffer,- " AND substring(pg_catalog.quote_ident(n.nspname) || '.',1,%d) ="- " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(n.nspname))+1)",- char_length, e_text);- appendPQExpBuffer(&query_buffer,- " AND (SELECT pg_catalog.count(*)"- " FROM pg_catalog.pg_namespace"- " WHERE substring(pg_catalog.quote_ident(nspname) || '.',1,%d) ="- " substring('%s',1,pg_catalog.length(pg_catalog.quote_ident(nspname))+1)) = 1",- char_length, e_text);-- /* If an addon query was provided, use it */- if (simple_query)- appendPQExpBuffer(&query_buffer, "\n%s", simple_query);+ appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s' AND ",+ schema_query->result,+ object_length, e_objectname);+ appendPQExpBuffer(&query_buffer, "n.nspname = '%s'",+ e_schemaname);+ }
}
else
{
Assert(simple_query);
/* simple_query is an sprintf-style format string */
appendPQExpBuffer(&query_buffer, simple_query,
- char_length, e_text,+ object_length, e_objectname,
e_info_charp, e_info_charp,
e_info_charp2, e_info_charp2);
}
@@ -4795,28 +4917,120 @@ _complete_from_query(const char *simple_query,
appendPQExpBuffer(&query_buffer, "\nLIMIT %d",
completion_max_records);
+ /* Finally, we can issue the query */
result = exec_query(query_buffer.data);
+ /* Clean up */
termPQExpBuffer(&query_buffer);
- free(e_text);+ if (e_schemaname)+ free(e_schemaname);+ free(e_objectname);
if (e_info_charp)
free(e_info_charp);
if (e_info_charp2)
free(e_info_charp2);
}
- /* Find something that matches */+ /* Return the next result, if any, but not if the query failed */
if (result && PQresultStatus(result) == PGRES_TUPLES_OK)
{
- const char *item;+ int nskip;- while (list_index < PQntuples(result) &&- (item = PQgetvalue(result, list_index++, 0)))- if (pg_strncasecmp(text, item, byte_length) == 0)+ while (list_index < PQntuples(result))+ {+ const char *item = NULL;+ const char *nsp = NULL;++ if (!PQgetisnull(result, list_index, 0))+ item = PQgetvalue(result, list_index, 0);+ if (PQnfields(result) > 1 &&+ !PQgetisnull(result, list_index, 1))+ nsp = PQgetvalue(result, list_index, 1);+ list_index++;++ /* In verbatim mode, we return all the items as-is */+ if (verbatim)
return pg_strdup(item);
++ /*+ * In normal mode, a name requiring quoting will be returned only+ * if the input was empty or quoted. Otherwise the user might see+ * completion inserting a quote she didn't type, which is+ * surprising. This restriction also dodges some odd behaviors of+ * some versions of readline/libedit.+ */+ if (non_empty_object)+ {+ if (item && !objectquoted && identifier_needs_quotes(item))+ continue;+ if (nsp && !schemaquoted && identifier_needs_quotes(nsp))+ continue;+ }++ /* Count schema-only results for hack below */+ if (item == NULL && nsp != NULL)+ num_schema_only++;+ else+ num_other++;++ return requote_identifier(nsp, item, schemaquoted, objectquoted);+ }++ /*+ * When the query result is exhausted, check for hard-wired keywords.+ * These will only be returned if they match the input-so-far,+ * ignoring case.+ */+ nskip = list_index - PQntuples(result);+ if (schema_query && schema_query->keywords)+ {+ const char *const *itemp = schema_query->keywords;++ while (*itemp)+ {+ const char *item = *itemp++;++ if (nskip-- > 0)+ continue;+ list_index++;+ if (pg_strncasecmp(text, item, strlen(text)) == 0)+ {+ num_other++;+ return pg_strdup(item);+ }+ }+ }+ if (keywords)+ {+ const char *const *itemp = keywords;++ while (*itemp)+ {+ const char *item = *itemp++;++ if (nskip-- > 0)+ continue;+ list_index++;+ if (pg_strncasecmp(text, item, strlen(text)) == 0)+ {+ num_other++;+ return pg_strdup(item);+ }+ }+ }
}
- /* If nothing matches, free the db structure and return null */+ /*+ * Hack: if we returned only bare schema names, don't let Readline add a+ * space afterwards. Otherwise the schema will stop being part of the+ * completion subject text, which is not what we want.+ */+#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER+ if (num_schema_only > 0 && num_other == 0)+ rl_completion_append_character = '\0';+#endif++ /* No more matches, so free the result structure and return null */
PQclear(result);
result = NULL;
return NULL;
@@ -5150,6 +5364,228 @@ escape_string(const char *text)
}
+/*+ * parse_identifier - Parse a possibly-schema-qualified SQL identifier.+ *+ * This involves splitting off the schema name if present, de-quoting,+ * and downcasing any unquoted text. We are a bit laxer than the backend+ * in that we allow just portions of a name to be quoted --- that's because+ * psql metacommands have traditionally behaved that way.+ *+ * Outputs are a malloc'd schema name (NULL if none), malloc'd object name,+ * and booleans telling whether any part of the schema and object name was+ * double-quoted.+ */+static void+parse_identifier(const char *ident,+ char **schemaname, char **objectname,+ bool *schemaquoted, bool *objectquoted)+{+ size_t buflen = strlen(ident) + 1;+ bool enc_is_single_byte = (pg_encoding_max_length(pset.encoding) == 1);+ char *sname;+ char *oname;+ char *optr;+ bool inquotes;++ /* Initialize, making a certainly-large-enough output buffer */+ sname = NULL;+ oname = pg_malloc(buflen);+ *schemaquoted = *objectquoted = false;+ /* Scan */+ optr = oname;+ inquotes = false;+ while (*ident)+ {+ unsigned char ch = (unsigned char) *ident++;++ if (ch == '"')+ {+ if (inquotes && *ident == '"')+ {+ /* two quote marks within a quoted identifier = emit quote */+ *optr++ = '"';+ ident++;+ }+ else+ {+ inquotes = !inquotes;+ *objectquoted = true;+ }+ }+ else if (ch == '.' && !inquotes)+ {+ /* Found a schema name, transfer it to sname / *schemaquoted */+ *optr = '\0';+ free(sname); /* drop any catalog name */+ sname = oname;+ oname = pg_malloc(buflen);+ optr = oname;+ *schemaquoted = *objectquoted;+ *objectquoted = false;+ }+ else if (!enc_is_single_byte && IS_HIGHBIT_SET(ch))+ {+ /*+ * Transfer multibyte characters without further processing. They+ * wouldn't be affected by our downcasing rule anyway, and this+ * avoids possibly doing the wrong thing in unsafe client+ * encodings.+ */+ int chlen = PQmblenBounded(ident - 1, pset.encoding);++ *optr++ = (char) ch;+ while (--chlen > 0)+ *optr++ = *ident++;+ }+ else+ {+ if (!inquotes)+ {+ /*+ * This downcasing transformation should match the backend's+ * downcase_identifier() as best we can. We do not know the+ * backend's locale, though, so it's necessarily approximate.+ * We assume that psql is operating in the same locale and+ * encoding as the backend.+ */+ if (ch >= 'A' && ch <= 'Z')+ ch += 'a' - 'A';+ else if (enc_is_single_byte && IS_HIGHBIT_SET(ch) && isupper(ch))+ ch = tolower(ch);+ }+ *optr++ = (char) ch;+ }+ }++ *optr = '\0';+ *schemaname = sname;+ *objectname = oname;+}+++/*+ * requote_identifier - Reconstruct a possibly-schema-qualified SQL identifier.+ *+ * Build a malloc'd string containing the identifier, with quoting applied+ * as necessary. This is more or less the inverse of parse_identifier;+ * in particular, if an input component was quoted, we'll quote the output+ * even when that isn't strictly required.+ *+ * Unlike parse_identifier, we handle the case where a schema and no+ * object name is provided, producing just "schema.".+ */+static char *+requote_identifier(const char *schemaname, const char *objectname,+ bool quote_schema, bool quote_object)+{+ char *result;+ size_t buflen = 1; /* count the trailing \0 */+ char *ptr;++ /*+ * We could use PQescapeIdentifier for some of this, but not all, and it+ * adds more notational cruft than it seems worth.+ */+ if (schemaname)+ {+ buflen += strlen(schemaname) + 1; /* +1 for the dot */+ if (!quote_schema)+ quote_schema = identifier_needs_quotes(schemaname);+ if (quote_schema)+ {+ buflen += 2; /* account for quote marks */+ for (const char *p = schemaname; *p; p++)+ {+ if (*p == '"')+ buflen++;+ }+ }+ }+ if (objectname)+ {+ buflen += strlen(objectname);+ if (!quote_object)+ quote_object = identifier_needs_quotes(objectname);+ if (quote_object)+ {+ buflen += 2; /* account for quote marks */+ for (const char *p = objectname; *p; p++)+ {+ if (*p == '"')+ buflen++;+ }+ }+ }+ result = pg_malloc(buflen);+ ptr = result;+ if (schemaname)+ {+ if (quote_schema)+ *ptr++ = '"';+ for (const char *p = schemaname; *p; p++)+ {+ *ptr++ = *p;+ if (*p == '"')+ *ptr++ = '"';+ }+ if (quote_schema)+ *ptr++ = '"';+ *ptr++ = '.';+ }+ if (objectname)+ {+ if (quote_object)+ *ptr++ = '"';+ for (const char *p = objectname; *p; p++)+ {+ *ptr++ = *p;+ if (*p == '"')+ *ptr++ = '"';+ }+ if (quote_object)+ *ptr++ = '"';+ }+ *ptr = '\0';+ return result;+}+++/*+ * Detect whether an identifier must be double-quoted.+ *+ * Note we'll quote anything that's not ASCII; the backend's quote_ident()+ * does the same. Perhaps this could be relaxed in future.+ */+static bool+identifier_needs_quotes(const char *ident)+{+ int kwnum;++ /* Check syntax. */+ if (!((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_'))+ return true;+ if (strspn(ident, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(ident))+ return true;++ /*+ * Check for keyword. We quote keywords except for unreserved ones.+ *+ * It is possible that our keyword list doesn't quite agree with the+ * server's, but this should be close enough for tab-completion purposes.+ *+ * Note: ScanKeywordLookup() does case-insensitive comparison, but that's+ * fine, since we already know we have all-lower-case.+ */+ kwnum = ScanKeywordLookup(ident, &ScanKeywords);++ if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)+ return true;++ return false;+}++
/*
* Execute a query and report any errors. This should be the preferred way of
* talking to the database in this file.
v16-0002-fix-up-completion-info.patch (text/x-diff, 50.3K) downloaddiff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.cindex e2026275b0..176f7e74c2 100644--- a/src/bin/psql/tab-complete.c+++ b/src/bin/psql/tab-complete.c@@ -110,6 +110,12 @@ typedef struct VersionedQuery
* time. So we put the components of each query into this struct and
* assemble them with the common boilerplate in _complete_from_query().
*
+ * We also use this struct to define queries that use completion_info_object,+ * which is some object related to the one(s) we want to get the names of+ * (for example, the table we want the indexes of). In that usage the+ * objects we're completing might not have a schema of their own, but the+ * related object almost always does (passed in completion_info_schema).+ *
* As with VersionedQuery, we can use an array of these if the query details
* must vary across versions.
*/
@@ -123,8 +129,9 @@ typedef struct SchemaQuery
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.+ * Name of catalog or catalogs to be queried, with alias(es), eg.+ * "pg_catalog.pg_class c". Note that "pg_namespace n" and/or+ * "pg_namespace ni" will be added automatically when needed.
*/
const char *catname;
@@ -140,12 +147,15 @@ typedef struct SchemaQuery
/*
* Visibility condition --- which rows are visible without schema
* qualification? For example, "pg_catalog.pg_table_is_visible(c.oid)".
+ * NULL if not needed.
*/
const char *viscondition;
/*
- * Namespace --- name of field to join to pg_namespace.oid. For example,- * "c.relnamespace".+ * Namespace --- name of field to join to pg_namespace.oid when there is+ * schema qualification. For example, "c.relnamespace". NULL if we don't+ * want to join to pg_namespace (then any schema part in the input word+ * will be ignored).
*/
const char *namespace;
@@ -154,12 +164,43 @@ typedef struct SchemaQuery
*/
const char *result;
+ /*+ * In some cases, it's difficult to keep the query from returning the same+ * object multiple times. Specify use_distinct to filter out duplicates.+ */+ bool use_distinct;+
/*
* Additional literal strings (usually keywords) to be offered along with
* the query results. Provide a NULL-terminated array of constant
* strings, or NULL if none.
*/
const char *const *keywords;
++ /*+ * If this query uses completion_info_object/completion_info_schema,+ * populate the remaining fields, else leave them NULL. When using this+ * capability, catname must include the catalog that defines the+ * completion_info_object, and selcondition must include the join+ * condition that connects it to the result's catalog.+ *+ * infoname is the field that should be equated to completion_info_object,+ * for example "ci.relname".+ */+ const char *infoname;++ /*+ * Visibility condition to use when completion_info_schema is not set. For+ * example, "pg_catalog.pg_table_is_visible(ci.oid)". NULL if not needed.+ */+ const char *infoviscondition;++ /*+ * Name of field to join to pg_namespace.oid when completion_info_schema+ * is set. For example, "ci.relnamespace". NULL if we don't want to+ * consider completion_info_schema.+ */+ const char *infonamespace;
} SchemaQuery;
@@ -176,10 +217,10 @@ static int completion_max_records;
static char completion_last_char; /* last char of input word */
static const char *completion_charp; /* to pass a string */
static const char *const *completion_charpp; /* to pass a list of strings */
-static const char *completion_info_charp; /* to pass a second string */-static const char *completion_info_charp2; /* to pass a third string */
static const VersionedQuery *completion_vquery; /* to pass a VersionedQuery */
static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */
+static char *completion_info_object; /* name of a related object */+static char *completion_info_schema; /* schema name of a related object */
static bool completion_case_sensitive; /* completion is case sensitive */
static bool completion_verbatim; /* completion is verbatim */
static bool completion_force_quote; /* true to force-quote filenames */
@@ -257,6 +298,14 @@ do { \
COMPLETE_WITH_SCHEMA_QUERY_LIST(query, list); \
} while (0)
+#define COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(query) \+do { \+ completion_squery = &(query); \+ completion_charpp = NULL; \+ completion_verbatim = true; \+ matches = rl_completion_matches(text, complete_from_schema_query); \+} while (0)+
#define COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(query) \
COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(query, NULL)
@@ -312,29 +361,11 @@ do { \
#define COMPLETE_WITH_ATTR_LIST(relation, list) \
do { \
- char *_completion_schema; \- char *_completion_table; \-\- _completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \- false, false, pset.encoding); \- (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \- false, false, pset.encoding); \- _completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \- false, false, pset.encoding); \- if (_completion_table == NULL) \- { \- completion_charp = Query_for_list_of_attributes; \- completion_info_charp = relation; \- } \- else \- { \- completion_charp = Query_for_list_of_attributes_with_schema; \- completion_info_charp = _completion_table; \- completion_info_charp2 = _completion_schema; \- } \+ setup_completion_info(relation); \+ completion_squery = &(Query_for_list_of_attributes); \
completion_charpp = list; \
completion_verbatim = false; \
- matches = rl_completion_matches(text, complete_from_query); \+ matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0)
#define COMPLETE_WITH_ATTR_PLUS(relation, ...) \
@@ -351,65 +382,24 @@ do { \
*/
#define COMPLETE_WITH_ENUM_VALUE(type) \
do { \
- char *_completion_schema; \- char *_completion_type; \- bool use_quotes; \-\- _completion_schema = strtokx(type, " \t\n\r", ".", "\"", 0, \- false, false, pset.encoding); \- (void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \- false, false, pset.encoding); \- _completion_type = strtokx(NULL, " \t\n\r", ".", "\"", 0, \- false, false, pset.encoding); \- use_quotes = (text[0] == '\'' || \- start == 0 || rl_line_buffer[start - 1] != '\''); \- if (_completion_type == NULL) \- { \- if (use_quotes) \- completion_charp = Query_for_list_of_enum_values_quoted; \- else \- completion_charp = Query_for_list_of_enum_values_unquoted; \- completion_info_charp = type; \- } \+ setup_completion_info(type); \+ if (text[0] == '\'' || \+ start == 0 || rl_line_buffer[start - 1] != '\'') \+ completion_squery = &(Query_for_list_of_enum_values_quoted); \
else \
- { \- if (use_quotes) \- completion_charp = Query_for_list_of_enum_values_with_schema_quoted; \- else \- completion_charp = Query_for_list_of_enum_values_with_schema_unquoted; \- completion_info_charp = _completion_type; \- completion_info_charp2 = _completion_schema; \- } \+ completion_squery = &(Query_for_list_of_enum_values_unquoted); \
completion_charpp = NULL; \
completion_verbatim = true; \
- matches = rl_completion_matches(text, complete_from_query); \+ matches = rl_completion_matches(text, complete_from_schema_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; \- } \+ setup_completion_info(function); \+ completion_squery = &(Query_for_list_of_arguments); \
completion_charpp = NULL; \
completion_verbatim = true; \
- matches = rl_completion_matches(text, complete_from_query); \+ matches = rl_completion_matches(text, complete_from_schema_query); \
} while (0)
/*
@@ -419,6 +409,51 @@ do { \
* unnecessary bloat in the completions generated.
*/
+static const SchemaQuery Query_for_constraint_of_table = {+ .catname = "pg_catalog.pg_constraint con, pg_catalog.pg_class c1",+ .selcondition = "con.conrelid=c1.oid",+ .result = "con.conname",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};++static const SchemaQuery Query_for_constraint_of_table_not_validated = {+ .catname = "pg_catalog.pg_constraint con, pg_catalog.pg_class c1",+ .selcondition = "con.conrelid=c1.oid and not con.convalidated",+ .result = "con.conname",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};++static const SchemaQuery Query_for_constraint_of_type = {+ .catname = "pg_catalog.pg_constraint con, pg_catalog.pg_type t",+ .selcondition = "con.contypid=t.oid",+ .result = "con.conname",+ .infoname = "t.typname",+ .infoviscondition = "pg_catalog.pg_type_is_visible(t.oid)",+ .infonamespace = "t.typnamespace",+};++static const SchemaQuery Query_for_index_of_table = {+ .catname = "pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i",+ .selcondition = "c1.oid=i.indrelid and i.indexrelid=c2.oid",+ .result = "c2.relname",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};++static const SchemaQuery Query_for_unique_index_of_table = {+ .catname = "pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i",+ .selcondition = "c1.oid=i.indrelid and i.indexrelid=c2.oid and i.indisunique",+ .result = "c2.relname",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};+
static const SchemaQuery Query_for_list_of_aggregates[] = {
{
.min_server_version = 110000,
@@ -437,6 +472,32 @@ static const SchemaQuery Query_for_list_of_aggregates[] = {
}
};
+static const SchemaQuery Query_for_list_of_arguments = {+ .catname = "pg_catalog.pg_proc p",+ .result = "pg_catalog.oidvectortypes(p.proargtypes)||')'",+ .infoname = "p.proname",+ .infoviscondition = "pg_catalog.pg_function_is_visible(p.oid)",+ .infonamespace = "p.pronamespace",+};++static const SchemaQuery Query_for_list_of_attributes = {+ .catname = "pg_catalog.pg_attribute a, pg_catalog.pg_class c",+ .selcondition = "c.oid = a.attrelid and a.attnum > 0 and not a.attisdropped",+ .result = "a.attname",+ .infoname = "c.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c.oid)",+ .infonamespace = "c.relnamespace",+};++static const SchemaQuery Query_for_list_of_attribute_numbers = {+ .catname = "pg_catalog.pg_attribute a, pg_catalog.pg_class c",+ .selcondition = "c.oid = a.attrelid and a.attnum > 0 and not a.attisdropped",+ .result = "a.attnum::pg_catalog.text",+ .infoname = "c.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c.oid)",+ .infonamespace = "c.relnamespace",+};+
static const char *const Keywords_for_list_of_datatypes[] = {
"bigint",
"boolean",
@@ -497,6 +558,24 @@ static const SchemaQuery Query_for_list_of_domains = {
.result = "t.typname",
};
+static const SchemaQuery Query_for_list_of_enum_values_quoted = {+ .catname = "pg_catalog.pg_enum e, pg_catalog.pg_type t",+ .selcondition = "t.oid = e.enumtypid",+ .result = "pg_catalog.quote_literal(enumlabel)",+ .infoname = "t.typname",+ .infoviscondition = "pg_catalog.pg_type_is_visible(t.oid)",+ .infonamespace = "t.typnamespace",+};++static const SchemaQuery Query_for_list_of_enum_values_unquoted = {+ .catname = "pg_catalog.pg_enum e, pg_catalog.pg_type t",+ .selcondition = "t.oid = e.enumtypid",+ .result = "e.enumlabel",+ .infoname = "t.typname",+ .infoviscondition = "pg_catalog.pg_type_is_visible(t.oid)",+ .infonamespace = "t.typnamespace",+};+
/* Note: this intentionally accepts aggregates as well as plain functions */
static const SchemaQuery Query_for_list_of_functions[] = {
{
@@ -571,6 +650,48 @@ static const SchemaQuery Query_for_list_of_partitioned_tables = {
.result = "c.relname",
};
+static const SchemaQuery Query_for_list_of_tables_for_constraint = {+ .catname = "pg_catalog.pg_class c, pg_catalog.pg_constraint con",+ .selcondition = "c.oid=con.conrelid and c.relkind IN ("+ CppAsString2(RELKIND_RELATION) ", "+ CppAsString2(RELKIND_PARTITIONED_TABLE) ")",+ .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",+ .namespace = "c.relnamespace",+ .result = "c.relname",+ .use_distinct = true,+ .infoname = "con.conname",+};++static const SchemaQuery Query_for_list_of_tables_for_policy = {+ .catname = "pg_catalog.pg_class c, pg_catalog.pg_policy p",+ .selcondition = "c.oid=p.polrelid",+ .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",+ .namespace = "c.relnamespace",+ .result = "c.relname",+ .use_distinct = true,+ .infoname = "p.polname",+};++static const SchemaQuery Query_for_list_of_tables_for_rule = {+ .catname = "pg_catalog.pg_class c, pg_catalog.pg_rewrite r",+ .selcondition = "c.oid=r.ev_class",+ .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",+ .namespace = "c.relnamespace",+ .result = "c.relname",+ .use_distinct = true,+ .infoname = "r.rulename",+};++static const SchemaQuery Query_for_list_of_tables_for_trigger = {+ .catname = "pg_catalog.pg_class c, pg_catalog.pg_trigger t",+ .selcondition = "c.oid=t.tgrelid",+ .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",+ .namespace = "c.relnamespace",+ .result = "c.relname",+ .use_distinct = true,+ .infoname = "t.tgname",+};+
static const SchemaQuery Query_for_list_of_views = {
.catname = "pg_catalog.pg_class c",
.selcondition = "c.relkind IN (" CppAsString2(RELKIND_VIEW) ")",
@@ -719,7 +840,6 @@ static const SchemaQuery Query_for_list_of_clusterables = {
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 = "c.conname",
};
@@ -739,96 +859,56 @@ static const SchemaQuery Query_for_list_of_collations = {
.result = "c.collname",
};
+static const SchemaQuery Query_for_partition_of_table = {+ .catname = "pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i",+ .selcondition = "c1.oid=i.inhparent and i.inhrelid=c2.oid and c2.relispartition",+ .viscondition = "pg_catalog.pg_table_is_visible(c2.oid)",+ .namespace = "c2.relnamespace",+ .result = "c2.relname",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};++static const SchemaQuery Query_for_rule_of_table = {+ .catname = "pg_catalog.pg_rewrite r, pg_catalog.pg_class c1",+ .selcondition = "r.ev_class=c1.oid",+ .result = "r.rulename",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};++static const SchemaQuery Query_for_trigger_of_table = {+ .catname = "pg_catalog.pg_trigger t, pg_catalog.pg_class c1",+ .selcondition = "t.tgrelid=c1.oid and not t.tgisinternal",+ .result = "t.tgname",+ .infoname = "c1.relname",+ .infoviscondition = "pg_catalog.pg_table_is_visible(c1.oid)",+ .infonamespace = "c1.relnamespace",+};+
/*
* Queries to get lists of names of various kinds of things, possibly
- * restricted to names matching a partially entered name. In these queries,- * the first %s will be replaced by the text entered so far (suitably escaped- * to become a SQL literal string). %d will be replaced by the length of the- * string (in unescaped form). A second and third %s, if present, will be- * replaced by a suitably-escaped version of the string provided in- * completion_info_charp. A fourth and fifth %s are similarly replaced by- * completion_info_charp2.+ * restricted to names matching a partially entered name. Don't use+ * this method where the user might wish to enter a schema-qualified+ * name; make a SchemaQuery instead.+ *+ * In these queries, there must be a restriction clause of the form+ * substring(OUTPUT,1,%d)='%s'+ * where "OUTPUT" is the same string that the query returns. The %d and %s+ * will be replaced by the string length of the text and the text itself,+ * causing the results to be limited to those matching the already-typed text.
*
- * Beware that the allowed sequences of %s and %d are determined by- * _complete_from_query().+ * There can be a second '%s', which will be replaced by a suitably-escaped+ * version of the string provided in completion_info_object. If there is a+ * third '%s', it will be replaced by a suitably-escaped version of the string+ * provided in completion_info_schema. NOTE: using completion_info_object+ * that way is usually the wrong thing, and using completion_info_schema+ * that way is always the wrong thing. Make a SchemaQuery instead.
*/
-#define Query_for_list_of_attributes \-"SELECT attname "\-" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\-" WHERE c.oid = a.attrelid "\-" AND a.attnum > 0 "\-" AND NOT a.attisdropped "\-" AND substring(attname,1,%d)='%s' "\-" AND (relname='%s' "\-" OR '\"' || relname || '\"'='%s') "\-" AND pg_catalog.pg_table_is_visible(c.oid)"--#define Query_for_list_of_attribute_numbers \-"SELECT attnum "\-" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c "\-" WHERE c.oid = a.attrelid "\-" AND a.attnum > 0 "\-" AND NOT a.attisdropped "\-" AND substring(attnum::pg_catalog.text,1,%d)='%s' "\-" AND (relname='%s' "\-" OR '\"' || relname || '\"'='%s') "\-" AND pg_catalog.pg_table_is_visible(c.oid)"--#define Query_for_list_of_attributes_with_schema \-"SELECT attname "\-" FROM pg_catalog.pg_attribute a, pg_catalog.pg_class c, pg_catalog.pg_namespace n "\-" WHERE c.oid = a.attrelid "\-" AND n.oid = c.relnamespace "\-" AND a.attnum > 0 "\-" AND NOT a.attisdropped "\-" AND substring(attname,1,%d)='%s' "\-" AND (relname='%s' "\-" OR '\"' || relname || '\"' ='%s') "\-" AND (nspname='%s' "\-" OR '\"' || nspname || '\"' ='%s') "--#define Query_for_list_of_enum_values_quoted \-"SELECT pg_catalog.quote_literal(enumlabel) "\-" FROM pg_catalog.pg_enum e, pg_catalog.pg_type t "\-" WHERE t.oid = e.enumtypid "\-" AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\-" AND (typname='%s' "\-" OR '\"' || typname || '\"'='%s') "\-" AND pg_catalog.pg_type_is_visible(t.oid)"--#define Query_for_list_of_enum_values_unquoted \-"SELECT enumlabel "\-" FROM pg_catalog.pg_enum e, pg_catalog.pg_type t "\-" WHERE t.oid = e.enumtypid "\-" AND substring(enumlabel,1,%d)='%s' "\-" AND (typname='%s' "\-" OR '\"' || typname || '\"'='%s') "\-" AND pg_catalog.pg_type_is_visible(t.oid)"--#define Query_for_list_of_enum_values_with_schema_quoted \-"SELECT pg_catalog.quote_literal(enumlabel) "\-" FROM pg_catalog.pg_enum e, pg_catalog.pg_type t, pg_catalog.pg_namespace n "\-" WHERE t.oid = e.enumtypid "\-" AND n.oid = t.typnamespace "\-" AND substring(pg_catalog.quote_literal(enumlabel),1,%d)='%s' "\-" AND (typname='%s' "\-" OR '\"' || typname || '\"'='%s') "\-" AND (nspname='%s' "\-" OR '\"' || nspname || '\"' ='%s') "--#define Query_for_list_of_enum_values_with_schema_unquoted \-"SELECT enumlabel "\-" FROM pg_catalog.pg_enum e, pg_catalog.pg_type t, pg_catalog.pg_namespace n "\-" WHERE t.oid = e.enumtypid "\-" AND n.oid = t.typnamespace "\-" AND substring(enumlabel,1,%d)='%s' "\-" AND (typname='%s' "\-" OR '\"' || typname || '\"'='%s') "\-" AND (nspname='%s' "\-" OR '\"' || nspname || '\"' ='%s') "-
#define Query_for_list_of_template_databases \
"SELECT d.datname "\
" FROM pg_catalog.pg_database d "\
@@ -887,81 +967,12 @@ static const SchemaQuery Query_for_list_of_collations = {
#define Keywords_for_list_of_grant_roles \
"PUBLIC", "CURRENT_ROLE", "CURRENT_USER", "SESSION_USER"
-#define Query_for_index_of_table \-"SELECT c2.relname "\-" FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_index i"\-" WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid"\-" and substring(c2.relname,1,%d)='%s'"\-" and c1.relname='%s'"\-" and pg_catalog.pg_table_is_visible(c2.oid)"--#define Query_for_unique_index_of_table \-Query_for_index_of_table \-" and i.indisunique"--#define Query_for_constraint_of_table \-"SELECT conname "\-" FROM pg_catalog.pg_class c1, pg_catalog.pg_constraint con "\-" WHERE c1.oid=conrelid and substring(conname,1,%d)='%s'"\-" and c1.relname='%s'"\-" and pg_catalog.pg_table_is_visible(c1.oid)"--#define Query_for_constraint_of_table_not_validated \-Query_for_constraint_of_table \-" and not con.convalidated"-
#define Query_for_all_table_constraints \
"SELECT conname "\
" FROM pg_catalog.pg_constraint c "\
" WHERE c.conrelid <> 0 "\
" and substring(conname,1,%d)='%s'"
-#define Query_for_constraint_of_type \-"SELECT conname "\-" FROM pg_catalog.pg_type t, pg_catalog.pg_constraint con "\-" WHERE t.oid=contypid and substring(conname,1,%d)='%s'"\-" and t.typname='%s'"\-" and pg_catalog.pg_type_is_visible(t.oid)"--#define Query_for_list_of_tables_for_constraint \-"SELECT relname "\-" FROM pg_catalog.pg_class"\-" WHERE substring(relname,1,%d)='%s'"\-" AND oid IN "\-" (SELECT conrelid FROM pg_catalog.pg_constraint "\-" WHERE conname='%s')"--#define Query_for_rule_of_table \-"SELECT rulename "\-" FROM pg_catalog.pg_class c1, pg_catalog.pg_rewrite "\-" WHERE c1.oid=ev_class and substring(rulename,1,%d)='%s'"\-" and c1.relname='%s'"\-" and pg_catalog.pg_table_is_visible(c1.oid)"--#define Query_for_list_of_tables_for_rule \-"SELECT relname "\-" FROM pg_catalog.pg_class"\-" WHERE substring(relname,1,%d)='%s'"\-" AND oid IN "\-" (SELECT ev_class FROM pg_catalog.pg_rewrite "\-" WHERE rulename='%s')"--#define Query_for_trigger_of_table \-"SELECT tgname "\-" FROM pg_catalog.pg_class c1, pg_catalog.pg_trigger "\-" WHERE c1.oid=tgrelid and substring(tgname,1,%d)='%s'"\-" and c1.relname='%s'"\-" and pg_catalog.pg_table_is_visible(c1.oid)"\-" and not tgisinternal"--#define Query_for_list_of_tables_for_trigger \-"SELECT relname "\-" FROM pg_catalog.pg_class"\-" WHERE substring(relname,1,%d)='%s'"\-" AND oid IN "\-" (SELECT tgrelid FROM pg_catalog.pg_trigger "\-" WHERE tgname='%s')"-
#define Query_for_list_of_ts_configurations \
"SELECT cfgname FROM pg_catalog.pg_ts_config "\
" WHERE substring(cfgname,1,%d)='%s'"
@@ -1010,24 +1021,6 @@ Query_for_constraint_of_table \
" WHERE substring(amname,1,%d)='%s' AND "\
" amtype=" CppAsString2(AMTYPE_TABLE)
-#define Query_for_list_of_arguments \-"SELECT pg_catalog.oidvectortypes(proargtypes)||')' "\-" FROM pg_catalog.pg_proc "\-" WHERE substring(pg_catalog.oidvectortypes(proargtypes)||')',1,%d)='%s'"\-" AND (proname='%s'"\-" OR '\"' || proname || '\"'='%s') "\-" AND (pg_catalog.pg_function_is_visible(pg_proc.oid))"--#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 substring(pg_catalog.oidvectortypes(proargtypes)||')',1,%d)='%s'"\-" AND n.oid = p.pronamespace "\-" AND (proname='%s' "\-" OR '\"' || proname || '\"' ='%s') "\-" AND (nspname='%s' "\-" OR '\"' || nspname || '\"' ='%s') "-
#define Query_for_list_of_extensions \
" SELECT extname "\
" FROM pg_catalog.pg_extension "\
@@ -1074,30 +1067,18 @@ Query_for_constraint_of_table \
" FROM pg_catalog.pg_policy "\
" WHERE substring(polname,1,%d)='%s'"
-#define Query_for_list_of_tables_for_policy \-"SELECT relname "\-" FROM pg_catalog.pg_class"\-" WHERE substring(relname,1,%d)='%s'"\-" AND oid IN "\-" (SELECT polrelid FROM pg_catalog.pg_policy "\-" WHERE polname='%s')"--#define Query_for_enum \-" SELECT name FROM ( "\-" SELECT pg_catalog.unnest(enumvals) AS name "\+#define Query_for_values_of_enum_GUC \+" SELECT val FROM ( "\+" SELECT name, pg_catalog.unnest(enumvals) AS val "\
" FROM pg_catalog.pg_settings "\
-" WHERE pg_catalog.lower(name)=pg_catalog.lower('%s') "\
" ) ss "\
-" WHERE pg_catalog.substring(name,1,%%d)='%%s'"+" WHERE substring(val,1,%d)='%s'"\+" and pg_catalog.lower(name)=pg_catalog.lower('%s')"-#define Query_for_partition_of_table \-"SELECT c2.relname "\-" FROM pg_catalog.pg_class c1, pg_catalog.pg_class c2, pg_catalog.pg_inherits i"\-" WHERE c1.oid=i.inhparent and i.inhrelid=c2.oid"\-" and substring(c2.relname,1,%d)='%s'"\-" and c1.relname='%s'"\-" and pg_catalog.pg_table_is_visible(c2.oid)"\-" and c2.relispartition = 'true'"+#define Query_for_list_of_channels \+" SELECT channel "\+" FROM pg_catalog.pg_listening_channels() AS channel "\+" WHERE substring(channel,1,%d)='%s'"
#define Query_for_list_of_cursors \
" SELECT name "\
@@ -1280,6 +1261,7 @@ static char *_complete_from_query(const char *simple_query,
const char *const *keywords,
bool verbatim,
const char *text, int state);
+static void setup_completion_info(const char *word);
static char *complete_from_list(const char *text, int state);
static char *complete_from_const(const char *text, int state);
static void append_variable_names(char ***varnames, int *nvars,
@@ -1685,8 +1667,10 @@ psql_completion(const char *text, int start, int end)
/* Clear a few things. */
completion_charp = NULL;
completion_charpp = NULL;
- completion_info_charp = NULL;- completion_info_charp2 = NULL;+ completion_vquery = NULL;+ completion_squery = NULL;+ completion_info_object = NULL;+ completion_info_schema = NULL;
/*
* Scan the input line to extract the words before our current position.
@@ -1872,14 +1856,14 @@ psql_completion(const char *text, int start, int end)
/* ALTER EXTENSION <name> UPDATE */
else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE"))
{
- completion_info_charp = prev2_wd;+ setup_completion_info(prev2_wd);
COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_available_extension_versions_with_TO);
}
/* ALTER EXTENSION <name> UPDATE TO */
else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"))
{
- completion_info_charp = prev3_wd;+ setup_completion_info(prev3_wd);
COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_available_extension_versions);
}
@@ -1919,8 +1903,8 @@ psql_completion(const char *text, int start, int end)
/* ALTER INDEX <name> ALTER COLUMN */
else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_attribute_numbers);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(Query_for_list_of_attribute_numbers);
}
/* ALTER INDEX <name> ALTER COLUMN <colnum> */
else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny))
@@ -2030,8 +2014,8 @@ psql_completion(const char *text, int start, int end)
/* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
else if (Matches("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_constraint_of_type);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_type);
}
/* ALTER DOMAIN <sth> RENAME */
else if (Matches("ALTER", "DOMAIN", MatchAny, "RENAME"))
@@ -2116,8 +2100,8 @@ psql_completion(const char *text, int start, int end)
/* ALTER POLICY <name> ON <table> */
else if (Matches("ALTER", "POLICY", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
}
/* ALTER POLICY <name> ON <table> - show options */
else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny))
@@ -2140,8 +2124,8 @@ psql_completion(const char *text, int start, int end)
/* If we have ALTER RULE <name> ON, then add the correct tablename */
else if (Matches("ALTER", "RULE", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
}
/* ALTER RULE <name> ON <name> */
@@ -2156,18 +2140,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "TRIGGER", MatchAny))
COMPLETE_WITH("ON");
- else if (Matches("ALTER", "TRIGGER", MatchAny, MatchAny))+ else if (Matches("ALTER", "TRIGGER", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
}
- /*- * If we have ALTER TRIGGER <sth> ON, then add the correct tablename- */- else if (Matches("ALTER", "TRIGGER", MatchAny, "ON"))- COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);-
/* ALTER TRIGGER <name> ON <name> */
else if (Matches("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
COMPLETE_WITH("RENAME TO", "DEPENDS ON EXTENSION",
@@ -2206,28 +2184,28 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE xxx ADD PRIMARY KEY USING INDEX */
else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY", "USING", "INDEX"))
{
- completion_info_charp = prev6_wd;- COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);+ setup_completion_info(prev6_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ADD UNIQUE USING INDEX */
else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE", "USING", "INDEX"))
{
- completion_info_charp = prev5_wd;- COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);+ setup_completion_info(prev5_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ADD CONSTRAINT yyy PRIMARY KEY USING INDEX */
else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny,
"PRIMARY", "KEY", "USING", "INDEX"))
{
- completion_info_charp = prev8_wd;- COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);+ setup_completion_info(prev8_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ADD CONSTRAINT yyy UNIQUE USING INDEX */
else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny,
"UNIQUE", "USING", "INDEX"))
{
- completion_info_charp = prev7_wd;- COMPLETE_WITH_QUERY(Query_for_unique_index_of_table);+ setup_completion_info(prev7_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
}
/* ALTER TABLE xxx ENABLE */
else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE"))
@@ -2237,23 +2215,23 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("RULE", "TRIGGER");
else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_rule_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
}
else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
{
- completion_info_charp = prev4_wd;- COMPLETE_WITH_QUERY(Query_for_rule_of_table);+ setup_completion_info(prev4_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
}
else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_trigger_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
}
else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"))
{
- completion_info_charp = prev4_wd;- COMPLETE_WITH_QUERY(Query_for_trigger_of_table);+ setup_completion_info(prev4_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
}
/* ALTER TABLE xxx INHERIT */
else if (Matches("ALTER", "TABLE", MatchAny, "INHERIT"))
@@ -2269,13 +2247,13 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ROW LEVEL SECURITY", "RULE", "TRIGGER");
else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_rule_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
}
else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_trigger_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
}
/* ALTER TABLE xxx ALTER */
@@ -2305,14 +2283,14 @@ psql_completion(const char *text, int start, int end)
/* ALTER TABLE <sth> ALTER|DROP|RENAME CONSTRAINT <constraint> */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME", "CONSTRAINT"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_constraint_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table);
}
/* ALTER TABLE <sth> VALIDATE CONSTRAINT <non-validated constraint> */
else if (Matches("ALTER", "TABLE", MatchAny, "VALIDATE", "CONSTRAINT"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_constraint_of_table_not_validated);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table_not_validated);
}
/* ALTER TABLE ALTER [COLUMN] <foo> */
else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny) ||
@@ -2344,8 +2322,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_index_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
}
/* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
else if (Matches("ALTER", "TABLE", MatchAny, "SET"))
@@ -2376,8 +2354,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_LIST(table_storage_parameters);
else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
{
- completion_info_charp = prev5_wd;- COMPLETE_WITH_QUERY(Query_for_index_of_table);+ setup_completion_info(prev5_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
}
else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
COMPLETE_WITH("INDEX");
@@ -2404,8 +2382,8 @@ psql_completion(const char *text, int start, int end)
*/
else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION"))
{
- completion_info_charp = prev3_wd;- COMPLETE_WITH_QUERY(Query_for_partition_of_table);+ setup_completion_info(prev3_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
}
else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny))
COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
@@ -2542,8 +2520,8 @@ psql_completion(const char *text, int start, int end)
else if (Matches("CLUSTER", MatchAny, "USING") ||
Matches("CLUSTER", "VERBOSE|(*)", MatchAny, "USING"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_index_of_table);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
}
else if (HeadMatches("CLUSTER", "(*") &&
!HeadMatches("CLUSTER", "(*)"))
@@ -2580,9 +2558,9 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_tables_for_constraint,- "DOMAIN");+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables_for_constraint,+ "DOMAIN");
}
else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON", "DOMAIN"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
@@ -2600,8 +2578,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (Matches("COMMENT", "ON", "POLICY", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
}
else if (Matches("COMMENT", "ON", "PROCEDURAL", "LANGUAGE"))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
@@ -2609,8 +2587,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (Matches("COMMENT", "ON", "RULE", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
}
else if (Matches("COMMENT", "ON", "TEXT", "SEARCH"))
COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
@@ -2628,15 +2606,15 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("LANGUAGE");
else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
{
- completion_info_charp = prev2_wd;+ setup_completion_info(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
}
else if (Matches("COMMENT", "ON", "TRIGGER", MatchAny))
COMPLETE_WITH("ON");
else if (Matches("COMMENT", "ON", "TRIGGER", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
}
else if (Matches("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) ||
@@ -2752,7 +2730,7 @@ psql_completion(const char *text, int start, int end)
/* CREATE EXTENSION <name> VERSION */
else if (Matches("CREATE", "EXTENSION", MatchAny, "VERSION"))
{
- completion_info_charp = prev2_wd;+ setup_completion_info(prev2_wd);
COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_available_extension_versions);
}
@@ -3063,7 +3041,7 @@ psql_completion(const char *text, int start, int end)
else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE") ||
Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
{
- completion_info_charp = prev2_wd;+ setup_completion_info(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
}
@@ -3486,8 +3464,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (Matches("DROP", "TRIGGER", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
}
else if (Matches("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
COMPLETE_WITH("CASCADE", "RESTRICT");
@@ -3513,8 +3491,8 @@ psql_completion(const char *text, int start, int end)
/* DROP POLICY <name> ON <table> */
else if (Matches("DROP", "POLICY", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
}
else if (Matches("DROP", "POLICY", MatchAny, "ON", MatchAny))
COMPLETE_WITH("CASCADE", "RESTRICT");
@@ -3524,8 +3502,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("ON");
else if (Matches("DROP", "RULE", MatchAny, "ON"))
{
- completion_info_charp = prev2_wd;- COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
}
else if (Matches("DROP", "RULE", MatchAny, "ON", MatchAny))
COMPLETE_WITH("CASCADE", "RESTRICT");
@@ -3539,7 +3517,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("LANGUAGE");
else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
{
- completion_info_charp = prev2_wd;+ setup_completion_info(prev2_wd);
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
}
else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE", MatchAny))
@@ -3923,7 +3901,7 @@ psql_completion(const char *text, int start, int end)
/* NOTIFY --- can be inside EXPLAIN, RULE, etc */
else if (TailMatches("NOTIFY"))
- COMPLETE_WITH_QUERY("SELECT channel FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(channel,1,%d)='%s'");+ COMPLETE_WITH_QUERY(Query_for_list_of_channels);
/* OPTIONS */
else if (TailMatches("OPTIONS"))
@@ -4159,11 +4137,9 @@ psql_completion(const char *text, int start, int end)
{
if (strcmp(guctype, "enum") == 0)
{
- char querybuf[1024];-- snprintf(querybuf, sizeof(querybuf),- Query_for_enum, prev2_wd);- COMPLETE_WITH_QUERY_PLUS(querybuf, "DEFAULT");+ setup_completion_info(prev2_wd);+ COMPLETE_WITH_QUERY_PLUS(Query_for_values_of_enum_GUC,+ "DEFAULT");
}
else if (strcmp(guctype, "bool") == 0)
COMPLETE_WITH("on", "off", "true", "false", "yes", "no",
@@ -4208,9 +4184,7 @@ psql_completion(const char *text, int start, int end)
/* UNLISTEN */
else if (Matches("UNLISTEN"))
- COMPLETE_WITH_QUERY_PLUS("SELECT channel FROM pg_catalog.pg_listening_channels() AS channel"- " WHERE substring(channel,1,%d)='%s'",- "*");+ COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_channels, "*");
/* UPDATE --- can be inside EXPLAIN, RULE, etc */
/* If prev. word is UPDATE suggest a list of tables */
@@ -4411,7 +4385,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
else if (TailMatchesCS("\\encoding"))
- COMPLETE_WITH_QUERY(Query_for_list_of_encodings);+ COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_encodings);
else if (TailMatchesCS("\\h|\\help"))
COMPLETE_WITH_LIST(sql_commands);
else if (TailMatchesCS("\\h|\\help", MatchAny))
@@ -4570,6 +4544,12 @@ psql_completion(const char *text, int start, int end)
free(previous_words);
free(words_buffer);
free(text_copy);
+ if (completion_info_object)+ free(completion_info_object);+ completion_info_object = NULL;+ if (completion_info_schema)+ free(completion_info_schema);+ completion_info_schema = NULL;
/* Return our Grand List O' Matches */
return matches;
@@ -4713,10 +4693,11 @@ complete_from_versioned_schema_query(const char *text, int state)
* where "OUTPUT" is the same string that the query returns. The %d and %s
* will be replaced by the string length of the text and the text itself,
* causing the results to be limited to those matching the already-typed text.
- * The query may also have up to four more %s in it; the first two such will- * be replaced by the value of completion_info_charp, the next two by the- * value of completion_info_charp2. (These strings will be escaped to- * become SQL literals, so what is actually in the query should be '%s'.)+ * There can be a second '%s', which will be replaced by a suitably-escaped+ * version of the string provided in completion_info_object. If there is a+ * third '%s', it will be replaced by a suitably-escaped version of the string+ * provided in completion_info_schema. Those strings should be set up+ * by calling setup_completion_info().
* Simple queries should return a single column of matches. If "verbatim"
* is true, the matches are returned as-is; otherwise, they are taken to
* be SQL identifiers and quoted if necessary.
@@ -4769,8 +4750,8 @@ _complete_from_query(const char *simple_query,
int object_length = 0;
char *e_schemaname;
char *e_objectname;
- char *e_info_charp;- char *e_info_charp2;+ char *e_info_object;+ char *e_info_schema;
/* Reset static state, ensuring no memory leaks */
list_index = 0;
@@ -4812,15 +4793,15 @@ _complete_from_query(const char *simple_query,
e_objectname = escape_string(objectname);
- if (completion_info_charp)- e_info_charp = escape_string(completion_info_charp);+ if (completion_info_object)+ e_info_object = escape_string(completion_info_object);
else
- e_info_charp = NULL;+ e_info_object = NULL;- if (completion_info_charp2)- e_info_charp2 = escape_string(completion_info_charp2);+ if (completion_info_schema)+ e_info_schema = escape_string(completion_info_schema);
else
- e_info_charp2 = NULL;+ e_info_schema = NULL;
initPQExpBuffer(&query_buffer);
@@ -4833,20 +4814,44 @@ _complete_from_query(const char *simple_query,
* already qualified or not. schema_query gives us the pieces to
* assemble.
*/
- if (schemaname == NULL)+ if (schemaname == NULL || schema_query->namespace == NULL)
{
/* Get unqualified names matching the input-so-far */
- appendPQExpBuffer(&query_buffer, "SELECT %s, NULL::pg_catalog.text FROM %s WHERE ",+ appendPQExpBufferStr(&query_buffer, "SELECT ");+ if (schema_query->use_distinct)+ appendPQExpBufferStr(&query_buffer, "DISTINCT ");+ appendPQExpBuffer(&query_buffer,+ "%s, NULL::pg_catalog.text FROM %s",
schema_query->result,
schema_query->catname);
+ if (schema_query->infonamespace && completion_info_schema)+ appendPQExpBufferStr(&query_buffer,+ ", pg_catalog.pg_namespace ni");+ appendPQExpBufferStr(&query_buffer, " WHERE ");
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
schema_query->selcondition);
appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",
schema_query->result,
object_length, e_objectname);
+ if (schema_query->viscondition)
appendPQExpBuffer(&query_buffer, " AND %s",
schema_query->viscondition);
+ if (schema_query->infoname)+ {+ Assert(completion_info_object);+ appendPQExpBuffer(&query_buffer, " AND %s = '%s'",+ schema_query->infoname, e_info_object);+ if (schema_query->infonamespace && completion_info_schema)+ appendPQExpBuffer(&query_buffer,+ " AND %s = ni.oid AND ni.nspname = '%s'",+ schema_query->infonamespace,+ e_info_schema);+ else if (schema_query->infoviscondition)+ appendPQExpBuffer(&query_buffer,+ " AND %s",+ schema_query->infoviscondition);+ }
/*
* When fetching relation names, suppress system catalogs unless
@@ -4863,7 +4868,12 @@ _complete_from_query(const char *simple_query,
" pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");
}
- /* Add in schema names matching the input-so-far */+ /*+ * If the target object type can be schema-qualified, add in+ * schema names matching the input-so-far.+ */+ if (schema_query->namespace)+ {
appendPQExpBuffer(&query_buffer, "\nUNION ALL\n"
"SELECT NULL::pg_catalog.text, n.nspname "
"FROM pg_catalog.pg_namespace n "
@@ -4884,14 +4894,21 @@ _complete_from_query(const char *simple_query,
*/
schemaquoted = objectquoted;
}
+ }
else
{
/* Input is qualified, so produce only qualified names */
- appendPQExpBuffer(&query_buffer, "SELECT %s, n.nspname "- "FROM %s, pg_catalog.pg_namespace n "- "WHERE %s = n.oid AND ",+ appendPQExpBufferStr(&query_buffer, "SELECT ");+ if (schema_query->use_distinct)+ appendPQExpBufferStr(&query_buffer, "DISTINCT ");+ appendPQExpBuffer(&query_buffer, "%s, n.nspname "+ "FROM %s, pg_catalog.pg_namespace n",
schema_query->result,
- schema_query->catname,+ schema_query->catname);+ if (schema_query->infonamespace && completion_info_schema)+ appendPQExpBufferStr(&query_buffer,+ ", pg_catalog.pg_namespace ni");+ appendPQExpBuffer(&query_buffer, " WHERE %s = n.oid AND ",
schema_query->namespace);
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
@@ -4901,6 +4918,21 @@ _complete_from_query(const char *simple_query,
object_length, e_objectname);
appendPQExpBuffer(&query_buffer, "n.nspname = '%s'",
e_schemaname);
+ if (schema_query->infoname)+ {+ Assert(completion_info_object);+ appendPQExpBuffer(&query_buffer, " AND %s = '%s'",+ schema_query->infoname, e_info_object);+ if (schema_query->infonamespace && completion_info_schema)+ appendPQExpBuffer(&query_buffer,+ " AND %s = ni.oid AND ni.nspname = '%s'",+ schema_query->infonamespace,+ e_info_schema);+ else if (schema_query->infoviscondition)+ appendPQExpBuffer(&query_buffer,+ " AND %s",+ schema_query->infoviscondition);+ }
}
}
else
@@ -4909,8 +4941,7 @@ _complete_from_query(const char *simple_query,
/* simple_query is an sprintf-style format string */
appendPQExpBuffer(&query_buffer, simple_query,
object_length, e_objectname,
- e_info_charp, e_info_charp,- e_info_charp2, e_info_charp2);+ e_info_object, e_info_schema);
}
/* Limit the number of records in the result */
@@ -4925,10 +4956,10 @@ _complete_from_query(const char *simple_query,
if (e_schemaname)
free(e_schemaname);
free(e_objectname);
- if (e_info_charp)- free(e_info_charp);- if (e_info_charp2)- free(e_info_charp2);+ if (e_info_object)+ free(e_info_object);+ if (e_info_schema)+ free(e_info_schema);
}
/* Return the next result, if any, but not if the query failed */
@@ -5037,6 +5068,23 @@ _complete_from_query(const char *simple_query,
}
+/*+ * Set up completion_info_object and completion_info_schema+ * by parsing the given word. These variables can then be+ * used in a query passed to _complete_from_query.+ */+static void+setup_completion_info(const char *word)+{+ bool schemaquoted,+ objectquoted;++ parse_identifier(word,+ &completion_info_schema, &completion_info_object,+ &schemaquoted, &objectquoted);+}++
/*
* This function returns in order one of a fixed, NULL pointer terminated list
* of strings (if matching). This can be used if there are only a fixed number
v16-0003-re-pgindent.patch (text/x-diff, 9.1K) downloaddiff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.cindex 176f7e74c2..d13cfc3adf 100644--- a/src/bin/psql/tab-complete.c+++ b/src/bin/psql/tab-complete.c@@ -4816,123 +4816,125 @@ _complete_from_query(const char *simple_query,
*/
if (schemaname == NULL || schema_query->namespace == NULL)
{
- /* Get unqualified names matching the input-so-far */- appendPQExpBufferStr(&query_buffer, "SELECT ");- if (schema_query->use_distinct)- appendPQExpBufferStr(&query_buffer, "DISTINCT ");- appendPQExpBuffer(&query_buffer,- "%s, NULL::pg_catalog.text FROM %s",- schema_query->result,- schema_query->catname);- if (schema_query->infonamespace && completion_info_schema)- appendPQExpBufferStr(&query_buffer,- ", pg_catalog.pg_namespace ni");- appendPQExpBufferStr(&query_buffer, " WHERE ");- if (schema_query->selcondition)- appendPQExpBuffer(&query_buffer, "%s AND ",- schema_query->selcondition);- appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",- schema_query->result,- object_length, e_objectname);- if (schema_query->viscondition)- appendPQExpBuffer(&query_buffer, " AND %s",- schema_query->viscondition);- if (schema_query->infoname)- {- Assert(completion_info_object);- appendPQExpBuffer(&query_buffer, " AND %s = '%s'",- schema_query->infoname, e_info_object);+ /* Get unqualified names matching the input-so-far */+ appendPQExpBufferStr(&query_buffer, "SELECT ");+ if (schema_query->use_distinct)+ appendPQExpBufferStr(&query_buffer, "DISTINCT ");+ appendPQExpBuffer(&query_buffer,+ "%s, NULL::pg_catalog.text FROM %s",+ schema_query->result,+ schema_query->catname);
if (schema_query->infonamespace && completion_info_schema)
- appendPQExpBuffer(&query_buffer,- " AND %s = ni.oid AND ni.nspname = '%s'",- schema_query->infonamespace,- e_info_schema);- else if (schema_query->infoviscondition)- appendPQExpBuffer(&query_buffer,- " AND %s",- schema_query->infoviscondition);- }-- /*- * When fetching relation names, suppress system catalogs unless- * the input-so-far begins with "pg_". This is a compromise- * between not offering system catalogs for completion at all, and- * having them swamp the result when the input is just "p".- */- if (strcmp(schema_query->catname,- "pg_catalog.pg_class c") == 0 &&- strncmp(objectname, "pg_", 3) != 0)- {- appendPQExpBufferStr(&query_buffer,- " AND c.relnamespace <> (SELECT oid FROM"- " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");- }-- /*- * If the target object type can be schema-qualified, add in- * schema names matching the input-so-far.- */- if (schema_query->namespace)- {- appendPQExpBuffer(&query_buffer, "\nUNION ALL\n"- "SELECT NULL::pg_catalog.text, n.nspname "- "FROM pg_catalog.pg_namespace n "- "WHERE substring(n.nspname,1,%d)='%s'",- object_length, e_objectname);+ appendPQExpBufferStr(&query_buffer,+ ", pg_catalog.pg_namespace ni");+ appendPQExpBufferStr(&query_buffer, " WHERE ");+ if (schema_query->selcondition)+ appendPQExpBuffer(&query_buffer, "%s AND ",+ schema_query->selcondition);+ appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",+ schema_query->result,+ object_length, e_objectname);+ if (schema_query->viscondition)+ appendPQExpBuffer(&query_buffer, " AND %s",+ schema_query->viscondition);+ if (schema_query->infoname)+ {+ Assert(completion_info_object);+ appendPQExpBuffer(&query_buffer, " AND %s = '%s'",+ schema_query->infoname, e_info_object);+ if (schema_query->infonamespace && completion_info_schema)+ appendPQExpBuffer(&query_buffer,+ " AND %s = ni.oid AND ni.nspname = '%s'",+ schema_query->infonamespace,+ e_info_schema);+ else if (schema_query->infoviscondition)+ appendPQExpBuffer(&query_buffer,+ " AND %s",+ schema_query->infoviscondition);+ }- /*- * Likewise, suppress system schemas unless the input-so-far- * begins with "pg_".- */- if (strncmp(objectname, "pg_", 3) != 0)- appendPQExpBufferStr(&query_buffer,- " AND n.nspname NOT LIKE 'pg\\_%'");+ /*+ * When fetching relation names, suppress system catalogs+ * unless the input-so-far begins with "pg_". This is a+ * compromise between not offering system catalogs for+ * completion at all, and having them swamp the result when+ * the input is just "p".+ */+ if (strcmp(schema_query->catname,+ "pg_catalog.pg_class c") == 0 &&+ strncmp(objectname, "pg_", 3) != 0)+ {+ appendPQExpBufferStr(&query_buffer,+ " AND c.relnamespace <> (SELECT oid FROM"+ " pg_catalog.pg_namespace WHERE nspname = 'pg_catalog')");+ }- /*- * Since we're matching these schema names to the object name,- * handle their quoting using the object name's quoting state.- */- schemaquoted = objectquoted;- }+ /*+ * If the target object type can be schema-qualified, add in+ * schema names matching the input-so-far.+ */+ if (schema_query->namespace)+ {+ appendPQExpBuffer(&query_buffer, "\nUNION ALL\n"+ "SELECT NULL::pg_catalog.text, n.nspname "+ "FROM pg_catalog.pg_namespace n "+ "WHERE substring(n.nspname,1,%d)='%s'",+ object_length, e_objectname);++ /*+ * Likewise, suppress system schemas unless the+ * input-so-far begins with "pg_".+ */+ if (strncmp(objectname, "pg_", 3) != 0)+ appendPQExpBufferStr(&query_buffer,+ " AND n.nspname NOT LIKE 'pg\\_%'");++ /*+ * Since we're matching these schema names to the object+ * name, handle their quoting using the object name's+ * quoting state.+ */+ schemaquoted = objectquoted;+ }
}
else
{
- /* Input is qualified, so produce only qualified names */- appendPQExpBufferStr(&query_buffer, "SELECT ");- if (schema_query->use_distinct)- appendPQExpBufferStr(&query_buffer, "DISTINCT ");- appendPQExpBuffer(&query_buffer, "%s, n.nspname "- "FROM %s, pg_catalog.pg_namespace n",- schema_query->result,- schema_query->catname);- if (schema_query->infonamespace && completion_info_schema)- appendPQExpBufferStr(&query_buffer,- ", pg_catalog.pg_namespace ni");- appendPQExpBuffer(&query_buffer, " WHERE %s = n.oid AND ",- schema_query->namespace);- if (schema_query->selcondition)- appendPQExpBuffer(&query_buffer, "%s AND ",- schema_query->selcondition);- appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s' AND ",- schema_query->result,- object_length, e_objectname);- appendPQExpBuffer(&query_buffer, "n.nspname = '%s'",- e_schemaname);- if (schema_query->infoname)- {- Assert(completion_info_object);- appendPQExpBuffer(&query_buffer, " AND %s = '%s'",- schema_query->infoname, e_info_object);+ /* Input is qualified, so produce only qualified names */+ appendPQExpBufferStr(&query_buffer, "SELECT ");+ if (schema_query->use_distinct)+ appendPQExpBufferStr(&query_buffer, "DISTINCT ");+ appendPQExpBuffer(&query_buffer, "%s, n.nspname "+ "FROM %s, pg_catalog.pg_namespace n",+ schema_query->result,+ schema_query->catname);
if (schema_query->infonamespace && completion_info_schema)
- appendPQExpBuffer(&query_buffer,- " AND %s = ni.oid AND ni.nspname = '%s'",- schema_query->infonamespace,- e_info_schema);- else if (schema_query->infoviscondition)- appendPQExpBuffer(&query_buffer,- " AND %s",- schema_query->infoviscondition);- }+ appendPQExpBufferStr(&query_buffer,+ ", pg_catalog.pg_namespace ni");+ appendPQExpBuffer(&query_buffer, " WHERE %s = n.oid AND ",+ schema_query->namespace);+ if (schema_query->selcondition)+ appendPQExpBuffer(&query_buffer, "%s AND ",+ schema_query->selcondition);+ appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s' AND ",+ schema_query->result,+ object_length, e_objectname);+ appendPQExpBuffer(&query_buffer, "n.nspname = '%s'",+ e_schemaname);+ if (schema_query->infoname)+ {+ Assert(completion_info_object);+ appendPQExpBuffer(&query_buffer, " AND %s = '%s'",+ schema_query->infoname, e_info_object);+ if (schema_query->infonamespace && completion_info_schema)+ appendPQExpBuffer(&query_buffer,+ " AND %s = ni.oid AND ni.nspname = '%s'",+ schema_query->infonamespace,+ e_info_schema);+ else if (schema_query->infoviscondition)+ appendPQExpBuffer(&query_buffer,+ " AND %s",+ schema_query->infoviscondition);+ }
}
}
else
v16-0004-add-test-cases.patch (text/x-diff, 3.2K) downloaddiff --git a/src/bin/psql/t/010_tab_completion.pl b/src/bin/psql/t/010_tab_completion.plindex d3d1bd650e..8fda7c7f40 100644--- a/src/bin/psql/t/010_tab_completion.pl+++ b/src/bin/psql/t/010_tab_completion.pl@@ -40,10 +40,11 @@ $node->start;
# set up a few database objects
$node->safe_psql('postgres',
- "CREATE TABLE tab1 (f1 int, f2 text);\n"+ "CREATE TABLE tab1 (f1 int primary key, f2 text);\n"
. "CREATE TABLE mytab123 (f1 int, f2 text);\n"
. "CREATE TABLE mytab246 (f1 int, f2 text);\n"
- . "CREATE TYPE enum1 AS ENUM ('foo', 'bar', 'baz');\n");+ . "CREATE TABLE \"mixedName\" (f1 int, f2 text);\n"+ . "CREATE TYPE enum1 AS ENUM ('foo', 'bar', 'baz', 'BLACK');\n");
# Developers would not appreciate this test adding a bunch of junk to
# their ~/.psql_history, so be sure to redirect history into a temp file.
@@ -176,6 +177,38 @@ check_completion("2\t", qr/246 /,
clear_query();
+# check handling of quoted names+check_completion(+ "select * from \"my\t",+ qr/select \* from "my\a?tab/,+ "complete \"my<tab> to \"mytab when there are multiple choices");++check_completion(+ "\t\t",+ qr/"mytab123" +"mytab246"/,+ "offer multiple quoted table choices");++check_completion("2\t", qr/246" /,+ "finish completion of one of multiple quoted table choices");++clear_query();++# check handling of mixed-case names+check_completion(+ "select * from \"mi\t",+ qr/"mixedName"/,+ "complete a mixed-case name");++clear_query();++# check case folding+check_completion(+ "select * from TAB\t",+ qr/tab1 /,+ "automatically fold case");++clear_query();+
# check case-sensitive keyword replacement
# note: various versions of readline/libedit handle backspacing
# differently, so just check that the replacement comes out correctly
@@ -183,6 +216,48 @@ check_completion("\\DRD\t", qr/drds /, "complete \\DRD<tab> to \\drds");
clear_query();
+# check completion of a schema-qualified name+check_completion(+ "select * from pub\t",+ qr/public\./,+ "complete schema when relevant");++check_completion(+ "tab\t",+ qr/tab1 /,+ "complete schema-qualified name");++clear_query();++check_completion(+ "select * from PUBLIC.t\t",+ qr/public\.tab1 /,+ "automatically fold case in schema-qualified name");++clear_query();++# check interpretation of referenced names+check_completion(+ "alter table tab1 drop constraint \t",+ qr/tab1_pkey /,+ "complete index name for referenced table");++clear_query();++check_completion(+ "alter table TAB1 drop constraint \t",+ qr/tab1_pkey /,+ "complete index name for referenced table, with downcasing");++clear_query();++check_completion(+ "alter table public.\"tab1\" drop constraint \t",+ qr/tab1_pkey /,+ "complete index name for referenced table, with schema and quoting");++clear_query();+
# check filename completion
check_completion(
"\\lo_import tmp_check/some\t",
@@ -234,6 +309,14 @@ check_completion(
clear_line();
+# enum labels are case sensitive, so this should complete BLACK immediately+check_completion(+ "ALTER TYPE enum1 RENAME VALUE 'B\t",+ qr|BLACK|,+ "enum labels are case sensitive");++clear_line();+
# send psql an explicit \q to shut it down, else pty won't close properly
$timer->start(5);
$in .= "\\q\n";
v16-0005-use-LIKE-not-substring.patch (text/x-diff, 17.7K) downloaddiff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.cindex d13cfc3adf..15205a8e08 100644--- a/src/bin/psql/tab-complete.c+++ b/src/bin/psql/tab-complete.c@@ -896,10 +896,9 @@ static const SchemaQuery Query_for_trigger_of_table = {
* name; make a SchemaQuery instead.
*
* In these queries, there must be a restriction clause of the form
- * substring(OUTPUT,1,%d)='%s'- * where "OUTPUT" is the same string that the query returns. The %d and %s- * will be replaced by the string length of the text and the text itself,- * causing the results to be limited to those matching the already-typed text.+ * output LIKE '%s'+ * where "output" is the same string that the query returns. The %s+ * will be replaced by a LIKE pattern to match the already-typed text.
*
* There can be a second '%s', which will be replaced by a suitably-escaped
* version of the string provided in completion_info_object. If there is a
@@ -912,56 +911,56 @@ static const SchemaQuery Query_for_trigger_of_table = {
#define Query_for_list_of_template_databases \
"SELECT d.datname "\
" FROM pg_catalog.pg_database d "\
-" WHERE substring(d.datname,1,%d)='%s' "\+" WHERE d.datname LIKE '%s' "\
" AND (d.datistemplate OR pg_catalog.pg_has_role(d.datdba, 'USAGE'))"
#define Query_for_list_of_databases \
"SELECT datname FROM pg_catalog.pg_database "\
-" WHERE substring(datname,1,%d)='%s'"+" WHERE datname LIKE '%s'"
#define Query_for_list_of_tablespaces \
"SELECT spcname FROM pg_catalog.pg_tablespace "\
-" WHERE substring(spcname,1,%d)='%s'"+" WHERE spcname LIKE '%s'"
#define Query_for_list_of_encodings \
" SELECT DISTINCT pg_catalog.pg_encoding_to_char(conforencoding) "\
" FROM pg_catalog.pg_conversion "\
-" WHERE substring(pg_catalog.pg_encoding_to_char(conforencoding),1,%d)=UPPER('%s')"+" WHERE pg_catalog.pg_encoding_to_char(conforencoding) LIKE UPPER('%s')"
#define Query_for_list_of_languages \
"SELECT lanname "\
" FROM pg_catalog.pg_language "\
" WHERE lanname != 'internal' "\
-" AND substring(lanname,1,%d)='%s'"+" AND lanname LIKE '%s'"
#define Query_for_list_of_schemas \
"SELECT nspname FROM pg_catalog.pg_namespace "\
-" WHERE substring(nspname,1,%d)='%s'"+" WHERE nspname LIKE '%s'"
#define Query_for_list_of_alter_system_set_vars \
"SELECT name FROM "\
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
" WHERE context != 'internal' "\
" ) ss "\
-" WHERE substring(name,1,%d)='%s'"+" WHERE name LIKE '%s'"
#define Query_for_list_of_set_vars \
"SELECT name FROM "\
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
" WHERE context IN ('user', 'superuser') "\
" ) ss "\
-" WHERE substring(name,1,%d)='%s'"+" WHERE name LIKE '%s'"
#define Query_for_list_of_show_vars \
"SELECT name FROM "\
" (SELECT pg_catalog.lower(name) AS name FROM pg_catalog.pg_settings "\
" ) ss "\
-" WHERE substring(name,1,%d)='%s'"+" WHERE name LIKE '%s'"
#define Query_for_list_of_roles \
" SELECT rolname "\
" FROM pg_catalog.pg_roles "\
-" WHERE substring(rolname,1,%d)='%s'"+" WHERE rolname LIKE '%s'"
/* add these to Query_for_list_of_roles in GRANT contexts */
#define Keywords_for_list_of_grant_roles \
@@ -971,119 +970,119 @@ static const SchemaQuery Query_for_trigger_of_table = {
"SELECT conname "\
" FROM pg_catalog.pg_constraint c "\
" WHERE c.conrelid <> 0 "\
-" and substring(conname,1,%d)='%s'"+" and conname LIKE '%s'"
#define Query_for_list_of_ts_configurations \
"SELECT cfgname FROM pg_catalog.pg_ts_config "\
-" WHERE substring(cfgname,1,%d)='%s'"+" WHERE cfgname LIKE '%s'"
#define Query_for_list_of_ts_dictionaries \
"SELECT dictname FROM pg_catalog.pg_ts_dict "\
-" WHERE substring(dictname,1,%d)='%s'"+" WHERE dictname LIKE '%s'"
#define Query_for_list_of_ts_parsers \
"SELECT prsname FROM pg_catalog.pg_ts_parser "\
-" WHERE substring(prsname,1,%d)='%s'"+" WHERE prsname LIKE '%s'"
#define Query_for_list_of_ts_templates \
"SELECT tmplname FROM pg_catalog.pg_ts_template "\
-" WHERE substring(tmplname,1,%d)='%s'"+" WHERE tmplname LIKE '%s'"
#define Query_for_list_of_fdws \
" SELECT fdwname "\
" FROM pg_catalog.pg_foreign_data_wrapper "\
-" WHERE substring(fdwname,1,%d)='%s'"+" WHERE fdwname LIKE '%s'"
#define Query_for_list_of_servers \
" SELECT srvname "\
" FROM pg_catalog.pg_foreign_server "\
-" WHERE substring(srvname,1,%d)='%s'"+" WHERE srvname LIKE '%s'"
#define Query_for_list_of_user_mappings \
" SELECT usename "\
" FROM pg_catalog.pg_user_mappings "\
-" WHERE substring(usename,1,%d)='%s'"+" WHERE usename LIKE '%s'"
#define Query_for_list_of_access_methods \
" SELECT amname "\
" FROM pg_catalog.pg_am "\
-" WHERE substring(amname,1,%d)='%s'"+" WHERE amname LIKE '%s'"
#define Query_for_list_of_index_access_methods \
" SELECT amname "\
" FROM pg_catalog.pg_am "\
-" WHERE substring(amname,1,%d)='%s' AND "\+" WHERE amname LIKE '%s' AND "\
" amtype=" CppAsString2(AMTYPE_INDEX)
#define Query_for_list_of_table_access_methods \
" SELECT amname "\
" FROM pg_catalog.pg_am "\
-" WHERE substring(amname,1,%d)='%s' AND "\+" WHERE amname LIKE '%s' AND "\
" amtype=" CppAsString2(AMTYPE_TABLE)
#define Query_for_list_of_extensions \
" SELECT extname "\
" FROM pg_catalog.pg_extension "\
-" WHERE substring(extname,1,%d)='%s'"+" WHERE extname LIKE '%s'"
#define Query_for_list_of_available_extensions \
" SELECT name "\
" FROM pg_catalog.pg_available_extensions "\
-" WHERE substring(name,1,%d)='%s' AND installed_version IS NULL"+" WHERE name LIKE '%s' AND installed_version IS NULL"
/* the result of this query is not an identifier, so use VERBATIM */
#define Query_for_list_of_available_extension_versions \
" SELECT version "\
" FROM pg_catalog.pg_available_extension_versions "\
-" WHERE substring(version,1,%d)='%s'"\+" WHERE version LIKE '%s'"\
" AND name='%s'"
/* the result of this query is not an identifier, so use VERBATIM */
#define Query_for_list_of_available_extension_versions_with_TO \
" SELECT 'TO ' || version "\
" FROM pg_catalog.pg_available_extension_versions "\
-" WHERE substring('TO ' || version,1,%d)='%s'"\+" WHERE ('TO ' || version) LIKE '%s'"\
" AND name='%s'"
#define Query_for_list_of_prepared_statements \
" SELECT name "\
" FROM pg_catalog.pg_prepared_statements "\
-" WHERE substring(name,1,%d)='%s'"+" WHERE name LIKE '%s'"
#define Query_for_list_of_event_triggers \
" SELECT evtname "\
" FROM pg_catalog.pg_event_trigger "\
-" WHERE substring(evtname,1,%d)='%s'"+" WHERE evtname LIKE '%s'"
#define Query_for_list_of_tablesample_methods \
" SELECT proname "\
" FROM pg_catalog.pg_proc "\
" WHERE prorettype = 'pg_catalog.tsm_handler'::pg_catalog.regtype AND "\
" proargtypes[0] = 'pg_catalog.internal'::pg_catalog.regtype AND "\
-" substring(proname,1,%d)='%s'"+" proname LIKE '%s'"
#define Query_for_list_of_policies \
" SELECT polname "\
" FROM pg_catalog.pg_policy "\
-" WHERE substring(polname,1,%d)='%s'"+" WHERE polname LIKE '%s'"
#define Query_for_values_of_enum_GUC \
" SELECT val FROM ( "\
" SELECT name, pg_catalog.unnest(enumvals) AS val "\
" FROM pg_catalog.pg_settings "\
" ) ss "\
-" WHERE substring(val,1,%d)='%s'"\+" WHERE val LIKE '%s'"\
" and pg_catalog.lower(name)=pg_catalog.lower('%s')"
#define Query_for_list_of_channels \
" SELECT channel "\
" FROM pg_catalog.pg_listening_channels() AS channel "\
-" WHERE substring(channel,1,%d)='%s'"+" WHERE channel LIKE '%s'"
#define Query_for_list_of_cursors \
" SELECT name "\
" FROM pg_catalog.pg_cursors "\
-" WHERE substring(name,1,%d)='%s'"+" WHERE name LIKE '%s'"
/*
* These object types were introduced later than our support cutoff of
@@ -1095,7 +1094,7 @@ static const VersionedQuery Query_for_list_of_publications[] = {
{100000,
" SELECT pubname "
" FROM pg_catalog.pg_publication "
- " WHERE substring(pubname,1,%d)='%s'"+ " WHERE pubname LIKE '%s'"
},
{0, NULL}
};
@@ -1104,7 +1103,7 @@ static const VersionedQuery Query_for_list_of_subscriptions[] = {
{100000,
" SELECT s.subname "
" FROM pg_catalog.pg_subscription s, pg_catalog.pg_database d "
- " WHERE substring(s.subname,1,%d)='%s' "+ " WHERE s.subname LIKE '%s' "
" AND d.datname = pg_catalog.current_database() "
" AND s.subdbid = d.oid"
},
@@ -1150,7 +1149,7 @@ static const pgsql_thing_t words_after_create[] = {
* to be used only by pg_dump.
*/
{"CONFIGURATION", Query_for_list_of_ts_configurations, NULL, NULL, NULL, THING_NO_SHOW},
- {"CONVERSION", "SELECT conname FROM pg_catalog.pg_conversion WHERE substring(conname,1,%d)='%s'"},+ {"CONVERSION", "SELECT conname FROM pg_catalog.pg_conversion WHERE conname LIKE '%s'"},
{"DATABASE", Query_for_list_of_databases},
{"DEFAULT PRIVILEGES", NULL, NULL, NULL, NULL, THING_NO_CREATE | THING_NO_DROP},
{"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, NULL, NULL, THING_NO_SHOW},
@@ -1175,7 +1174,7 @@ static const pgsql_thing_t words_after_create[] = {
{"PUBLICATION", NULL, Query_for_list_of_publications},
{"ROLE", Query_for_list_of_roles},
{"ROUTINE", NULL, NULL, &Query_for_list_of_routines, NULL, THING_NO_CREATE},
- {"RULE", "SELECT rulename FROM pg_catalog.pg_rules WHERE substring(rulename,1,%d)='%s'"},+ {"RULE", "SELECT rulename FROM pg_catalog.pg_rules WHERE rulename LIKE '%s'"},
{"SCHEMA", Query_for_list_of_schemas},
{"SEQUENCE", NULL, NULL, &Query_for_list_of_sequences},
{"SERVER", Query_for_list_of_servers},
@@ -1191,7 +1190,7 @@ static const pgsql_thing_t words_after_create[] = {
* TABLE ... */
{"TEXT SEARCH", NULL, NULL, NULL},
{"TRANSFORM", NULL, NULL, NULL, NULL, THING_NO_ALTER},
- {"TRIGGER", "SELECT tgname FROM pg_catalog.pg_trigger WHERE substring(tgname,1,%d)='%s' AND NOT tgisinternal"},+ {"TRIGGER", "SELECT tgname FROM pg_catalog.pg_trigger WHERE tgname LIKE '%s' AND NOT tgisinternal"},
{"TYPE", NULL, NULL, &Query_for_list_of_datatypes},
{"UNIQUE", NULL, NULL, NULL, NULL, THING_NO_DROP | THING_NO_ALTER}, /* for CREATE UNIQUE
* INDEX ... */
@@ -1273,6 +1272,7 @@ static char *complete_from_files(const char *text, int state);
static char *pg_strdup_keyword_case(const char *s, const char *ref);
static char *escape_string(const char *text);
+static char *make_like_pattern(const char *word);
static void parse_identifier(const char *ident,
char **schemaname, char **objectname,
bool *schemaquoted, bool *objectquoted);
@@ -1775,9 +1775,7 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("(", "ALL TABLES IN SCHEMA", "TABLE");
else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "ALL", "TABLES", "IN", "SCHEMA"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
- " AND nspname != 'pg_catalog' "- " AND nspname not like 'pg\\_toast%%' "- " AND nspname not like 'pg\\_temp%%' ",+ " AND nspname NOT LIKE E'pg\\\\_%'",
"CURRENT_SCHEMA");
/* ALTER PUBLICATION <name> SET ( */
else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("SET", "("))
@@ -2911,9 +2909,7 @@ psql_completion(const char *text, int start, int end)
*/
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA"))
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
- " AND nspname != 'pg_catalog' "- " AND nspname not like 'pg\\_toast%%' "- " AND nspname not like 'pg\\_temp%%' ",+ " AND nspname NOT LIKE E'pg\\\\_%'",
"CURRENT_SCHEMA");
else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES", "IN", "SCHEMA", MatchAny) && (!ends_with(prev_wd, ',')))
COMPLETE_WITH("WITH (");
@@ -4119,10 +4115,13 @@ psql_completion(const char *text, int start, int end)
"US", "European", "NonEuropean",
"DEFAULT");
else if (TailMatches("search_path", "TO|="))
+ {+ /* Here, we want to allow pg_catalog, so use narrower exclusion */
COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
- " AND nspname not like 'pg\\_toast%%' "- " AND nspname not like 'pg\\_temp%%' ",+ " AND nspname NOT LIKE E'pg\\\\_toast%%'"+ " AND nspname NOT LIKE E'pg\\\\_temp%%'",
"DEFAULT");
+ }
else
{
/* generic, type based, GUC support */
@@ -4689,10 +4688,9 @@ complete_from_versioned_schema_query(const char *text, int state)
* The query can be one of two kinds:
*
* 1. A simple query, which must contain a restriction clause of the form
- * substring(OUTPUT,1,%d)='%s'- * where "OUTPUT" is the same string that the query returns. The %d and %s- * will be replaced by the string length of the text and the text itself,- * causing the results to be limited to those matching the already-typed text.+ * output LIKE '%s'+ * where "output" is the same string that the query returns. The %s+ * will be replaced by a LIKE pattern to match the already-typed text.
* There can be a second '%s', which will be replaced by a suitably-escaped
* version of the string provided in completion_info_object. If there is a
* third '%s', it will be replaced by a suitably-escaped version of the string
@@ -4747,9 +4745,8 @@ _complete_from_query(const char *simple_query,
PQExpBufferData query_buffer;
char *schemaname;
char *objectname;
- int object_length = 0;+ char *e_object_like;
char *e_schemaname;
- char *e_objectname;
char *e_info_object;
char *e_info_schema;
@@ -4777,22 +4774,16 @@ _complete_from_query(const char *simple_query,
non_empty_object = (*objectname != '\0');
/*
- * Count length as number of characters (not bytes), for passing to- * substring+ * Convert objectname to a LIKE prefix pattern (e.g. 'foo%'), and set+ * up suitably-escaped copies of all the strings we need.
*/
- for (const char *p = objectname;- *p;- p += PQmblenBounded(p, pset.encoding))- object_length++;+ e_object_like = make_like_pattern(objectname);- /* Set up suitably-escaped copies of textual inputs */
if (schemaname)
e_schemaname = escape_string(schemaname);
else
e_schemaname = NULL;
- e_objectname = escape_string(objectname);-
if (completion_info_object)
e_info_object = escape_string(completion_info_object);
else
@@ -4831,9 +4822,9 @@ _complete_from_query(const char *simple_query,
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
schema_query->selcondition);
- appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s'",+ appendPQExpBuffer(&query_buffer, "(%s) LIKE '%s'",
schema_query->result,
- object_length, e_objectname);+ e_object_like);
if (schema_query->viscondition)
appendPQExpBuffer(&query_buffer, " AND %s",
schema_query->viscondition);
@@ -4878,8 +4869,8 @@ _complete_from_query(const char *simple_query,
appendPQExpBuffer(&query_buffer, "\nUNION ALL\n"
"SELECT NULL::pg_catalog.text, n.nspname "
"FROM pg_catalog.pg_namespace n "
- "WHERE substring(n.nspname,1,%d)='%s'",- object_length, e_objectname);+ "WHERE n.nspname LIKE '%s'",+ e_object_like);
/*
* Likewise, suppress system schemas unless the
@@ -4887,7 +4878,7 @@ _complete_from_query(const char *simple_query,
*/
if (strncmp(objectname, "pg_", 3) != 0)
appendPQExpBufferStr(&query_buffer,
- " AND n.nspname NOT LIKE 'pg\\_%'");+ " AND n.nspname NOT LIKE E'pg\\\\_%'");
/*
* Since we're matching these schema names to the object
@@ -4915,9 +4906,9 @@ _complete_from_query(const char *simple_query,
if (schema_query->selcondition)
appendPQExpBuffer(&query_buffer, "%s AND ",
schema_query->selcondition);
- appendPQExpBuffer(&query_buffer, "substring(%s,1,%d)='%s' AND ",+ appendPQExpBuffer(&query_buffer, "(%s) LIKE '%s' AND ",
schema_query->result,
- object_length, e_objectname);+ e_object_like);
appendPQExpBuffer(&query_buffer, "n.nspname = '%s'",
e_schemaname);
if (schema_query->infoname)
@@ -4942,7 +4933,7 @@ _complete_from_query(const char *simple_query,
Assert(simple_query);
/* simple_query is an sprintf-style format string */
appendPQExpBuffer(&query_buffer, simple_query,
- object_length, e_objectname,+ e_object_like,
e_info_object, e_info_schema);
}
@@ -4955,9 +4946,9 @@ _complete_from_query(const char *simple_query,
/* Clean up */
termPQExpBuffer(&query_buffer);
+ free(e_object_like);
if (e_schemaname)
free(e_schemaname);
- free(e_objectname);
if (e_info_object)
free(e_info_object);
if (e_info_schema)
@@ -5414,6 +5405,48 @@ escape_string(const char *text)
}
+/*+ * make_like_pattern - Convert argument to a LIKE prefix pattern.+ *+ * We escape _ and % in the given text by backslashing, append a % to+ * represent "any subsequent characters", and then pass the string through+ * escape_string() so it's ready to insert in a query. The result needs+ * to be freed.+ */+static char *+make_like_pattern(const char *word)+{+ char *result;+ char *buffer = pg_malloc(strlen(word) * 2 + 2);+ char *bptr = buffer;++ while (*word)+ {+ if (*word == '_' || *word == '%')+ *bptr++ = '\\';+ if (IS_HIGHBIT_SET(*word))+ {+ /*+ * Transfer multibyte characters without further processing, to+ * avoid getting confused in unsafe client encodings.+ */+ int chlen = PQmblenBounded(word, pset.encoding);++ while (chlen-- > 0)+ *bptr++ = *word++;+ }+ else+ *bptr++ = *word++;+ }+ *bptr++ = '%';+ *bptr = '\0';++ result = escape_string(buffer);+ free(buffer);+ return result;+}++
/*
* parse_identifier - Parse a possibly-schema-qualified SQL identifier.
*
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: tgl@sss.pgh.pa.us, tanghy.fnst@fujitsu.com, peter.eisentraut@enterprisedb.com, japinli@hotmail.com, horikyota.ntt@gmail.com, laurenz.albe@cybertec.at, smithpb2250@gmail.com, david.zhang@highgo.ca, pgsql-hackers@lists.postgresql.org
Subject: Re: Support tab completion for upper character inputs in psql
In-Reply-To: <2818574.1643408192@sss.pgh.pa.us>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox