From 63a03319aa8a168c660bf205eba6dc95ac25b012 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Tue, 29 Mar 2016 19:01:13 +0900
Subject: [PATCH 2/3] Make added keywords for completion queries follow to
 input.

Currently some keyword shown 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.
Only COMPLETE_WITH_QUERY/COMPLETE_WITH_SCHEMA_QUERY are fixed.
---
 src/bin/psql/tab-complete.c | 239 +++++++++++++++++++++++++-------------------
 1 file changed, 136 insertions(+), 103 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9b6e704..3edbdb7 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -312,6 +312,18 @@ do { \
 	COMPLETE_WITH_LIST_CS(list); \
 } while (0)
 
+#define ADDLIST1(p, s1) additional_kw_query(p, text, 1, s1)
+#define ADDLIST2(p, s1, s2) additional_kw_query(p, text, 2, s1, s2)
+#define ADDLIST3(p, s1, s2, s3) additional_kw_query(p, text, 3, s1, s2, s3)
+#define ADDLIST4(p, s1, s2, s3, s4) \
+	additional_kw_query(p, text, 4, s1, s2, s3, s4)
+#define ADDLIST13(p, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \
+	additional_kw_query(p, text, 12, s1, s2, s3, s4, s5, s6, s7,		\
+						s8, s9, s10, s11, s12, s13)
+#define ADDLIST15(p, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \
+	additional_kw_query(p, text, 12, s1, s2, s3, s4, s5, s6, s7,		\
+						s8, s9, s10, s11, s12, s13, s14, s15)
+
 /*
  * Assembly instructions for schema queries
  */
@@ -956,6 +968,7 @@ static char *complete_from_files(const char *text, int state);
 
 static int find_last_index_of(char *w, char **previous_words, int len);
 static char *pg_strdup_keyword_case(const char *s, const char *ref);
+static char *additional_kw_query(char *prefix, const char *ref, int n, ...);
 static char *escape_string(const char *text);
 static PGresult *exec_query(const char *query);
 
@@ -1440,8 +1453,8 @@ psql_completion(const char *text, int start, int end)
 	/* ALTER TABLE */
 	else if (Matches2("ALTER", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   "UNION SELECT 'IF EXISTS'"
-								   "UNION SELECT 'ALL IN TABLESPACE'");
+			ADDLIST2("", "IF EXISTS", "ALL IN TABLESPACE"));
+
 	/* ALTER TABLE after removing optional words IF EXISTS*/
 	else if (HeadMatches2("ALTER", "TABLE") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -1527,7 +1540,7 @@ psql_completion(const char *text, int start, int end)
 	/* ALTER FOREIGN TABLE */
 	else if (Matches3("ALTER|DROP", "FOREIGN", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 
 	/* ALTER|DROP FOREIGN TABLE after removing optinal words IF EXISTS */
 	else if (HeadMatches3("ALTER|DROP", "FOREIGN", "TABLE") &&
@@ -1553,8 +1566,7 @@ psql_completion(const char *text, int start, int end)
 	/* ALTER INDEX */
 	else if (Matches2("ALTER", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   "UNION SELECT 'IF EXISTS'"
-								   "UNION SELECT 'ALL IN TABLESPACE'");
+			   ADDLIST2("", "IF EXISTS", "ALL IN TABLESPACE"));
 	/* ALTER INDEX after removing optional words IF EXISTS */
 	else if (HeadMatches2("ALTER", "INDEX") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -1589,8 +1601,7 @@ psql_completion(const char *text, int start, int end)
 	/* ALTER MATERIALIZED VIEW */
 	else if (Matches3("ALTER", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
-								   "UNION SELECT 'IF EXISTS'"
-								   "UNION SELECT 'ALL IN TABLESPACE'");
+			   ADDLIST2("", "IF EXISTS", "ALL IN TABLESPACE"));
 
 	/* ALTER MATERIALIZED VIEW with name after removing optional words */
 	else if (HeadMatches3("ALTER", "MATERIALIZED", "VIEW") &&
@@ -1658,8 +1669,8 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
-		COMPLETE_WITH_QUERY(Query_for_constraint_of_type
-							"UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_constraint_of_type, "IF EXISTS"));
 	}
 	/* Try the same match after removing optional words IF EXISTS */
 	else if (HeadMatches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT") &&
@@ -1681,7 +1692,7 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST3("DEFAULT", "NOT NULL", "SCHEMA");
 	else if (Matches2("ALTER", "SEQUENCE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	/* ALTER SEQUENCE with name after removing optional words IF EXISTS */
 	else if (HeadMatches2("ALTER", "SEQUENCE") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -1711,7 +1722,7 @@ psql_completion(const char *text, int start, int end)
 	/* ALTER VIEW */
 	else if (Matches2("ALTER", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
-								   "UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	/* ALTER VIEW <name> with subcommands after removing optional worlds */
 	else if (HeadMatches2("ALTER", "VIEW") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -1725,8 +1736,8 @@ psql_completion(const char *text, int start, int end)
 
 	/* ALTER POLICY */
 	else if (Matches2("ALTER", "POLICY"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_policies
-							"UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_policies, "IF EXISTS"));
 	/* ALTER POLICY <name> with ON after removing optional words IF EXISTS */
 	else if (HeadMatches2("ALTER", "POLICY") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -1901,8 +1912,8 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
-		COMPLETE_WITH_QUERY(Query_for_constraint_of_table
-							"UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_constraint_of_table, "IF EXISTS"));
 	}
 	/* Try the same after removing optional words IF EXISTS */
 	else if (HeadMatches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT") &&
@@ -2096,7 +2107,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
 /* CLUSTER */
 	else if (Matches1("CLUSTER"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "UNION SELECT 'VERBOSE'");
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
+								   ADDLIST1("", "VERBOSE"));
 	else if (Matches2("CLUSTER", "VERBOSE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
 	/* If we have CLUSTER <sth>, then add "USING" */
@@ -2158,7 +2170,7 @@ psql_completion(const char *text, int start, int end)
 	 */
 	else if (Matches1("COPY|\\copy"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   " UNION ALL SELECT '('");
+								   ADDLIST1("", "("));
 	/* If we have COPY BINARY, complete with list of tables */
 	else if (Matches2("COPY", "BINARY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
@@ -2213,8 +2225,8 @@ psql_completion(const char *text, int start, int end)
 	/* CREATE EXTENSION */
 	/* Complete with available extensions rather than installed ones. */
 	else if (Matches2("CREATE", "EXTENSION"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions
-							" UNION SELECT 'IF NOT EXISTS'");
+		COMPLETE_WITH_QUERY(ADDLIST1(Query_for_list_of_available_extensions,
+									 "IF NOT EXISTS"));
 	/* Try the same after removing optional words IF NOT EXISTS */
 	else if (HeadMatches2("CREATE", "EXTENSION") &&
 			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
@@ -2237,7 +2249,7 @@ psql_completion(const char *text, int start, int end)
 	/* CREATE FOREIGN TABLE */
 	else if (Matches3("CREATE", "FOREIGN", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables,
-								   " UNION SELECT 'IF NOT EXISTS'");
+								   ADDLIST1("", "IF NOT EXISTS"));
 	/* Remove optional words IF NOT EXISTS */
 	else if (HeadMatches3("CREATE", "FOREIGN", "TABLE") &&
 			 MidMatchAndRemove3(3, "IF", "NOT", "EXISTS") &&
@@ -2259,15 +2271,12 @@ psql_completion(const char *text, int start, int end)
 	   and existing indexes */
 	else if (Matches2("CREATE", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'ON'"
-								   " UNION SELECT 'CONCURRENTLY'"
-								   " UNION SELECT 'IF NOT EXISTS'");
+				   ADDLIST3("", "ON", "CONCURRENTLY", "IF NOT EXISTS"));
 	/* Complete CREATE INDEX CONCURRENTLY with "ON" or IF NOT EXISTS and
 	 * existing indexes */
 	else if (Matches3("CREATE", "INDEX", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'IF NOT EXISTS'"
-								   " UNION SELECT 'ON'");
+								   ADDLIST2("", "IF NOT EXISTS", "ON"));
 
 	/* Remove optional words "CONCURRENTLY",  "IF NOT EXISTS" */
 	else if (HeadMatches2("CREATE", "INDEX") &&
@@ -2359,7 +2368,7 @@ psql_completion(const char *text, int start, int end)
 /* CREATE SEQUENCE */
 	else if(Matches2("CREATE", "SEQUENCE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences,
-								   " UNION SELECT 'IF NOT EXISTS'");
+								   ADDLIST1("", "IF NOT EXISTS"));
 	else if(HeadMatches2("CREATE", "SEQUENCE") &&
 			MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
 			Matches3("CREATE", "SEQUENCE", MatchAny))
@@ -2374,8 +2383,8 @@ psql_completion(const char *text, int start, int end)
 
 /* CREATE SCHEMA <name> */
 	else if (Matches2("CREATE", "SCHEMA"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_schemas
-							" UNION SELECT 'IF NOT EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_schemas, "IF NOT EXISTS"));
 	/* Remove optional words IF NOT EXISTS */
 	else if (HeadMatches2("CREATE", "SCHEMA") &&
 			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
@@ -2395,7 +2404,7 @@ psql_completion(const char *text, int start, int end)
 			 false) {} /* FALL THROUGH*/
 	else if (Matches2("CREATE", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   " UNION SELECT 'IF NOT EXISTS'");
+								   ADDLIST1("", "IF NOT EXISTS"));
 
 	/* Remove optional words here */
 	else if (HeadMatches2("CREATE", "TABLE") &&
@@ -2486,7 +2495,7 @@ psql_completion(const char *text, int start, int end)
 /* CREATE VIEW  */
 	else if (Matches2("CREATE", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
-								   " UNION SELECT 'IF NOT EXISTS'");
+								   ADDLIST1("", "IF NOT EXISTS"));
 	/* CREATE VIEW <name> with AS after removing optional words */
 	else if (HeadMatches2("CREATE", "VIEW") &&
 			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
@@ -2501,7 +2510,7 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("VIEW");
 	else if (Matches3("CREATE", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
-								   " UNION SELECT 'IF NOT EXISTS'");
+								   ADDLIST1("", "IF NOT EXISTS"));
 	/* Try the same after removing optional words IF NOT EXISTS. VIEW will be
 	 * completed afterwards */
 	else if (HeadMatches3("CREATE", "MATERIALIZED", "VIEW") &&
@@ -2569,10 +2578,10 @@ psql_completion(const char *text, int start, int end)
 	/* help completing some of the variants */
 	else if (Matches2("DROP", "AGGREGATE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	else if (Matches2("DROP", "FUNCTION"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
 	else if (HeadMatches2("DROP", "AGGREGATE|FUNCTION") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -2583,8 +2592,8 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches2("DROP", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
 	else if (Matches4("DROP", "FOREIGN", "DATA", "WRAPPER"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_fdws
-							" UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_fdws, "IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
 	else if (HeadMatches4("DROP", "FOREIGN", "DATA", "WRAPPER") &&
 			 MidMatchAndRemove2(4, "IF", "EXISTS") &&
@@ -2593,11 +2602,10 @@ psql_completion(const char *text, int start, int end)
 	/* DROP INDEX */
 	else if (Matches2("DROP", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'IF EXISTS'"
-								   " UNION SELECT 'CONCURRENTLY'");
+								   ADDLIST2("", "IF EXISTS","CONCURRENTLY"));
 	else if (Matches3("DROP", "INDEX", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	/* Try the same after optional words CONCURRENTLY and IF NOT EXISTS */
 	else if (HeadMatches2("DROP", "INDEX") &&
 			 MidMatchAndRemove1(2, "CONCURRENTLY") &&
@@ -2610,14 +2618,14 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("VIEW");
 	else if (Matches2("DROP", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	/* Remove optional words IF EXISTS  */
 	else if (HeadMatches2("DROP", "VIEW") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
 			 false) {} /* FALL THROUGH */
 	else if (Matches3("DROP", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
-								   " UNION SELECT 'IF EXISTS'");
+								   ADDLIST1("", "IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
 	else if (HeadMatches3("DROP", "MATERIALIZED", "VIEW") &&
 			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
@@ -2635,8 +2643,8 @@ psql_completion(const char *text, int start, int end)
 
 	/* DROP TRIGGER */
 	else if (Matches2("DROP", "TRIGGER"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_triggers
-							" UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_triggers, "IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
 	else if (HeadMatches2("DROP", "TRIGGER") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -2654,8 +2662,8 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches2("DROP", "EVENT"))
 		COMPLETE_WITH_CONST("TRIGGER");
 	else if (Matches3("DROP", "EVENT", "TRIGGER"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers
-							" UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_event_triggers, "IF EXISTS"));
 	/* Trye the same after removing optional words IF EXISTS */
 	else if (HeadMatches3("DROP", "EVENT", "TRIGGER") &&
 			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
@@ -2664,8 +2672,8 @@ psql_completion(const char *text, int start, int end)
 
 	/* DROP POLICY */
 	else if (Matches2("DROP", "POLICY"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_policies
-							" UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_policies, "IF EXISTS"));
 	/* Try the same after after removing optional words IF EXISTS */
 	else if (HeadMatches2("DROP", "POLICY") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -2683,8 +2691,8 @@ psql_completion(const char *text, int start, int end)
 
 	/* DROP RULE */
 	else if (Matches2("DROP", "RULE"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_rules
-							"UNION SELECT 'IF EXISTS'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_rules, "IF EXISTS"));
 	/* DROP RULE <name>, after removing optional words IF EXISTS */
 	else if (HeadMatches2("DROP", "RULE") &&
 			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
@@ -2708,15 +2716,15 @@ psql_completion(const char *text, int start, int end)
 
 	{
 		const pgsql_thing_t *ent = find_thing_entry(prev_wd);
-		char *addition = " UNION SELECT 'IF EXISTS'";
+		char *addition = ADDLIST1("", "IF EXISTS");
 
 		if (ent)
 		{
 			/* Completing USER needs special treat */
 			if (pg_strcasecmp(prev_wd, "USER") == 0)
 			{
-				COMPLETE_WITH_QUERY(Query_for_list_of_roles 
-									"UNION SELECT 'MAPPING' UNION SELECT 'IF EXISTS'");
+				COMPLETE_WITH_QUERY(
+					ADDLIST2(Query_for_list_of_roles, "MAPPING", "IF EXISTS"));
 			}
 			else if (ent->query)
 			{
@@ -2729,7 +2737,7 @@ psql_completion(const char *text, int start, int end)
 			}
 			else if (ent->squery)
 				COMPLETE_WITH_SCHEMA_QUERY(*ent->squery,
-										   " UNION SELECT 'IF EXISTS'");
+										   ADDLIST1("", "IF EXISTS"));
 		}
 	}
 	/* Remove optional IF EXISTS from DROP */
@@ -2800,20 +2808,11 @@ psql_completion(const char *text, int start, int end)
 /* GRANT && REVOKE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	/* Complete GRANT/REVOKE with a list of roles and privileges */
 	else 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(
+			ADDLIST13(Query_for_list_of_roles,
+					  "SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE",
+					  "REFERENCES", "TRIGGER", "CREATE", "CONNECT", "TEMPORARY",
+					  "EXECUTE", "USAGE", "ALL"));
 
 	/*
 	 * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
@@ -2842,21 +2841,22 @@ psql_completion(const char *text, int start, int end)
 	 */
 	else 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'");
+			   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"));
 
 	else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
 		COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA",
@@ -2982,7 +2982,7 @@ psql_completion(const char *text, int start, int end)
 	/* Complete LOCK [TABLE] with a list of tables */
 	else if (Matches1("LOCK"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   " UNION SELECT 'TABLE'");
+								   ADDLIST1("", "TABLE"));
 	else if (Matches2("LOCK", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 
@@ -3048,7 +3048,7 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("VIEW");
 	else if (Matches3("REFRESH", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
-								   " UNION SELECT 'CONCURRENTLY'");
+								   ADDLIST1("", "CONCURRENTLY"));
 	else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 	else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
@@ -3124,7 +3124,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST2("ONLY", "WRITE");
 	/* SET CONSTRAINTS */
 	else if (Matches2("SET", "CONSTRAINTS"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema,
+								   ADDLIST1("", "ALL"));
 	/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
 	else if (Matches3("SET", "CONSTRAINTS", MatchAny))
 		COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE");
@@ -3136,7 +3137,8 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
 	/* Complete SET SESSION AUTHORIZATION with username */
 	else if (Matches3("SET", "SESSION", "AUTHORIZATION"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
+		COMPLETE_WITH_QUERY(
+			ADDLIST1(Query_for_list_of_roles, "DEFAULT"));
 	/* Complete RESET SESSION with AUTHORIZATION */
 	else if (Matches2("RESET", "SESSION"))
 		COMPLETE_WITH_CONST("AUTHORIZATION");
@@ -3162,10 +3164,11 @@ psql_completion(const char *text, int start, int end)
 			COMPLETE_WITH_LIST(my_list);
 		}
 		else 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(
+				ADDLIST1(Query_for_list_of_schemas
+						 " AND nspname not like 'pg\\_toast%%' "
+						 " AND nspname not like 'pg\\_temp%%' ",
+						 "DEFAULT"));
 		else
 		{
 			/* generic, type based, GUC support */
@@ -3234,10 +3237,9 @@ psql_completion(const char *text, int start, int end)
 			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
 			 false) {} /* FALL THROUGH */
 	else 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(
+			ADDLIST3(Query_for_list_of_roles, 
+					 "CURRENT_USER", "PUBLIC", "USER"));
 	else if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
 	else if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
@@ -3251,26 +3253,22 @@ psql_completion(const char *text, int start, int end)
  */
 	else if (Matches1("VACUUM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'FULL'"
-								   " UNION SELECT 'FREEZE'"
-								   " UNION SELECT 'ANALYZE'"
-								   " UNION SELECT 'VERBOSE'");
+		   ADDLIST4("", "FULL", "FREEZE", "ANALYZE", "VERBOSE"));
 	else if (Matches2("VACUUM", "FULL|FREEZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'ANALYZE'"
-								   " UNION SELECT 'VERBOSE'");
+		   ADDLIST2("", "ANALYZE", "VERBOSE"));
 	else if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'VERBOSE'");
+		   ADDLIST1("", "VERBOSE"));
 	else if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'ANALYZE'");
+		   ADDLIST1("", "ANALYZE"));
 	else if (Matches2("VACUUM", "VERBOSE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'ANALYZE'");
+		   ADDLIST1("", "ANALYZE"));
 	else if (Matches2("VACUUM", "ANALYZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
-								   " UNION SELECT 'VERBOSE'");
+		   ADDLIST1("", "VERBOSE"));
 	else if (HeadMatches1("VACUUM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
 
@@ -4035,6 +4033,41 @@ pg_strdup_keyword_case(const char *s, const char *ref)
 	return ret;
 }
 
+/* Construct codelet to append given keywords  */
+static char *
+additional_kw_query(char *prefix, const char *ref, int n, ...)
+{
+	va_list ap;
+	static PQExpBuffer qbuf = NULL;
+	int i;
+
+	if (qbuf == NULL)
+		qbuf = createPQExpBuffer();
+	else
+		resetPQExpBuffer(qbuf);
+
+	appendPQExpBufferStr(qbuf, prefix);
+
+	/* Construct an additional queriy to append keywords */
+	appendPQExpBufferStr(qbuf, " UNION 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.
-- 
1.8.3.1

