On Sun, Dec 20, 2015 at 8:08 AM, Michael Paquier
<michael.paqu...@gmail.com> wrote:
> On Sun, Dec 20, 2015 at 6:24 AM, Tom Lane <t...@sss.pgh.pa.us> wrote:
>> 1. I think it would be a good idea to convert the matching rules for
>> backslash commands too.  To do that, we'd need to provide a case-sensitive
>> equivalent to word_match and the matching macros.  I think we'd also have
>> to extend word_match to allow a trailing wildcard character, maybe "*".

I am not really sure I follow much the use of the wildcard, do you
mean to be able to work with the [S] extensions of the backslash
commands which are not completed now? I am attaching a patch that adds
support for a case-sensitive comparison facility without this wildcard
system, simplifying the backslash commands.

>> 2. I believe that a very large fraction of the TailMatches() rules really
>> ought to be Matches(), ie, they should not consider matches that don't
>> start at the start of the line.  And there's another bunch that could
>> be Matches() if the author hadn't been unaccountably lazy about checking
>> all words of the expected command.  If we converted as much as we could
>> that way, it would make psql_completion faster because many inapplicable
>> rules could be discarded after a single integer comparison on
>> previous_words_count, and it would greatly reduce the risk of inapplicable
>> matches.  We can't do that for rules meant to apply to DML statements,
>> since they can be buried in WITH, EXPLAIN, etc ... but an awful lot of
>> the DDL rules could be changed.

Yep, clearly. We may gain a bit of performance by matching directly
with an equal number of words using Matches instead of a lower bound
with TailMatches. I have looked at this thing and hacked a patch as
attached.

>> 3. The HeadMatches macros are pretty iffy because they can only look back
>> nine words.  I'm tempted to redesign get_previous_words so it just
>> tokenizes the whole line rather than having an arbitrary limitation.
>> (For that matter, it's long overdue for it to be able to deal with
>> multiline input...)
>>
>> I might go look at #3, but I can't currently summon the energy to tackle
>> #1 or #2 --- any volunteers?

#3 has been already done in the mean time...

> I could have a look at both of them and submit patch for next CF, both
> things do not seem that much complicated.

Those things are as well added to the next CF.
-- 
Michael
From 351894c975e72d62b6c49e8ea203fc194ccc59ee Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Tue, 29 Dec 2015 22:13:27 +0900
Subject: [PATCH 1/2] Improve performance of psql tab completion

TailMatches are based on a lower-bound check and Matches uses a direct
match for the number of words. It happens that the former is used in many
places where the latter could be used. Doing the switch improve the
performance of tab completion by having to match only a number of words
for many commands.
---
 src/bin/psql/tab-complete.c | 567 +++++++++++++++++++++++---------------------
 1 file changed, 291 insertions(+), 276 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 4c93ae9..c920353 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1137,6 +1137,21 @@ psql_completion(const char *text, int start, int end)
 #define Matches4(p1, p2, p3, p4) \
 	(previous_words_count == 4 && \
 	 TailMatches4(p1, p2, p3, p4))
+#define Matches5(p1, p2, p3, p4, p5) \
+	(previous_words_count == 5 && \
+	 TailMatches5(p1, p2, p3, p4, p5))
+#define Matches6(p1, p2, p3, p4, p5, p6) \
+	(previous_words_count == 6 && \
+	 TailMatches6(p1, p2, p3, p4, p5, p6))
+#define Matches7(p1, p2, p3, p4, p5, p6, p7) \
+	(previous_words_count == 7 && \
+	 TailMatches7(p1, p2, p3, p4, p5, p6, p7))
+#define Matches8(p1, p2, p3, p4, p5, p6, p7, p8) \
+	(previous_words_count == 8 && \
+	 TailMatches8(p1, p2, p3, p4, p5, p6, p7, p8))
+#define Matches9(p1, p2, p3, p4, p5, p6, p7, p8, p9) \
+	(previous_words_count == 9 && \
+	 TailMatches9(p1, p2, p3, p4, p5, p6, p7, p8, p9))
 
 	/*
 	 * Macros for matching N words at the start of the line, regardless of
@@ -1266,10 +1281,10 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches7("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny))
 		COMPLETE_WITH_CONST("SET TABLESPACE");
 	/* ALTER AGGREGATE,FUNCTION <name> */
-	else if (TailMatches3("ALTER", "AGGREGATE|FUNCTION", MatchAny))
+	else if (Matches3("ALTER", "AGGREGATE|FUNCTION", MatchAny))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER AGGREGATE,FUNCTION <name> (...) */
-	else if (TailMatches4("ALTER", "AGGREGATE|FUNCTION", MatchAny, MatchAny))
+	else if (Matches4("ALTER", "AGGREGATE|FUNCTION", MatchAny, MatchAny))
 	{
 		if (ends_with(prev_wd, ')'))
 			COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
@@ -1278,49 +1293,49 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* ALTER SCHEMA <name> */
-	else if (TailMatches3("ALTER", "SCHEMA", MatchAny))
+	else if (Matches3("ALTER", "SCHEMA", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER TO", "RENAME TO");
 
 	/* ALTER COLLATION <name> */
-	else if (TailMatches3("ALTER", "COLLATION", MatchAny))
+	else if (Matches3("ALTER", "COLLATION", MatchAny))
 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
 
 	/* ALTER CONVERSION <name> */
-	else if (TailMatches3("ALTER", "CONVERSION", MatchAny))
+	else if (Matches3("ALTER", "CONVERSION", MatchAny))
 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
 
 	/* ALTER DATABASE <name> */
-	else if (TailMatches3("ALTER", "DATABASE", MatchAny))
+	else if (Matches3("ALTER", "DATABASE", MatchAny))
 		COMPLETE_WITH_LIST7("RESET", "SET", "OWNER TO", "RENAME TO",
 							"IS_TEMPLATE", "ALLOW_CONNECTIONS",
 							"CONNECTION LIMIT");
 
 	/* ALTER EVENT TRIGGER */
-	else if (TailMatches3("ALTER", "EVENT", "TRIGGER"))
+	else if (Matches3("ALTER", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
 
 	/* ALTER EVENT TRIGGER <name> */
-	else if (TailMatches4("ALTER", "EVENT", "TRIGGER", MatchAny))
+	else if (Matches4("ALTER", "EVENT", "TRIGGER", MatchAny))
 		COMPLETE_WITH_LIST4("DISABLE", "ENABLE", "OWNER TO", "RENAME TO");
 
 	/* ALTER EVENT TRIGGER <name> ENABLE */
-	else if (TailMatches5("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"))
+	else if (Matches5("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"))
 		COMPLETE_WITH_LIST2("REPLICA", "ALWAYS");
 
 	/* ALTER EXTENSION <name> */
-	else if (TailMatches3("ALTER", "EXTENSION", MatchAny))
+	else if (Matches3("ALTER", "EXTENSION", MatchAny))
 		COMPLETE_WITH_LIST4("ADD", "DROP", "UPDATE", "SET SCHEMA");
 
 	/* ALTER FOREIGN */
-	else if (TailMatches2("ALTER", "FOREIGN"))
+	else if (Matches2("ALTER", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
 
 	/* ALTER FOREIGN DATA WRAPPER <name> */
-	else if (TailMatches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+	else if (Matches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 		COMPLETE_WITH_LIST4("HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO");
 
 	/* ALTER FOREIGN TABLE <name> */
-	else if (TailMatches4("ALTER", "FOREIGN", "TABLE", MatchAny))
+	else if (Matches4("ALTER", "FOREIGN", "TABLE", MatchAny))
 	{
 		static const char *const list_ALTER_FOREIGN_TABLE[] =
 		{"ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE", "INHERIT",
@@ -1331,41 +1346,41 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* ALTER INDEX */
-	else if (TailMatches2("ALTER", "INDEX"))
+	else if (Matches2("ALTER", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
 								   "UNION SELECT 'ALL IN TABLESPACE'");
 	/* ALTER INDEX <name> */
-	else if (TailMatches3("ALTER", "INDEX", MatchAny))
+	else if (Matches3("ALTER", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET");
 	/* ALTER INDEX <name> SET */
-	else if (TailMatches4("ALTER", "INDEX", MatchAny, "SET"))
+	else if (Matches4("ALTER", "INDEX", MatchAny, "SET"))
 		COMPLETE_WITH_LIST2("(", "TABLESPACE");
 	/* ALTER INDEX <name> RESET */
-	else if (TailMatches4("ALTER", "INDEX", MatchAny, "RESET"))
+	else if (Matches4("ALTER", "INDEX", MatchAny, "RESET"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER INDEX <foo> SET|RESET ( */
-	else if (TailMatches5("ALTER", "INDEX", MatchAny, "RESET", "("))
+	else if (Matches5("ALTER", "INDEX", MatchAny, "RESET", "("))
 		COMPLETE_WITH_LIST3("fillfactor", "fastupdate",
 							"gin_pending_list_limit");
-	else if (TailMatches5("ALTER", "INDEX", MatchAny, "SET", "("))
+	else if (Matches5("ALTER", "INDEX", MatchAny, "SET", "("))
 		COMPLETE_WITH_LIST3("fillfactor =", "fastupdate =",
 							"gin_pending_list_limit =");
 
 	/* ALTER LANGUAGE <name> */
-	else if (TailMatches3("ALTER", "LANGUAGE", MatchAny))
+	else if (Matches3("ALTER", "LANGUAGE", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER_TO", "RENAME TO");
 
 	/* ALTER LARGE OBJECT <oid> */
-	else if (TailMatches4("ALTER", "LARGE", "OBJECT", MatchAny))
+	else if (Matches4("ALTER", "LARGE", "OBJECT", MatchAny))
 		COMPLETE_WITH_CONST("OWNER TO");
 
 	/* ALTER MATERIALIZED VIEW */
-	else if (TailMatches3("ALTER", "MATERIALIZED", "VIEW"))
+	else if (Matches3("ALTER", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 								   "UNION SELECT 'ALL IN TABLESPACE'");
 
 	/* ALTER USER,ROLE <name> */
-	else if (TailMatches3("ALTER", "USER|ROLE", MatchAny) &&
+	else if (Matches3("ALTER", "USER|ROLE", MatchAny) &&
 			 !TailMatches2("USER", "MAPPING"))
 	{
 		static const char *const list_ALTERUSER[] =
@@ -1380,7 +1395,7 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* ALTER USER,ROLE <name> WITH */
-	else if (TailMatches4("ALTER", "USER|ROLE", MatchAny, "WITH"))
+	else if (Matches4("ALTER", "USER|ROLE", MatchAny, "WITH"))
 	{
 		/* Similar to the above, but don't complete "WITH" again. */
 		static const char *const list_ALTERUSER_WITH[] =
@@ -1395,43 +1410,43 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
-	else if (TailMatches4("ALTER", "USER|ROLE", MatchAny, "ENCRYPTED|UNENCRYPTED"))
+	else if (Matches4("ALTER", "USER|ROLE", MatchAny, "ENCRYPTED|UNENCRYPTED"))
 		COMPLETE_WITH_CONST("PASSWORD");
 	/* ALTER DEFAULT PRIVILEGES */
-	else if (TailMatches3("ALTER", "DEFAULT", "PRIVILEGES"))
+	else if (Matches3("ALTER", "DEFAULT", "PRIVILEGES"))
 		COMPLETE_WITH_LIST3("FOR ROLE", "FOR USER", "IN SCHEMA");
 	/* ALTER DEFAULT PRIVILEGES FOR */
-	else if (TailMatches4("ALTER", "DEFAULT", "PRIVILEGES", "FOR"))
+	else if (Matches4("ALTER", "DEFAULT", "PRIVILEGES", "FOR"))
 		COMPLETE_WITH_LIST2("ROLE", "USER");
 	/* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */
-	else if (TailMatches5("DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny) ||
-			 TailMatches5("DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny))
+	else if (Matches5("DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny) ||
+			 Matches5("DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny))
 		COMPLETE_WITH_LIST2("GRANT", "REVOKE");
 	/* ALTER DOMAIN <name> */
-	else if (TailMatches3("ALTER", "DOMAIN", MatchAny))
+	else if (Matches3("ALTER", "DOMAIN", MatchAny))
 		COMPLETE_WITH_LIST6("ADD", "DROP", "OWNER TO", "RENAME", "SET",
 							"VALIDATE CONSTRAINT");
 	/* ALTER DOMAIN <sth> DROP */
-	else if (TailMatches4("ALTER", "DOMAIN", MatchAny, "DROP"))
+	else if (Matches4("ALTER", "DOMAIN", MatchAny, "DROP"))
 		COMPLETE_WITH_LIST3("CONSTRAINT", "DEFAULT", "NOT NULL");
 	/* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
-	else if (TailMatches5("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"))
+	else if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_type);
 	}
 	/* ALTER DOMAIN <sth> RENAME */
-	else if (TailMatches4("ALTER", "DOMAIN", MatchAny, "RENAME"))
+	else if (Matches4("ALTER", "DOMAIN", MatchAny, "RENAME"))
 		COMPLETE_WITH_LIST2("CONSTRAINT", "TO");
 	/* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
-	else if (TailMatches5("DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny))
+	else if (Matches5("DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny))
 		COMPLETE_WITH_CONST("TO");
 
 	/* ALTER DOMAIN <sth> SET */
-	else if (TailMatches4("ALTER", "DOMAIN", MatchAny, "SET"))
+	else if (Matches4("ALTER", "DOMAIN", MatchAny, "SET"))
 		COMPLETE_WITH_LIST3("DEFAULT", "NOT NULL", "SCHEMA");
 	/* ALTER SEQUENCE <name> */
-	else if (TailMatches3("ALTER", "SEQUENCE", MatchAny))
+	else if (Matches3("ALTER", "SEQUENCE", MatchAny))
 	{
 		static const char *const list_ALTERSEQUENCE[] =
 		{"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
@@ -1440,71 +1455,71 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
 	}
 	/* ALTER SEQUENCE <name> NO */
-	else if (TailMatches4("ALTER", "SEQUENCE", MatchAny, "NO"))
+	else if (Matches4("ALTER", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE");
 	/* ALTER SERVER <name> */
-	else if (TailMatches3("ALTER", "SERVER", MatchAny))
+	else if (Matches3("ALTER", "SERVER", MatchAny))
 		COMPLETE_WITH_LIST3("VERSION", "OPTIONS", "OWNER TO");
 	/* ALTER SYSTEM SET, RESET, RESET ALL */
-	else if (TailMatches2("ALTER", "SYSTEM"))
+	else if (Matches2("ALTER", "SYSTEM"))
 		COMPLETE_WITH_LIST2("SET", "RESET");
 	/* ALTER SYSTEM SET|RESET <name> */
-	else if (TailMatches4("ALTER", "SYSTEM", "SET|RESET", MatchAny))
+	else if (Matches4("ALTER", "SYSTEM", "SET|RESET", MatchAny))
 		COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars);
 	/* ALTER VIEW <name> */
-	else if (TailMatches3("ALTER", "VIEW", MatchAny))
+	else if (Matches3("ALTER", "VIEW", MatchAny))
 		COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
 							"SET SCHEMA");
 	/* ALTER MATERIALIZED VIEW <name> */
-	else if (TailMatches4("ALTER", "MATERIALIZED", "VIEW", MatchAny))
+	else if (Matches4("ALTER", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
 							"SET SCHEMA");
 
 	/* ALTER POLICY <name> */
-	else if (TailMatches2("ALTER", "POLICY"))
+	else if (Matches2("ALTER", "POLICY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_policies);
 	/* ALTER POLICY <name> ON */
-	else if (TailMatches3("ALTER", "POLICY", MatchAny))
+	else if (Matches3("ALTER", "POLICY", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* ALTER POLICY <name> ON <table> */
-	else if (TailMatches4("ALTER", "POLICY", MatchAny, "ON"))
+	else if (Matches4("ALTER", "POLICY", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);
 	}
 	/* ALTER POLICY <name> ON <table> - show options */
-	else if (TailMatches5("ALTER", "POLICY", MatchAny, "ON", MatchAny))
+	else if (Matches5("ALTER", "POLICY", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST4("RENAME TO", "TO", "USING", "WITH CHECK");
 	/* ALTER POLICY <name> ON <table> TO <role> */
-	else if (TailMatches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"))
+	else if (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
 	/* ALTER POLICY <name> ON <table> USING ( */
-	else if (TailMatches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
+	else if (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER POLICY <name> ON <table> WITH CHECK ( */
-	else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
+	else if (Matches6("POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
 		COMPLETE_WITH_CONST("(");
 
 	/* ALTER RULE <name>, add ON */
-	else if (TailMatches3("ALTER", "RULE", MatchAny))
+	else if (Matches3("ALTER", "RULE", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 
 	/* If we have ALTER RULE <name> ON, then add the correct tablename */
-	else if (TailMatches4("ALTER", "RULE", MatchAny, "ON"))
+	else if (Matches4("ALTER", "RULE", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
 	}
 
 	/* ALTER RULE <name> ON <name> */
-	else if (TailMatches5("ALTER", "RULE", MatchAny, "ON", MatchAny))
+	else if (Matches5("ALTER", "RULE", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_CONST("RENAME TO");
 
 	/* ALTER TRIGGER <name>, add ON */
-	else if (TailMatches3("ALTER", "TRIGGER", MatchAny))
+	else if (Matches3("ALTER", "TRIGGER", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 
-	else if (TailMatches4("ALTER", "TRIGGER", MatchAny, MatchAny))
+	else if (Matches4("ALTER", "TRIGGER", MatchAny, MatchAny))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
@@ -1513,17 +1528,17 @@ psql_completion(const char *text, int start, int end)
 	/*
 	 * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
 	 */
-	else if (TailMatches4("ALTER", "TRIGGER", MatchAny, "ON"))
+	else if (Matches4("ALTER", "TRIGGER", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
 	/* ALTER TRIGGER <name> ON <name> */
-	else if (TailMatches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
+	else if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_CONST("RENAME TO");
 
 	/*
 	 * If we detect ALTER TABLE <name>, suggest sub commands
 	 */
-	else if (TailMatches3("ALTER", "TABLE", MatchAny))
+	else if (Matches3("ALTER", "TABLE", MatchAny))
 	{
 		static const char *const list_ALTER2[] =
 		{"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
@@ -1533,134 +1548,134 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST(list_ALTER2);
 	}
 	/* ALTER TABLE xxx ENABLE */
-	else if (TailMatches4("ALTER", "TABLE", MatchAny, "ENABLE"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "ENABLE"))
 		COMPLETE_WITH_LIST5("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE",
 							"TRIGGER");
-	else if (TailMatches4("TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"))
+	else if (Matches4("TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"))
 		COMPLETE_WITH_LIST2("RULE", "TRIGGER");
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_rule_of_table);
 	}
-	else if (TailMatches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
+	else if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
 	{
 		completion_info_charp = prev4_wd;
 		COMPLETE_WITH_QUERY(Query_for_rule_of_table);
 	}
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
 	}
-	else if (TailMatches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"))
+	else if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"))
 	{
 		completion_info_charp = prev4_wd;
 		COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
 	}
 	/* ALTER TABLE xxx INHERIT */
-	else if (TailMatches4("ALTER", "TABLE", MatchAny, "INHERIT"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "INHERIT"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* ALTER TABLE xxx NO INHERIT */
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* ALTER TABLE xxx DISABLE */
-	else if (TailMatches4("ALTER", "TABLE", MatchAny, "DISABLE"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE"))
 		COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER");
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_rule_of_table);
 	}
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
 	}
-	else if (TailMatches4("DISABLE", "ROW", "LEVEL", "SECURITY"))
+	else if (Matches4("DISABLE", "ROW", "LEVEL", "SECURITY"))
 		COMPLETE_WITH_CONST("CASCADE");
 
 	/* ALTER TABLE xxx ALTER */
-	else if (TailMatches4("ALTER", "TABLE", MatchAny, "ALTER"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "ALTER"))
 		COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT'");
 
 	/* ALTER TABLE xxx RENAME */
-	else if (TailMatches4("ALTER", "TABLE", MatchAny, "RENAME"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "RENAME"))
 		COMPLETE_WITH_ATTR(prev2_wd, " UNION SELECT 'COLUMN' UNION SELECT 'CONSTRAINT' UNION SELECT 'TO'");
 
 	/*
 	 * If we have TABLE <sth> ALTER COLUMN|RENAME COLUMN, provide list of
 	 * columns
 	 */
-	else if (TailMatches4("TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
 
 	/* ALTER TABLE xxx RENAME yyy */
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")))
 		COMPLETE_WITH_CONST("TO");
 
 	/* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
-	else if (TailMatches5("TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")))
+	else if (Matches6("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")))
 		COMPLETE_WITH_CONST("TO");
 
 	/* If we have TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
-	else if (TailMatches3("TABLE", MatchAny, "DROP"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "DROP"))
 		COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT");
 	/* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
+	else 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
 	 */
-	else if (TailMatches5("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME|VALIDATE", "CONSTRAINT"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME|VALIDATE", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_table);
 	}
 	/* ALTER TABLE ALTER [COLUMN] <foo> */
-	else if (TailMatches3("ALTER", "COLUMN", MatchAny) ||
-			 TailMatches4("TABLE", MatchAny, "ALTER", MatchAny))
+	else if (Matches6("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny) ||
+			 Matches5("ALTER", "TABLE", MatchAny, "ALTER", MatchAny))
 		COMPLETE_WITH_LIST4("TYPE", "SET", "RESET", "DROP");
 	/* ALTER TABLE ALTER [COLUMN] <foo> SET */
-	else if (TailMatches4("ALTER", "COLUMN", MatchAny, "SET") ||
-			 TailMatches5("TABLE", MatchAny, "ALTER", MatchAny, "SET"))
+	else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
+			 Matches6("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
 		COMPLETE_WITH_LIST5("(", "DEFAULT", "NOT NULL", "STATISTICS", "STORAGE");
 	/* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
-	else if (TailMatches5("ALTER", "COLUMN", MatchAny, "SET", "(") ||
-			 TailMatches4("ALTER", MatchAny, "SET", "("))
+	else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
+			 Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
 		COMPLETE_WITH_LIST2("n_distinct", "n_distinct_inherited");
 	/* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
-	else if (TailMatches5("ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
-			 TailMatches4("ALTER", MatchAny, "SET", "STORAGE"))
+	else if (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
+			 Matches7("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
 		COMPLETE_WITH_LIST4("PLAIN", "EXTERNAL", "EXTENDED", "MAIN");
 	/* ALTER TABLE ALTER [COLUMN] <foo> DROP */
-	else if (TailMatches4("ALTER", "COLUMN", MatchAny, "DROP") ||
-			 TailMatches5("TABLE", MatchAny, "ALTER", MatchAny, "DROP"))
+	else if (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP") ||
+			 Matches8("ALTER", "TABLE", MatchAny, "TABLE", MatchAny, "ALTER", MatchAny, "DROP"))
 		COMPLETE_WITH_LIST2("DEFAULT", "NOT NULL");
-	else if (TailMatches3("TABLE", MatchAny, "CLUSTER"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "CLUSTER"))
 		COMPLETE_WITH_CONST("ON");
-	else if (TailMatches4("TABLE", MatchAny, "CLUSTER", "ON"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_index_of_table);
 	}
-	/* If we have TABLE <sth> SET, provide list of attributes and '(' */
-	else if (TailMatches3("TABLE", MatchAny, "SET"))
+	/* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
+	else if (Matches4("ALTER", "TABLE", MatchAny, "SET"))
 		COMPLETE_WITH_LIST7("(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED",
 							"WITH", "WITHOUT");
-	/* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
-	else if (TailMatches4("TABLE", MatchAny, "SET", "TABLESPACE"))
+	/* If we have ALTER TABLE <sth> SET TABLESPACE provide a list of tablespaces */
+	else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
-	/* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
-	else if (TailMatches4("TABLE", MatchAny, "SET", "WITHOUT"))
+	/* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
+	else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"))
 		COMPLETE_WITH_LIST2("CLUSTER", "OIDS");
 	/* ALTER TABLE <foo> RESET */
-	else if (TailMatches3("TABLE", MatchAny, "RESET"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "RESET"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER TABLE <foo> SET|RESET ( */
-	else if (TailMatches4("TABLE", MatchAny, "SET|RESET", "("))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "SET|RESET", "("))
 	{
 		static const char *const list_TABLEOPTIONS[] =
 		{
@@ -1697,59 +1712,59 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST(list_TABLEOPTIONS);
 	}
-	else if (TailMatches5(MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
+	else if (Matches7("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
 	{
 		completion_info_charp = prev5_wd;
 		COMPLETE_WITH_QUERY(Query_for_index_of_table);
 	}
-	else if (TailMatches5("TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
+	else if (Matches6("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
 		COMPLETE_WITH_CONST("INDEX");
-	else if (TailMatches4("TABLE", MatchAny, "REPLICA", "IDENTITY"))
+	else if (Matches5("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"))
 		COMPLETE_WITH_LIST4("FULL", "NOTHING", "DEFAULT", "USING");
-	else if (TailMatches3("TABLE", MatchAny, "REPLICA"))
+	else if (Matches4("ALTER", "TABLE", MatchAny, "REPLICA"))
 		COMPLETE_WITH_CONST("IDENTITY");
 
 	/* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
-	else if (TailMatches3("ALTER", "TABLESPACE", MatchAny))
+	else if (Matches3("ALTER", "TABLESPACE", MatchAny))
 		COMPLETE_WITH_LIST4("RENAME TO", "OWNER TO", "SET", "RESET");
 	/* ALTER TABLESPACE <foo> SET|RESET */
-	else if (TailMatches4("ALTER", "TABLESPACE", MatchAny, "SET|RESET"))
+	else if (Matches4("ALTER", "TABLESPACE", MatchAny, "SET|RESET"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER TABLESPACE <foo> SET|RESET ( */
-	else if (TailMatches5("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("))
+	else if (Matches5("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("))
 		COMPLETE_WITH_LIST3("seq_page_cost", "random_page_cost",
 							"effective_io_concurrency");
 
 	/* ALTER TEXT SEARCH */
-	else if (TailMatches3("ALTER", "TEXT", "SEARCH"))
+	else if (Matches3("ALTER", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-	else if (TailMatches5("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny))
+	else if (Matches5("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny))
 		COMPLETE_WITH_LIST2("RENAME TO", "SET SCHEMA");
-	else if (TailMatches5("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny))
+	else if (Matches5("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny))
 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
-	else if (TailMatches5("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
+	else if (Matches5("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
 		COMPLETE_WITH_LIST6("ADD MAPPING FOR", "ALTER MAPPING",
 							"DROP MAPPING FOR",
 							"OWNER TO", "RENAME TO", "SET SCHEMA");
 
 	/* complete ALTER TYPE <foo> with actions */
-	else if (TailMatches3("ALTER", "TYPE", MatchAny))
+	else if (Matches3("ALTER", "TYPE", MatchAny))
 		COMPLETE_WITH_LIST7("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE",
 							"DROP ATTRIBUTE",
 							"OWNER TO", "RENAME", "SET SCHEMA");
 	/* complete ALTER TYPE <foo> ADD with actions */
-	else if (TailMatches4("ALTER", "TYPE", MatchAny, "ADD"))
+	else if (Matches4("ALTER", "TYPE", MatchAny, "ADD"))
 		COMPLETE_WITH_LIST2("ATTRIBUTE", "VALUE");
 	/* ALTER TYPE <foo> RENAME	*/
-	else if (TailMatches4("ALTER", "TYPE", MatchAny, "RENAME"))
+	else if (Matches4("ALTER", "TYPE", MatchAny, "RENAME"))
 		COMPLETE_WITH_LIST2("ATTRIBUTE", "TO");
 	/* ALTER TYPE xxx RENAME ATTRIBUTE yyy */
-	else if (TailMatches5("TYPE", MatchAny, "RENAME", "ATTRIBUTE", MatchAny))
+	else if (Matches6("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE", MatchAny))
 		COMPLETE_WITH_CONST("TO");
 
 	/*
-	 * If we have TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list of
-	 * attributes
+	 * If we have TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list
+	 * of attributes
 	 */
 	else if (TailMatches4("TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
@@ -1757,26 +1772,26 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches3("ALTER", "ATTRIBUTE", MatchAny))
 		COMPLETE_WITH_CONST("TYPE");
 	/* complete ALTER GROUP <foo> */
-	else if (TailMatches3("ALTER", "GROUP", MatchAny))
+	else if (Matches3("ALTER", "GROUP", MatchAny))
 		COMPLETE_WITH_LIST3("ADD USER", "DROP USER", "RENAME TO");
 	/* complete ALTER GROUP <foo> ADD|DROP with USER */
-	else if (TailMatches4("ALTER", "GROUP", MatchAny, "ADD|DROP"))
+	else if (Matches4("ALTER", "GROUP", MatchAny, "ADD|DROP"))
 		COMPLETE_WITH_CONST("USER");
 	/* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
-	else if (TailMatches5("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"))
+	else if (Matches5("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
 /* BEGIN, END, ABORT */
-	else if (TailMatches1("BEGIN|END|ABORT"))
+	else if (Matches1("BEGIN|END|ABORT"))
 		COMPLETE_WITH_LIST2("WORK", "TRANSACTION");
 /* COMMIT */
-	else if (TailMatches1("COMMIT"))
+	else if (Matches1("COMMIT"))
 		COMPLETE_WITH_LIST3("WORK", "TRANSACTION", "PREPARED");
 /* RELEASE SAVEPOINT */
-	else if (TailMatches1("RELEASE"))
+	else if (Matches1("RELEASE"))
 		COMPLETE_WITH_CONST("SAVEPOINT");
 /* ROLLBACK*/
-	else if (TailMatches1("ROLLBACK"))
+	else if (Matches1("ROLLBACK"))
 		COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
 /* CLUSTER */
 
@@ -1818,9 +1833,9 @@ psql_completion(const char *text, int start, int end)
 	}
 
 /* COMMENT */
-	else if (TailMatches1("COMMENT"))
+	else if (Matches1("COMMENT"))
 		COMPLETE_WITH_CONST("ON");
-	else if (TailMatches2("COMMENT", "ON"))
+	else if (Matches2("COMMENT", "ON"))
 	{
 		static const char *const list_COMMENT[] =
 		{"CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION",
@@ -1832,26 +1847,26 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST(list_COMMENT);
 	}
-	else if (TailMatches3("COMMENT", "ON", "FOREIGN"))
+	else if (Matches3("COMMENT", "ON", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
-	else if (TailMatches4("COMMENT", "ON", "TEXT", "SEARCH"))
+	else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-	else if (TailMatches3("COMMENT", "ON", "CONSTRAINT"))
+	else if (Matches3("COMMENT", "ON", "CONSTRAINT"))
 		COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
-	else if (TailMatches4("COMMENT", "ON", "CONSTRAINT", MatchAny))
+	else if (Matches4("COMMENT", "ON", "CONSTRAINT", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (TailMatches5("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
+	else if (Matches5("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint);
 	}
-	else if (TailMatches4("COMMENT", "ON", "MATERIALIZED", "VIEW"))
+	else if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
-	else if (TailMatches4("COMMENT", "ON", "EVENT", "TRIGGER"))
+	else if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
-	else if (TailMatches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
-			 TailMatches5("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) ||
-			 TailMatches6("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")))
+	else if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
+			 Matches5("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) ||
+			 Matches6("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")))
 		COMPLETE_WITH_CONST("IS");
 
 /* COPY */
@@ -1860,7 +1875,7 @@ psql_completion(const char *text, int start, int end)
 	 * If we have COPY [BINARY] (which you'd have to type yourself), offer
 	 * list of tables (Also cover the analogous backslash command)
 	 */
-	else if (TailMatches1("COPY|\\copy") || TailMatches2("COPY", "BINARY"))
+	else if (Matches1("COPY|\\copy") || Matches2("COPY", "BINARY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 	/* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
 	else if (TailMatches2("COPY|\\copy|BINARY", MatchAny))
@@ -1883,40 +1898,40 @@ psql_completion(const char *text, int start, int end)
 							"FORCE NOT NULL");
 
 	/* CREATE DATABASE */
-	else if (TailMatches3("CREATE", "DATABASE", MatchAny))
+	else if (Matches3("CREATE", "DATABASE", MatchAny))
 		COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
 							"IS_TEMPLATE",
 							"ALLOW_CONNECTIONS", "CONNECTION LIMIT",
 							"LC_COLLATE", "LC_CTYPE");
 
-	else if (TailMatches4("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
+	else if (Matches4("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
 
 	/* CREATE EXTENSION */
 	/* Complete with available extensions rather than installed ones. */
-	else if (TailMatches2("CREATE", "EXTENSION"))
+	else if (Matches2("CREATE", "EXTENSION"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
 	/* CREATE EXTENSION <name> */
-	else if (TailMatches3("CREATE", "EXTENSION", MatchAny))
+	else if (Matches3("CREATE", "EXTENSION", MatchAny))
 		COMPLETE_WITH_LIST3("WITH SCHEMA", "CASCADE", "VERSION");
 	/* CREATE EXTENSION <name> VERSION */
-	else if (TailMatches4("CREATE", "EXTENSION", MatchAny, "VERSION"))
+	else if (Matches4("CREATE", "EXTENSION", MatchAny, "VERSION"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
 	}
 
 	/* CREATE FOREIGN */
-	else if (TailMatches2("CREATE", "FOREIGN"))
+	else if (Matches2("CREATE", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
 
 	/* CREATE FOREIGN DATA WRAPPER */
-	else if (TailMatches5("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+	else if (Matches5("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 		COMPLETE_WITH_LIST2("HANDLER", "VALIDATOR");
 
 	/* CREATE INDEX */
 	/* First off we complete CREATE UNIQUE with "INDEX" */
-	else if (TailMatches2("CREATE", "UNIQUE"))
+	else if (Matches2("CREATE", "UNIQUE"))
 		COMPLETE_WITH_CONST("INDEX");
 	/* If we have CREATE|UNIQUE INDEX, then add "ON" and existing indexes */
 	else if (TailMatches2("CREATE|UNIQUE", "INDEX"))
@@ -1960,39 +1975,39 @@ psql_completion(const char *text, int start, int end)
 
 	/* CREATE POLICY */
 	/* Complete "CREATE POLICY <name> ON" */
-	else if (TailMatches3("CREATE", "POLICY", MatchAny))
+	else if (Matches3("CREATE", "POLICY", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* Complete "CREATE POLICY <name> ON <table>" */
-	else if (TailMatches4("CREATE", "POLICY", MatchAny, "ON"))
+	else if (Matches4("CREATE", "POLICY", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 	/* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */
-	else if (TailMatches5("CREATE", "POLICY", MatchAny, "ON", MatchAny))
+	else if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST4("FOR", "TO", "USING", "WITH CHECK");
 	/* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE */
-	else if (TailMatches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"))
+	else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"))
 		COMPLETE_WITH_LIST5("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
 	/* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */
-	else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"))
+	else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"))
 		COMPLETE_WITH_LIST2("TO", "WITH CHECK");
 	/* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE TO|USING" */
-	else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"))
+	else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"))
 		COMPLETE_WITH_LIST2("TO", "USING");
 	/* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */
-	else if (TailMatches6("POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"))
+	else if (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"))
 		COMPLETE_WITH_LIST3("TO", "USING", "WITH CHECK");
 	/* Complete "CREATE POLICY <name> ON <table> TO <role>" */
-	else if (TailMatches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"))
+	else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
 	/* Complete "CREATE POLICY <name> ON <table> USING (" */
-	else if (TailMatches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
+	else if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
 		COMPLETE_WITH_CONST("(");
 
 /* CREATE RULE */
 	/* Complete "CREATE RULE <sth>" with "AS" */
-	else if (TailMatches3("CREATE", "RULE", MatchAny))
+	else if (Matches3("CREATE", "RULE", MatchAny))
 		COMPLETE_WITH_CONST("AS");
 	/* Complete "CREATE RULE <sth> AS" with "ON" */
-	else if (TailMatches4("CREATE", "RULE", MatchAny, "AS"))
+	else if (Matches4("CREATE", "RULE", MatchAny, "AS"))
 		COMPLETE_WITH_CONST("ON");
 	/* Complete "RULE * AS ON" with SELECT|UPDATE|INSERT|DELETE */
 	else if (TailMatches4("RULE", MatchAny, "AS", "ON"))
@@ -2005,60 +2020,60 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
 /* CREATE TEMP/TEMPORARY SEQUENCE <name> */
-	else if (TailMatches3("CREATE", "SEQUENCE", MatchAny) ||
-			 TailMatches4("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
+	else if (Matches3("CREATE", "SEQUENCE", MatchAny) ||
+			 Matches4("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
 		COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE",
 							"CYCLE", "OWNED BY", "START WITH");
 /* CREATE TEMP/TEMPORARY SEQUENCE <name> NO */
-	else if (TailMatches4("CREATE", "SEQUENCE", MatchAny, "NO") ||
+	else if (Matches4("CREATE", "SEQUENCE", MatchAny, "NO") ||
 		TailMatches5("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE");
 
 /* CREATE SERVER <name> */
-	else if (TailMatches3("CREATE", "SERVER", MatchAny))
+	else if (Matches3("CREATE", "SERVER", MatchAny))
 		COMPLETE_WITH_LIST3("TYPE", "VERSION", "FOREIGN DATA WRAPPER");
 
 /* CREATE TABLE */
 	/* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
-	else if (TailMatches2("CREATE", "TEMP|TEMPORARY"))
+	else if (Matches2("CREATE", "TEMP|TEMPORARY"))
 		COMPLETE_WITH_LIST3("SEQUENCE", "TABLE", "VIEW");
 	/* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */
-	else if (TailMatches2("CREATE", "UNLOGGED"))
+	else if (Matches2("CREATE", "UNLOGGED"))
 		COMPLETE_WITH_LIST2("TABLE", "MATERIALIZED VIEW");
 
 /* CREATE TABLESPACE */
-	else if (TailMatches3("CREATE", "TABLESPACE", MatchAny))
+	else if (Matches3("CREATE", "TABLESPACE", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER", "LOCATION");
 	/* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
-	else if (TailMatches5("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny))
+	else if (Matches5("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny))
 		COMPLETE_WITH_CONST("LOCATION");
 
 /* CREATE TEXT SEARCH */
-	else if (TailMatches3("CREATE", "TEXT", "SEARCH"))
+	else if (Matches3("CREATE", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-	else if (TailMatches4("TEXT", "SEARCH", "CONFIGURATION", MatchAny))
+	else if (TailMatches5("CREATE", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
 		COMPLETE_WITH_CONST("(");
 
 /* CREATE TRIGGER */
 	/* complete CREATE TRIGGER <name> with BEFORE,AFTER,INSTEAD OF */
-	else if (TailMatches3("CREATE", "TRIGGER", MatchAny))
+	else if (Matches3("CREATE", "TRIGGER", MatchAny))
 		COMPLETE_WITH_LIST3("BEFORE", "AFTER", "INSTEAD OF");
 	/* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */
-	else if (TailMatches4("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER"))
+	else if (Matches4("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER"))
 		COMPLETE_WITH_LIST4("INSERT", "DELETE", "UPDATE", "TRUNCATE");
 	/* complete CREATE TRIGGER <name> INSTEAD OF with an event */
-	else if (TailMatches5("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF"))
+	else if (Matches5("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF"))
 		COMPLETE_WITH_LIST3("INSERT", "DELETE", "UPDATE");
 	/* complete CREATE TRIGGER <name> BEFORE,AFTER sth with OR,ON */
-	else if (TailMatches5("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
-	  TailMatches6("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny))
+	else if (Matches5("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
+		 Matches6("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny))
 		COMPLETE_WITH_LIST2("ON", "OR");
 
 	/*
 	 * complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of
 	 * tables
 	 */
-	else if (TailMatches5("TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
+	else if (Matches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 	/* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */
 	else if (TailMatches4("INSTEAD", "OF", MatchAny, "ON"))
@@ -2068,7 +2083,7 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("PROCEDURE");
 
 /* CREATE ROLE,USER,GROUP <name> */
-	else if (TailMatches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
+	else if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
 			 !TailMatches2("USER", "MAPPING"))
 	{
 		static const char *const list_CREATEROLE[] =
@@ -2083,7 +2098,7 @@ psql_completion(const char *text, int start, int end)
 	}
 
 /* CREATE ROLE,USER,GROUP <name> WITH */
-	else if (TailMatches4("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"))
+	else if (Matches4("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"))
 	{
 		/* Similar to the above, but don't complete "WITH" again. */
 		static const char *const list_CREATEROLE_WITH[] =
@@ -2101,47 +2116,47 @@ psql_completion(const char *text, int start, int end)
 	 * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
 	 * PASSWORD
 	 */
-	else if (TailMatches4("CREATE", "ROLE|USER|GROUP", MatchAny, "ENCRYPTED|UNENCRYPTED"))
+	else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "ENCRYPTED|UNENCRYPTED"))
 		COMPLETE_WITH_CONST("PASSWORD");
 	/* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
-	else if (TailMatches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
+	else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
 		COMPLETE_WITH_LIST2("GROUP", "ROLE");
 
 /* CREATE VIEW */
 	/* Complete CREATE VIEW <name> with AS */
-	else if (TailMatches3("CREATE", "VIEW", MatchAny))
+	else if (Matches3("CREATE", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("AS");
 	/* Complete "CREATE VIEW <sth> AS with "SELECT" */
-	else if (TailMatches4("CREATE", "VIEW", MatchAny, "AS"))
+	else if (Matches4("CREATE", "VIEW", MatchAny, "AS"))
 		COMPLETE_WITH_CONST("SELECT");
 
 /* CREATE MATERIALIZED VIEW */
-	else if (TailMatches2("CREATE", "MATERIALIZED"))
+	else if (Matches2("CREATE", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
 	/* Complete CREATE MATERIALIZED VIEW <name> with AS */
-	else if (TailMatches4("CREATE", "MATERIALIZED", "VIEW", MatchAny))
+	else if (Matches4("CREATE", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("AS");
 	/* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
-	else if (TailMatches5("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"))
+	else if (Matches5("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"))
 		COMPLETE_WITH_CONST("SELECT");
 
 /* CREATE EVENT TRIGGER */
-	else if (TailMatches2("CREATE", "EVENT"))
+	else if (Matches2("CREATE", "EVENT"))
 		COMPLETE_WITH_CONST("TRIGGER");
 	/* Complete CREATE EVENT TRIGGER <name> with ON */
-	else if (TailMatches4("CREATE", "EVENT", "TRIGGER", MatchAny))
+	else if (Matches4("CREATE", "EVENT", "TRIGGER", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* Complete CREATE EVENT TRIGGER <name> ON with event_type */
-	else if (TailMatches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
+	else if (Matches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
 		COMPLETE_WITH_LIST3("ddl_command_start", "ddl_command_end", "sql_drop");
 
 /* DECLARE */
-	else if (TailMatches2("DECLARE", MatchAny))
+	else if (Matches2("DECLARE", MatchAny))
 		COMPLETE_WITH_LIST5("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
 							"CURSOR");
 
 /* CURSOR */
-	else if (TailMatches1("CURSOR"))
+	else if (Matches1("CURSOR"))
 		COMPLETE_WITH_LIST3("WITH HOLD", "WITHOUT HOLD", "FOR");
 
 /* DELETE */
@@ -2150,15 +2165,15 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches1("DELETE"))
 		COMPLETE_WITH_CONST("FROM");
 	/* Complete DELETE FROM with a list of tables */
-	else if (TailMatches2("DELETE", "FROM"))
+	else if (Matches2("DELETE", "FROM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
 	/* Complete DELETE FROM <table> */
-	else if (TailMatches3("DELETE", "FROM", MatchAny))
+	else if (Matches3("DELETE", "FROM", MatchAny))
 		COMPLETE_WITH_LIST2("USING", "WHERE");
 	/* XXX: implement tab completion for DELETE ... USING */
 
 /* DISCARD */
-	else if (TailMatches1("DISCARD"))
+	else if (Matches1("DISCARD"))
 		COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
 
 /* DO */
@@ -2166,84 +2181,84 @@ psql_completion(const char *text, int start, int end)
 	/*
 	 * Complete DO with LANGUAGE.
 	 */
-	else if (TailMatches1("DO"))
+	else if (Matches1("DO"))
 		COMPLETE_WITH_CONST("LANGUAGE");
 
 /* DROP (when not the previous word) */
 	/* DROP object with CASCADE / RESTRICT */
-	else if (TailMatches3("DROP",
+	else if (Matches3("DROP",
 						  "COLLATION|CONVERSION|DOMAIN|EXTENSION|INDEX|LANGUAGE|SCHEMA|SEQUENCE|SERVER|TABLE|TYPE|VIEW",
 						  MatchAny) ||
-		   (TailMatches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
+		   (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) &&
 			ends_with(prev_wd, ')')) ||
-			 TailMatches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
-			 TailMatches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
-			 TailMatches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
-			 TailMatches5("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
+			 Matches4("DROP", "EVENT", "TRIGGER", MatchAny) ||
+			 Matches5("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
+			 Matches4("DROP", "FOREIGN", "TABLE", MatchAny) ||
+			 Matches5("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 	/* help completing some of the variants */
-	else if (TailMatches3("DROP", "AGGREGATE|FUNCTION", MatchAny))
+	else if (Matches3("DROP", "AGGREGATE|FUNCTION", MatchAny))
 		COMPLETE_WITH_CONST("(");
-	else if (TailMatches4("DROP", "AGGREGATE|FUNCTION", MatchAny, "("))
+	else if (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, "("))
 		COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-	else if (TailMatches2("DROP", "FOREIGN"))
+	else if (Matches2("DROP", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
 
 	/* DROP MATERIALIZED VIEW */
-	else if (TailMatches2("DROP", "MATERIALIZED"))
+	else if (Matches2("DROP", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
-	else if (TailMatches3("DROP", "MATERIALIZED", "VIEW"))
+	else if (Matches3("DROP", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
 
 	/* DROP OWNED BY */
-	else if (TailMatches2("DROP", "OWNED"))
+	else if (Matches2("DROP", "OWNED"))
 		COMPLETE_WITH_CONST("BY");
-	else if (TailMatches3("DROP", "OWNED", "BY"))
+	else if (Matches3("DROP", "OWNED", "BY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
-	else if (TailMatches3("DROP", "TEXT", "SEARCH"))
+	else if (Matches3("DROP", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
 
 	/* DROP TRIGGER */
-	else if (TailMatches3("DROP", "TRIGGER", MatchAny))
+	else if (Matches3("DROP", "TRIGGER", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (TailMatches4("DROP", "TRIGGER", MatchAny, "ON"))
+	else if (Matches4("DROP", "TRIGGER", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger);
 	}
-	else if (TailMatches5("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
+	else if (Matches5("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 	/* DROP EVENT TRIGGER */
-	else if (TailMatches2("DROP", "EVENT"))
+	else if (Matches2("DROP", "EVENT"))
 		COMPLETE_WITH_CONST("TRIGGER");
-	else if (TailMatches3("DROP", "EVENT", "TRIGGER"))
+	else if (Matches3("DROP", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
 
 	/* DROP POLICY <name>  */
-	else if (TailMatches2("DROP", "POLICY"))
+	else if (Matches2("DROP", "POLICY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_policies);
 	/* DROP POLICY <name> ON */
-	else if (TailMatches3("DROP", "POLICY", MatchAny))
+	else if (Matches3("DROP", "POLICY", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* DROP POLICY <name> ON <table> */
-	else if (TailMatches4("DROP", "POLICY", MatchAny, "ON"))
+	else if (Matches4("DROP", "POLICY", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_policy);
 	}
 
 	/* DROP RULE */
-	else if (TailMatches3("DROP", "RULE", MatchAny))
+	else if (Matches3("DROP", "RULE", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (TailMatches4("DROP", "RULE", MatchAny, "ON"))
+	else if (Matches4("DROP", "RULE", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule);
 	}
-	else if (TailMatches5("DROP", "RULE", MatchAny, "ON", MatchAny))
+	else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 /* EXECUTE */
@@ -2255,22 +2270,22 @@ psql_completion(const char *text, int start, int end)
 	/*
 	 * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
 	 */
-	else if (TailMatches1("EXPLAIN"))
+	else if (Matches1("EXPLAIN"))
 		COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
 							"ANALYZE", "VERBOSE");
-	else if (TailMatches2("EXPLAIN", "ANALYZE"))
+	else if (Matches2("EXPLAIN", "ANALYZE"))
 		COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
 							"VERBOSE");
-	else if (TailMatches2("EXPLAIN", "VERBOSE") ||
-			 TailMatches3("EXPLAIN", "ANALYZE", "VERBOSE"))
+	else if (Matches2("EXPLAIN", "VERBOSE") ||
+			 Matches3("EXPLAIN", "ANALYZE", "VERBOSE"))
 		COMPLETE_WITH_LIST5("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE");
 
 /* FETCH && MOVE */
 	/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
-	else if (TailMatches1("FETCH|MOVE"))
+	else if (Matches1("FETCH|MOVE"))
 		COMPLETE_WITH_LIST4("ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE");
 	/* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
-	else if (TailMatches2("FETCH|MOVE", MatchAny))
+	else if (Matches2("FETCH|MOVE", MatchAny))
 		COMPLETE_WITH_LIST3("ALL", "NEXT", "PRIOR");
 
 	/*
@@ -2278,7 +2293,7 @@ psql_completion(const char *text, int start, int end)
 	 * but we may as well tab-complete both: perhaps some users prefer one
 	 * variant or the other.
 	 */
-	else if (TailMatches3("FETCH|MOVE", MatchAny, MatchAny))
+	else if (Matches3("FETCH|MOVE", MatchAny, MatchAny))
 		COMPLETE_WITH_LIST2("FROM", "IN");
 
 /* FOREIGN DATA WRAPPER */
@@ -2298,7 +2313,7 @@ psql_completion(const char *text, int start, int end)
 
 /* GRANT && REVOKE */
 	/* Complete GRANT/REVOKE with a list of roles and privileges */
-	else if (TailMatches1("GRANT|REVOKE"))
+	else if (Matches1("GRANT|REVOKE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles
 							" UNION SELECT 'SELECT'"
 							" UNION SELECT 'INSERT'"
@@ -2318,7 +2333,7 @@ psql_completion(const char *text, int start, int end)
 	 * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
 	 * TO/FROM
 	 */
-	else if (TailMatches2("GRANT|REVOKE", MatchAny))
+	else if (Matches2("GRANT|REVOKE", MatchAny))
 	{
 		if (TailMatches1("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
 			COMPLETE_WITH_CONST("ON");
@@ -2339,7 +2354,7 @@ psql_completion(const char *text, int start, int end)
 	 * here will only work if the privilege list contains exactly one
 	 * privilege.
 	 */
-	else if (TailMatches3("GRANT|REVOKE", MatchAny, "ON"))
+	else if (Matches3("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'"
@@ -2357,11 +2372,11 @@ psql_completion(const char *text, int start, int end)
 								   " UNION SELECT 'TABLESPACE'"
 								   " UNION SELECT 'TYPE'");
 
-	else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
+	else if (Matches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
 		COMPLETE_WITH_LIST3("FUNCTIONS IN SCHEMA", "SEQUENCES IN SCHEMA",
 							"TABLES IN SCHEMA");
 
-	else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
+	else if (Matches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
 
 	/*
@@ -2370,7 +2385,7 @@ psql_completion(const char *text, int start, int end)
 	 *
 	 * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
 	 */
-	else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", MatchAny))
+	else if (Matches4("GRANT|REVOKE", MatchAny, "ON", MatchAny))
 	{
 		if (TailMatches1("DATABASE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
@@ -2411,27 +2426,27 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("FROM");
 
 	/* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
-	else if (TailMatches8("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
+	else if (Matches8("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
 	{
-		if (TailMatches8("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+		if (Matches8("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 */
-	else if (TailMatches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+	else if (Matches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 	{
-		if (TailMatches7("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+		if (Matches7("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 */
-	else if (TailMatches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
+	else if (Matches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
 	{
-		if (TailMatches6("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+		if (Matches6("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
 			COMPLETE_WITH_CONST("TO");
 		else
 			COMPLETE_WITH_CONST("FROM");
@@ -2442,34 +2457,34 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("BY");
 
 /* IMPORT FOREIGN SCHEMA */
-	else if (TailMatches1("IMPORT"))
+	else if (Matches1("IMPORT"))
 		COMPLETE_WITH_CONST("FOREIGN SCHEMA");
-	else if (TailMatches2("IMPORT", "FOREIGN"))
+	else if (Matches2("IMPORT", "FOREIGN"))
 		COMPLETE_WITH_CONST("SCHEMA");
 
 /* INSERT */
 	/* Complete INSERT with "INTO" */
-	else if (TailMatches1("INSERT"))
+	else if (Matches1("INSERT"))
 		COMPLETE_WITH_CONST("INTO");
 	/* Complete INSERT INTO with table names */
-	else if (TailMatches2("INSERT", "INTO"))
+	else if (Matches2("INSERT", "INTO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, NULL);
 	/* Complete "INSERT INTO <table> (" with attribute names */
-	else if (TailMatches4("INSERT", "INTO", MatchAny, "("))
+	else if (Matches4("INSERT", "INTO", MatchAny, "("))
 		COMPLETE_WITH_ATTR(prev2_wd, "");
 
 	/*
 	 * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
 	 * "TABLE" or "DEFAULT VALUES"
 	 */
-	else if (TailMatches3("INSERT", "INTO", MatchAny))
+	else if (Matches3("INSERT", "INTO", MatchAny))
 		COMPLETE_WITH_LIST5("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES");
 
 	/*
 	 * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
 	 * "TABLE"
 	 */
-	else if (TailMatches4("INSERT", "INTO", MatchAny, MatchAny) &&
+	else if (Matches4("INSERT", "INTO", MatchAny, MatchAny) &&
 			 ends_with(prev_wd, ')'))
 		COMPLETE_WITH_LIST3("SELECT", "TABLE", "VALUES");
 
@@ -2479,22 +2494,22 @@ psql_completion(const char *text, int start, int end)
 
 /* LOCK */
 	/* Complete LOCK [TABLE] with a list of tables */
-	else if (TailMatches1("LOCK"))
+	else if (Matches1("LOCK"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 								   " UNION SELECT 'TABLE'");
-	else if (TailMatches2("LOCK", "TABLE"))
+	else if (Matches2("LOCK", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 
 	/* For the following, handle the case of a single table only for now */
 
 	/* Complete LOCK [TABLE] <table> with "IN" */
-	else if (TailMatches2("LOCK", MatchAnyExcept("TABLE")) ||
-			 TailMatches3("LOCK", "TABLE", MatchAny))
+	else if (Matches2("LOCK", MatchAnyExcept("TABLE")) ||
+			 Matches3("LOCK", "TABLE", MatchAny))
 		COMPLETE_WITH_CONST("IN");
 
 	/* Complete LOCK [TABLE] <table> IN with a lock mode */
-	else if (TailMatches3("LOCK", MatchAny, "IN") ||
-			 TailMatches4("LOCK", "TABLE", MatchAny, "IN"))
+	else if (Matches3("LOCK", MatchAny, "IN") ||
+			 Matches4("LOCK", "TABLE", MatchAny, "IN"))
 		COMPLETE_WITH_LIST8("ACCESS SHARE MODE",
 							"ROW SHARE MODE", "ROW EXCLUSIVE MODE",
 							"SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
@@ -2502,7 +2517,7 @@ psql_completion(const char *text, int start, int end)
 							"EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
 
 /* NOTIFY */
-	else if (TailMatches1("NOTIFY"))
+	else if (Matches1("NOTIFY"))
 		COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s'");
 
 /* OPTIONS */
@@ -2520,7 +2535,7 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_ATTR(prev3_wd, "");
 
 /* PREPARE xx AS */
-	else if (TailMatches3("PREPARE", MatchAny, "AS"))
+	else if (Matches3("PREPARE", MatchAny, "AS"))
 		COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE");
 
 /*
@@ -2529,42 +2544,42 @@ psql_completion(const char *text, int start, int end)
  */
 
 /* REASSIGN OWNED BY xxx TO yyy */
-	else if (TailMatches1("REASSIGN"))
+	else if (Matches1("REASSIGN"))
 		COMPLETE_WITH_CONST("OWNED BY");
-	else if (TailMatches2("REASSIGN", "OWNED"))
+	else if (Matches2("REASSIGN", "OWNED"))
 		COMPLETE_WITH_CONST("BY");
-	else if (TailMatches3("REASSIGN", "OWNED", "BY"))
+	else if (Matches3("REASSIGN", "OWNED", "BY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-	else if (TailMatches4("REASSIGN", "OWNED", "BY", MatchAny))
+	else if (Matches4("REASSIGN", "OWNED", "BY", MatchAny))
 		COMPLETE_WITH_CONST("TO");
-	else if (TailMatches5("REASSIGN", "OWNED", "BY", MatchAny, "TO"))
+	else if (Matches5("REASSIGN", "OWNED", "BY", MatchAny, "TO"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
 /* REFRESH MATERIALIZED VIEW */
-	else if (TailMatches1("REFRESH"))
+	else if (Matches1("REFRESH"))
 		COMPLETE_WITH_CONST("MATERIALIZED VIEW");
-	else if (TailMatches2("REFRESH", "MATERIALIZED"))
+	else if (Matches2("REFRESH", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
-	else if (TailMatches3("REFRESH", "MATERIALIZED", "VIEW"))
+	else if (Matches3("REFRESH", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 								   " UNION SELECT 'CONCURRENTLY'");
-	else if (TailMatches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
+	else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
-	else if (TailMatches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
+	else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("WITH");
-	else if (TailMatches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
+	else if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
 		COMPLETE_WITH_CONST("WITH DATA");
-	else if (TailMatches5("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"))
+	else if (Matches5("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"))
 		COMPLETE_WITH_LIST2("NO DATA", "DATA");
 	else if (TailMatches6("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"))
 		COMPLETE_WITH_CONST("DATA");
-	else if (TailMatches6("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"))
+	else if (Matches6("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"))
 		COMPLETE_WITH_CONST("DATA");
 
 /* REINDEX */
-	else if (TailMatches1("REINDEX"))
+	else if (Matches1("REINDEX"))
 		COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
-	else if (TailMatches2("REINDEX", MatchAny))
+	else if (Matches2("REINDEX", MatchAny))
 	{
 		if (TailMatches1("TABLE"))
 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, NULL);
@@ -2602,13 +2617,13 @@ psql_completion(const char *text, int start, int end)
 
 /* SET, RESET, SHOW */
 	/* Complete with a variable name */
-	else if (TailMatches1("SET|RESET") && !Matches3("UPDATE", MatchAny, "SET"))
+	else if (Matches1("SET|RESET"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_set_vars);
-	else if (TailMatches1("SHOW"))
+	else if (Matches1("SHOW"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_show_vars);
 	/* Complete "SET TRANSACTION" */
-	else if (TailMatches2("SET|BEGIN|START", "TRANSACTION") ||
-			 TailMatches2("BEGIN", "WORK") ||
+	else if (Matches2("SET|BEGIN|START", "TRANSACTION") ||
+			 Matches2("BEGIN", "WORK") ||
 			 TailMatches4("SESSION", "CHARACTERISTICS", "AS", "TRANSACTION"))
 		COMPLETE_WITH_LIST2("ISOLATION LEVEL", "READ");
 	else if (TailMatches3("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") ||
@@ -2626,19 +2641,19 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches2("SET", "CONSTRAINTS"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema, "UNION SELECT 'ALL'");
 	/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
-	else if (TailMatches3("SET", "CONSTRAINTS", MatchAny))
+	else if (Matches3("SET", "CONSTRAINTS", MatchAny))
 		COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE");
 	/* Complete SET ROLE */
-	else if (TailMatches2("SET", "ROLE"))
+	else if (Matches2("SET", "ROLE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 	/* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
-	else if (TailMatches2("SET", "SESSION"))
+	else if (Matches2("SET", "SESSION"))
 		COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
 	/* Complete SET SESSION AUTHORIZATION with username */
-	else if (TailMatches3("SET", "SESSION", "AUTHORIZATION"))
+	else if (Matches3("SET", "SESSION", "AUTHORIZATION"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles " UNION SELECT 'DEFAULT'");
 	/* Complete RESET SESSION with AUTHORIZATION */
-	else if (TailMatches2("RESET", "SESSION"))
+	else if (Matches2("RESET", "SESSION"))
 		COMPLETE_WITH_CONST("AUTHORIZATION");
 	/* Complete SET <var> with "TO" */
 	else if (Matches2("SET", MatchAny))
@@ -2701,11 +2716,11 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("(");
 
 /* TRUNCATE */
-	else if (TailMatches1("TRUNCATE"))
+	else if (Matches1("TRUNCATE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
 /* UNLISTEN */
-	else if (TailMatches1("UNLISTEN"))
+	else if (Matches1("UNLISTEN"))
 		COMPLETE_WITH_QUERY("SELECT pg_catalog.quote_ident(channel) FROM pg_catalog.pg_listening_channels() AS channel WHERE substring(pg_catalog.quote_ident(channel),1,%d)='%s' UNION SELECT '*'");
 
 /* UPDATE */
@@ -2729,16 +2744,16 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_CONST("=");
 
 /* USER MAPPING */
-	else if (TailMatches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
+	else if (Matches3("ALTER|CREATE|DROP", "USER", "MAPPING"))
 		COMPLETE_WITH_CONST("FOR");
-	else if (TailMatches4("CREATE", "USER", "MAPPING", "FOR"))
+	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'");
-	else if (TailMatches4("ALTER|DROP", "USER", "MAPPING", "FOR"))
+	else if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
-	else if (TailMatches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
+	else if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
 		COMPLETE_WITH_CONST("SERVER");
 
 /*
-- 
2.6.4

From 3648a0ccf0aee97b328053fef6fa7c336d7ee56b Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@otacoo.com>
Date: Tue, 29 Dec 2015 23:07:27 +0900
Subject: [PATCH 2/2] Adopt more compact tab completion for backslash commands
 in psql

This upgrades a bit the existing psql facility so as case-sensitive
comparisons can be done, which is a requirement contrary to other query
types that do not need to mind about that.
---
 src/bin/psql/tab-complete.c | 182 +++++++++++++++++++++++++-------------------
 1 file changed, 104 insertions(+), 78 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index c920353..ffa1942 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -963,8 +963,15 @@ initialize_readline(void)
 #define MatchAny  NULL
 #define MatchAnyExcept(pattern)  ("!" pattern)
 
+/*
+ * word_matches_internal
+ * Internal worker routine to check if a word matches a given pattern. Caller
+ * can optionally decide to make the check case-sensitive or not.
+ */
 static bool
-word_matches(const char *pattern, const char *word)
+word_matches_internal(const char *pattern,
+					  const char *word,
+					  bool case_sensitive)
 {
 	size_t		wordlen;
 
@@ -974,7 +981,7 @@ word_matches(const char *pattern, const char *word)
 
 	/* Handle negated patterns from the MatchAnyExcept macro. */
 	if (*pattern == '!')
-		return !word_matches(pattern + 1, word);
+		return !word_matches_internal(pattern + 1, word, case_sensitive);
 
 	/* Else consider each alternative in the pattern. */
 	wordlen = strlen(word);
@@ -987,9 +994,13 @@ word_matches(const char *pattern, const char *word)
 		while (*c != '\0' && *c != '|')
 			c++;
 		/* Match? */
-		if (wordlen == (c - pattern) &&
-			pg_strncasecmp(word, pattern, wordlen) == 0)
-			return true;
+		if (wordlen == (c - pattern))
+		{
+			/* Do the pattern comparison, depending on the sensitiveness */
+			if ((!case_sensitive && pg_strncasecmp(word, pattern, wordlen) == 0) ||
+				(case_sensitive && strncmp(word, pattern, wordlen) == 0))
+				return true;
+		}
 		/* Out of alternatives? */
 		if (*c == '\0')
 			break;
@@ -1001,6 +1012,26 @@ word_matches(const char *pattern, const char *word)
 }
 
 /*
+ * word_matches
+ * Utility routine to match a word with a pattern as case-insensitive.
+ */
+static bool
+word_matches(const char *pattern, const char *word)
+{
+	return word_matches_internal(pattern, word, false);
+}
+
+/*
+ * word_case_matches
+ * Utility routine to match a word with a patterm as case-sensitive.
+ */
+static bool
+word_case_matches(const char *pattern, const char *word)
+{
+	return word_matches_internal(pattern, word, true);
+}
+
+/*
  * Check if the final character of 's' is 'c'.
  */
 static bool
@@ -1172,6 +1203,18 @@ psql_completion(const char *text, int start, int end)
 	 word_matches(p2, previous_words[previous_words_count - 2]) && \
 	 word_matches(p3, previous_words[previous_words_count - 3]))
 
+	/*
+	 * Macros for matching N words before point with case-sensitive
+	 * comparison.
+	 */
+#define CaseMatches1(p1) \
+	(previous_words_count >= 1 && \
+	 word_case_matches(p1, prev_wd))
+#define CaseMatches2(p2, p1) \
+	(previous_words_count >= 2 && \
+	 word_case_matches(p1, prev_wd) && \
+	 word_case_matches(p2, prev2_wd))
+
 	/* Known command-starting keywords. */
 	static const char *const sql_commands[] = {
 		"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
@@ -2815,95 +2858,91 @@ psql_completion(const char *text, int start, int end)
 
 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
-	else if (strcmp(prev_wd, "\\?") == 0)
+	else if (CaseMatches1("\\?"))
 	{
 		static const char *const my_list[] =
 		{"commands", "options", "variables", NULL};
 
 		COMPLETE_WITH_LIST_CS(my_list);
 	}
-	else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0)
+	else if (CaseMatches1("\\connect|\\c"))
 	{
 		if (!recognized_connection_string(text))
 			COMPLETE_WITH_QUERY(Query_for_list_of_databases);
 	}
-	else if (previous_words_count >= 2 &&
-			 (strcmp(prev2_wd, "\\connect") == 0 ||
-			  strcmp(prev2_wd, "\\c") == 0))
+	else if (CaseMatches2("\\connect|\\c", MatchAny))
 	{
 		if (!recognized_connection_string(prev_wd))
 			COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 	}
-	else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0)
+	else if (CaseMatches1("\\da"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL);
-	else if (strncmp(prev_wd, "\\db", strlen("\\db")) == 0)
+	else if (CaseMatches1("\\db"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
-	else if (strncmp(prev_wd, "\\dD", strlen("\\dD")) == 0)
+	else if (CaseMatches1("\\dD"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, NULL);
-	else if (strncmp(prev_wd, "\\des", strlen("\\des")) == 0)
+	else if (CaseMatches1("\\des"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_servers);
-	else if (strncmp(prev_wd, "\\deu", strlen("\\deu")) == 0)
+	else if (CaseMatches1("\\deu"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
-	else if (strncmp(prev_wd, "\\dew", strlen("\\dew")) == 0)
+	else if (CaseMatches1("\\dew"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
 
-	else if (strncmp(prev_wd, "\\df", strlen("\\df")) == 0)
+	else if (CaseMatches1("\\df"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
-	else if (strncmp(prev_wd, "\\dFd", strlen("\\dFd")) == 0)
+	else if (CaseMatches1("\\dFd"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries);
-	else if (strncmp(prev_wd, "\\dFp", strlen("\\dFp")) == 0)
+	else if (CaseMatches1("\\dFp"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers);
-	else if (strncmp(prev_wd, "\\dFt", strlen("\\dFt")) == 0)
+	else if (CaseMatches1("\\dFt"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates);
 	/* must be at end of \dF */
-	else if (strncmp(prev_wd, "\\dF", strlen("\\dF")) == 0)
+	else if (CaseMatches1("\\dF"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations);
 
-	else if (strncmp(prev_wd, "\\di", strlen("\\di")) == 0)
+	else if (CaseMatches1("\\di"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, NULL);
-	else if (strncmp(prev_wd, "\\dL", strlen("\\dL")) == 0)
+	else if (CaseMatches1("\\dL"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-	else if (strncmp(prev_wd, "\\dn", strlen("\\dn")) == 0)
+	else if (CaseMatches1("\\dn"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
-	else if (strncmp(prev_wd, "\\dp", strlen("\\dp")) == 0
-			 || strncmp(prev_wd, "\\z", strlen("\\z")) == 0)
+	else if (CaseMatches1("\\dp") || CaseMatches1("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, NULL);
-	else if (strncmp(prev_wd, "\\ds", strlen("\\ds")) == 0)
+	else if (CaseMatches1("\\ds"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, NULL);
-	else if (strncmp(prev_wd, "\\dt", strlen("\\dt")) == 0)
+	else if (CaseMatches1("\\dt"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
-	else if (strncmp(prev_wd, "\\dT", strlen("\\dT")) == 0)
+	else if (CaseMatches1("\\dT"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
-	else if (strncmp(prev_wd, "\\du", strlen("\\du")) == 0
-			 || (strncmp(prev_wd, "\\dg", strlen("\\dg")) == 0))
+	else if (CaseMatches1("\\du") || CaseMatches1("\\dg"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-	else if (strncmp(prev_wd, "\\dv", strlen("\\dv")) == 0)
+	else if (CaseMatches1("\\dv"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
-	else if (strncmp(prev_wd, "\\dx", strlen("\\dx")) == 0)
+	else if (CaseMatches1("\\dx"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
-	else if (strncmp(prev_wd, "\\dm", strlen("\\dm")) == 0)
+	else if (CaseMatches1("\\dm"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, NULL);
-	else if (strncmp(prev_wd, "\\dE", strlen("\\dE")) == 0)
+	else if (CaseMatches1("\\dE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, NULL);
-	else if (strncmp(prev_wd, "\\dy", strlen("\\dy")) == 0)
+	else if (CaseMatches1("\\dy"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
 
 	/* must be at end of \d list */
-	else if (strncmp(prev_wd, "\\d", strlen("\\d")) == 0)
+	else if (CaseMatches1("\\d"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, NULL);
 
-	else if (strcmp(prev_wd, "\\ef") == 0)
+	else if (CaseMatches1("\\ef"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
-	else if (strcmp(prev_wd, "\\ev") == 0)
+	else if (CaseMatches1("\\ev"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
 
-	else if (strcmp(prev_wd, "\\encoding") == 0)
+	else if (CaseMatches1("\\encoding"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
-	else if (strcmp(prev_wd, "\\h") == 0 || strcmp(prev_wd, "\\help") == 0)
+	else if (CaseMatches1("\\h") || CaseMatches1("\\help"))
 		COMPLETE_WITH_LIST(sql_commands);
-	else if (strcmp(prev_wd, "\\password") == 0)
+	else if (CaseMatches1("\\password"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-	else if (strcmp(prev_wd, "\\pset") == 0)
+	else if (CaseMatches1("\\pset"))
 	{
 		static const char *const my_list[] =
 		{"border", "columns", "expanded", "fieldsep", "fieldsep_zero",
@@ -2914,10 +2953,9 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST_CS(my_list);
 	}
-	else if (previous_words_count >= 2 &&
-			 strcmp(prev2_wd, "\\pset") == 0)
+	else if (CaseMatches2("\\pset", MatchAny))
 	{
-		if (strcmp(prev_wd, "format") == 0)
+		if (CaseMatches1("format"))
 		{
 			static const char *const my_list[] =
 			{"unaligned", "aligned", "wrapped", "html", "asciidoc",
@@ -2925,16 +2963,14 @@ psql_completion(const char *text, int start, int end)
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "linestyle") == 0)
+		else if (CaseMatches1("linestyle"))
 		{
 			static const char *const my_list[] =
 			{"ascii", "old-ascii", "unicode", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "unicode_border_linestyle") == 0 ||
-				 strcmp(prev_wd, "unicode_column_linestyle") == 0 ||
-				 strcmp(prev_wd, "unicode_header_linestyle") == 0)
+		else if (CaseMatches1("unicode_border_linestyle|unicode_column_linestyle|unicode_header_linestyle"))
 		{
 			static const char *const my_list[] =
 			{"single", "double", NULL};
@@ -2943,73 +2979,72 @@ psql_completion(const char *text, int start, int end)
 
 		}
 	}
-	else if (strcmp(prev_wd, "\\unset") == 0)
+	else if (CaseMatches1("\\unset"))
 	{
 		matches = complete_from_variables(text, "", "", true);
 	}
-	else if (strcmp(prev_wd, "\\set") == 0)
+	else if (CaseMatches1("\\set"))
 	{
 		matches = complete_from_variables(text, "", "", false);
 	}
-	else if (previous_words_count >= 2 &&
-			 strcmp(prev2_wd, "\\set") == 0)
+	else if (CaseMatches2("\\set", MatchAny))
 	{
 		static const char *const boolean_value_list[] =
 		{"on", "off", NULL};
 
-		if (strcmp(prev_wd, "AUTOCOMMIT") == 0)
+		if (CaseMatches1("AUTOCOMMIT"))
 			COMPLETE_WITH_LIST_CS(boolean_value_list);
-		else if (strcmp(prev_wd, "COMP_KEYWORD_CASE") == 0)
+		else if (CaseMatches1("COMP_KEYWORD_CASE"))
 		{
 			static const char *const my_list[] =
 			{"lower", "upper", "preserve-lower", "preserve-upper", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "ECHO") == 0)
+		else if (CaseMatches1("ECHO"))
 		{
 			static const char *const my_list[] =
 			{"errors", "queries", "all", "none", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "ECHO_HIDDEN") == 0)
+		else if (CaseMatches1("ECHO_HIDDEN"))
 		{
 			static const char *const my_list[] =
 			{"noexec", "off", "on", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "HISTCONTROL") == 0)
+		else if (CaseMatches1("HISTCONTROL"))
 		{
 			static const char *const my_list[] =
 			{"ignorespace", "ignoredups", "ignoreboth", "none", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "ON_ERROR_ROLLBACK") == 0)
+		else if (CaseMatches1("ON_ERROR_ROLLBACK"))
 		{
 			static const char *const my_list[] =
 			{"on", "off", "interactive", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "ON_ERROR_STOP") == 0)
+		else if (CaseMatches1("ON_ERROR_STOP"))
 			COMPLETE_WITH_LIST_CS(boolean_value_list);
-		else if (strcmp(prev_wd, "QUIET") == 0)
+		else if (CaseMatches1("QUIET"))
 			COMPLETE_WITH_LIST_CS(boolean_value_list);
-		else if (strcmp(prev_wd, "SHOW_CONTEXT") == 0)
+		else if (CaseMatches1("SHOW_CONTEXT"))
 		{
 			static const char *const my_list[] =
 			{"never", "errors", "always", NULL};
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (strcmp(prev_wd, "SINGLELINE") == 0)
+		else if (CaseMatches1("SINGLELINE"))
 			COMPLETE_WITH_LIST_CS(boolean_value_list);
-		else if (strcmp(prev_wd, "SINGLESTEP") == 0)
+		else if (CaseMatches1("SINGLESTEP"))
 			COMPLETE_WITH_LIST_CS(boolean_value_list);
-		else if (strcmp(prev_wd, "VERBOSITY") == 0)
+		else if (CaseMatches1("VERBOSITY"))
 		{
 			static const char *const my_list[] =
 			{"default", "verbose", "terse", NULL};
@@ -3017,20 +3052,11 @@ psql_completion(const char *text, int start, int end)
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
 	}
-	else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
+	else if (CaseMatches1("\\sf|\\sf+"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
-	else if (strcmp(prev_wd, "\\sv") == 0 || strcmp(prev_wd, "\\sv+") == 0)
+	else if (CaseMatches1("\\sv|\\sv+"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
-	else if (strcmp(prev_wd, "\\cd") == 0 ||
-			 strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
-			 strcmp(prev_wd, "\\g") == 0 ||
-		  strcmp(prev_wd, "\\i") == 0 || strcmp(prev_wd, "\\include") == 0 ||
-			 strcmp(prev_wd, "\\ir") == 0 || strcmp(prev_wd, "\\include_relative") == 0 ||
-			 strcmp(prev_wd, "\\o") == 0 || strcmp(prev_wd, "\\out") == 0 ||
-			 strcmp(prev_wd, "\\s") == 0 ||
-			 strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0 ||
-			 strcmp(prev_wd, "\\lo_import") == 0
-		)
+	else if (CaseMatches1("\\cd|\\e|\\edit|\\g|\\i|\\include|\\ir|\\include_relative|\\o|\\out|\\s|\\w|\\write|\\lo_import"))
 	{
 		completion_charp = "\\";
 		matches = completion_matches(text, complete_from_files);
-- 
2.6.4

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to