>From ac65ecdc92fcae899d13b7b6df51211f6cc7e69d Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Tue, 22 Dec 2015 12:34:13 +0900
Subject: [PATCH 1/2] Fix completion for CREATE INDEX type 2.

This patch introduces multilevel matching for completion. This can
simplify individual matching and make it easier to avoid false
matching.
---
 src/bin/psql/tab-complete.c | 103 ++++++++++++++++++++++++++++----------------
 1 file changed, 65 insertions(+), 38 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 1a7d184..fb3833b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1918,45 +1918,72 @@ psql_completion(const char *text, int start, int end)
 	/* First off we complete CREATE UNIQUE with "INDEX" */
 	else if (TailMatches2("CREATE", "UNIQUE"))
 		COMPLETE_WITH_CONST("INDEX");
-	/* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
-	else if (TailMatches2("CREATE|UNIQUE", "INDEX"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   " UNION SELECT 'ON'"
-								   " UNION SELECT 'CONCURRENTLY'");
-	/* Complete ... INDEX [<name>] ON with a list of tables  */
-	else if (TailMatches3("INDEX", MatchAny, "ON") ||
-			 TailMatches2("INDEX|CONCURRENTLY", "ON"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
-	/* If we have CREATE|UNIQUE INDEX <sth> CONCURRENTLY, then add "ON" */
-	else if (TailMatches3("INDEX", MatchAny, "CONCURRENTLY") ||
-			 TailMatches2("INDEX", "CONCURRENTLY"))
-		COMPLETE_WITH_CONST("ON");
-	/* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" or "CONCURRENTLY" */
-	else if (TailMatches3("CREATE|UNIQUE", "INDEX", MatchAny))
-		COMPLETE_WITH_LIST2("CONCURRENTLY", "ON");
 
-	/*
-	 * Complete INDEX <name> ON <table> with a list of table columns (which
-	 * should really be in parens)
-	 */
-	else if (TailMatches4("INDEX", MatchAny, "ON", MatchAny) ||
-			 TailMatches3("INDEX|CONCURRENTLY", "ON", MatchAny))
-		COMPLETE_WITH_LIST2("(", "USING");
-	else if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "(") ||
-			 TailMatches4("INDEX|CONCURRENTLY", "ON", MatchAny, "("))
-		COMPLETE_WITH_ATTR(prev2_wd, "");
-	/* same if you put in USING */
-	else if (TailMatches5("ON", MatchAny, "USING", MatchAny, "("))
-		COMPLETE_WITH_ATTR(prev4_wd, "");
-	/* Complete USING with an index method */
-	else if (TailMatches6("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") ||
-			 TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") ||
-			 TailMatches4("INDEX", "ON", MatchAny, "USING"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
-	else if (TailMatches4("ON", MatchAny, "USING", MatchAny) &&
-			 !TailMatches6("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) &&
-			 !TailMatches4("FOR", MatchAny, MatchAny, MatchAny))
-		COMPLETE_WITH_CONST("(");
+	/* Completion for CREATE INDEX */
+	else if (HeadMatches2("CREATE", "UNIQUE|INDEX"))
+	{
+		/* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
+		if (TailMatches1("INDEX"))
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
+									   " UNION SELECT 'ON'"
+									   " UNION SELECT 'IF NOT EXISTS'"
+									   " UNION SELECT 'CONCURRENTLY'");
+		/*
+		 * If we have CREATE [UNIQUE] INDEX [CONCURRENTLY], suggest existing
+		 * indexes or ON/IF NOT EXISTS.
+		 */
+		else if (TailMatches1("CONCURRENTLY"))
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
+									   " UNION SELECT 'ON'"
+									   " UNION SELECT 'IF NOT EXISTS'");
+		/*
+		 * If we have CREATE [UNIQUE] INDEX [CONCURRENTLY] IF NOT EXISTS,
+		 * suggest existing indexes
+		 */
+		else if (TailMatches3("IF", "NOT", "EXISTS"))
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
+		/*
+		 * If we have CREATE [UNIQUE] INDEX [CONCURRENTLY] [IF NOT EXISTS
+		 * sth], then add "ON"
+		 */
+		else if (TailMatches4("IF", "NOT", "EXISTS", MatchAny) ||
+				 TailMatches2("INDEX|CONCURRENTLY", "!ON"))
+			COMPLETE_WITH_CONST("ON");
+
+		/*
+		 * Complete ... INDEX [[IF NOT EXISTS] <name>] ON with a list of
+		 * tables
+		 */
+		else if (TailMatches3("INDEX|CONCURRENTLY", MatchAny, "ON") ||
+				 TailMatches2("INDEX|CONCURRENTLY", "ON") ||
+				 TailMatches5("IF", "NOT", "EXISTS", MatchAny, "ON"))
+			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
+
+		/*
+		 * Complete INDEX <name> ON <table> with a list of table columns
+		 * (which should really be in parens)
+		 */
+		else if (TailMatches3("INDEX|CONCURRENTLY", "ON", MatchAny) ||
+				 TailMatches4("INDEX|CONCURRENTLY", MatchAny, "ON", MatchAny) ||
+				 TailMatches6("IF", "NOT", "EXISTS", MatchAny, "ON", MatchAny))
+			COMPLETE_WITH_LIST2("(", "USING");
+		else if (TailMatches5("INDEX|CONCURRENTLY", MatchAny,
+							  "ON", MatchAny, "(") ||
+				 TailMatches7("IF", "NOT", "EXISTS", MatchAny,
+							  "ON", MatchAny, "(") ||
+				 TailMatches4("INDEX|CONCURRENTLY", "ON", MatchAny, "("))
+			COMPLETE_WITH_ATTR(prev2_wd, "");
+		/* same if you put in USING */
+		else if (TailMatches5("ON", MatchAny, "USING", MatchAny, "("))
+			COMPLETE_WITH_ATTR(prev4_wd, "");
+		/* Complete USING with an index method */
+		else if (TailMatches3("ON", MatchAny, "USING"))
+			COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+		else if (TailMatches4("ON", MatchAny, "USING", MatchAny) &&
+				 !TailMatches6("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) &&
+				 !TailMatches4("FOR", MatchAny, MatchAny, MatchAny))
+			COMPLETE_WITH_CONST("(");
+	} /* CREATE [UNIQUE] INDEX */
 
 	/* CREATE POLICY */
 	/* Complete "CREATE POLICY <name> ON" */
-- 
1.8.3.1

