From 26fe0b403f9848f42845b435902c0661e3673a24 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 15 Sep 2016 20:49:45 +0900
Subject: [PATCH 5/5] Add suggestion for IF (NOT) EXISTS for some syntaxes

Add suggestion for IF EXISTS or IF NOT EXISTS for some arbitrary
syntaxes, ALTER TABLE/ALTER FOREIGN TABLE/ALTER INDEX/
ALTER MATERIALIZED VIEW/ALTER DOMAIN DROP CONSTRAINT/ALTER SEQUENCE/
ALTER VIEW/ALTER POLICY/ALTER TABLE DROP COLUMN/
ALTER TABLE DROP CONSTRAINT/CREATE INDEX/CREATE SEQUENCE/
CREATE TABLE/CREATE VIEW/DROP INDEX/DROP (MATERIALIZED) VIEW/
DROP USER MAPPING
---
 src/bin/psql/tab-complete.c | 178 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 167 insertions(+), 11 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 1fb70e1..528cfff 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1074,7 +1074,9 @@ psql_completion_internal(const char *text, char **previous_words,
 	/* ALTER TABLE */
 	if (Matches2("ALTER", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-								   ADDLIST1("ALL IN TABLESPACE"));
+								   ADDLIST2("ALL IN TABLESPACE", "IF EXISTS"));
+	if (HeadMatches4("ALTER", "TABLE", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
 
 	/* ALTER something */
 	if (Matches1("ALTER"))
@@ -1177,6 +1179,14 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 		COMPLETE_WITH_LIST5("HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", "RENAME TO");
 
+	/* ALTER FOREIGN TABLE */
+	if (Matches3("ALTER", "FOREIGN", "TABLE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables,
+								   ADDLIST1("IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (HeadMatches5("ALTER", "FOREIGN", "TABLE", "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+
 	/* ALTER FOREIGN TABLE <name> */
 	if (Matches4("ALTER", "FOREIGN", "TABLE", MatchAny))
 	{
@@ -1191,7 +1201,11 @@ psql_completion_internal(const char *text, char **previous_words,
 	/* ALTER INDEX */
 	if (Matches2("ALTER", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   ADDLIST1("ALL IN TABLESPACE"));
+								   ADDLIST2("ALL IN TABLESPACE", "IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (HeadMatches4("ALTER", "INDEX", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
 	/* ALTER INDEX <name> */
 	if (Matches3("ALTER", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET");
@@ -1220,7 +1234,10 @@ 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,
-								   ADDLIST1("ALL IN TABLESPACE"));
+								   ADDLIST2("ALL IN TABLESPACE", "IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (HeadMatches5("ALTER", "MATERIALIZED", "VIEW", "IF", "EXISTS"))
+		COLLAPSE(4, 2);
 
 	/* ALTER USER,ROLE <name> */
 	if (Matches3("ALTER", "USER|ROLE", MatchAny) &&
@@ -1276,6 +1293,19 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
+		if (word_matches("DROP", prev2_wd))
+			COMPLETE_WITH_QUERY(Query_for_constraint_of_type,
+								ADDLIST1("IF EXISTS"));
+		else
+			COMPLETE_WITH_QUERY(Query_for_constraint_of_type, "");
+	}
+	/* Remove optional words for further completion */
+	if (HeadMatches7("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT", "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+	/*  Complete constraint name again without IF EXISTS */
+	if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT"))
+	{
+		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_type, "");
 	}
 	/* ALTER DOMAIN <sth> RENAME */
@@ -1288,6 +1318,16 @@ psql_completion_internal(const char *text, char **previous_words,
 	/* ALTER DOMAIN <sth> SET */
 	if (Matches4("ALTER", "DOMAIN", MatchAny, "SET"))
 		COMPLETE_WITH_LIST3("DEFAULT", "NOT NULL", "SCHEMA");
+	/* ALTER SEQUENCE */
+	if (Matches2("ALTER", "SEQUENCE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences,
+								   ADDLIST1("IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (HeadMatches4("ALTER", "SEQUENCE", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches2("ALTER", "SEQUENCE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, "");
 	/* ALTER SEQUENCE <name> */
 	if (Matches3("ALTER", "SEQUENCE", MatchAny))
 	{
@@ -1313,6 +1353,16 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches3("ALTER", "SYSTEM", "SET|RESET"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars,
 							ADDLIST1("ALL"));
+	/* ALTER VIEW */
+	if (Matches2("ALTER", "VIEW"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
+								   ADDLIST1("IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (HeadMatches4("ALTER", "VIEW", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches2("ALTER", "VIEW"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, "");
 	/* ALTER VIEW <name> */
 	if (Matches3("ALTER", "VIEW", MatchAny))
 		COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
@@ -1324,6 +1374,12 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* ALTER POLICY <name> */
 	if (Matches2("ALTER", "POLICY"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_policies, ADDLIST1("IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (Matches4("ALTER", "POLICY", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches2("ALTER", "POLICY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_policies, "");
 	/* ALTER POLICY <name> ON */
 	if (Matches3("ALTER", "POLICY", MatchAny))
@@ -1463,8 +1519,13 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT");
 	/* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
 	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
+		COMPLETE_WITH_ATTR(prev3_wd, ADDLIST1("IF EXISTS"));
+	/* Remove optional words for further completion */
+	if (HeadMatches7("ALTER", "TABLE", MatchAny, "DROP", "COLUMN", "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
-
 	/*
 	 * If we have ALTER TABLE <sth> ALTER|DROP|RENAME|VALIDATE CONSTRAINT,
 	 * provide list of constraints
@@ -1472,6 +1533,19 @@ psql_completion_internal(const char *text, char **previous_words,
 	if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME|VALIDATE", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
+		if (word_matches("DROP", prev2_wd))
+			COMPLETE_WITH_QUERY(Query_for_constraint_of_table,
+								ADDLIST1("IF EXISTS"));
+		else
+			COMPLETE_WITH_QUERY(Query_for_constraint_of_table, "");
+	}
+	/* Remove IF EXISTS for further completion */
+	if (HeadMatches7("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT", "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT"))
+	{
+		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_table, "");
 	}
 	/* Remove COLUMN just after ALTER */
@@ -1614,7 +1688,18 @@ psql_completion_internal(const char *text, char **previous_words,
 	 * of attributes
 	 */
 	if (Matches5("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"))
-		COMPLETE_WITH_ATTR(prev3_wd, "");
+	{
+		if (word_matches("DROP", prev2_wd))
+			COMPLETE_WITH_ATTR(prev3_wd, ADDLIST1("IF EXISTS"));
+		else
+			COMPLETE_WITH_ATTR(prev3_wd, "");
+	}
+	/* Remove IF EXISTS for further completion */
+	if (HeadMatches7("ALTER", "TYPE", MatchAny, "DROP", "ATTRIBUTE", "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches5("ALTER", "TYPE", MatchAny, "DROP", "ATTRIBUTE"))
+			COMPLETE_WITH_ATTR(prev3_wd, "");	
 	/* ALTER TYPE ALTER ATTRIBUTE <foo> */
 	if (Matches6("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny))
 		COMPLETE_WITH_CONST("TYPE");
@@ -1762,6 +1847,13 @@ psql_completion_internal(const char *text, char **previous_words,
 	/* CREATE EXTENSION */
 	/* Complete with available extensions rather than installed ones. */
 	if (Matches2("CREATE", "EXTENSION"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions,
+							ADDLIST1("IF NOT EXISTS"));
+	/* Remove IF NOT EXISTS for further completion*/
+	if (HeadMatches5("CREATE", "EXTENSION", "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+	/* Complete again without IF NOT EXISTS */
+	if (Matches2("CREATE", "EXTENSION"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions, "");
 	/* CREATE EXTENSION <name> */
 	if (Matches3("CREATE", "EXTENSION", MatchAny))
@@ -1792,14 +1884,20 @@ psql_completion_internal(const char *text, char **previous_words,
 	/* Complete with index names as category suggestion and possible keywords */
 	if (Matches2("CREATE", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   ADDLIST2("ON", "CONCURRENTLY"));
+						   ADDLIST3("ON", "CONCURRENTLY", "IF NOT EXISTS"));
 	/* Remove CONCURRENTLY for further completion */
 	if (HeadMatches3("CREATE", "INDEX", "CONCURRENTLY"))
 		COLLAPSE(3, 1);
 	/* Complete with existing index names as word category suggestion */
 	if (Matches2("CREATE", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   ADDLIST1("ON"));
+								   ADDLIST2("ON", "IF NOT EXISTS"));
+	/* Remove IF NOT EXISTS for further completion */
+	if (HeadMatches5("CREATE", "INDEX", "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+	/* Complete again without IF NOT EXISTS */
+	if (Matches2("CREATE", "INDEX"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "");
 	/* Suggest ON just after index name */
 	if (Matches3("CREATE", "INDEX", MatchAnyExcept("ON")))
 		COMPLETE_WITH_CONST("ON");
@@ -1892,6 +1990,13 @@ psql_completion_internal(const char *text, char **previous_words,
 
 	/* CREATE SEQUENCE */
 	if (Matches2("CREATE", "SEQUENCE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences,
+								   ADDLIST1("IF NOT EXISTS"));
+	/* Remove IF NOT EXISTS for further completion */
+	if (HeadMatches5("CREATE", "SEQUENCE", "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+	/* Complete again without IF NOT EXISTS */
+	if (Matches2("CREATE", "SEQUENCE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, "");
 	if (Matches3("CREATE", "SEQUENCE", MatchAny))
 		COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO",
@@ -1917,6 +2022,13 @@ psql_completion_internal(const char *text, char **previous_words,
 		COLLAPSE(2, 1);
 	/* Complete CREATE TABLE with existing table names */
 	if (Matches2("CREATE", "TABLE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
+								   ADDLIST1("IF NOT EXISTS"));
+	/* Remove IF NOT EXIST for further completion */
+	if (HeadMatches5("CREATE", "TABLE", "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+	/* Complete again without IF NOT EXISTS */
+	if (Matches2("CREATE", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 
 /* CREATE TABLESPACE */
@@ -2001,6 +2113,16 @@ psql_completion_internal(const char *text, char **previous_words,
 		COMPLETE_WITH_LIST2("GROUP", "ROLE");
 
 /* CREATE VIEW */
+	/* Complete CREATE VIEW with name */
+	if (Matches2("CREATE", "VIEW"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
+								   ADDLIST1("IF NOT EXISTS"));
+	/* Remove IF NOT EXISTS for further completion */
+	if (HeadMatches5("CREATE", "VIEW", "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+	/* Complete again without IF NOT EXISTS */
+	if (Matches2("CREATE", "VIEW"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, "");
 	/* Complete CREATE VIEW <name> with AS */
 	if (Matches3("CREATE", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("AS");
@@ -2080,20 +2202,43 @@ psql_completion_internal(const char *text, char **previous_words,
 	/* DROP INDEX */
 	if (Matches2("DROP", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
-								   ADDLIST1("CONCURRENTLY"));
+								   ADDLIST2("CONCURRENTLY", "IF EXISTS"));
 	if (Matches3("DROP", "INDEX", "CONCURRENTLY"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "");
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
+								   ADDLIST1("IF EXISTS"));
 	if (HeadMatches3("DROP", "INDEX", "CONCURRENTLY"))
 		COLLAPSE(3, 1);
+	/* Remove IF EXISTS for further completion */
+	if (HeadMatches4("DROP", "INDEX", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches2("DROP", "INDEX"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "");
 	if (Matches3("DROP", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
+	/* DROP VIEW */
+	if (Matches2("DROP", "VIEW"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
+								   ADDLIST1("IF EXISTS"));
+	/* Remove IF EXISTS for further completion */
+	if (HeadMatches4("DROP", "VIEW", "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+	/* Complet again without IF EXISTS */
+	if (Matches2("DROP", "VIEW"))
+		COMPLETE_THING(-1);
+
 	/* DROP MATERIALIZED VIEW */
 	if (Matches2("DROP", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
 
-	/* DROP VIEW is suggested as a general thing */
-
+	if (Matches3("DROP", "MATERIALIZED", "VIEW"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
+								   ADDLIST1("IF EXISTS"));
+	/* Remove IF EXISTS for further completion */
+	if (HeadMatches5("DROP", "MATERIALIZED", "VIEW", "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+	/* Complet again without IF EXISTS */
 	if (Matches3("DROP", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
 
@@ -2652,6 +2797,17 @@ psql_completion_internal(const char *text, char **previous_words,
 
 /* USER MAPPING */
 	if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
+	{
+		if (word_matches("DROP", prev3_wd))
+			COMPLETE_WITH_LIST2("FOR", "IF EXISTS FOR");
+		else
+			COMPLETE_WITH_CONST("FOR");
+	}
+	/* Remove IF EXISTS for further completion */
+	if (HeadMatches5("DROP", "USER", "MAPPING", "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+	/* Complete again without IF EXISTS */
+	if (Matches3("DROP", "USER", "MAPPING"))
 		COMPLETE_WITH_CONST("FOR");
 	if (Matches4("CREATE", "USER", "MAPPING", "FOR"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, 
-- 
2.9.2

