From d9015e8d52fc11305e42a704ee9eb3459a62a882 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Wed, 14 Sep 2016 12:48:16 +0900
Subject: [PATCH 2/6] Make keywords' case follow to input

Currently some keywords suggested along with database objects are
always in upper case. This patch changes the behavior so that the case
of the additional keywords follow the setting of COMP_KEYWORD_CASE.

COMPLETE_WITH_ATTR needs completion_charp to be appendable, so this
patch changes it to a PQExpBuffer and adjust COMPLET_WITH_* macros to
that change.

This leaves keywords contained in Query_for_list_of_grant_roles and
Query_for_enum, but it is another problem.
---
 src/bin/psql/tab-complete-macros.h |  62 ++++++--
 src/bin/psql/tab-complete.c        | 300 ++++++++++++++++++++-----------------
 2 files changed, 212 insertions(+), 150 deletions(-)

diff --git a/src/bin/psql/tab-complete-macros.h b/src/bin/psql/tab-complete-macros.h
index 8ee52b8..3e91bbe 100644
--- a/src/bin/psql/tab-complete-macros.h
+++ b/src/bin/psql/tab-complete-macros.h
@@ -231,16 +231,46 @@
  * 5) The list of attributes of the given table (possibly schema-qualified).
  * 6) The list of arguments to the given function (possibly schema-qualified).
  */
-#define COMPLETE_WITH_QUERY(query) \
+#define APPEND_COMP_CHARP(charp) \
+	appendPQExpBufferStr(completion_charp, charp);
+
+#define SET_COMP_CHARP(charp) \
+	resetPQExpBuffer(completion_charp);	\
+	APPEND_COMP_CHARP(charp);
+
+#define COMPLETION_CHARP (completion_charp->data)
+
+#define COMPLETE_WITH_QUERY(query)				\
+do { \
+	SET_COMP_CHARP(query);	\
+	return completion_matches(text, complete_from_query);	\
+} while (0)
+
+/*
+ * COMPLETE_WITH_QUERY with additional keywords. Keywords are complete
+ * case-sensitively
+ */
+#define COMPLETE_WITH_QUERY_KW(query, addon)				\
 do { \
-	completion_charp = query;	\
+	SET_COMP_CHARP(query);	\
+	APPEND_COMP_CHARP(addon); \
 	return completion_matches(text, complete_from_query);	\
 } while (0)
 
-#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
+#define COMPLETE_WITH_SCHEMA_QUERY(query) \
 do { \
 	completion_squery = &(query); \
-	completion_charp = addon; \
+	return completion_matches(text, complete_from_schema_query); \
+} while (0)
+
+/*
+ * COMPLETE_WITH_SCHEMA_QUERY with additional keywords. Keywords are complete
+ * case-sensitively
+ */
+#define COMPLETE_WITH_SCHEMA_QUERY_KW(query, addon)	\
+do { \
+	completion_squery = &(query); \
+	SET_COMP_CHARP(addon); \
 	return completion_matches(text, complete_from_schema_query); \
 } while (0)
 
@@ -260,7 +290,7 @@ do { \
 
 #define COMPLETE_WITH_CONST(string) \
 do { \
-	completion_charp = string;	\
+	SET_COMP_CHARP(string);	\
 	completion_case_sensitive = false; \
 	return completion_matches(text, complete_from_const);	\
 } while (0)
@@ -278,12 +308,14 @@ do { \
 								false, false, pset.encoding); \
 	if (_completion_table == NULL) \
 	{ \
-		completion_charp = Query_for_list_of_attributes addon; \
+		SET_COMP_CHARP(Query_for_list_of_attributes); \
+		APPEND_COMP_CHARP(addon);					  \
 		completion_info_charp = relation; \
 	} \
 	else \
 	{ \
-		completion_charp = Query_for_list_of_attributes_with_schema addon; \
+		SET_COMP_CHARP(Query_for_list_of_attributes_with_schema); \
+		APPEND_COMP_CHARP(addon); \
 		completion_info_charp = _completion_table; \
 		completion_info_charp2 = _completion_schema; \
 	} \
@@ -303,12 +335,12 @@ do { \
 								   false, false, pset.encoding); \
 	if (_completion_function == NULL) \
 	{ \
-		completion_charp = Query_for_list_of_arguments; \
+		SET_COMP_CHARP(Query_for_list_of_arguments); \
 		completion_info_charp = function; \
 	} \
 	else \
 	{ \
-		completion_charp = Query_for_list_of_arguments_with_schema;	\
+		SET_COMP_CHARP(Query_for_list_of_arguments_with_schema); \
 		completion_info_charp = _completion_function; \
 		completion_info_charp2 = _completion_schema; \
 	} \
@@ -400,6 +432,16 @@ do { \
 	COMPLETE_WITH_LIST_CS(list); \
 } while (0)
 
-
+#define ADDLIST1(s1) additional_kw_query(text, 1, s1)
+#define ADDLIST2(s1, s2) additional_kw_query(text, 2, s1, s2)
+#define ADDLIST3(s1, s2, s3) additional_kw_query(text, 3, s1, s2, s3)
+#define ADDLIST4(s1, s2, s3, s4) \
+	additional_kw_query(text, 4, s1, s2, s3, s4)
+#define ADDLIST13(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \
+	additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7,		\
+						s8, s9, s10, s11, s12, s13)
+#define ADDLIST15(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \
+	additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7,		\
+						s8, s9, s10, s11, s12, s13, s14, s15)
 
 #endif   /* TAB_COMPLETE_MACROS_H */
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 679e58f..85bb363 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -126,7 +126,7 @@ static int	completion_max_records;
  * Communication variables set by COMPLETE_WITH_FOO macros and then used by
  * the completion callback functions.  Ugly but there is no better way.
  */
-static const char *completion_charp;	/* to pass a string */
+static PQExpBuffer completion_charp = NULL;		/* 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 */
@@ -776,6 +776,7 @@ static char **complete_from_variables(const char *text,
 static char *complete_from_files(const char *text, int state);
 
 static char *pg_strdup_keyword_case(const char *s, const char *ref);
+static char *additional_kw_query( const char *ref, int n, ...);
 static char *escape_string(const char *text);
 static PGresult *exec_query(const char *query);
 
@@ -947,7 +948,8 @@ psql_completion(const char *text, int start, int end)
 #endif
 
 	/* Clear a few things. */
-	completion_charp = NULL;
+	if (completion_charp == NULL)
+		completion_charp = createPQExpBuffer();
 	completion_charpp = NULL;
 	completion_info_charp = NULL;
 	completion_info_charp2 = NULL;
@@ -1056,8 +1058,8 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* ALTER TABLE */
 	if (Matches2("ALTER", "TABLE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   "UNION SELECT 'ALL IN TABLESPACE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables,
+									  ADDLIST1("ALL IN TABLESPACE"));
 
 	/* ALTER something */
 	if (Matches1("ALTER"))
@@ -1173,8 +1175,8 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* ALTER INDEX */
 	if (Matches2("ALTER", "INDEX"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   "UNION SELECT 'ALL IN TABLESPACE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes,
+									  ADDLIST1("ALL IN TABLESPACE"));
 	/* ALTER INDEX <name> */
 	if (Matches3("ALTER", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET");
@@ -1202,8 +1204,8 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* ALTER MATERIALIZED VIEW */
 	if (Matches3("ALTER", "MATERIALIZED", "VIEW"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
-								   "UNION SELECT 'ALL IN TABLESPACE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews,
+									  ADDLIST1("ALL IN TABLESPACE"));
 
 	/* ALTER USER,ROLE <name> */
 	if (Matches3("ALTER", "USER|ROLE", MatchAny) &&
@@ -1358,7 +1360,7 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
 	 */
 	if (Matches4("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> */
 	if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
@@ -1404,10 +1406,10 @@ psql_completion_internal(const char *text, char **previous_words,
 	}
 	/* ALTER TABLE xxx INHERIT */
 	if (Matches4("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 INHERIT */
 	if (Matches5("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 */
 	if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE"))
 		COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER");
@@ -1424,11 +1426,11 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* ALTER TABLE xxx ALTER */
 	if (Matches4("ALTER", "TABLE", MatchAny, "ALTER"))
-		COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'");
+		COMPLETE_WITH_ATTR(prev2_wd, ADDLIST2("COLUMN", "CONSTRAINT"));
 
 	/* ALTER TABLE xxx RENAME */
 	if (Matches4("ALTER", "TABLE", MatchAny, "RENAME"))
-		COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'");
+		COMPLETE_WITH_ATTR(prev2_wd, ADDLIST3("COLUMN", "CONSTRAINT", "TO"));
 	if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
 
@@ -1628,9 +1630,10 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
 /* CLUSTER */
 	if (Matches1("CLUSTER"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST1("VERBOSE"));
 	if (Matches2("CLUSTER", "VERBOSE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm);
 	/* If we have CLUSTER <sth>, then add "USING" */
 	if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON")))
 		COMPLETE_WITH_CONST("USING");
@@ -1677,7 +1680,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint);
 	}
 	if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
 	if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
 	if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
@@ -1692,11 +1695,11 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * backslash command).
 	 */
 	if (Matches1("COPY|\\copy"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   " UNION ALL SELECT '('");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables,
+									  ADDLIST1("("));
 	/* If we have COPY BINARY, complete with list of tables */
 	if (Matches2("COPY", "BINARY"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
 	/* If we have COPY (, complete it with legal commands */
 	if (Matches2("COPY|\\copy", "("))
 		COMPLETE_WITH_LIST7("SELECT", "TABLE", "VALUES", "INSERT", "UPDATE", "DELETE", "WITH");
@@ -1708,7 +1711,7 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") ||
 			 Matches4("COPY", "BINARY", MatchAny, "FROM|TO"))
 	{
-		completion_charp = "";
+		SET_COMP_CHARP("");
 		return completion_matches(text, complete_from_files);
 	}
 
@@ -1777,21 +1780,20 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * existing indexes
 	 */
 	if (TailMatches2("CREATE|UNIQUE", "INDEX"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'ON'"
-								   " UNION SELECT 'CONCURRENTLY'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes,
+									  ADDLIST2("ON", "CONCURRENTLY"));
 	/* Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of tables  */
 	if (TailMatches3("INDEX|CONCURRENTLY", MatchAny, "ON") ||
 			 TailMatches2("INDEX|CONCURRENTLY", "ON"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm);
 
 	/*
 	 * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing
 	 * indexes
 	 */
 	if (TailMatches3("CREATE|UNIQUE", "INDEX", "CONCURRENTLY"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'ON'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes,
+									  ADDLIST1("ON"));
 	/* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
 	if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny) ||
 			 TailMatches4("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny))
@@ -1826,7 +1828,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_CONST("ON");
 	/* Complete "CREATE POLICY <name> ON <table>" */
 	if (Matches4("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> FOR|TO|USING|WITH CHECK" */
 	if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST4("FOR", "TO", "USING (", "WITH CHECK (");
@@ -1864,7 +1866,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_CONST("TO");
 	/* Complete "AS ON <sth> TO" with a table name */
 	if (TailMatches4("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 */
 	if (TailMatches3("CREATE", "SEQUENCE", MatchAny) ||
@@ -1920,10 +1922,10 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * tables
 	 */
 	if (TailMatches6("CREATE", "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 TRIGGER ... INSTEAD OF event ON with a list of views */
 	if (TailMatches7("CREATE", "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);
 	/* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */
 	if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE"))
 		COMPLETE_WITH_CONST("PROCEDURE");
@@ -2009,7 +2011,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_CONST("FROM");
 	/* Complete DELETE FROM with a list of tables */
 	if (TailMatches2("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> */
 	if (TailMatches3("DELETE", "FROM", MatchAny))
 		COMPLETE_WITH_LIST2("USING", "WHERE");
@@ -2047,10 +2049,10 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* DROP INDEX */
 	if (Matches2("DROP", "INDEX"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'CONCURRENTLY'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_indexes,
+									  ADDLIST1("CONCURRENTLY"));
 	if (Matches3("DROP", "INDEX", "CONCURRENTLY"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
 	if (Matches3("DROP", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 	if (Matches4("DROP", "INDEX", "CONCURRENTLY", MatchAny))
@@ -2060,7 +2062,7 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches2("DROP", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
 	if (Matches3("DROP", "MATERIALIZED", "VIEW"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
 
 	/* DROP OWNED BY */
 	if (Matches2("DROP", "OWNED"))
@@ -2166,7 +2168,7 @@ psql_completion_internal(const char *text, char **previous_words,
 /* FOREIGN TABLE */
 	if (TailMatches2("FOREIGN", "TABLE") &&
 			 !TailMatches3("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 */
 	if (TailMatches2("FOREIGN", "SERVER"))
@@ -2175,20 +2177,10 @@ psql_completion_internal(const char *text, char **previous_words,
 /* GRANT && REVOKE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	/* Complete GRANT/REVOKE with a list of roles and privileges */
 	if (TailMatches1("GRANT|REVOKE"))
-		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_KW(Query_for_list_of_roles,
+			ADDLIST13("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE",
+					  "REFERENCES", "TRIGGER", "CREATE", "CONNECT", "TEMPORARY",
+					  "EXECUTE", "USAGE", "ALL"));
 
 	/*
 	 * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
@@ -2216,22 +2208,22 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * privilege.
 	 */
 	if (TailMatches3("GRANT|REVOKE", MatchAny, "ON"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf,
-								   " UNION SELECT 'ALL FUNCTIONS 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 'SCHEMA'"
-								   " UNION SELECT 'SEQUENCE'"
-								   " UNION SELECT 'TABLE'"
-								   " UNION SELECT 'TABLESPACE'"
-								   " UNION SELECT 'TYPE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tsvmf,
+			   ADDLIST15("ALL FUNCTIONS IN SCHEMA",
+						 "ALL SEQUENCES IN SCHEMA",
+						 "ALL TABLES IN SCHEMA",
+						 "DATABASE",
+						 "DOMAIN",
+						 "FOREIGN DATA WRAPPER",
+						 "FOREIGN SERVER",
+						 "FUNCTION",
+						 "LANGUAGE",
+						 "LARGE OBJECT",
+						 "SCHEMA",
+						 "SEQUENCE",
+						 "TABLE",
+						 "TABLESPACE",
+						 "TYPE"));
 
 	if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
 		COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA",
@@ -2251,21 +2243,21 @@ psql_completion_internal(const char *text, char **previous_words,
 		if (TailMatches1("DATABASE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
 		if (TailMatches1("DOMAIN"))
-			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
 		if (TailMatches1("FUNCTION"))
-			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions);
 		if (TailMatches1("LANGUAGE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_languages);
 		if (TailMatches1("SCHEMA"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 		if (TailMatches1("SEQUENCE"))
-			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
 		if (TailMatches1("TABLE"))
-			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf);
 		if (TailMatches1("TABLESPACE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
 		if (TailMatches1("TYPE"))
-			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
 		if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
 			COMPLETE_WITH_CONST("TO");
 		else
@@ -2329,7 +2321,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_CONST("INTO");
 	/* Complete INSERT INTO with table names */
 	if (TailMatches2("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 */
 	if (TailMatches4("INSERT", "INTO", MatchAny, "("))
 		COMPLETE_WITH_ATTR(prev2_wd, "");
@@ -2356,10 +2348,10 @@ psql_completion_internal(const char *text, char **previous_words,
 /* LOCK */
 	/* Complete LOCK [TABLE] with a list of tables */
 	if (Matches1("LOCK"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   " UNION SELECT 'TABLE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tables,
+									  ADDLIST1("TABLE"));
 	if (Matches2("LOCK", "TABLE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
 
 	/* For the following, handle the case of a single table only for now */
 
@@ -2422,10 +2414,10 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches2("REFRESH", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
 	if (Matches3("REFRESH", "MATERIALIZED", "VIEW"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
-								   " UNION SELECT 'CONCURRENTLY'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_matviews,
+									  ADDLIST1("CONCURRENTLY"));
 	if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
 	if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("WITH");
 	if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
@@ -2443,9 +2435,9 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches1("REINDEX"))
 		COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
 	if (Matches2("REINDEX", "TABLE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm);
 	if (Matches2("REINDEX", "INDEX"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
 	if (Matches2("REINDEX", "SCHEMA"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	if (Matches2("REINDEX", "SYSTEM|DATABASE"))
@@ -2515,7 +2507,8 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_LIST2("ONLY", "WRITE");
 	/* SET CONSTRAINTS */
 	if (Matches2("SET", "CONSTRAINTS"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_constraints_with_schema,
+									  ADDLIST1("ALL"));
 	/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
 	if (Matches3("SET", "CONSTRAINTS", MatchAny))
 		COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE");
@@ -2527,7 +2520,8 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
 	/* Complete SET SESSION AUTHORIZATION with username */
 	if (Matches3("SET", "SESSION", "AUTHORIZATION"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
+		COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles,
+							   ADDLIST1("DEFAULT"));
 	/* Complete RESET SESSION with AUTHORIZATION */
 	if (Matches2("RESET", "SESSION"))
 		COMPLETE_WITH_CONST("AUTHORIZATION");
@@ -2553,10 +2547,10 @@ psql_completion_internal(const char *text, char **previous_words,
 			COMPLETE_WITH_LIST(my_list);
 		}
 		if (TailMatches2("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_KW(Query_for_list_of_schemas
+								   " AND nspname not like 'pg\\_toast%%' "
+								   " AND nspname not like 'pg\\_temp%%' ",
+								   ADDLIST1("DEFAULT"));
 		else
 		{
 			/* generic, type based, GUC support, guctype is malloc'ed */
@@ -2591,7 +2585,7 @@ psql_completion_internal(const char *text, char **previous_words,
 
 /* TABLE, but not TABLE embedded in other commands */
 	if (Matches1("TABLE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
 
 /* TABLESAMPLE */
 	if (TailMatches1("TABLESAMPLE"))
@@ -2601,7 +2595,7 @@ psql_completion_internal(const char *text, char **previous_words,
 
 /* TRUNCATE */
 	if (Matches1("TRUNCATE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
 
 /* UNLISTEN */
 	if (Matches1("UNLISTEN"))
@@ -2610,7 +2604,7 @@ psql_completion_internal(const char *text, char **previous_words,
 /* UPDATE --- can be inside EXPLAIN, RULE, etc */
 	/* If prev. word is UPDATE suggest a list of tables */
 	if (TailMatches1("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" */
 	if (TailMatches2("UPDATE", MatchAny))
 		COMPLETE_WITH_CONST("SET");
@@ -2625,10 +2619,8 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
 		COMPLETE_WITH_CONST("FOR");
 	if (Matches4("CREATE", "USER", "MAPPING", "FOR"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_roles
-							" UNION SELECT 'CURRENT_USER'"
-							" UNION SELECT 'PUBLIC'"
-							" UNION SELECT 'USER'");
+		COMPLETE_WITH_QUERY_KW(Query_for_list_of_roles,
+			ADDLIST3("CURRENT_USER", "PUBLIC", "USER"));
 	if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
 	if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
@@ -2641,29 +2633,26 @@ psql_completion_internal(const char *text, char **previous_words,
  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
  */
 	if (Matches1("VACUUM"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'FULL'"
-								   " UNION SELECT 'FREEZE'"
-								   " UNION SELECT 'ANALYZE'"
-								   " UNION SELECT 'VERBOSE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST4("FULL", "FREEZE",
+											   "ANALYZE", "VERBOSE"));
 	if (Matches2("VACUUM", "FULL|FREEZE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'ANALYZE'"
-								   " UNION SELECT 'VERBOSE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST2("ANALYZE", "VERBOSE"));
 	if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'VERBOSE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST1("VERBOSE"));
 	if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'ANALYZE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST1("ANALYZE"));
 	if (Matches2("VACUUM", "VERBOSE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'ANALYZE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST1("ANALYZE"));
 	if (Matches2("VACUUM", "ANALYZE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'VERBOSE'");
+		COMPLETE_WITH_SCHEMA_QUERY_KW(Query_for_list_of_tm,
+									  ADDLIST1("VERBOSE"));
 	if (HeadMatches1("VACUUM"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm);
 
 /* WITH [RECURSIVE] */
 
@@ -2677,7 +2666,7 @@ psql_completion_internal(const char *text, char **previous_words,
 /* ANALYZE */
 	/* Complete with list of tables */
 	if (Matches1("ANALYZE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf);
 
 /* WHERE */
 	/* Simple case of the word before the where being the table name */
@@ -2687,11 +2676,11 @@ psql_completion_internal(const char *text, char **previous_words,
 /* ... FROM ... */
 /* TODO: also include SRF ? */
 	if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf);
 
 /* ... JOIN ... */
 	if (TailMatches1("JOIN"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf);
 
 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
@@ -2708,13 +2697,13 @@ psql_completion_internal(const char *text, char **previous_words,
 			COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 	}
 	if (TailMatchesCS1("\\da*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates);
 	if (TailMatchesCS1("\\dA*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
 	if (TailMatchesCS1("\\db*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
 	if (TailMatchesCS1("\\dD*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
 	if (TailMatchesCS1("\\des*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_servers);
 	if (TailMatchesCS1("\\deu*"))
@@ -2722,7 +2711,7 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (TailMatchesCS1("\\dew*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
 	if (TailMatchesCS1("\\df*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions);
 
 	if (TailMatchesCS1("\\dFd*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
@@ -2735,40 +2724,40 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
 
 	if (TailMatchesCS1("\\di*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
 	if (TailMatchesCS1("\\dL*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_languages);
 	if (TailMatchesCS1("\\dn*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
 	if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf);
 	if (TailMatchesCS1("\\ds*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
 	if (TailMatchesCS1("\\dt*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
 	if (TailMatchesCS1("\\dT*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
 	if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 	if (TailMatchesCS1("\\dv*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
 	if (TailMatchesCS1("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
 	if (TailMatchesCS1("\\dm*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
 	if (TailMatchesCS1("\\dE*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
 	if (TailMatchesCS1("\\dy*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
 
 	/* must be at end of \d alternatives: */
 	if (TailMatchesCS1("\\d*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
 
 	if (TailMatchesCS1("\\ef"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions);
 	if (TailMatchesCS1("\\ev"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
 
 	if (TailMatchesCS1("\\encoding"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
@@ -2837,14 +2826,14 @@ psql_completion_internal(const char *text, char **previous_words,
 			COMPLETE_WITH_LIST_CS3("default", "verbose", "terse");
 	}
 	if (TailMatchesCS1("\\sf*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions);
 	if (TailMatchesCS1("\\sv*"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
 	if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|"
 							"\\ir|\\include_relative|\\o|\\out|"
 							"\\s|\\w|\\write|\\lo_import"))
 	{
-		completion_charp = "\\";
+		SET_COMP_CHARP("\\");
 		return completion_matches(text, complete_from_files);
 	}
 
@@ -2864,8 +2853,7 @@ psql_completion_internal(const char *text, char **previous_words,
 				if (words_after_create[i].query)
 					COMPLETE_WITH_QUERY(words_after_create[i].query);
 				if (words_after_create[i].squery)
-					COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery,
-											   NULL);
+					COMPLETE_WITH_SCHEMA_QUERY(*words_after_create[i].squery);
 				break;
 			}
 		}
@@ -3112,13 +3100,13 @@ _complete_from_query(int is_schema_query, const char *text, int state)
 							  char_length, e_text);
 
 			/* If an addon query was provided, use it */
-			if (completion_charp)
-				appendPQExpBuffer(&query_buffer, "\n%s", completion_charp);
+			if (COMPLETION_CHARP[0])
+				appendPQExpBuffer(&query_buffer, "\n%s", COMPLETION_CHARP);
 		}
 		else
 		{
 			/* completion_charp is an sprintf-style format string */
-			appendPQExpBuffer(&query_buffer, completion_charp,
+			appendPQExpBuffer(&query_buffer, COMPLETION_CHARP,
 							  char_length, e_text,
 							  e_info_charp, e_info_charp,
 							  e_info_charp2, e_info_charp2);
@@ -3233,18 +3221,17 @@ complete_from_list(const char *text, int state)
 static char *
 complete_from_const(const char *text, int state)
 {
-	Assert(completion_charp != NULL);
 	if (state == 0)
 	{
 		if (completion_case_sensitive)
-			return pg_strdup(completion_charp);
+			return pg_strdup(COMPLETION_CHARP);
 		else
 
 			/*
 			 * If case insensitive matching was requested initially, adjust
 			 * the case according to setting.
 			 */
-			return pg_strdup_keyword_case(completion_charp, text);
+			return pg_strdup_keyword_case(COMPLETION_CHARP, text);
 	}
 	else
 		return NULL;
@@ -3345,7 +3332,7 @@ complete_from_files(const char *text, int state)
 	if (state == 0)
 	{
 		/* Initialization: stash the unquoted input. */
-		unquoted_text = strtokx(text, "", NULL, "'", *completion_charp,
+		unquoted_text = strtokx(text, "", NULL, "'", COMPLETION_CHARP[0],
 								false, true, pset.encoding);
 		/* expect a NULL return for the empty string only */
 		if (!unquoted_text)
@@ -3366,7 +3353,7 @@ complete_from_files(const char *text, int state)
 		 * bother providing a macro to simplify this.
 		 */
 		ret = quote_if_needed(unquoted_match, " \t\r\n\"`",
-							  '\'', *completion_charp, pset.encoding);
+							  '\'', COMPLETION_CHARP[0], pset.encoding);
 		if (ret)
 			free(unquoted_match);
 		else
@@ -3410,6 +3397,39 @@ pg_strdup_keyword_case(const char *s, const char *ref)
 	return ret;
 }
 
+/* Construct codelet to append given keywords  */
+static char *
+additional_kw_query(const char *ref, int n, ...)
+{
+	va_list ap;
+	static PQExpBuffer qbuf = NULL;
+	int i;
+
+	if (qbuf == NULL)
+		qbuf = createPQExpBuffer();
+	else
+		resetPQExpBuffer(qbuf);
+
+	/* Construct an additional queriy to append keywords */
+	appendPQExpBufferStr(qbuf, " UNION ALL SELECT * FROM (VALUES ");
+
+	va_start(ap, n);
+	for (i = 0 ; i < n ; i++)
+	{
+		char *item = pg_strdup_keyword_case(va_arg(ap, char *), ref);
+		if (i > 0) appendPQExpBufferChar(qbuf, ',');
+		appendPQExpBufferStr(qbuf, "('");
+		appendPQExpBufferStr(qbuf, item);
+		appendPQExpBufferStr(qbuf, "')");
+		pg_free(item);
+	}
+	va_end(ap);
+
+	appendPQExpBufferStr(qbuf, ") as x");
+
+	return qbuf->data;
+}
+
 
 /*
  * escape_string - Escape argument for use as string literal.
-- 
2.9.2

