From 5a3b2ba812c3a4df1f0e93a93e3fb7ea2e0ca831 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 7 Apr 2016 17:16:50 +0900
Subject: [PATCH 5/5] Restructure completion code for CREATE/GRANT/REVOKE
 commands.

Since we can write completion code more freely, this patch enhances
CREATE SCHEMA and its subcommands. Code for GRANT/REVOKE is rewritten
in maybe simpler and firm way.
---
 src/bin/psql/tab-complete-macros.h |  72 +++-----
 src/bin/psql/tab-complete.c        | 337 ++++++++++++++++++++-----------------
 2 files changed, 205 insertions(+), 204 deletions(-)

diff --git a/src/bin/psql/tab-complete-macros.h b/src/bin/psql/tab-complete-macros.h
index bd89fbb..889ecd9 100644
--- a/src/bin/psql/tab-complete-macros.h
+++ b/src/bin/psql/tab-complete-macros.h
@@ -25,9 +25,14 @@
 #define prev8_wd  (previous_words[7])
 #define prev9_wd  (previous_words[8])
 
+
 /* Move the position of the beginning word for matching macros.  */
 #define SHIFTHEAD(n) \
-	(head_shift += n)
+	(head_shift += (n))
+
+/* Set the position of the beginning word for matching macros.  */
+#define SETHEAD(n) \
+	(head_shift = (n))
 
 /* Return the number of stored words counting head shift */
 #define WORD_COUNT() (previous_words_count - head_shift)
@@ -42,7 +47,7 @@
  */
 #define COLLAPSE(s, n)							\
     do { \
-		memmove(previous_words + HEAD_INDEX(n), previous_words + HEAD_INDEX(0), \
+		memmove(previous_words + HEAD_INDEX((s) + n), previous_words + HEAD_INDEX((s)), \
 				sizeof(char *) * head_shift); \
 		previous_words_count -= (n); \
 	} while(0)
@@ -220,59 +225,20 @@
 	 word_matches(p6, previous_words[HEAD_INDEX((s) + 5)]) &&			\
 	 word_matches(p7, previous_words[HEAD_INDEX((s) + 6)]))
 
-/*
- * Macros for matching N words from the head of the line, this is equivelant
- * to MidMatchesN from the first word in the line.
- */
 #define HeadMatches1(p1) \
-	(HEAD_INDEX(1) >=0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]))
-
+	MidMatches1(1, p1)
 #define HeadMatches2(p1, p2) \
-	(HEAD_INDEX(2) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) &&	\
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]))
-
+	MidMatches2(1, p1, p2)
 #define HeadMatches3(p1, p2, p3) \
-	(HEAD_INDEX(3) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]))
-
+	MidMatches3(1, p1, p2, p3)
 #define HeadMatches4(p1, p2, p3, p4) \
-	(HEAD_INDEX(4) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]))
-
+	MidMatches4(1, p1, p2, p3, p4)
 #define HeadMatches5(p1, p2, p3, p4, p5) \
-	(HEAD_INDEX(5) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]) && \
-	 word_matches(p5, previous_words[HEAD_INDEX(5)]))
-
-#define HeadMatches6(p1, p2, p3, p4, p5, p6)		\
-	(HEAD_INDEX(6) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]) && \
-	 word_matches(p5, previous_words[HEAD_INDEX(5)]) && \
-	 word_matches(p6, previous_words[HEAD_INDEX(6)]))
-
-#define HeadMatches7(p1, p2, p3, p4, p5, p6, p7)	\
-	(HEAD_INDEX(7) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]) && \
-	 word_matches(p5, previous_words[HEAD_INDEX(5)]) && \
-	 word_matches(p6, previous_words[HEAD_INDEX(6)]) && \
-	 word_matches(p7, previous_words[HEAD_INDEX(7)]))
-
+	MidMatches5(1, p1, p2, p3, p4, p5)
+#define HeadMatches6(p1, p2, p3, p4, p5, p6) \
+	MidMatches6(1, p1, p2, p3, p4, p5, p6)
+#define HeadMatches7(p1, p2, p3, p4, p5, p6, p7) \
+	MidMatches7(1, p1, p2, p3, p4, p5, p6, p7)
 
 /*
  * completion_charp is now not a simple string, but a PQExpBuffer. These
@@ -482,7 +448,7 @@ do { \
  */
 #define COMPLETE_THING(p) \
 do { \
-	const pgsql_thing_t *ent = find_thing_entry(prev_wd);	\
+	const pgsql_thing_t *ent = find_thing_entry(previous_words[-(p) - 1]);	\
 	if (ent) \
 	{ \
 		if (ent->query) \
@@ -503,10 +469,10 @@ do { \
 #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,		\
+	additional_kw_query(text, 13, 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,		\
+	additional_kw_query(text, 15, s1, s2, s3, s4, s5, s6, s7,		\
 						s8, s9, s10, s11, s12, s13, s14, s15)
 
 
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 6f227b0..0c8248d 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1059,30 +1059,196 @@ psql_completion_internal(const char *text, char **previous_words,
 	 */
 	if (HeadMatches2("CREATE", "SCHEMA"))
 	{
-		int n;
+		int n, ng;
 
 		/* CREATE SCHEMA <name> */
 		if (Matches2("CREATE", "SCHEMA"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_schemas,
-								ADDLIST1("IF NOT EXISTS"));
-		/* Remove optional words IF NOT EXISTS */
-		if (HeadMatches2("CREATE", "SCHEMA") &&
-			MidMatches3(2, "IF", "NOT", "EXISTS"))
-			COLLAPSE(2, 3);
+								ADDLIST2("IF NOT EXISTS", "AUTHORIZATION"));
+		/* Move to just after CREATE SCHEMA */
+		SHIFTHEAD(2);
+		/* Move to just after IF NOT EXISTS if any */
+		if (Matches3("IF", "NOT", "EXISTS"))
+			SHIFTHEAD(3);
+
+		/* CREATE SCHEMA with schemas */
+		if (WORD_COUNT() == 0)
+			COMPLETE_WITH_QUERY(Query_for_list_of_schemas,
+								ADDLIST1("AUTHORIZATION"));
+
+		/* AUTHORIZATION or subcommands if the first word seems a shema name */
+		if (WORD_COUNT() == 1 && pg_strcasecmp(prev_wd, "AUTHORIZATION") != 0)
+			COMPLETE_WITH_LIST3("AUTHORIZATION", "CREATE", "GRANT");
+
+		/* AUTHORIZATION with roles*/
+		if (Matches1("AUTHORIZATION"))
+			COMPLETE_WITH_QUERY(Query_for_list_of_roles,
+								ADDLIST2("CURRENT_USER", "SESSION_USER"));
+
+		/* Move to just after AUTHORIZATION <role> */
+		if (Matches2("AUTHORIZATION", MatchAny))
+			SHIFTHEAD(2);
+
+		/* CREATE SCHEMA [<name>] with subcommands */
+		if (WORD_COUNT() < 2)
+			COMPLETE_WITH_LIST2("CREATE", "GRANT");
 
-		if (Matches2("CREATE", "SCHEMA"))
-			COMPLETE_THING();
-		
 		/* Else, move head match point past CREATE SCHEMA and go through */
-		if ((n = find_last_index_of("CREATE",
-									previous_words, previous_words_count)) > 0)
-		SHIFTHEAD(n);
+		n = find_last_index_of("CREATE", previous_words, previous_words_count);
+		ng = find_last_index_of("GRANT", previous_words, previous_words_count);
+		if (ng > n) n = ng;
+
+		if (n == 0)
+			return NULL;
+
+		SETHEAD(n);				/* n is an absolute position */
 	}
 
 /* CREATE */
 	/* complete with something you can create */
-	if (Matches1("CREATE"))
-		return completion_matches(text, create_command_generator);
+	if (HeadMatches1("CREATE"))
+	{
+		if (Matches1("CREATE"))
+		{
+			if (head_shift == 0)
+				return completion_matches(text, create_command_generator);
+
+			/* Completion for CREATE as a subcommand of CREATE SCHEMA */
+			COMPLETE_WITH_LIST7("UNLOGGED", "TABLE", "UNIQUE", "INDEX",
+								"SEQUENCE", "TRIGGER", "VIEW");
+		}
+
+		/* CREATE TABLE  */
+		if (HeadMatches2("CREATE", "TABLE"))
+		{
+			int pos_of_thingname = -1;
+
+			/* Move to just after CREATE TABLE */
+			SHIFTHEAD(2);
+			/* CREATE TABLE with table names */
+			if (WORD_COUNT() == 0)
+				COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
+										   ADDLIST1("IF NOT EXISTS"));
+			/* Move to just after IF NOT EXISTS, if any */
+			if (HeadMatches3("IF", "NOT", "EXISTS"))
+			{
+				pos_of_thingname -= 3;
+				SHIFTHEAD(3);
+			}
+			/* CREATE TABLE with table names */
+			if (WORD_COUNT() == 0)
+				COMPLETE_THING(pos_of_thingname);
+		}
+	}
+
+	if (HeadMatches1("GRANT|REVOKE"))
+	{
+		bool is_grant = HeadMatches1("GRANT");
+
+		/* Move to just after GRANT|REVOKE */
+		SHIFTHEAD(1);
+
+		/* Complete GRANT/REVOKE with a list of roles and privileges */
+		if (WORD_COUNT() == 0)
+			COMPLETE_WITH_QUERY(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" */
+		if (Matches1("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|"
+					 "TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
+			COMPLETE_WITH_CONST("ON");
+		/* Complete GRANT/REVOKE <role> with TO/FROM */
+		if (WORD_COUNT() == 1)
+			COMPLETE_WITH_CONST(is_grant ? "TO" : "FROM");
+		
+		/* Move to just after ON/TO/FROM */
+		SHIFTHEAD(1);
+
+		/*
+		 * Complete GRANT/REVOKE <sth> ON with a list of tables, views, and
+		 * sequences along with some keywords like DATABASE.
+		 *
+		 * Note: GRANT/REVOKE can get quite complex; tab-completion as
+		 * implemented here will only work if the privilege list contains
+		 * exactly one privilege.
+		 */
+		if (Matches1("ON"))
+			COMPLETE_WITH_SCHEMA_QUERY(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"));
+		/* Move to just after ON */
+		SHIFTHEAD(1);
+
+		/* Complete fixed words  */
+		if (Matches1("ALL"))
+			COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA",
+								"TABLES IN SCHEMA");
+
+		if (Matches1("FOREIGN"))
+			COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
+
+		/*
+		 * Complete object names and move to just after the name
+		 */
+		if (HeadMatches1("DATABASE|DOMAIN|FUNCTION|LANGUAGE|SCHEMA|SEQUENCE"
+						 "TABLE|TABLESPACE|TYPE"))
+		{
+			if (WORD_COUNT() == 1)
+				COMPLETE_THING(-1);
+			SHIFTHEAD(2);
+		}
+		else if(HeadMatches3("FOREIGN", "DATA", "WRAPPER"))
+		{
+			if (WORD_COUNT() == 3)
+				COMPLETE_WITH_QUERY(Query_for_list_of_fdws, "");
+			SHIFTHEAD(4);
+		}
+		else if(HeadMatches2("FOREIGN", "SERVER"))
+		{
+			if (WORD_COUNT() == 2)
+				COMPLETE_THING(-1);
+			SHIFTHEAD(3);
+		}
+		else if (HeadMatches4("ALL", MatchAny, "IN", "SCHEMA"))
+		{
+			if (WORD_COUNT() == 4)
+				COMPLETE_THING(-1);
+			SHIFTHEAD(5);
+		}
+		else
+			SHIFTHEAD(1);	/* Must be a bare tablename */
+
+		/* Complete "GRANT/REVOKE * ON *" with "TO/FROM" */
+		if (WORD_COUNT() == 0)
+			COMPLETE_WITH_CONST(is_grant ? "TO" : "FROM");
+
+		/*
+		 * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
+		 * CURRENT_USER, or SESSION_USER.
+		 */
+		if (WORD_COUNT() == 1)
+			COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles, "");
+	}
+/*
+ * Fail if we saw CREATE SCHEMA; no rules below here should be considered.
+ */
+	if (head_shift > 0)
+		return NULL;
 
 /* DROP, but not DROP embedded in other commands */
 	/* complete with something you can drop */
@@ -1965,7 +2131,7 @@ psql_completion_internal(const char *text, char **previous_words,
 			COLLAPSE(2, 3);
 	}
 	if (Matches2("CREATE", "INDEX"))
-		COMPLETE_THING();
+		COMPLETE_THING(0);
 	/* Complete CREATE INDEX [<name>] ON with a list of tables */
 	if (Matches4("CREATE", "INDEX", MatchAny, "ON") ||
 			 Matches3("CREATE", "INDEX", "ON"))
@@ -2085,7 +2251,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COLLAPSE(2, 3);
 
 	if (Matches2("CREATE", "TABLE"))
-		COMPLETE_THING();
+		COMPLETE_THING(-1);
 /* CREATE TABLESPACE */
 	if (Matches3("CREATE", "TABLESPACE", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER", "LOCATION");
@@ -2313,7 +2479,7 @@ psql_completion_internal(const char *text, char **previous_words,
 		COLLAPSE(2, 2);
 
 	if (Matches2("DROP", "VIEW"))
-		COMPLETE_THING();
+		COMPLETE_THING(0);
 	if (Matches3("DROP", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 								   ADDLIST1("IF EXISTS"));
@@ -2450,14 +2616,14 @@ psql_completion_internal(const char *text, char **previous_words,
 		COLLAPSE(2, 2);
 
 	if (Matches2("DROP", MatchAny))
-		COMPLETE_THING();
+		COMPLETE_THING(0);
 	if (HeadMatches4("DROP", "TEXT", "SEARCH",
 					 "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE") &&
 		MidMatches2(4, "IF", "EXISTS"))
 		COLLAPSE(4, 2);
 
 	if (Matches4("DROP", "TEXT", "SEARCH", MatchAny))
-		COMPLETE_THING();
+		COMPLETE_THING(0);
 
 /* EXECUTE */
 	if (Matches1("EXECUTE"))
@@ -2512,137 +2678,6 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (TailMatches2("FOREIGN", "SERVER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_servers, "");
 
-/* 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,
-			ADDLIST13("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE",
-					  "REFERENCES", "TRIGGER", "CREATE", "CONNECT", "TEMPORARY",
-					  "EXECUTE", "USAGE", "ALL"));
-
-	/*
-	 * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
-	 * TO/FROM
-	 */
-	if (TailMatches2("GRANT|REVOKE", MatchAny))
-	{
-		if (TailMatches1("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
-			COMPLETE_WITH_CONST("ON");
-		if (TailMatches2("GRANT", MatchAny))
-			COMPLETE_WITH_CONST("TO");
-		else
-			COMPLETE_WITH_CONST("FROM");
-	}
-
-	/*
-	 * Complete GRANT/REVOKE <sth> ON with a list of tables, views, and
-	 * sequences.
-	 *
-	 * 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.
-	 */
-	if (TailMatches3("GRANT|REVOKE", MatchAny, "ON"))
-		COMPLETE_WITH_SCHEMA_QUERY(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",
-							"TABLES IN SCHEMA");
-
-	if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
-		COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
-
-	/*
-	 * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of
-	 * appropriate objects.
-	 *
-	 * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
-	 */
-	if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", MatchAny))
-	{
-		if (TailMatches1("DATABASE"))
-			COMPLETE_WITH_QUERY(Query_for_list_of_databases, "");
-		if (TailMatches1("DOMAIN"))
-			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, "");
-		if (TailMatches1("FUNCTION"))
-			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, "");
-		if (TailMatches1("TABLE"))
-			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, "");
-		if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
-			COMPLETE_WITH_CONST("TO");
-		else
-			COMPLETE_WITH_CONST("FROM");
-	}
-
-	/*
-	 * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
-	 * CURRENT_USER, or SESSION_USER.
-	 */
-	if ((HeadMatches1("GRANT") && TailMatches1("TO")) ||
-			 (HeadMatches1("REVOKE") && TailMatches1("FROM")))
-		COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles, "");
-
-	/* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
-	if (HeadMatches1("GRANT") && TailMatches3("ON", MatchAny, MatchAny))
-		COMPLETE_WITH_CONST("TO");
-	if (HeadMatches1("REVOKE") && TailMatches3("ON", MatchAny, MatchAny))
-		COMPLETE_WITH_CONST("FROM");
-
-	/* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
-	if (TailMatches8("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
-	{
-		if (TailMatches8("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
-			COMPLETE_WITH_CONST("TO");
-		else
-			COMPLETE_WITH_CONST("FROM");
-	}
-
-	/* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
-	if (TailMatches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
-	{
-		if (TailMatches7("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
-			COMPLETE_WITH_CONST("TO");
-		else
-			COMPLETE_WITH_CONST("FROM");
-	}
-
-	/* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
-	if (TailMatches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
-	{
-		if (TailMatches6("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
-			COMPLETE_WITH_CONST("TO");
-		else
-			COMPLETE_WITH_CONST("FROM");
-	}
-
 /* GROUP BY */
 	if (TailMatches3("FROM", MatchAny, "GROUP"))
 		COMPLETE_WITH_CONST("BY");
@@ -3164,7 +3199,7 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * check if that was the previous word. If so, execute the query to get a
 	 * list of them.
 	 */
-	COMPLETE_THING();
+	COMPLETE_THING(0);
 }
 
 /*
-- 
1.8.3.1

