From 1bdf05097a3f2fda11bc244628d19217409c2403 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 7 Apr 2016 13:38:54 +0900
Subject: [PATCH 4/4] Refactoring tab-complete to make psql_completion code
 simpler.

psql_completion consists of a sequence of "else-if"s and succeeding
finishing code. This structure prevents us from having anything other
than if-conditions and completion code in every then-clause in the
else-if sequence. For the reason we had no way to have operations like
modifying previous_words other than implement it is boolean functions
always return false. This patch separates the else-if sequence into
individual function "psql_completion_internal". This modification
allows the sequence to be simple if sequence and allows us to put any
code midst of the sequence.
---
 src/bin/psql/Makefile              |    2 +
 src/bin/psql/tab-complete-macros.h |  479 +++++++++
 src/bin/psql/tab-complete.c        | 1864 +++++++++++++++---------------------
 3 files changed, 1241 insertions(+), 1104 deletions(-)
 create mode 100644 src/bin/psql/tab-complete-macros.h

diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index 1f6a289..51f88ba 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -47,6 +47,8 @@ ifeq ($(GCC),yes)
 psqlscanslash.o: CFLAGS += -Wno-error
 endif
 
+tab-complete.o: tab-complete-macros.h
+
 distprep: sql_help.h psqlscanslash.c
 
 install: all installdirs
diff --git a/src/bin/psql/tab-complete-macros.h b/src/bin/psql/tab-complete-macros.h
new file mode 100644
index 0000000..e7ca851
--- /dev/null
+++ b/src/bin/psql/tab-complete-macros.h
@@ -0,0 +1,479 @@
+/*
+ * psql - the PostgreSQL interactive terminal
+ *
+ * Copyright (c) 2000-2016, PostgreSQL Global Development Group
+ *
+ * src/bin/psql/tab-complete-macros.h
+ */
+#ifndef TAB_COMPLETE_MACROS_H
+#define TAB_COMPLETE_MACROS_H
+
+/*
+ * For compactness, we use these macros to reference previous_words[].
+ * Caution: do not access a previous_words[] entry without having checked
+ * previous_words_count to be sure it's valid.  In most cases below, that
+ * check is implicit in a TailMatches() or similar macro, but in some places
+ * we have to check it explicitly.
+ */
+#define prev_wd   (previous_words[0])
+#define prev2_wd  (previous_words[1])
+#define prev3_wd  (previous_words[2])
+#define prev4_wd  (previous_words[3])
+#define prev5_wd  (previous_words[4])
+#define prev6_wd  (previous_words[5])
+#define prev7_wd  (previous_words[6])
+#define prev8_wd  (previous_words[7])
+#define prev9_wd  (previous_words[8])
+
+/* Move the position of the beginning word for matching macros.  */
+#define SHIFTHEAD(n) \
+	(head_shift += (n))
+
+/* Set the position of the beginning word for matching macros.  */
+#define SETHEAD(n) \
+	(head_shift = (n))
+
+/* Return the number of stored words counting head shift */
+#define WORD_COUNT() (previous_words_count - head_shift)
+
+/*
+ * Return the true index in previous_words for index from the
+ * beginning. n is 1-based and the result is 0-based.
+ */
+#define HEAD_INDEX(n) \
+	(previous_words_count - head_shift - (n))
+/*
+ * remove n words from current shifted position. s and n are 1-based index.
+ */
+#define COLLAPSE(s, n)							\
+    do { \
+		memmove(previous_words + HEAD_INDEX((s) + (n) - 1), previous_words + HEAD_INDEX((s) - 1), \
+				sizeof(char *) * (previous_words_count - HEAD_INDEX((s) - 1))); \
+		previous_words_count -= (n); \
+	} while(0)
+
+/*
+ * Macros for matching the last N words before point, and after head_sift,
+ * case-insensitively.
+ */
+#define TailMatches1(p1) \
+	(WORD_COUNT() >= 1 && \
+	 word_matches(p1, prev_wd))
+
+#define TailMatches2(p2, p1) \
+	(WORD_COUNT() >= 2 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd))
+
+#define TailMatches3(p3, p2, p1) \
+	(WORD_COUNT() >= 3 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd))
+
+#define TailMatches4(p4, p3, p2, p1) \
+	(WORD_COUNT() >= 4 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd) && \
+	 word_matches(p4, prev4_wd))
+
+#define TailMatches5(p5, p4, p3, p2, p1) \
+	(WORD_COUNT() >= 5 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd) && \
+	 word_matches(p4, prev4_wd) && \
+	 word_matches(p5, prev5_wd))
+
+#define TailMatches6(p6, p5, p4, p3, p2, p1) \
+	(WORD_COUNT() >= 6 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd) && \
+	 word_matches(p4, prev4_wd) && \
+	 word_matches(p5, prev5_wd) && \
+	 word_matches(p6, prev6_wd))
+
+#define TailMatches7(p7, p6, p5, p4, p3, p2, p1) \
+	(WORD_COUNT() >= 7 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd) && \
+	 word_matches(p4, prev4_wd) && \
+	 word_matches(p5, prev5_wd) && \
+	 word_matches(p6, prev6_wd) && \
+	 word_matches(p7, prev7_wd))
+
+#define TailMatches8(p8, p7, p6, p5, p4, p3, p2, p1) \
+	(WORD_COUNT() >= 8 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd) && \
+	 word_matches(p4, prev4_wd) && \
+	 word_matches(p5, prev5_wd) && \
+	 word_matches(p6, prev6_wd) && \
+	 word_matches(p7, prev7_wd) && \
+	 word_matches(p8, prev8_wd))
+
+#define TailMatches9(p9, p8, p7, p6, p5, p4, p3, p2, p1) \
+	(WORD_COUNT() >= 9 && \
+	 word_matches(p1, prev_wd) && \
+	 word_matches(p2, prev2_wd) && \
+	 word_matches(p3, prev3_wd) && \
+	 word_matches(p4, prev4_wd) && \
+	 word_matches(p5, prev5_wd) && \
+	 word_matches(p6, prev6_wd) && \
+	 word_matches(p7, prev7_wd) && \
+	 word_matches(p8, prev8_wd) && \
+	 word_matches(p9, prev9_wd))
+
+	/*
+	 * Macros for matching the last N words before point, and after
+	 * head_shift, case-sensitively.
+	 */
+#define TailMatchesCS1(p1) \
+	(WORD_COUNT() >= 1 && \
+	 word_matches_cs(p1, prev_wd))
+#define TailMatchesCS2(p2, p1) \
+	(WORD_COUNT() >= 2 && \
+	 word_matches_cs(p1, prev_wd) && \
+	 word_matches_cs(p2, prev2_wd))
+
+	/*
+	 * Macros for matching N words exactly to the line, and after head_shift,
+	 * case-insensitively.
+	 */
+#define Matches1(p1) \
+	(WORD_COUNT() == 1 && \
+	 TailMatches1(p1))
+#define Matches2(p1, p2) \
+	(WORD_COUNT() == 2 && \
+	 TailMatches2(p1, p2))
+#define Matches3(p1, p2, p3) \
+	(WORD_COUNT() == 3 && \
+	 TailMatches3(p1, p2, p3))
+#define Matches4(p1, p2, p3, p4) \
+	(WORD_COUNT() == 4 && \
+	 TailMatches4(p1, p2, p3, p4))
+#define Matches5(p1, p2, p3, p4, p5) \
+	(WORD_COUNT() == 5 && \
+	 TailMatches5(p1, p2, p3, p4, p5))
+#define Matches6(p1, p2, p3, p4, p5, p6) \
+	(WORD_COUNT() == 6 && \
+	 TailMatches6(p1, p2, p3, p4, p5, p6))
+#define Matches7(p1, p2, p3, p4, p5, p6, p7) \
+	(WORD_COUNT() == 7 && \
+	 TailMatches7(p1, p2, p3, p4, p5, p6, p7))
+#define Matches8(p1, p2, p3, p4, p5, p6, p7, p8) \
+	(WORD_COUNT() == 8 && \
+	 TailMatches8(p1, p2, p3, p4, p5, p6, p7, p8))
+#define Matches9(p1, p2, p3, p4, p5, p6, p7, p8, p9) \
+	(WORD_COUNT() == 9 && \
+	 TailMatches9(p1, p2, p3, p4, p5, p6, p7, p8, p9))
+
+/*
+ * Macros for matching N words after head_shift + sth in the line, regardless
+ * of what is after them, case-insensitively.
+ */
+#define MidMatches1(s, p1) \
+	(HEAD_INDEX(s) >=0 && \
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]))
+
+#define MidMatches2(s, p1, p2) \
+	(HEAD_INDEX((s) + 1) >= 0 &&						\
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]) &&	\
+	 word_matches(p2, previous_words[HEAD_INDEX((s) + 1)]))
+
+#define MidMatches3(s, p1, p2, p3) \
+	(HEAD_INDEX((s) + 2) >= 0 &&						\
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]) && \
+	 word_matches(p2, previous_words[HEAD_INDEX((s) + 1)]) &&	\
+	 word_matches(p3, previous_words[HEAD_INDEX((s) + 2)]))
+
+#define MidMatches4(s, p1, p2, p3, p4) \
+	(HEAD_INDEX((s) + 3) >= 0 &&						\
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]) && \
+	 word_matches(p2, previous_words[HEAD_INDEX((s) + 1)]) && \
+	 word_matches(p3, previous_words[HEAD_INDEX((s) + 2)]) && \
+	 word_matches(p4, previous_words[HEAD_INDEX((s) + 3)]))
+
+#define MidMatches5(s, p1, p2, p3, p4, p5) \
+	(HEAD_INDEX((s) + 4) >= 0 &&						\
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]) &&	\
+	 word_matches(p2, previous_words[HEAD_INDEX((s) + 1)]) &&	\
+	 word_matches(p3, previous_words[HEAD_INDEX((s) + 2)]) &&	\
+	 word_matches(p4, previous_words[HEAD_INDEX((s) + 3)]) &&		\
+	 word_matches(p5, previous_words[HEAD_INDEX((s) + 4)]))
+
+#define MidMatches6(s,p1, p2, p3, p4, p5, p6)		\
+	(HEAD_INDEX((s) + 5) >= 0 &&						\
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]) &&	\
+	 word_matches(p2, previous_words[HEAD_INDEX((s) + 1)]) &&	\
+	 word_matches(p3, previous_words[HEAD_INDEX((s) + 2)]) &&		\
+	 word_matches(p4, previous_words[HEAD_INDEX((s) + 3)]) &&			\
+	 word_matches(p5, previous_words[HEAD_INDEX((s) + 4)]) &&			\
+	 word_matches(p6, previous_words[HEAD_INDEX((s) + 5)]))
+
+#define MidMatches7(s,p1, p2, p3, p4, p5, p6, p7)	\
+	(HEAD_INDEX((s) + 6) >= 0 &&							\
+	 word_matches(p1, previous_words[HEAD_INDEX(s)]) &&	\
+	 word_matches(p2, previous_words[HEAD_INDEX((s) + 1)]) &&	\
+	 word_matches(p3, previous_words[HEAD_INDEX((s) + 2)]) &&		\
+	 word_matches(p4, previous_words[HEAD_INDEX((s) + 3)]) &&			\
+	 word_matches(p5, previous_words[HEAD_INDEX((s) + 4)]) &&			\
+	 word_matches(p6, previous_words[HEAD_INDEX((s) + 5)]) &&			\
+	 word_matches(p7, previous_words[HEAD_INDEX((s) + 6)]))
+
+#define HeadMatches1(p1) \
+	MidMatches1(1, p1)
+#define HeadMatches2(p1, p2) \
+	MidMatches2(1, p1, p2)
+#define HeadMatches3(p1, p2, p3) \
+	MidMatches3(1, p1, p2, p3)
+#define HeadMatches4(p1, p2, p3, p4) \
+	MidMatches4(1, p1, p2, p3, p4)
+#define HeadMatches5(p1, p2, p3, p4, p5) \
+	MidMatches5(1, p1, p2, p3, p4, p5)
+#define HeadMatches6(p1, p2, p3, p4, p5, p6) \
+	MidMatches6(1, p1, p2, p3, p4, p5, p6)
+#define HeadMatches7(p1, p2, p3, p4, p5, p6, p7) \
+	MidMatches7(1, p1, p2, p3, p4, p5, p6, p7)
+
+/*
+ * completion_charp is now not a simple string, but a PQExpBuffer. These
+ * macros are to make the appearance of completion_charp to be simpler.
+ */
+
+/* Append a string to existing completion_charp  */
+#define APPEND_COMPLETION_CHARP(charp) \
+	appendPQExpBufferStr(completion_charp, charp);
+
+/* Set a string to completion_charp  */
+#define SET_COMPLETION_CHARP(charp) \
+	resetPQExpBuffer(completion_charp);	\
+	APPEND_COMPLETION_CHARP(charp);
+
+/* Read the completion_charp as a string */
+#define COMPLETION_CHARP (completion_charp->data)
+
+
+/*
+ * A few macros to ease typing. You can use these to complete the given
+ * string with
+ * 1) The results from a query you pass it. (Perhaps one of those below?)
+ * 2) The results from a schema query you pass it.
+ * 3) The items from a null-pointer-terminated list (with or without
+ *	  case-sensitive comparison; see also COMPLETE_WITH_LISTn, below).
+ * 4) A string constant.
+ * 5) The list of attributes of the given table (possibly schema-qualified).
+ * 6) The list of arguments to the given function (possibly schema-qualified).
+ */
+#define COMPLETE_WITH_QUERY(query, addon) \
+do { \
+	SET_COMPLETION_CHARP(query);	\
+	APPEND_COMPLETION_CHARP(addon); \
+	return completion_matches(text, complete_from_query);	\
+} while (0)
+
+#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
+do { \
+	completion_squery = &(query); \
+	SET_COMPLETION_CHARP(addon); \
+	return completion_matches(text, complete_from_schema_query); \
+} while (0)
+
+#define COMPLETE_WITH_LIST_CS(list) \
+do { \
+	completion_charpp = list; \
+	completion_case_sensitive = true; \
+	return completion_matches(text, complete_from_list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST(list) \
+do { \
+	completion_charpp = list; \
+	completion_case_sensitive = false; \
+	return completion_matches(text, complete_from_list); \
+} while (0)
+
+#define COMPLETE_WITH_CONST(string) \
+do { \
+	SET_COMPLETION_CHARP(string);	\
+	completion_case_sensitive = false; \
+	return completion_matches(text, complete_from_const);	\
+} while (0)
+
+#define COMPLETE_WITH_ATTR(relation, addon) \
+do { \
+	char   *_completion_schema; \
+	char   *_completion_table; \
+\
+	_completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \
+								 false, false, pset.encoding); \
+	(void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+				   false, false, pset.encoding); \
+	_completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+								false, false, pset.encoding); \
+	if (_completion_table == NULL) \
+	{ \
+		SET_COMPLETION_CHARP(Query_for_list_of_attributes); \
+		APPEND_COMPLETION_CHARP(addon);					  \
+		completion_info_charp = relation; \
+	} \
+	else \
+	{ \
+		SET_COMPLETION_CHARP(Query_for_list_of_attributes_with_schema); \
+		APPEND_COMPLETION_CHARP(addon); \
+		completion_info_charp = _completion_table; \
+		completion_info_charp2 = _completion_schema; \
+	} \
+	return completion_matches(text, complete_from_query); \
+} while (0)
+
+#define COMPLETE_WITH_FUNCTION_ARG(function) \
+do { \
+	char   *_completion_schema; \
+	char   *_completion_function; \
+\
+	_completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \
+								 false, false, pset.encoding); \
+	(void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+				   false, false, pset.encoding); \
+	_completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
+								   false, false, pset.encoding); \
+	if (_completion_function == NULL) \
+	{ \
+		SET_COMPLETION_CHARP(Query_for_list_of_arguments); \
+		completion_info_charp = function; \
+	} \
+	else \
+	{ \
+		SET_COMPLETION_CHARP(Query_for_list_of_arguments_with_schema); \
+		completion_info_charp = _completion_function; \
+		completion_info_charp2 = _completion_schema; \
+	} \
+	return completion_matches(text, complete_from_query); \
+} while (0)
+
+/*
+ * These macros simplify use of COMPLETE_WITH_LIST for short, fixed lists.
+ * There is no COMPLETE_WITH_LIST1; use COMPLETE_WITH_CONST for that case.
+ */
+#define COMPLETE_WITH_LIST2(s1, s2) \
+do { \
+	static const char *const list[] = { s1, s2, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST3(s1, s2, s3) \
+do { \
+	static const char *const list[] = { s1, s2, s3, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST4(s1, s2, s3, s4) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST5(s1, s2, s3, s4, s5) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST6(s1, s2, s3, s4, s5, s6) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, s6, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST7(s1, s2, s3, s4, s5, s6, s7) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST8(s1, s2, s3, s4, s5, s6, s7, s8) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST9(s1, s2, s3, s4, s5, s6, s7, s8, s9) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST10(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, NULL }; \
+	COMPLETE_WITH_LIST(list); \
+} while (0)
+
+/*
+ * Likewise for COMPLETE_WITH_LIST_CS.
+ */
+#define COMPLETE_WITH_LIST_CS2(s1, s2) \
+do { \
+	static const char *const list[] = { s1, s2, NULL }; \
+	COMPLETE_WITH_LIST_CS(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST_CS3(s1, s2, s3) \
+do { \
+	static const char *const list[] = { s1, s2, s3, NULL }; \
+	COMPLETE_WITH_LIST_CS(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST_CS4(s1, s2, s3, s4) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, NULL }; \
+	COMPLETE_WITH_LIST_CS(list); \
+} while (0)
+
+#define COMPLETE_WITH_LIST_CS5(s1, s2, s3, s4, s5) \
+do { \
+	static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \
+	COMPLETE_WITH_LIST_CS(list); \
+} while (0)
+
+/*
+ * A macro to comlete "thing" at the pth position before from the current
+ * word. COMPLETE_THING(-1) completes the thing of the last word in
+ * previous_words.
+ */
+#define COMPLETE_THING(p) \
+do { \
+	const pgsql_thing_t *ent = find_thing_entry(previous_words[-(p) - 1]);	\
+	if (ent) \
+	{ \
+		if (ent->query) \
+			COMPLETE_WITH_QUERY(ent->query, ""); \
+		else if (ent->squery) \
+			COMPLETE_WITH_SCHEMA_QUERY(*ent->squery, ""); \
+	} \
+	return NULL; \
+} while (0)
+
+/*
+ * Macros to construct a query to add additional keyword, additional_kw_query
+ * changes the case of the keywords following an input.
+ */
+#define ADDLIST1(s1) additional_kw_query(text, 1, s1)
+#define ADDLIST2(s1, s2) additional_kw_query(text, 2, s1, s2)
+#define ADDLIST3(s1, s2, s3) additional_kw_query(text, 3, s1, s2, s3)
+#define ADDLIST4(s1, s2, s3, s4) \
+	additional_kw_query(text, 4, s1, s2, s3, s4)
+#define ADDLIST13(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \
+	additional_kw_query(text, 13, s1, s2, s3, s4, s5, s6, s7,		\
+						s8, s9, s10, s11, s12, s13)
+#define ADDLIST15(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \
+	additional_kw_query(text, 15, s1, s2, s3, s4, s5, s6, s7,		\
+						s8, s9, s10, s11, s12, s13, s14, s15)
+
+
+#endif   /* TAB_COMPLETE_MACROS_H */
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 832bcd7..d68d886 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -34,6 +34,7 @@
 
 #include "postgres_fe.h"
 #include "tab-complete.h"
+#include "tab-complete-macros.h"
 #include "input.h"
 
 /* If we don't have this, we might as well forget about the whole thing: */
@@ -133,210 +134,6 @@ static const SchemaQuery *completion_squery;	/* to pass a SchemaQuery */
 static bool completion_case_sensitive;	/* completion is case sensitive */
 
 /*
- * A few macros to ease typing. You can use these to complete the given
- * string with
- * 1) The results from a query you pass it. (Perhaps one of those below?)
- * 2) The results from a schema query you pass it.
- * 3) The items from a null-pointer-terminated list (with or without
- *	  case-sensitive comparison; see also COMPLETE_WITH_LISTn, below).
- * 4) A string constant.
- * 5) The list of attributes of the given table (possibly schema-qualified).
- * 6/ The list of arguments to the given function (possibly schema-qualified).
- */
-#define APPEND_COMP_CHARP(charp) \
-	appendPQExpBufferStr(completion_charp, charp);
-
-#define SET_COMP_CHARP(charp) \
-	resetPQExpBuffer(completion_charp);	\
-	APPEND_COMP_CHARP(charp);
-
-#define COMPLETION_CHARP (completion_charp->data)
-
-#define COMPLETE_WITH_QUERY(query, addon) \
-do { \
-	SET_COMP_CHARP(query);	\
-	APPEND_COMP_CHARP(addon); \
-	matches = completion_matches(text, complete_from_query); \
-} while (0)
-
-#define COMPLETE_WITH_SCHEMA_QUERY(query, addon) \
-do { \
-	completion_squery = &(query); \
-	SET_COMP_CHARP(addon); \
-	matches = completion_matches(text, complete_from_schema_query); \
-} while (0)
-
-#define COMPLETE_WITH_LIST_CS(list) \
-do { \
-	completion_charpp = list; \
-	completion_case_sensitive = true; \
-	matches = completion_matches(text, complete_from_list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST(list) \
-do { \
-	completion_charpp = list; \
-	completion_case_sensitive = false; \
-	matches = completion_matches(text, complete_from_list); \
-} while (0)
-
-#define COMPLETE_WITH_CONST(string) \
-do { \
-	SET_COMP_CHARP(string);	\
-	completion_case_sensitive = false; \
-	matches = completion_matches(text, complete_from_const); \
-} while (0)
-
-#define COMPLETE_WITH_ATTR(relation, addon) \
-do { \
-	char   *_completion_schema; \
-	char   *_completion_table; \
-\
-	_completion_schema = strtokx(relation, " \t\n\r", ".", "\"", 0, \
-								 false, false, pset.encoding); \
-	(void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
-				   false, false, pset.encoding); \
-	_completion_table = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
-								false, false, pset.encoding); \
-	if (_completion_table == NULL) \
-	{ \
-		SET_COMP_CHARP(Query_for_list_of_attributes); \
-		APPEND_COMP_CHARP(addon);					  \
-		completion_info_charp = relation; \
-	} \
-	else \
-	{ \
-		SET_COMP_CHARP(Query_for_list_of_attributes_with_schema); \
-		APPEND_COMP_CHARP(addon); \
-		completion_info_charp = _completion_table; \
-		completion_info_charp2 = _completion_schema; \
-	} \
-	matches = completion_matches(text, complete_from_query); \
-} while (0)
-
-#define COMPLETE_WITH_FUNCTION_ARG(function) \
-do { \
-	char   *_completion_schema; \
-	char   *_completion_function; \
-\
-	_completion_schema = strtokx(function, " \t\n\r", ".", "\"", 0, \
-								 false, false, pset.encoding); \
-	(void) strtokx(NULL, " \t\n\r", ".", "\"", 0, \
-				   false, false, pset.encoding); \
-	_completion_function = strtokx(NULL, " \t\n\r", ".", "\"", 0, \
-								   false, false, pset.encoding); \
-	if (_completion_function == NULL) \
-	{ \
-		SET_COMP_CHARP(Query_for_list_of_arguments); \
-		completion_info_charp = function; \
-	} \
-	else \
-	{ \
-		SET_COMP_CHARP(Query_for_list_of_arguments_with_schema); \
-		completion_info_charp = _completion_function; \
-		completion_info_charp2 = _completion_schema; \
-	} \
-	matches = completion_matches(text, complete_from_query); \
-} while (0)
-
-/*
- * These macros simplify use of COMPLETE_WITH_LIST for short, fixed lists.
- * There is no COMPLETE_WITH_LIST1; use COMPLETE_WITH_CONST for that case.
- */
-#define COMPLETE_WITH_LIST2(s1, s2) \
-do { \
-	static const char *const list[] = { s1, s2, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST3(s1, s2, s3) \
-do { \
-	static const char *const list[] = { s1, s2, s3, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST4(s1, s2, s3, s4) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST5(s1, s2, s3, s4, s5) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST6(s1, s2, s3, s4, s5, s6) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, s6, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST7(s1, s2, s3, s4, s5, s6, s7) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST8(s1, s2, s3, s4, s5, s6, s7, s8) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST9(s1, s2, s3, s4, s5, s6, s7, s8, s9) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST10(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, NULL }; \
-	COMPLETE_WITH_LIST(list); \
-} while (0)
-
-/*
- * Likewise for COMPLETE_WITH_LIST_CS.
- */
-#define COMPLETE_WITH_LIST_CS2(s1, s2) \
-do { \
-	static const char *const list[] = { s1, s2, NULL }; \
-	COMPLETE_WITH_LIST_CS(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST_CS3(s1, s2, s3) \
-do { \
-	static const char *const list[] = { s1, s2, s3, NULL }; \
-	COMPLETE_WITH_LIST_CS(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST_CS4(s1, s2, s3, s4) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, NULL }; \
-	COMPLETE_WITH_LIST_CS(list); \
-} while (0)
-
-#define COMPLETE_WITH_LIST_CS5(s1, s2, s3, s4, s5) \
-do { \
-	static const char *const list[] = { s1, s2, s3, s4, s5, NULL }; \
-	COMPLETE_WITH_LIST_CS(list); \
-} while (0)
-
-#define ADDLIST1(s1) additional_kw_query(text, 1, s1)
-#define ADDLIST2(s1, s2) additional_kw_query(text, 2, s1, s2)
-#define ADDLIST3(s1, s2, s3) additional_kw_query(text, 3, s1, s2, s3)
-#define ADDLIST4(s1, s2, s3, s4) \
-	additional_kw_query(text, 4, s1, s2, s3, s4)
-#define ADDLIST13(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13) \
-	additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7,		\
-						s8, s9, s10, s11, s12, s13)
-#define ADDLIST15(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15) \
-	additional_kw_query(text, 12, s1, s2, s3, s4, s5, s6, s7,		\
-						s8, s9, s10, s11, s12, s13, s14, s15)
-
-/*
  * Assembly instructions for schema queries
  */
 
@@ -993,9 +790,11 @@ static PGresult *exec_query(const char *query);
 
 static char **get_previous_words(int point, char **buffer, int *nwords);
 
-static char *get_guctype(const char *varname);
+static bool get_guctype(char *buf, size_t len, const char *varname);
 
 static const pgsql_thing_t *find_thing_entry(char *word);
+static char **psql_completion_internal(const char *text, char **previous_words,
+										   int previous_words_count);
 #ifdef NOT_USED
 static char *quote_file_name(char *text, int match_type, char *quote_pointer);
 static char *dequote_file_name(char *text, char quote_char);
@@ -1151,235 +950,60 @@ psql_completion(const char *text, int start, int end)
 	/* The number of words found on the input line. */
 	int			previous_words_count;
 
-	/* The number of prefixing words to be ignored */
-	int			head_shift = 0;
+	(void) end;					/* "end" is not used */
 
-	/*
-	 * For compactness, we use these macros to reference previous_words[].
-	 * Caution: do not access a previous_words[] entry without having checked
-	 * previous_words_count to be sure it's valid.  In most cases below, that
-	 * check is implicit in a TailMatches() or similar macro, but in some
-	 * places we have to check it explicitly.
-	 */
-#define prev_wd   (previous_words[0])
-#define prev2_wd  (previous_words[1])
-#define prev3_wd  (previous_words[2])
-#define prev4_wd  (previous_words[3])
-#define prev5_wd  (previous_words[4])
-#define prev6_wd  (previous_words[5])
-#define prev7_wd  (previous_words[6])
-#define prev8_wd  (previous_words[7])
-#define prev9_wd  (previous_words[8])
-
-	/* Move the position of the beginning word for matching macros.  */
-#define HEADSHIFT(n) \
-	((head_shift += n) || true)
-
-	/* Return the number of stored words counting head shift */
-#define WORD_COUNT() (previous_words_count - head_shift)
-
-	/* Return the true index in previous_words for index from the beginning */
-#define HEAD_INDEX(n) \
-	(previous_words_count - head_shift - (n))
+#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
+	rl_completion_append_character = ' ';
+#endif
 
-	/*
-	 * remove n words from current shifted position, see MidMatchAndRevmove2
-	 * for the reason for the return value
-	 */
-#define COLLAPSE(n) \
-	((memmove(previous_words + HEAD_INDEX(n), previous_words + HEAD_INDEX(0), \
-			 sizeof(char *) * head_shift), \
-	 previous_words_count -= (n)) && false)
+	/* Clear a few things. */
+	if (completion_charp == NULL)
+		completion_charp = createPQExpBuffer();
+	completion_charpp = NULL;
+	completion_info_charp = NULL;
+	completion_info_charp2 = NULL;
 
 	/*
-	 * Find the position the specified word occurs last and shift to there.
-	 * This is used to ignore the words before there.
+	 * Scan the input line to extract the words before our current position.
+	 * According to those we'll make some smart decisions on what the user is
+	 * probably intending to type.
 	 */
-#define SHIFT_TO_LAST1(p1) \
-	(HEADSHIFT(find_last_index_of(p1, previous_words, previous_words_count))|| \
-	 true)
+	previous_words = get_previous_words(start,
+										&words_buffer,
+										&previous_words_count);
 
-	/*
-	 * Remove the specified words if they match from the sth word in
-	 * previous_words. This is a bit tricky, COLLAPSE is skipped when
-	 * HeadMatches failed but the last HEADSHIFT anyway should be done.
-	 */
-#define MidMatchAndRemove1(s, p1) \
-	((HEADSHIFT(s) && HeadMatches1(p1) && COLLAPSE(1)) || HEADSHIFT(-s))
-
-#define MidMatchAndRemove2(s, p1, p2) \
-	((HEADSHIFT(s) && HeadMatches2(p1, p2) && COLLAPSE(2)) || HEADSHIFT(-s))
-
-#define MidMatchAndRemove3(s, p1, p2, p3)									\
-	((HEADSHIFT(s) && HeadMatches3(p1, p2, p3) && COLLAPSE(3)) || HEADSHIFT(-s))
-
-	/* Macros for matching the last N words before point, case-insensitively. */
-#define TailMatches1(p1) \
-	(WORD_COUNT() >= 1 && \
-	 word_matches(p1, prev_wd))
-
-#define TailMatches2(p2, p1) \
-	(WORD_COUNT() >= 2 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd))
-
-#define TailMatches3(p3, p2, p1) \
-	(WORD_COUNT() >= 3 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd))
-
-#define TailMatches4(p4, p3, p2, p1) \
-	(WORD_COUNT() >= 4 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd) && \
-	 word_matches(p4, prev4_wd))
-
-#define TailMatches5(p5, p4, p3, p2, p1) \
-	(WORD_COUNT() >= 5 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd) && \
-	 word_matches(p4, prev4_wd) && \
-	 word_matches(p5, prev5_wd))
-
-#define TailMatches6(p6, p5, p4, p3, p2, p1) \
-	(WORD_COUNT() >= 6 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd) && \
-	 word_matches(p4, prev4_wd) && \
-	 word_matches(p5, prev5_wd) && \
-	 word_matches(p6, prev6_wd))
-
-#define TailMatches7(p7, p6, p5, p4, p3, p2, p1) \
-	(WORD_COUNT() >= 7 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd) && \
-	 word_matches(p4, prev4_wd) && \
-	 word_matches(p5, prev5_wd) && \
-	 word_matches(p6, prev6_wd) && \
-	 word_matches(p7, prev7_wd))
-
-#define TailMatches8(p8, p7, p6, p5, p4, p3, p2, p1) \
-	(WORD_COUNT() >= 8 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd) && \
-	 word_matches(p4, prev4_wd) && \
-	 word_matches(p5, prev5_wd) && \
-	 word_matches(p6, prev6_wd) && \
-	 word_matches(p7, prev7_wd) && \
-	 word_matches(p8, prev8_wd))
-
-#define TailMatches9(p9, p8, p7, p6, p5, p4, p3, p2, p1) \
-	(WORD_COUNT() >= 9 && \
-	 word_matches(p1, prev_wd) && \
-	 word_matches(p2, prev2_wd) && \
-	 word_matches(p3, prev3_wd) && \
-	 word_matches(p4, prev4_wd) && \
-	 word_matches(p5, prev5_wd) && \
-	 word_matches(p6, prev6_wd) && \
-	 word_matches(p7, prev7_wd) && \
-	 word_matches(p8, prev8_wd) && \
-	 word_matches(p9, prev9_wd))
-
-	/* Macros for matching the last N words before point, case-sensitively. */
-#define TailMatchesCS1(p1) \
-	(WORD_COUNT() >= 1 && \
-	 word_matches_cs(p1, prev_wd))
-#define TailMatchesCS2(p2, p1) \
-	(WORD_COUNT() >= 2 && \
-	 word_matches_cs(p1, prev_wd) && \
-	 word_matches_cs(p2, prev2_wd))
+	matches  = psql_completion_internal(text, previous_words,
+										previous_words_count);
 
-	/*
-	 * Macros for matching N words exactly to the line,
-	 * case-insensitively.
-	 */
-#define Matches1(p1) \
-	(WORD_COUNT() == 1 && \
-	 TailMatches1(p1))
-#define Matches2(p1, p2) \
-	(WORD_COUNT() == 2 && \
-	 TailMatches2(p1, p2))
-#define Matches3(p1, p2, p3) \
-	(WORD_COUNT() == 3 && \
-	 TailMatches3(p1, p2, p3))
-#define Matches4(p1, p2, p3, p4) \
-	(WORD_COUNT() == 4 && \
-	 TailMatches4(p1, p2, p3, p4))
-#define Matches5(p1, p2, p3, p4, p5) \
-	(WORD_COUNT() == 5 && \
-	 TailMatches5(p1, p2, p3, p4, p5))
-#define Matches6(p1, p2, p3, p4, p5, p6) \
-	(WORD_COUNT() == 6 && \
-	 TailMatches6(p1, p2, p3, p4, p5, p6))
-#define Matches7(p1, p2, p3, p4, p5, p6, p7) \
-	(WORD_COUNT() == 7 && \
-	 TailMatches7(p1, p2, p3, p4, p5, p6, p7))
-#define Matches8(p1, p2, p3, p4, p5, p6, p7, p8) \
-	(WORD_COUNT() == 8 && \
-	 TailMatches8(p1, p2, p3, p4, p5, p6, p7, p8))
-#define Matches9(p1, p2, p3, p4, p5, p6, p7, p8, p9) \
-	(WORD_COUNT() == 9 && \
-	 TailMatches9(p1, p2, p3, p4, p5, p6, p7, p8, p9))
+	/* free storage */
+	free(previous_words);
+	free(words_buffer);
+
+	if (matches != NULL)
+		return matches;
 
 	/*
-	 * Macros for matching N words at the start of the line, regardless of
-	 * what is after them, case-insensitively.
+	 * If we still don't have anything to match we have to fabricate some sort
+	 * of default list. If we were to just return NULL, readline automatically
+	 * attempts filename completion, and that's usually no good.
 	 */
-#define HeadMatches1(p1) \
-	(HEAD_INDEX(1) >=0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]))
-
-#define HeadMatches2(p1, p2) \
-	(HEAD_INDEX(2) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) &&	\
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]))
-
-#define HeadMatches3(p1, p2, p3) \
-	(HEAD_INDEX(3) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]))
-
-#define HeadMatches4(p1, p2, p3, p4) \
-	(HEAD_INDEX(4) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]))
-
-#define HeadMatches5(p1, p2, p3, p4, p5) \
-	(HEAD_INDEX(5) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]) && \
-	 word_matches(p5, previous_words[HEAD_INDEX(5)]))
-
-#define HeadMatches6(p1, p2, p3, p4, p5, p6)		\
-	(HEAD_INDEX(6) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]) && \
-	 word_matches(p5, previous_words[HEAD_INDEX(5)]) && \
-	 word_matches(p6, previous_words[HEAD_INDEX(6)]))
-
-#define HeadMatches7(p1, p2, p3, p4, p5, p6, p7)	\
-	(HEAD_INDEX(7) >= 0 && \
-	 word_matches(p1, previous_words[HEAD_INDEX(1)]) && \
-	 word_matches(p2, previous_words[HEAD_INDEX(2)]) && \
-	 word_matches(p3, previous_words[HEAD_INDEX(3)]) && \
-	 word_matches(p4, previous_words[HEAD_INDEX(4)]) && \
-	 word_matches(p5, previous_words[HEAD_INDEX(5)]) && \
-	 word_matches(p6, previous_words[HEAD_INDEX(6)]) && \
-	 word_matches(p7, previous_words[HEAD_INDEX(7)]))
+#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
+	rl_completion_append_character = '\0';
+#endif
+	COMPLETE_WITH_CONST("");		/* This returns matches */
+}
 
+/*
+ * The completion function.
+ *
+ * Makes completion list. previous_words may be modified on the way of making
+ * the returning list. Note that COMPLETE_WITH_* macros immediately return to
+ * the caller of this function.
+ */
+static char **
+psql_completion_internal(const char *text, char **previous_words,
+						 int previous_words_count)
+{
 	/* Known command-starting keywords. */
 	static const char *const sql_commands[] = {
 		"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
@@ -1411,104 +1035,122 @@ psql_completion(const char *text, int start, int end)
 		"\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
 	};
 
-	(void) end;					/* "end" is not used */
-
-#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
-	rl_completion_append_character = ' ';
-#endif
-
-	/* Clear a few things. */
-	if (completion_charp == NULL)
-		completion_charp = createPQExpBuffer();
-	completion_charpp = NULL;
-	completion_info_charp = NULL;
-	completion_info_charp2 = NULL;
-
-	/*
-	 * Scan the input line to extract the words before our current position.
-	 * According to those we'll make some smart decisions on what the user is
-	 * probably intending to type.
-	 */
-	previous_words = get_previous_words(start,
-										&words_buffer,
-										&previous_words_count);
+	/* The number of prefixing words to be ignored */
+	int			head_shift = 0;
 
 	/* If current word is a backslash command, offer completions for that */
 	if (text[0] == '\\')
 		COMPLETE_WITH_LIST_CS(backslash_commands);
 
 	/* If current word is a variable interpolation, handle that case */
-	else if (text[0] == ':' && text[1] != ':')
+	if (text[0] == ':' && text[1] != ':')
 	{
+		char	  **matches = NULL;
+
 		if (text[1] == '\'')
 			matches = complete_from_variables(text, ":'", "'", true);
 		else if (text[1] == '"')
 			matches = complete_from_variables(text, ":\"", "\"", true);
 		else
 			matches = complete_from_variables(text, ":", "", true);
+
+		return matches;
 	}
 
 	/* If no previous word, suggest one of the basic sql commands */
-	else if (previous_words_count == 0)
+	if (previous_words_count == 0)
 		COMPLETE_WITH_LIST(sql_commands);
 
 	/*
 	 * If this is in CREATE SCHEMA, seek to the last CREATE and regard it as
 	 * current command to complete.
 	 */
-	else if (HeadMatches2("CREATE", "SCHEMA") &&
-			 SHIFT_TO_LAST1("CREATE") &&
-			 false) {} /* FALL THROUGH */
+	if (HeadMatches2("CREATE", "SCHEMA"))
+	{
+		int n;
+
+		/* CREATE SCHEMA <name> */
+		if (Matches2("CREATE", "SCHEMA"))
+			COMPLETE_WITH_QUERY(Query_for_list_of_schemas,
+								ADDLIST1("IF NOT EXISTS"));
+		/* Remove optional words IF NOT EXISTS */
+		if (HeadMatches2("CREATE", "SCHEMA") &&
+			MidMatches3(3, "IF", "NOT", "EXISTS"))
+			COLLAPSE(3, 3);
+
+		if (Matches2("CREATE", "SCHEMA"))
+			COMPLETE_THING(-1);
+		
+		/* Else, move head match point past CREATE SCHEMA and go through */
+		if ((n = find_last_index_of("CREATE",
+									previous_words, previous_words_count)) > 0)
+		SHIFTHEAD(n);
+	}
+
 /* CREATE */
 	/* complete with something you can create */
-	else if (Matches1("CREATE"))
-		matches = completion_matches(text, create_command_generator);
+	if (Matches1("CREATE"))
+		return completion_matches(text, create_command_generator);
 
 /* DROP, but not DROP embedded in other commands */
 	/* complete with something you can drop */
-	else if (Matches1("DROP"))
-		matches = completion_matches(text, drop_command_generator);
+	if (Matches1("DROP"))
+		return completion_matches(text, drop_command_generator);
 
 /* ALTER */
 
-	/* ALTER TABLE */
-	else if (Matches2("ALTER", "TABLE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
-			ADDLIST2("IF EXISTS", "ALL IN TABLESPACE"));
-
-	/* ALTER TABLE after removing optional words IF EXISTS*/
-	else if (HeadMatches2("ALTER", "TABLE") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches2("ALTER", "TABLE"))
-		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
-
 	/* ALTER something */
-	else if (Matches1("ALTER"))
+	if (Matches1("ALTER"))
 	{
 		static const char *const list_ALTER[] =
-		{"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
-			"EVENT TRIGGER", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
-			"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR",
-			"POLICY", "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "SYSTEM", "TABLE",
-			"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
-		"USER", "USER MAPPING FOR", "VIEW", NULL};
+		{"AGGREGATE",
+		 "COLLATION", "CONVERSION",
+		 "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
+		 "EVENT TRIGGER", "EXTENSION",
+		 "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
+		 "GROUP",
+		 "INDEX",
+		 "LANGUAGE", "LARGE OBJECT",
+		 "MATERIALIZED VIEW", 
+		 "OPERATOR",
+		 "POLICY",
+		 "ROLE", "RULE",
+		 "SCHEMA", "SERVER", "SEQUENCE", "SYSTEM",
+		 "TABLE", "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
+		 "USER", "USER MAPPING FOR",
+		 "VIEW",
+		 NULL};
 
 		COMPLETE_WITH_LIST(list_ALTER);
 	}
+	/* ALTER TABLE */
+	if (Matches2("ALTER", "TABLE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
+			ADDLIST2("IF EXISTS", "ALL IN TABLESPACE"));
+
+	/* Remove optional words IF EXISTS just after ALTER TABLE */
+	if (HeadMatches2("ALTER", "TABLE") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	/* ALTER TABLE */
+	if (Matches2("ALTER", "TABLE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
+
 	/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
-	else if (TailMatches4("ALL", "IN", "TABLESPACE", MatchAny))
+	if (TailMatches4("ALL", "IN", "TABLESPACE", MatchAny))
 		COMPLETE_WITH_LIST2("SET TABLESPACE", "OWNED BY");
 	/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */
-	else if (TailMatches6("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY"))
+	if (TailMatches6("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 	/* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */
-	else if (TailMatches7("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny))
+	if (TailMatches7("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny))
 		COMPLETE_WITH_CONST("SET TABLESPACE");
 	/* ALTER AGGREGATE,FUNCTION <name> */
-	else if (Matches3("ALTER", "AGGREGATE|FUNCTION", MatchAny))
+	if (Matches3("ALTER", "AGGREGATE|FUNCTION", MatchAny))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER AGGREGATE,FUNCTION <name> (...) */
-	else if (Matches4("ALTER", "AGGREGATE|FUNCTION", MatchAny, MatchAny))
+	if (Matches4("ALTER", "AGGREGATE|FUNCTION", MatchAny, MatchAny))
 	{
 		if (ends_with(prev_wd, ')'))
 			COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
@@ -1517,74 +1159,76 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* ALTER SCHEMA <name> */
-	else if (Matches3("ALTER", "SCHEMA", MatchAny))
+	if (Matches3("ALTER", "SCHEMA", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER TO", "RENAME TO");
 
 	/* ALTER COLLATION <name> */
-	else if (Matches3("ALTER", "COLLATION", MatchAny))
+	if (Matches3("ALTER", "COLLATION", MatchAny))
 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
 
 	/* ALTER CONVERSION <name> */
-	else if (Matches3("ALTER", "CONVERSION", MatchAny))
+	if (Matches3("ALTER", "CONVERSION", MatchAny))
 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
 
 	/* ALTER DATABASE <name> */
-	else if (Matches3("ALTER", "DATABASE", MatchAny))
+	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 (Matches3("ALTER", "EVENT", "TRIGGER"))
+	if (Matches3("ALTER", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers, "");
 
 	/* ALTER EVENT TRIGGER <name> */
-	else if (Matches4("ALTER", "EVENT", "TRIGGER", MatchAny))
+	if (Matches4("ALTER", "EVENT", "TRIGGER", MatchAny))
 		COMPLETE_WITH_LIST4("DISABLE", "ENABLE", "OWNER TO", "RENAME TO");
 
 	/* ALTER EVENT TRIGGER <name> ENABLE */
-	else if (Matches5("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"))
+	if (Matches5("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"))
 		COMPLETE_WITH_LIST2("REPLICA", "ALWAYS");
 
 	/* ALTER EXTENSION <name> */
-	else if (Matches3("ALTER", "EXTENSION", MatchAny))
+	if (Matches3("ALTER", "EXTENSION", MatchAny))
 		COMPLETE_WITH_LIST4("ADD", "DROP", "UPDATE", "SET SCHEMA");
 
 	/* ALTER EXTENSION <name> UPDATE */
-	else if (Matches4("ALTER", "EXTENSION", MatchAny, "UPDATE"))
+	if (Matches4("ALTER", "EXTENSION", MatchAny, "UPDATE"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions_with_TO,"");
 	}
 
 	/* ALTER EXTENSION <name> UPDATE TO */
-	else if (Matches5("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"))
+	if (Matches5("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions, "");
 	}
 
 	/* ALTER FOREIGN */
-	else if (Matches2("ALTER", "FOREIGN"))
+	if (Matches2("ALTER", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
 
 	/* ALTER FOREIGN DATA WRAPPER <name> */
-	else if (Matches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+	if (Matches5("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 		COMPLETE_WITH_LIST5("HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", "RENAME TO");
 
 	/* ALTER FOREIGN TABLE */
-	else if (Matches3("ALTER|DROP", "FOREIGN", "TABLE"))
+	if (Matches3("ALTER|DROP", "FOREIGN", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables,
 								   ADDLIST1("IF EXISTS"));
 
 	/* ALTER|DROP FOREIGN TABLE after removing optinal words IF EXISTS */
-	else if (HeadMatches3("ALTER|DROP", "FOREIGN", "TABLE") &&
-			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
-			 Matches3("ALTER|DROP", "FOREIGN", "TABLE"))
+	if (HeadMatches3("ALTER|DROP", "FOREIGN", "TABLE") &&
+		MidMatches2(4, "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+
+	if (Matches3("ALTER|DROP", "FOREIGN", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, "");
 
 	/* ALTER FOREIGN TABLE <name> */
-	else if (Matches4("ALTER", "FOREIGN", "TABLE", MatchAny))
+	if (Matches4("ALTER", "FOREIGN", "TABLE", MatchAny))
 	{
 		static const char *const list_ALTER_FOREIGN_TABLE[] =
 		{"ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE", "INHERIT",
@@ -1595,58 +1239,62 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* ALTER FOREIGN TABLE xxx RENAME */
-	else if (Matches5("ALTER", "FOREIGN", "TABLE", MatchAny, "RENAME"))
+	if (Matches5("ALTER", "FOREIGN", "TABLE", MatchAny, "RENAME"))
 		COMPLETE_WITH_ATTR(prev2_wd, ADDLIST2("COLUMN", "TO"));
 
 	/* ALTER INDEX */
-	else if (Matches2("ALTER", "INDEX"))
+	if (Matches2("ALTER", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
 			   ADDLIST2("IF EXISTS", "ALL IN TABLESPACE"));
 	/* ALTER INDEX after removing optional words IF EXISTS */
-	else if (HeadMatches2("ALTER", "INDEX") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches2("ALTER", "INDEX"))
+	if (HeadMatches2("ALTER", "INDEX") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches2("ALTER", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "");
 
 	/* ALTER INDEX <name> */
-	else if (Matches3("ALTER", "INDEX", MatchAny))
+	if (Matches3("ALTER", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST4("OWNER TO", "RENAME TO", "SET", "RESET");
 	/* ALTER INDEX <name> SET */
-	else if (Matches4("ALTER", "INDEX", MatchAny, "SET"))
+	if (Matches4("ALTER", "INDEX", MatchAny, "SET"))
 		COMPLETE_WITH_LIST2("(", "TABLESPACE");
 	/* ALTER INDEX <name> RESET */
-	else if (Matches4("ALTER", "INDEX", MatchAny, "RESET"))
+	if (Matches4("ALTER", "INDEX", MatchAny, "RESET"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER INDEX <foo> SET|RESET ( */
-	else if (Matches5("ALTER", "INDEX", MatchAny, "RESET", "("))
+	if (Matches5("ALTER", "INDEX", MatchAny, "RESET", "("))
 		COMPLETE_WITH_LIST3("fillfactor", "fastupdate",
 							"gin_pending_list_limit");
-	else if (Matches5("ALTER", "INDEX", MatchAny, "SET", "("))
+	if (Matches5("ALTER", "INDEX", MatchAny, "SET", "("))
 		COMPLETE_WITH_LIST3("fillfactor =", "fastupdate =",
 							"gin_pending_list_limit =");
 
 	/* ALTER LANGUAGE <name> */
-	else if (Matches3("ALTER", "LANGUAGE", MatchAny))
+	if (Matches3("ALTER", "LANGUAGE", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER_TO", "RENAME TO");
 
 	/* ALTER LARGE OBJECT <oid> */
-	else if (Matches4("ALTER", "LARGE", "OBJECT", MatchAny))
+	if (Matches4("ALTER", "LARGE", "OBJECT", MatchAny))
 		COMPLETE_WITH_CONST("OWNER TO");
 
 	/* ALTER MATERIALIZED VIEW */
-	else if (Matches3("ALTER", "MATERIALIZED", "VIEW"))
+	if (Matches3("ALTER", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 			   ADDLIST2("IF EXISTS", "ALL IN TABLESPACE"));
 
 	/* ALTER MATERIALIZED VIEW with name after removing optional words */
-	else if (HeadMatches3("ALTER", "MATERIALIZED", "VIEW") &&
-			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
-			 Matches3("ALTER", "MATERIALIZED", "VIEW"))
+	if (HeadMatches3("ALTER", "MATERIALIZED", "VIEW") &&
+		MidMatches2(4, "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+
+	if (Matches3("ALTER", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
 
 	/* ALTER USER,ROLE <name> */
-	else if (Matches3("ALTER", "USER|ROLE", MatchAny) &&
-			 !TailMatches2("USER", "MAPPING"))
+	if (Matches3("ALTER", "USER|ROLE", MatchAny) &&
+		!TailMatches2("USER", "MAPPING"))
 	{
 		static const char *const list_ALTERUSER[] =
 		{"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
@@ -1660,7 +1308,7 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* ALTER USER,ROLE <name> WITH */
-	else if (Matches4("ALTER", "USER|ROLE", MatchAny, "WITH"))
+	if (Matches4("ALTER", "USER|ROLE", MatchAny, "WITH"))
 	{
 		/* Similar to the above, but don't complete "WITH" again. */
 		static const char *const list_ALTERUSER_WITH[] =
@@ -1675,63 +1323,67 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* complete ALTER USER,ROLE <name> ENCRYPTED,UNENCRYPTED with PASSWORD */
-	else if (Matches4("ALTER", "USER|ROLE", MatchAny, "ENCRYPTED|UNENCRYPTED"))
+	if (Matches4("ALTER", "USER|ROLE", MatchAny, "ENCRYPTED|UNENCRYPTED"))
 		COMPLETE_WITH_CONST("PASSWORD");
 	/* ALTER DEFAULT PRIVILEGES */
-	else if (Matches3("ALTER", "DEFAULT", "PRIVILEGES"))
+	if (Matches3("ALTER", "DEFAULT", "PRIVILEGES"))
 		COMPLETE_WITH_LIST3("FOR ROLE", "FOR USER", "IN SCHEMA");
 	/* ALTER DEFAULT PRIVILEGES FOR */
-	else if (Matches4("ALTER", "DEFAULT", "PRIVILEGES", "FOR"))
+	if (Matches4("ALTER", "DEFAULT", "PRIVILEGES", "FOR"))
 		COMPLETE_WITH_LIST2("ROLE", "USER");
 	/* ALTER DEFAULT PRIVILEGES { FOR ROLE ... | IN SCHEMA ... } */
-	else if (Matches6("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny) ||
+	if (Matches6("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny) ||
 		Matches6("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny))
 		COMPLETE_WITH_LIST2("GRANT", "REVOKE");
 	/* ALTER DOMAIN <name> */
-	else if (Matches3("ALTER", "DOMAIN", MatchAny))
+	if (Matches3("ALTER", "DOMAIN", MatchAny))
 		COMPLETE_WITH_LIST6("ADD", "DROP", "OWNER TO", "RENAME", "SET",
 							"VALIDATE CONSTRAINT");
 	/* ALTER DOMAIN <sth> DROP */
-	else if (Matches4("ALTER", "DOMAIN", MatchAny, "DROP"))
+	if (Matches4("ALTER", "DOMAIN", MatchAny, "DROP"))
 		COMPLETE_WITH_LIST3("CONSTRAINT", "DEFAULT", "NOT NULL");
 	/* ALTER DOMAIN <sth> RENAME|VALIDATE CONSTRAINT */
-	else if (Matches5("ALTER", "DOMAIN", MatchAny, "RENAME|VALIDATE", "CONSTRAINT"))
+	if (Matches5("ALTER", "DOMAIN", MatchAny, "RENAME|VALIDATE", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_type, "");
 	}
 	/* ALTER DOMAIN <sth> DROP CONSTRAINT */
-	else if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT"))
+	if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_type,
 							ADDLIST1("IF EXISTS"));
 	}
 	/* Try the same match after removing optional words IF EXISTS */
-	else if (HeadMatches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT") &&
-			 MidMatchAndRemove2(5, "IF", "EXISTS") &&
-			 Matches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT"))
+	if (HeadMatches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT") &&
+		MidMatches2(6, "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+
+	if (Matches5("ALTER", "DOMAIN", MatchAny, "DROP", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_type, "");
 	}
 	/* ALTER DOMAIN <sth> RENAME */
-	else if (Matches4("ALTER", "DOMAIN", MatchAny, "RENAME"))
+	if (Matches4("ALTER", "DOMAIN", MatchAny, "RENAME"))
 		COMPLETE_WITH_LIST2("CONSTRAINT", "TO");
 	/* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
-	else if (Matches6("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny))
+	if (Matches6("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny))
 		COMPLETE_WITH_CONST("TO");
 
 	/* ALTER DOMAIN <sth> SET */
-	else if (Matches4("ALTER", "DOMAIN", MatchAny, "SET"))
+	if (Matches4("ALTER", "DOMAIN", MatchAny, "SET"))
 		COMPLETE_WITH_LIST3("DEFAULT", "NOT NULL", "SCHEMA");
-	else if (Matches2("ALTER", "SEQUENCE"))
+	if (Matches2("ALTER", "SEQUENCE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences,
 								   ADDLIST1("IF EXISTS"));
 	/* ALTER SEQUENCE with name after removing optional words IF EXISTS */
-	else if (HeadMatches2("ALTER", "SEQUENCE") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("ALTER", "SEQUENCE", MatchAny))
+	if (HeadMatches2("ALTER", "SEQUENCE") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches3("ALTER", "SEQUENCE", MatchAny))
 	{
 		static const char *const list_ALTERSEQUENCE[] =
 		{"INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "NO", "CACHE", "CYCLE",
@@ -1740,83 +1392,87 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST(list_ALTERSEQUENCE);
 	}
 	/* ALTER SEQUENCE <name> NO */
-	else if (Matches4("ALTER", "SEQUENCE", MatchAny, "NO"))
+	if (Matches4("ALTER", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE");
 	/* ALTER SERVER <name> */
-	else if (Matches3("ALTER", "SERVER", MatchAny))
+	if (Matches3("ALTER", "SERVER", MatchAny))
 		COMPLETE_WITH_LIST4("VERSION", "OPTIONS", "OWNER TO", "RENAME TO");
 	/* ALTER SERVER <name> VERSION <version> */
-	else if (Matches5("ALTER", "SERVER", MatchAny, "VERSION", MatchAny))
+	if (Matches5("ALTER", "SERVER", MatchAny, "VERSION", MatchAny))
 		COMPLETE_WITH_CONST("OPTIONS");
 	/* ALTER SYSTEM SET, RESET, RESET ALL */
-	else if (Matches2("ALTER", "SYSTEM"))
+	if (Matches2("ALTER", "SYSTEM"))
 		COMPLETE_WITH_LIST2("SET", "RESET");
 	/* ALTER SYSTEM SET|RESET <name> */
-	else if (Matches3("ALTER", "SYSTEM", "SET|RESET"))
+	if (Matches3("ALTER", "SYSTEM", "SET|RESET"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_alter_system_set_vars, "");
 	/* ALTER VIEW */
-	else if (Matches2("ALTER", "VIEW"))
+	if (Matches2("ALTER", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
 								   ADDLIST1("IF EXISTS"));
 	/* ALTER VIEW <name> with subcommands after removing optional worlds */
-	else if (HeadMatches2("ALTER", "VIEW") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("ALTER", "VIEW", MatchAny))
+	if (HeadMatches2("ALTER", "VIEW") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches3("ALTER", "VIEW", MatchAny))
 		COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
 							"SET SCHEMA");
 	/* ALTER MATERIALIZED VIEW <name> */
-	else if (Matches4("ALTER", "MATERIALIZED", "VIEW", MatchAny))
+	if (Matches4("ALTER", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_LIST4("ALTER COLUMN", "OWNER TO", "RENAME TO",
 							"SET SCHEMA");
 
 	/* ALTER POLICY */
-	else if (Matches2("ALTER", "POLICY"))
+	if (Matches2("ALTER", "POLICY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_policies,
 							ADDLIST1("IF EXISTS"));
 	/* ALTER POLICY <name> with ON after removing optional words IF EXISTS */
-	else if (HeadMatches2("ALTER", "POLICY") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("ALTER", "POLICY", MatchAny))
+	if (HeadMatches2("ALTER", "POLICY") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches3("ALTER", "POLICY", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* ALTER POLICY <name> ON <table> */
-	else if (Matches4("ALTER", "POLICY", MatchAny, "ON"))
+	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 (Matches5("ALTER", "POLICY", MatchAny, "ON", MatchAny))
+	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 (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"))
+	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 (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
+	if (Matches6("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER POLICY <name> ON <table> WITH CHECK ( */
-	else if (Matches7("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
+	if (Matches7("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
 		COMPLETE_WITH_CONST("(");
 
 	/* ALTER RULE <name>, add ON */
-	else if (Matches3("ALTER", "RULE", MatchAny))
+	if (Matches3("ALTER", "RULE", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 
 	/* If we have ALTER RULE <name> ON, then add the correct tablename */
-	else if (Matches4("ALTER", "RULE", MatchAny, "ON"))
+	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 (Matches5("ALTER", "RULE", MatchAny, "ON", MatchAny))
+	if (Matches5("ALTER", "RULE", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_CONST("RENAME TO");
 
 	/* ALTER TRIGGER <name>, add ON */
-	else if (Matches3("ALTER", "TRIGGER", MatchAny))
+	if (Matches3("ALTER", "TRIGGER", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 
-	else if (Matches4("ALTER", "TRIGGER", MatchAny, MatchAny))
+	if (Matches4("ALTER", "TRIGGER", MatchAny, MatchAny))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger, "");
@@ -1825,17 +1481,17 @@ psql_completion(const char *text, int start, int end)
 	/*
 	 * If we have ALTER TRIGGER <sth> ON, then add the correct tablename
 	 */
-	else if (Matches4("ALTER", "TRIGGER", MatchAny, "ON"))
+	if (Matches4("ALTER", "TRIGGER", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 
 	/* ALTER TRIGGER <name> ON <name> */
-	else if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
+	if (Matches5("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_CONST("RENAME TO");
 
 	/*
 	 * If we detect ALTER TABLE <name>, suggest sub commands
 	 */
-	else if (Matches3("ALTER", "TABLE", MatchAny))
+	if (Matches3("ALTER", "TABLE", MatchAny))
 	{
 		static const char *const list_ALTER2[] =
 		{"ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP", "ENABLE", "INHERIT",
@@ -1845,97 +1501,103 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST(list_ALTER2);
 	}
 	/* ALTER TABLE xxx ENABLE */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "ENABLE"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "ENABLE"))
 		COMPLETE_WITH_LIST5("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE",
 							"TRIGGER");
-	else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"))
 		COMPLETE_WITH_LIST2("RULE", "TRIGGER");
-	else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_rule_of_table, "");
 	}
-	else if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
+	if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
 	{
 		completion_info_charp = prev4_wd;
 		COMPLETE_WITH_QUERY(Query_for_rule_of_table, "");
 	}
-	else if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_trigger_of_table, "");
 	}
-	else if (Matches6("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"))
+	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 (Matches4("ALTER", "TABLE", MatchAny, "INHERIT"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "INHERIT"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* ALTER TABLE xxx NO INHERIT */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* ALTER TABLE xxx DISABLE */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "DISABLE"))
 		COMPLETE_WITH_LIST3("ROW LEVEL SECURITY", "RULE", "TRIGGER");
-	else if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_rule_of_table, "");
 	}
-	else if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_trigger_of_table, "");
 	}
 
 	/* ALTER TABLE xxx ALTER */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "ALTER"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "ALTER"))
 		COMPLETE_WITH_ATTR(prev2_wd, ADDLIST2("COLUMN", "CONSTRAINT"));
 
 	/* ALTER TABLE xxx RENAME */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "RENAME"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "RENAME"))
 		COMPLETE_WITH_ATTR(prev2_wd, ADDLIST3("COLUMN", "CONSTRAINT", "TO"));
-	else if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
 
 	/* ALTER [FOREIGN] TABLE xxx RENAME yyy */
-	else if ((HeadMatches2("ALTER", "TABLE") ||
+	if ((HeadMatches2("ALTER", "TABLE") ||
 			  HeadMatches3("ALTER", "FOREIGN", "TABLE")) &&
 			 TailMatches2("RENAME", MatchAnyExcept("CONSTRAINT|TO")))
 		COMPLETE_WITH_CONST("TO");
 
 	/* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
-	else if (Matches6("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")))
+	if (Matches6("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")))
 		COMPLETE_WITH_CONST("TO");
 
 	/* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "DROP"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "DROP"))
 		COMPLETE_WITH_LIST2("COLUMN", "CONSTRAINT");
 	/*  ALTER TABLE DROP COLUMN may take IF EXISTS */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, ADDLIST1("IF EXISTS"));
 	/* ALTER TABLE <name> with ADD/ALTER/DROP after removing optional words */
-	else if (HeadMatches4("ALTER", "TABLE", MatchAny, "ADD|ALTER|DROP") &&
-			 MidMatchAndRemove1(4, "COLUMN") &&
-			 MidMatchAndRemove2(4, "IF", "EXISTS") &&
-			 Matches4("ALTER", "TABLE", MatchAny, "ADD|ALTER|DROP"))
+	if (HeadMatches4("ALTER", "TABLE", MatchAny, "ADD|ALTER|DROP"))
+	{
+		if (MidMatches1(5, "COLUMN"))
+			COLLAPSE(5, 1);
+		if (MidMatches2(5, "IF", "EXISTS"))
+			COLLAPSE(5, 2);
+	}
+	if (Matches4("ALTER", "TABLE", MatchAny, "ADD|ALTER|DROP"))
 		COMPLETE_WITH_ATTR(prev2_wd, "");
 	/* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, ADDLIST1("IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
-	else if (HeadMatches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN") &&
-			 MidMatchAndRemove2(5, "IF", "EXISTS") &&
-			 Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
+	if (HeadMatches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN") &&
+		MidMatches2(6, "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
 
 	/*
 	 * If we have ALTER TABLE <sth> ALTER|RENAME|VALIDATE CONSTRAINT,
 	 * provide list of constraints
 	 */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME|VALIDATE", "CONSTRAINT"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "ALTER|RENAME|VALIDATE", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_table, "");
@@ -1944,49 +1606,51 @@ psql_completion(const char *text, int start, int end)
 	 * If we have ALTER TABLE <sth> DROP CONSTRAINT,
 	 * provide list of constraints
 	 */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_table,
 							ADDLIST1("IF EXISTS"));
 	}
 	/* Try the same after removing optional words IF EXISTS */
-	else if (HeadMatches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT") &&
-			 MidMatchAndRemove2(5, "IF", "EXISTS") &&
-			 Matches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT"))
+	if (HeadMatches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT") &&
+		MidMatches2(6, "IF", "EXISTS"))
+		COLLAPSE(6, 2);
+
+	if (Matches5("ALTER", "TABLE", MatchAny, "DROP", "CONSTRAINT"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_constraint_of_table, "");
 	}
 	/* ALTER TABLE ALTER [COLUMN] <foo> */
-	else if (Matches6("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny) ||
+	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 (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
+	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 (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
+	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 (Matches8("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
+	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 (Matches7("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP") ||
+	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 (Matches4("ALTER", "TABLE", MatchAny, "CLUSTER"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "CLUSTER"))
 		COMPLETE_WITH_CONST("ON");
-	else if (Matches5("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))
 	{
 		completion_info_charp = prev3_wd;
 		COMPLETE_WITH_QUERY(Query_for_index_of_table, "");
 	}
 	/* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "SET"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "SET"))
 		COMPLETE_WITH_LIST7("(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED",
 							"WITH", "WITHOUT");
 
@@ -1994,19 +1658,19 @@ psql_completion(const char *text, int start, int end)
 	 * If we have ALTER TABLE <sth> SET TABLESPACE provide a list of
 	 * tablespaces
 	 */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces, "");
 	/* If we have ALTER TABLE <sth> SET WITH provide OIDS */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITH"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITH"))
 		COMPLETE_WITH_CONST("OIDS");
 	/* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"))
 		COMPLETE_WITH_LIST2("CLUSTER", "OIDS");
 	/* ALTER TABLE <foo> RESET */
-	else if (Matches4("ALTER", "TABLE", MatchAny, "RESET"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "RESET"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER TABLE <foo> SET|RESET ( */
-	else if (Matches5("ALTER", "TABLE", MatchAny, "SET|RESET", "("))
+	if (Matches5("ALTER", "TABLE", MatchAny, "SET|RESET", "("))
 	{
 		static const char *const list_TABLEOPTIONS[] =
 		{
@@ -2044,120 +1708,117 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST(list_TABLEOPTIONS);
 	}
-	else if (Matches7("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
+	if (Matches7("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
 	{
 		completion_info_charp = prev5_wd;
 		COMPLETE_WITH_QUERY(Query_for_index_of_table, "");
 	}
-	else if (Matches6("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
+	if (Matches6("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
 		COMPLETE_WITH_CONST("INDEX");
-	else if (Matches5("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"))
+	if (Matches5("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"))
 		COMPLETE_WITH_LIST4("FULL", "NOTHING", "DEFAULT", "USING");
-	else if (Matches4("ALTER", "TABLE", MatchAny, "REPLICA"))
+	if (Matches4("ALTER", "TABLE", MatchAny, "REPLICA"))
 		COMPLETE_WITH_CONST("IDENTITY");
 
 	/* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
-	else if (Matches3("ALTER", "TABLESPACE", MatchAny))
+	if (Matches3("ALTER", "TABLESPACE", MatchAny))
 		COMPLETE_WITH_LIST4("RENAME TO", "OWNER TO", "SET", "RESET");
 	/* ALTER TABLESPACE <foo> SET|RESET */
-	else if (Matches4("ALTER", "TABLESPACE", MatchAny, "SET|RESET"))
+	if (Matches4("ALTER", "TABLESPACE", MatchAny, "SET|RESET"))
 		COMPLETE_WITH_CONST("(");
 	/* ALTER TABLESPACE <foo> SET|RESET ( */
-	else if (Matches5("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("))
+	if (Matches5("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("))
 		COMPLETE_WITH_LIST3("seq_page_cost", "random_page_cost",
 							"effective_io_concurrency");
 
 	/* ALTER TEXT SEARCH */
-	else if (Matches3("ALTER", "TEXT", "SEARCH"))
+	if (Matches3("ALTER", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-	else if (Matches5("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny))
+	if (Matches5("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny))
 		COMPLETE_WITH_LIST2("RENAME TO", "SET SCHEMA");
-	else if (Matches5("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny))
+	if (Matches5("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny))
 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
-	else if (Matches5("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
+	if (Matches5("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
 		COMPLETE_WITH_LIST6("ADD MAPPING FOR", "ALTER MAPPING",
 							"DROP MAPPING",	"OWNER TO", "RENAME TO", "SET SCHEMA");
-	else if (Matches7("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny, "DROP", "MAPPING"))
+	if (Matches7("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny, "DROP", "MAPPING"))
 		COMPLETE_WITH_LIST2("FOR", "IF EXISTS FOR");
-	/* Remove optional words IF EXISTS */
-	else if (HeadMatches7("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny, "DROP", "MAPPING") &&
-			 MidMatchAndRemove2(7, "IF", "EXISTS") &&
-			 false) {} /* Nothing to do for now */
 
 	/* complete ALTER TYPE <foo> with actions */
-	else if (Matches3("ALTER", "TYPE", MatchAny))
+	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 (Matches4("ALTER", "TYPE", MatchAny, "ADD"))
+	if (Matches4("ALTER", "TYPE", MatchAny, "ADD"))
 		COMPLETE_WITH_LIST2("ATTRIBUTE", "VALUE");
-	else if (Matches5("ALTER", "TYPE", MatchAny, "ADD", "VALUE"))
+	if (Matches5("ALTER", "TYPE", MatchAny, "ADD", "VALUE"))
 		COMPLETE_WITH_LIST2("IF NOT EXISTS", "");
 	/* Remove optional words IF NOT EXISTS */
-	else if (HeadMatches5("ALTER", "TYPE", MatchAny, "ADD", "VALUE") &&
-			 MidMatchAndRemove3(5, "IF", "NOT", "EXISTS") &&
-			 false) {} /* Nothing to do for now */
+	if (HeadMatches5("ALTER", "TYPE", MatchAny, "ADD", "VALUE") &&
+		MidMatches2(6, "IF", "EXISTS"))
+		COLLAPSE(6, 2);
 	/* ALTER TYPE <foo> RENAME	*/
-	else if (Matches4("ALTER", "TYPE", MatchAny, "RENAME"))
+	if (Matches4("ALTER", "TYPE", MatchAny, "RENAME"))
 		COMPLETE_WITH_LIST2("ATTRIBUTE", "TO");
 	/* ALTER TYPE xxx RENAME ATTRIBUTE yyy */
-	else if (Matches6("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE", MatchAny))
+	if (Matches6("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE", MatchAny))
 		COMPLETE_WITH_CONST("TO");
 
 	/*
 	 * If we have ALTER TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list
 	 * of attributes
 	 */
-	else if (Matches5("ALTER", "TYPE", MatchAny, "ALTER|RENAME", "ATTRIBUTE"))
+	if (Matches5("ALTER", "TYPE", MatchAny, "ALTER|RENAME", "ATTRIBUTE"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
-	else if (Matches5("ALTER", "TYPE", MatchAny, "DROP", "ATTRIBUTE"))
+	if (Matches5("ALTER", "TYPE", MatchAny, "DROP", "ATTRIBUTE"))
 		COMPLETE_WITH_ATTR(prev3_wd, ADDLIST1("IF EXISTS"));
 	/* Remove optional words IF EXISTS */
-	else if (HeadMatches5("ALTER", "TYPE", MatchAny, "DROP", "ATTRIBUTE") &&
-			 MidMatchAndRemove2(5, "IF", "EXISTS") &&
+	if (HeadMatches5("ALTER", "TYPE", MatchAny, "DROP", "ATTRIBUTE") &&
+		MidMatches2(6, "IF", "EXISTS"))
+		COLLAPSE(6, 2);
 	/* ALTER TYPE ALTER ATTRIBUTE <foo> */
-			 Matches6("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny))
+	if (Matches6("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny))
 		COMPLETE_WITH_CONST("TYPE");
 	/* complete ALTER GROUP <foo> */
-	else if (Matches3("ALTER", "GROUP", MatchAny))
+	if (Matches3("ALTER", "GROUP", MatchAny))
 		COMPLETE_WITH_LIST3("ADD USER", "DROP USER", "RENAME TO");
 	/* complete ALTER GROUP <foo> ADD|DROP with USER */
-	else if (Matches4("ALTER", "GROUP", MatchAny, "ADD|DROP"))
+	if (Matches4("ALTER", "GROUP", MatchAny, "ADD|DROP"))
 		COMPLETE_WITH_CONST("USER");
 	/* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
-	else if (Matches5("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"))
+	if (Matches5("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 
 /* BEGIN */
-	else if (Matches1("BEGIN"))
+	if (Matches1("BEGIN"))
 		COMPLETE_WITH_LIST6("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
 /* END, ABORT */
-	else if (Matches1("END|ABORT"))
+	if (Matches1("END|ABORT"))
 		COMPLETE_WITH_LIST2("WORK", "TRANSACTION");
 /* COMMIT */
-	else if (Matches1("COMMIT"))
+	if (Matches1("COMMIT"))
 		COMPLETE_WITH_LIST3("WORK", "TRANSACTION", "PREPARED");
 /* RELEASE SAVEPOINT */
-	else if (Matches1("RELEASE"))
+	if (Matches1("RELEASE"))
 		COMPLETE_WITH_CONST("SAVEPOINT");
 /* ROLLBACK */
-	else if (Matches1("ROLLBACK"))
+	if (Matches1("ROLLBACK"))
 		COMPLETE_WITH_LIST4("WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
 /* CLUSTER */
-	else if (Matches1("CLUSTER"))
+	if (Matches1("CLUSTER"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 								   ADDLIST1("VERBOSE"));
-	else if (Matches2("CLUSTER", "VERBOSE"))
+	if (Matches2("CLUSTER", "VERBOSE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "");
 	/* If we have CLUSTER <sth>, then add "USING" */
-	else if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON")))
+	if (Matches2("CLUSTER", MatchAnyExcept("VERBOSE|ON")))
 		COMPLETE_WITH_CONST("USING");
 	/* If we have CLUSTER VERBOSE <sth>, then add "USING" */
-	else if (Matches3("CLUSTER", "VERBOSE", MatchAny))
+	if (Matches3("CLUSTER", "VERBOSE", MatchAny))
 		COMPLETE_WITH_CONST("USING");
 	/* If we have CLUSTER <sth> USING, then add the index as well */
-	else if (Matches3("CLUSTER", MatchAny, "USING") ||
+	if (Matches3("CLUSTER", MatchAny, "USING") ||
 			 Matches4("CLUSTER", "VERBOSE", MatchAny, "USING"))
 	{
 		completion_info_charp = prev2_wd;
@@ -2165,9 +1826,9 @@ psql_completion(const char *text, int start, int end)
 	}
 
 /* COMMENT */
-	else if (Matches1("COMMENT"))
+	if (Matches1("COMMENT"))
 		COMPLETE_WITH_CONST("ON");
-	else if (Matches2("COMMENT", "ON"))
+	if (Matches2("COMMENT", "ON"))
 	{
 		static const char *const list_COMMENT[] =
 		{"ACCESS METHOD", "CAST", "COLLATION", "CONVERSION", "DATABASE",
@@ -2180,26 +1841,26 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST(list_COMMENT);
 	}
-	else if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
+	if (Matches4("COMMENT", "ON", "ACCESS", "METHOD"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods, "");
-	else if (Matches3("COMMENT", "ON", "FOREIGN"))
+	if (Matches3("COMMENT", "ON", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
-	else if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
+	if (Matches4("COMMENT", "ON", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-	else if (Matches3("COMMENT", "ON", "CONSTRAINT"))
+	if (Matches3("COMMENT", "ON", "CONSTRAINT"))
 		COMPLETE_WITH_QUERY(Query_for_all_table_constraints, "");
-	else if (Matches4("COMMENT", "ON", "CONSTRAINT", MatchAny))
+	if (Matches4("COMMENT", "ON", "CONSTRAINT", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (Matches5("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
+	if (Matches5("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_constraint, "");
 	}
-	else if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW"))
+	if (Matches4("COMMENT", "ON", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
-	else if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER"))
+	if (Matches4("COMMENT", "ON", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers, "");
-	else if (Matches4("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
+	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");
@@ -2210,274 +1871,278 @@ psql_completion(const char *text, int start, int end)
 	 * If we have COPY, offer list of tables or "(" (Also cover the analogous
 	 * backslash command).
 	 */
-	else if (Matches1("COPY|\\copy"))
+	if (Matches1("COPY|\\copy"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 								   ADDLIST1("("));
 	/* If we have COPY BINARY, complete with list of tables */
-	else if (Matches2("COPY", "BINARY"))
+	if (Matches2("COPY", "BINARY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* If we have COPY (, complete it with legal commands */
-	else if (Matches2("COPY|\\copy", "("))
+	if (Matches2("COPY|\\copy", "("))
 		COMPLETE_WITH_LIST7("SELECT", "TABLE", "VALUES", "INSERT", "UPDATE", "DELETE", "WITH");
 	/* If we have COPY [BINARY] <sth>, complete it with "TO" or "FROM" */
-	else if (Matches2("COPY|\\copy", MatchAny) ||
+	if (Matches2("COPY|\\copy", MatchAny) ||
 			 Matches3("COPY", "BINARY", MatchAny))
 		COMPLETE_WITH_LIST2("FROM", "TO");
 	/* If we have COPY [BINARY] <sth> FROM|TO, complete with filename */
-	else if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") ||
+	if (Matches3("COPY|\\copy", MatchAny, "FROM|TO") ||
 			 Matches4("COPY", "BINARY", MatchAny, "FROM|TO"))
 	{
-		SET_COMP_CHARP("");
-		matches = completion_matches(text, complete_from_files);
+		SET_COMPLETION_CHARP("");
+		return completion_matches(text, complete_from_files);
 	}
 
 	/* Handle COPY [BINARY] <sth> FROM|TO filename */
-	else if (Matches4("COPY|\\copy", MatchAny, "FROM|TO", MatchAny) ||
+	if (Matches4("COPY|\\copy", MatchAny, "FROM|TO", MatchAny) ||
 			 Matches5("COPY", "BINARY", MatchAny, "FROM|TO", MatchAny))
 		COMPLETE_WITH_LIST6("BINARY", "OIDS", "DELIMITER", "NULL", "CSV",
 							"ENCODING");
 
 	/* Handle COPY [BINARY] <sth> FROM|TO filename CSV */
-	else if (Matches5("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "CSV") ||
+	if (Matches5("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "CSV") ||
 			 Matches6("COPY", "BINARY", MatchAny, "FROM|TO", MatchAny, "CSV"))
 		COMPLETE_WITH_LIST5("HEADER", "QUOTE", "ESCAPE", "FORCE QUOTE",
 							"FORCE NOT NULL");
 
 	/* CREATE ACCESS METHOD */
 	/* Complete "CREATE ACCESS METHOD <name>" */
-	else if (Matches4("CREATE", "ACCESS", "METHOD", MatchAny))
+	if (Matches4("CREATE", "ACCESS", "METHOD", MatchAny))
 		COMPLETE_WITH_CONST("TYPE");
 	/* Complete "CREATE ACCESS METHOD <name> TYPE" */
-	else if (Matches5("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"))
+	if (Matches5("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"))
 		COMPLETE_WITH_CONST("INDEX");
 	/* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
-	else if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
+	if (Matches6("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
 		COMPLETE_WITH_CONST("HANDLER");
 
 	/* CREATE DATABASE */
-	else if (Matches3("CREATE", "DATABASE", MatchAny))
+	if (Matches3("CREATE", "DATABASE", MatchAny))
 		COMPLETE_WITH_LIST9("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
 							"IS_TEMPLATE",
 							"ALLOW_CONNECTIONS", "CONNECTION LIMIT",
 							"LC_COLLATE", "LC_CTYPE");
 
-	else if (Matches4("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
+	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 (Matches2("CREATE", "EXTENSION"))
+	if (Matches2("CREATE", "EXTENSION"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions,
 							ADDLIST1("IF NOT EXISTS"));
 	/* Try the same after removing optional words IF NOT EXISTS */
-	else if (HeadMatches2("CREATE", "EXTENSION") &&
-			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
-			 Matches2("CREATE", "EXTENSION"))
+	if (HeadMatches2("CREATE", "EXTENSION") &&
+		MidMatches3(3, "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+
+	if (Matches2("CREATE", "EXTENSION"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions, "");
 	/* CREATE EXTENSION <name> */
-	else if (Matches3("CREATE", "EXTENSION", MatchAny))
+	if (Matches3("CREATE", "EXTENSION", MatchAny))
 		COMPLETE_WITH_LIST3("WITH SCHEMA", "CASCADE", "VERSION");
 	/* CREATE EXTENSION <name> VERSION */
-	else if (Matches4("CREATE", "EXTENSION", MatchAny, "VERSION"))
+	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 (Matches2("CREATE", "FOREIGN"))
+	if (Matches2("CREATE", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
 
 	/* CREATE FOREIGN TABLE */
-	else if (Matches3("CREATE", "FOREIGN", "TABLE"))
+	if (Matches3("CREATE", "FOREIGN", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables,
 								   ADDLIST1("IF NOT EXISTS"));
 	/* Remove optional words IF NOT EXISTS */
-	else if (HeadMatches3("CREATE", "FOREIGN", "TABLE") &&
-			 MidMatchAndRemove3(3, "IF", "NOT", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
+	if (HeadMatches3("CREATE", "FOREIGN", "TABLE") &&
+		MidMatches3(4, "IF", "NOT", "EXISTS"))
+		COLLAPSE(4, 3);
+
+	if (Matches3("CREATE", "FOREIGN", "TABLE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, "");
 	/* CREATE FOREIGN DATA WRAPPER */
-	else if (Matches5("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+	if (Matches5("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 		COMPLETE_WITH_LIST3("HANDLER", "VALIDATOR", "OPTIONS");
 
 	/* CREATE INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	/* First off we complete CREATE UNIQUE with "INDEX" */
-	else if (TailMatches2("CREATE", "UNIQUE"))
+	if (TailMatches2("CREATE", "UNIQUE"))
 		COMPLETE_WITH_CONST("INDEX");
 
-	/* Remove optional word UNIQUE from CREATE UNIQUE INDEX */
-	else if (HeadMatches3("CREATE", MatchAny, "INDEX") &&
-			 MidMatchAndRemove1(1, "UNIQUE") &&
-			 false) {} /* FALL THROUGH */
-	/* If we have CREATE INDEX, then add "ON", "CONCURRENTLY" or IF NOT EXISTS,
-	   and existing indexes */
-	else if (Matches2("CREATE", "INDEX"))
+	/*
+	 * Complete CREATE INDEX with "ON", "CONCURRENTLY" or IF NOT EXISTS, and
+	 * existing indexes, after removing optional words
+	 */
+	if (HeadMatches2("CREATE", "UNIQUE") &&
+		MidMatches1(2, "UNIQUE"))
+		COLLAPSE(2, 1);
+
+	if (Matches2("CREATE", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
 				   ADDLIST3("ON", "CONCURRENTLY", "IF NOT EXISTS"));
 	/* Complete CREATE INDEX CONCURRENTLY with "ON" or IF NOT EXISTS and
 	 * existing indexes */
-	else if (Matches3("CREATE", "INDEX", "CONCURRENTLY"))
+	if (Matches3("CREATE", "INDEX", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
 								   ADDLIST2("IF NOT EXISTS", "ON"));
 
-	/* Remove optional words "CONCURRENTLY",  "IF NOT EXISTS" */
-	else if (HeadMatches2("CREATE", "INDEX") &&
-			 MidMatchAndRemove1(2, "CONCURRENTLY") &&
-			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
-
+	/* Complete CREATE INDEX after removing optional words */
+	if (HeadMatches2("CREATE", "INDEX"))
+	{
+		if (MidMatches1(3, "CONCURRENTLY"))
+			COLLAPSE(3, 1);
+		if (MidMatches3(3, "IF", "NOT", "EXISTS"))
+			COLLAPSE(3, 3);
+	}
+	if (Matches2("CREATE", "INDEX"))
+		COMPLETE_THING(-1);
 	/* Complete CREATE INDEX [<name>] ON with a list of tables */
-	else if (Matches4("CREATE", "INDEX", MatchAny, "ON") ||
+	if (Matches4("CREATE", "INDEX", MatchAny, "ON") ||
 			 Matches3("CREATE", "INDEX", "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "");
 
 	/* Complete CREATE INDEX <sth> with "ON" */
-	else if (Matches3("CREATE", "INDEX", MatchAny))
+	if (Matches3("CREATE", "INDEX", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 
 	/*
 	 * Complete INDEX <name> ON <table> with a list of table columns (which
 	 * should really be in parens)
 	 */
-	else if (TailMatches4("INDEX", MatchAny, "ON", MatchAny) ||
+	if (TailMatches4("INDEX", MatchAny, "ON", MatchAny) ||
 			 TailMatches3("INDEX", "ON", MatchAny))
 		COMPLETE_WITH_LIST2("(", "USING");
-	else if (Matches5("INDEX", MatchAny, "ON", MatchAny, "(") ||
+	if (Matches5("INDEX", MatchAny, "ON", MatchAny, "(") ||
 			 Matches4("INDEX", "ON", MatchAny, "("))
 		COMPLETE_WITH_ATTR(prev2_wd, "");
 	/* same if you put in USING */
-	else if (TailMatches5("ON", MatchAny, "USING", MatchAny, "("))
+	if (TailMatches5("ON", MatchAny, "USING", MatchAny, "("))
 		COMPLETE_WITH_ATTR(prev4_wd, "");
 	/* Complete USING with an index method */
-	else if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") ||
+	if (TailMatches5("INDEX", MatchAny, "ON", MatchAny, "USING") ||
 			 TailMatches4("INDEX", "ON", MatchAny, "USING"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods, "");
-	else if (TailMatches4("ON", MatchAny, "USING", MatchAny) &&
+	if (TailMatches4("ON", MatchAny, "USING", MatchAny) &&
 			 !TailMatches6("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) &&
 			 !TailMatches4("FOR", MatchAny, MatchAny, MatchAny))
 		COMPLETE_WITH_CONST("(");
 
 	/* CREATE POLICY */
 	/* Complete "CREATE POLICY <name> ON" */
-	else if (Matches3("CREATE", "POLICY", MatchAny))
+	if (Matches3("CREATE", "POLICY", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* Complete "CREATE POLICY <name> ON <table>" */
-	else if (Matches4("CREATE", "POLICY", MatchAny, "ON"))
+	if (Matches4("CREATE", "POLICY", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */
-	else if (Matches5("CREATE", "POLICY", MatchAny, "ON", MatchAny))
+	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 (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"))
+	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 (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"))
+	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 (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"))
+	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 (Matches7("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"))
+	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 (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"))
+	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 (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
+	if (Matches6("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
 		COMPLETE_WITH_CONST("(");
 
 /* CREATE RULE */
 	/* Complete "CREATE RULE <sth>" with "AS ON" */
-	else if (Matches3("CREATE", "RULE", MatchAny))
+	if (Matches3("CREATE", "RULE", MatchAny))
 		COMPLETE_WITH_CONST("AS ON");
 	/* Complete "CREATE RULE <sth> AS" with "ON" */
-	else if (Matches4("CREATE", "RULE", MatchAny, "AS"))
+	if (Matches4("CREATE", "RULE", MatchAny, "AS"))
 		COMPLETE_WITH_CONST("ON");
 	/* Complete "CREATE RULE <sth> AS ON" with SELECT|UPDATE|INSERT|DELETE */
-	else if (Matches5("CREATE", "RULE", MatchAny, "AS", "ON"))
+	if (Matches5("CREATE", "RULE", MatchAny, "AS", "ON"))
 		COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE");
 	/* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */
-	else if (TailMatches3("AS", "ON", "SELECT|UPDATE|INSERT|DELETE"))
+	if (TailMatches3("AS", "ON", "SELECT|UPDATE|INSERT|DELETE"))
 		COMPLETE_WITH_CONST("TO");
 	/* Complete "AS ON <sth> TO" with a table name */
-	else if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
+	if (TailMatches4("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 
-/* Remove optional words TEMPORARY/TEMP */
-	else if (HeadMatches3("CREATE", MatchAny, "SEQUENCE") &&
-			 MidMatchAndRemove1(1, "TEMP|TEMPORARY") &&
-			 false) {} /* FALL THROUGH */
-/* CREATE SEQUENCE */
-	else if(Matches2("CREATE", "SEQUENCE"))
+
+/* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
+	if (Matches2("CREATE", "TEMP|TEMPORARY"))
+		COMPLETE_WITH_LIST3("SEQUENCE", "TABLE", "VIEW");
+/* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */
+	if (Matches2("CREATE", "UNLOGGED"))
+		COMPLETE_WITH_LIST2("TABLE", "MATERIALIZED VIEW");
+
+/* CREATE SEQUCNE after removing optional words TEMPORARY/TEMP */
+	if (HeadMatches1("CREATE") &&
+		MidMatches1(2, "TEMP|TEMPORARY|UNLOGGED"))
+		COLLAPSE(2, 1);
+
+	if (Matches2("CREATE", "SEQUENCE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences,
 								   ADDLIST1("IF NOT EXISTS"));
-	else if(HeadMatches2("CREATE", "SEQUENCE") &&
-			MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
-			Matches3("CREATE", "SEQUENCE", MatchAny))
+	if(HeadMatches2("CREATE", "SEQUENCE") &&
+	   MidMatches3(3, "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+
+	if (Matches3("CREATE", "SEQUENCE", MatchAny))
 		COMPLETE_WITH_LIST8("INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", "CACHE",
 							"CYCLE", "OWNED BY", "START WITH");
-	else if (Matches4("CREATE", "SEQUENCE", MatchAny, "NO"))
+	if (Matches4("CREATE", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH_LIST3("MINVALUE", "MAXVALUE", "CYCLE");
 
 /* CREATE SERVER <name> */
-	else if (Matches3("CREATE", "SERVER", MatchAny))
+	if (Matches3("CREATE", "SERVER", MatchAny))
 		COMPLETE_WITH_LIST3("TYPE", "VERSION", "FOREIGN DATA WRAPPER");
 
-/* CREATE SCHEMA <name> */
-	else if (Matches2("CREATE", "SCHEMA"))
-		COMPLETE_WITH_QUERY(Query_for_list_of_schemas,
-							ADDLIST1("IF NOT EXISTS"));
-	/* Remove optional words IF NOT EXISTS */
-	else if (HeadMatches2("CREATE", "SCHEMA") &&
-			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
-			 false) {} /* FALL THROUGH*/
-
 /* CREATE TABLE  */
-	/* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
-	else if (Matches2("CREATE", "TEMP|TEMPORARY"))
-		COMPLETE_WITH_LIST3("SEQUENCE", "TABLE", "VIEW");
-	/* Complete "CREATE UNLOGGED" with TABLE or MATVIEW */
-	else if (Matches2("CREATE", "UNLOGGED"))
-		COMPLETE_WITH_LIST2("TABLE", "MATERIALIZED VIEW");
-
+	/* Optional words TEMP|TEMPORARY|UNLOGGED is already removed */
 	/* CREATE TABLE with name after removing optional words */
-	else if (HeadMatches3("CREATE", MatchAny, "TABLE") &&
-			 MidMatchAndRemove1(1, "TEMP|TEMPORARY|UNLOGGED") &&
-			 false) {} /* FALL THROUGH*/
-	else if (Matches2("CREATE", "TABLE"))
+	if (Matches2("CREATE", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 								   ADDLIST1("IF NOT EXISTS"));
+	/* CREATE TABLE after removing optional words here */
+	if (HeadMatches2("CREATE", "TABLE") &&
+		MidMatches3(3, "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
 
-	/* Remove optional words here */
-	else if (HeadMatches2("CREATE", "TABLE") &&
-			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
-
+	if (Matches2("CREATE", "TABLE"))
+		COMPLETE_THING(-1);
 /* CREATE TABLESPACE */
-	else if (Matches3("CREATE", "TABLESPACE", MatchAny))
+	if (Matches3("CREATE", "TABLESPACE", MatchAny))
 		COMPLETE_WITH_LIST2("OWNER", "LOCATION");
 	/* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
-	else if (Matches5("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny))
+	if (Matches5("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny))
 		COMPLETE_WITH_CONST("LOCATION");
 
 /* CREATE TEXT SEARCH */
-	else if (Matches3("CREATE", "TEXT", "SEARCH"))
+	if (Matches3("CREATE", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-	else if (Matches5("CREATE", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
+	if (Matches5("CREATE", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
 		COMPLETE_WITH_CONST("(");
 
 /* CREATE TRIGGER */
 	/* complete CREATE TRIGGER <name> with BEFORE,AFTER,INSTEAD OF */
-	else if (Matches3("CREATE", "TRIGGER", MatchAny))
+	if (Matches3("CREATE", "TRIGGER", MatchAny))
 		COMPLETE_WITH_LIST3("BEFORE", "AFTER", "INSTEAD OF");
 	/* complete CREATE TRIGGER <name> BEFORE,AFTER with an event */
-	else if (Matches4("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER"))
+	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 (Matches5("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF"))
+	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 (Matches5("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
+	if (Matches5("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
 	  TailMatches6("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny))
 		COMPLETE_WITH_LIST2("ON", "OR");
 
@@ -2485,17 +2150,17 @@ psql_completion(const char *text, int start, int end)
 	 * complete CREATE TRIGGER <name> BEFORE,AFTER event ON with a list of
 	 * tables
 	 */
-	else if (TailMatches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
+	if (TailMatches6("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 	/* complete CREATE TRIGGER ... INSTEAD OF event ON with a list of views */
-	else if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
+	if (TailMatches7("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, "");
 	/* complete CREATE TRIGGER ... EXECUTE with PROCEDURE */
-	else if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE"))
+	if (HeadMatches2("CREATE", "TRIGGER") && TailMatches1("EXECUTE"))
 		COMPLETE_WITH_CONST("PROCEDURE");
 
 /* CREATE ROLE,USER,GROUP <name> */
-	else if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
+	if (Matches3("CREATE", "ROLE|GROUP|USER", MatchAny) &&
 			 !TailMatches2("USER", "MAPPING"))
 	{
 		static const char *const list_CREATEROLE[] =
@@ -2510,7 +2175,7 @@ psql_completion(const char *text, int start, int end)
 	}
 
 /* CREATE ROLE,USER,GROUP <name> WITH */
-	else if (Matches4("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"))
+	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[] =
@@ -2528,84 +2193,88 @@ psql_completion(const char *text, int start, int end)
 	 * complete CREATE ROLE,USER,GROUP <name> ENCRYPTED,UNENCRYPTED with
 	 * PASSWORD
 	 */
-	else if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "ENCRYPTED|UNENCRYPTED"))
+	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 (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
+	if (Matches4("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
 		COMPLETE_WITH_LIST2("GROUP", "ROLE");
 
 /* CREATE VIEW  */
-	else if (Matches2("CREATE", "VIEW"))
+	if (Matches2("CREATE", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
 								   ADDLIST1("IF NOT EXISTS"));
 	/* CREATE VIEW <name> with AS after removing optional words */
-	else if (HeadMatches2("CREATE", "VIEW") &&
-			 MidMatchAndRemove3(2, "IF", "NOT", "EXISTS") &&
-			 Matches3("CREATE", "VIEW", MatchAny))
+	if (HeadMatches2("CREATE", "VIEW") &&
+		MidMatches3(3, "IF", "NOT", "EXISTS"))
+		COLLAPSE(3, 3);
+
+	if (Matches3("CREATE", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("AS");
 	/* Complete "CREATE VIEW <sth> AS with "SELECT" */
-	else if (TailMatches4("CREATE", "VIEW", MatchAny, "AS"))
+	if (TailMatches4("CREATE", "VIEW", MatchAny, "AS"))
 		COMPLETE_WITH_CONST("SELECT");
 
 /* CREATE MATERIALIZED VIEW */
-	else if (Matches2("CREATE", "MATERIALIZED"))
+	if (Matches2("CREATE", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
-	else if (Matches3("CREATE", "MATERIALIZED", "VIEW"))
+	if (Matches3("CREATE", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 								   ADDLIST1("IF NOT EXISTS"));
 	/* Try the same after removing optional words IF NOT EXISTS. VIEW will be
 	 * completed afterwards */
-	else if (HeadMatches3("CREATE", "MATERIALIZED", "VIEW") &&
-			 MidMatchAndRemove3(3, "IF", "NOT", "EXISTS") &&
-			 Matches3("CREATE", "MATERIALIZED", "VIEW"))
+	if (HeadMatches3("CREATE", "MATERIALIZED", "VIEW") &&
+		MidMatches3(4, "IF", "NOT", "EXISTS"))
+		COLLAPSE(4, 3);
+
+	if (Matches3("CREATE", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
 	/* Complete CREATE MATERIALIZED VIEW <name> with AS */
-	else if (Matches4("CREATE", "MATERIALIZED", "VIEW", MatchAny))
+	if (Matches4("CREATE", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("AS");
 	/* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
-	else if (Matches5("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"))
+	if (Matches5("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"))
 		COMPLETE_WITH_CONST("SELECT");
 
 /* CREATE EVENT TRIGGER */
-	else if (Matches2("CREATE", "EVENT"))
+	if (Matches2("CREATE", "EVENT"))
 		COMPLETE_WITH_CONST("TRIGGER");
 	/* Complete CREATE EVENT TRIGGER <name> with ON */
-	else if (Matches4("CREATE", "EVENT", "TRIGGER", MatchAny))
+	if (Matches4("CREATE", "EVENT", "TRIGGER", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* Complete CREATE EVENT TRIGGER <name> ON with event_type */
-	else if (Matches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
+	if (Matches5("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
 		COMPLETE_WITH_LIST3("ddl_command_start", "ddl_command_end", "sql_drop");
 
 /* DECLARE */
-	else if (Matches2("DECLARE", MatchAny))
+	if (Matches2("DECLARE", MatchAny))
 		COMPLETE_WITH_LIST5("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
 							"CURSOR");
-	else if (HeadMatches1("DECLARE") && TailMatches1("CURSOR"))
+	if (HeadMatches1("DECLARE") && TailMatches1("CURSOR"))
 		COMPLETE_WITH_LIST3("WITH HOLD", "WITHOUT HOLD", "FOR");
 
 /* DELETE --- can be inside EXPLAIN, RULE, etc */
 	/* ... despite which, only complete DELETE with FROM at start of line */
-	else if (Matches1("DELETE"))
+	if (Matches1("DELETE"))
 		COMPLETE_WITH_CONST("FROM");
 	/* Complete DELETE FROM with a list of tables */
-	else if (TailMatches2("DELETE", "FROM"))
+	if (TailMatches2("DELETE", "FROM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, "");
 	/* Complete DELETE FROM <table> */
-	else if (TailMatches3("DELETE", "FROM", MatchAny))
+	if (TailMatches3("DELETE", "FROM", MatchAny))
 		COMPLETE_WITH_LIST2("USING", "WHERE");
 	/* XXX: implement tab completion for DELETE ... USING */
 
 /* DISCARD */
-	else if (Matches1("DISCARD"))
+	if (Matches1("DISCARD"))
 		COMPLETE_WITH_LIST4("ALL", "PLANS", "SEQUENCES", "TEMP");
 
 /* DO */
-	else if (Matches1("DO"))
+	if (Matches1("DO"))
 		COMPLETE_WITH_CONST("LANGUAGE");
 
 /* DROP */
 	/* Complete DROP object with CASCADE / RESTRICT */
-	else if (Matches3("DROP",
+	if (Matches3("DROP",
 					  "COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|SCHEMA|SEQUENCE|SERVER|TABLE|TYPE|VIEW",
 					  MatchAny) ||
 			 Matches4("DROP", "ACCESS", "METHOD", MatchAny) ||
@@ -2618,144 +2287,166 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 	/* help completing some of the variants */
-	else if (Matches2("DROP", "AGGREGATE"))
+	if (Matches2("DROP", "AGGREGATE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates,
 								   ADDLIST1("IF EXISTS"));
-	else if (Matches2("DROP", "FUNCTION"))
+	if (Matches2("DROP", "FUNCTION"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions,
 								   ADDLIST1("IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
-	else if (HeadMatches2("DROP", "AGGREGATE|FUNCTION") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("DROP", "AGGREGATE|FUNCTION", MatchAny))
+	if (HeadMatches2("DROP", "AGGREGATE|FUNCTION") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches3("DROP", "AGGREGATE|FUNCTION", MatchAny))
 		COMPLETE_WITH_CONST("(");
-	else if (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, "("))
+	if (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, "("))
 		COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-	else if (Matches2("DROP", "FOREIGN"))
+	if (Matches2("DROP", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "TABLE");
-	else if (Matches4("DROP", "FOREIGN", "DATA", "WRAPPER"))
+	if (Matches4("DROP", "FOREIGN", "DATA", "WRAPPER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_fdws,
 							ADDLIST1("IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
-	else if (HeadMatches4("DROP", "FOREIGN", "DATA", "WRAPPER") &&
-			 MidMatchAndRemove2(4, "IF", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
+	if (HeadMatches4("DROP", "FOREIGN", "DATA", "WRAPPER") &&	
+		MidMatches2(5, "IF", "EXISTS"))
+		COLLAPSE(5, 2);
+
+	if (Matches4("DROP", "FOREIGN", "DATA", "WRAPPER"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_fdws, "");
 
 	/* DROP INDEX */
-	else if (Matches2("DROP", "INDEX"))
+	if (Matches2("DROP", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
 								   ADDLIST2("IF EXISTS","CONCURRENTLY"));
-	else if (Matches3("DROP", "INDEX", "CONCURRENTLY"))
+	if (Matches3("DROP", "INDEX", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes,
 								   ADDLIST1("IF EXISTS"));
 	/* Try the same after optional words CONCURRENTLY and IF NOT EXISTS */
-	else if (HeadMatches2("DROP", "INDEX") &&
-			 MidMatchAndRemove1(2, "CONCURRENTLY") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("DROP", "INDEX", MatchAny))
+	if (HeadMatches2("DROP", "INDEX"))
+	{
+		if (MidMatches1(3, "CONCURRENTLY"))
+			COLLAPSE(3, 1);
+		if (MidMatches2(3, "IF", "EXISTS"))
+			COLLAPSE(3, 2);
+	}
+	if (Matches3("DROP", "INDEX", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 	/* DROP MATERIALIZED VIEW */
-	else if (Matches2("DROP", "MATERIALIZED"))
+	if (Matches2("DROP", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
-	else if (Matches2("DROP", "VIEW"))
+	if (Matches2("DROP", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views,
 								   ADDLIST1("IF EXISTS"));
 	/* Remove optional words IF EXISTS  */
-	else if (HeadMatches2("DROP", "VIEW") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
-	else if (Matches3("DROP", "MATERIALIZED", "VIEW"))
+	if (HeadMatches2("DROP", "VIEW") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches2("DROP", "VIEW"))
+		COMPLETE_THING(-1);
+	if (Matches3("DROP", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 								   ADDLIST1("IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
-	else if (HeadMatches3("DROP", "MATERIALIZED", "VIEW") &&
-			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
-			 Matches3("DROP", "MATERIALIZED", "VIEW"))
+	if (HeadMatches3("DROP", "MATERIALIZED", "VIEW") &&
+		MidMatches2(4, "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+
+	if (Matches3("DROP", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
 
 	/* DROP OWNED BY */
-	else if (Matches2("DROP", "OWNED"))
+	if (Matches2("DROP", "OWNED"))
 		COMPLETE_WITH_CONST("BY");
-	else if (Matches3("DROP", "OWNED", "BY"))
+	if (Matches3("DROP", "OWNED", "BY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 
-	else if (Matches3("DROP", "TEXT", "SEARCH"))
+	if (Matches3("DROP", "TEXT", "SEARCH"))
 		COMPLETE_WITH_LIST4("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
 
 	/* DROP TRIGGER */
-	else if (Matches2("DROP", "TRIGGER"))
+	if (Matches2("DROP", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_triggers,
 							ADDLIST1("IF EXISTS"));
 	/* Try the same after removing optional words IF EXISTS */
-	else if (HeadMatches2("DROP", "TRIGGER") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("DROP", "TRIGGER", MatchAny))
+	if (HeadMatches2("DROP", "TRIGGER") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches3("DROP", "TRIGGER", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (Matches4("DROP", "TRIGGER", MatchAny, "ON"))
+	if (Matches4("DROP", "TRIGGER", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_trigger, "");
 	}
-	else if (Matches5("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
+	if (Matches5("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 	/* DROP ACCESS METHOD */
-	else if (Matches2("DROP", "ACCESS"))
+	if (Matches2("DROP", "ACCESS"))
 		COMPLETE_WITH_CONST("METHOD");
-	else if (Matches3("DROP", "ACCESS", "METHOD"))
+	if (Matches3("DROP", "ACCESS", "METHOD"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods, "");
 
 	/* DROP EVENT TRIGGER */
-	else if (Matches2("DROP", "EVENT"))
+	if (Matches2("DROP", "EVENT"))
 		COMPLETE_WITH_CONST("TRIGGER");
-	else if (Matches3("DROP", "EVENT", "TRIGGER"))
+	if (Matches3("DROP", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers,
 							ADDLIST1("IF EXISTS"));
 	/* Trye the same after removing optional words IF EXISTS */
-	else if (HeadMatches3("DROP", "EVENT", "TRIGGER") &&
-			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
-			 Matches3("DROP", "EVENT", "TRIGGER"))
+	if (HeadMatches3("DROP", "EVENT", "TRIGGER") &&
+		MidMatches2(4, "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+
+	if (Matches3("DROP", "EVENT", "TRIGGER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers, "");
 
 	/* DROP POLICY */
-	else if (Matches2("DROP", "POLICY"))
+	if (Matches2("DROP", "POLICY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_policies,
 							ADDLIST1("IF EXISTS"));
 	/* Try the same after after removing optional words IF EXISTS */
-	else if (HeadMatches2("DROP", "POLICY") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches2("DROP", "POLICY"))
+	if (HeadMatches2("DROP", "POLICY") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches2("DROP", "POLICY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_policies, "");
 	/* DROP POLICY <name> */
-	else if (Matches3("DROP", "POLICY", MatchAny))
+	if (Matches3("DROP", "POLICY", MatchAny))
 		COMPLETE_WITH_CONST("ON");
 	/* DROP POLICY <name> ON */
-	else if (Matches4("DROP", "POLICY", MatchAny, "ON"))
+	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 (Matches2("DROP", "RULE"))
+	if (Matches2("DROP", "RULE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_rules,
 							ADDLIST1("IF EXISTS"));
 	/* DROP RULE <name>, after removing optional words IF EXISTS */
-	else if (HeadMatches2("DROP", "RULE") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 Matches3("DROP", "RULE", MatchAny))
+	if (HeadMatches2("DROP", "RULE") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches3("DROP", "RULE", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (Matches4("DROP", "RULE", MatchAny, "ON"))
+	if (Matches4("DROP", "RULE", MatchAny, "ON"))
 	{
 		completion_info_charp = prev2_wd;
 		COMPLETE_WITH_QUERY(Query_for_list_of_tables_for_rule, "");
 	}
-	else if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
+	if (Matches5("DROP", "RULE", MatchAny, "ON", MatchAny))
 		COMPLETE_WITH_LIST2("CASCADE", "RESTRICT");
 
 	/* IF EXISTS processing for DROP everything else */
-	else if (Matches2("DROP",
+	if (Matches2("DROP",
 					  "CAST|COLLATION|CONVERSION|DATABASE|DOMAIN|"
 					  "GROUP|LANGUAGE|OPERATOR|ROLE|SCHEMA|SEQUENCE|"
 					  "SERVER|TABLE|TABLESPACE|TYPE|USER") ||
@@ -2789,19 +2480,25 @@ psql_completion(const char *text, int start, int end)
 		}
 	}
 	/* Remove optional IF EXISTS from DROP */
-	else if (HeadMatches2("DROP",
-						  "CAST|COLLATION|CONVERSION|DATABASE|DOMAIN|GROUP|"
-						  "LANGUAGE|OPERATOR|ROLE|SCHEMA|SEQUENCE|SERVER|"
-						  "TABLE|TABLESPACE|TYPE|USER") &&
-			 MidMatchAndRemove2(2, "IF", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
-	else if (HeadMatches4("DROP", "TEXT", "SEARCH",
-						  "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE") &&
-			 MidMatchAndRemove2(4, "IF", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
+	if (HeadMatches2("DROP",
+					 "CAST|COLLATION|CONVERSION|DATABASE|DOMAIN|GROUP|"
+					 "LANGUAGE|OPERATOR|ROLE|SCHEMA|SEQUENCE|SERVER|"
+					 "TABLE|TABLESPACE|TYPE|USER") &&
+		MidMatches2(3, "IF", "EXISTS"))
+		COLLAPSE(3, 2);
+
+	if (Matches2("DROP", MatchAny))
+		COMPLETE_THING(-1);
+	if (HeadMatches4("DROP", "TEXT", "SEARCH",
+					 "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE") &&
+		MidMatches2(5, "IF", "EXISTS"))
+		COLLAPSE(5, 2);
+
+	if (Matches4("DROP", "TEXT", "SEARCH", MatchAny))
+		COMPLETE_THING(-1);
 
 /* EXECUTE */
-	else if (Matches1("EXECUTE"))
+	if (Matches1("EXECUTE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements, "");
 
 /* EXPLAIN */
@@ -2809,22 +2506,22 @@ psql_completion(const char *text, int start, int end)
 	/*
 	 * Complete EXPLAIN [ANALYZE] [VERBOSE] with list of EXPLAIN-able commands
 	 */
-	else if (Matches1("EXPLAIN"))
+	if (Matches1("EXPLAIN"))
 		COMPLETE_WITH_LIST7("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
 							"ANALYZE", "VERBOSE");
-	else if (Matches2("EXPLAIN", "ANALYZE"))
+	if (Matches2("EXPLAIN", "ANALYZE"))
 		COMPLETE_WITH_LIST6("SELECT", "INSERT", "DELETE", "UPDATE", "DECLARE",
 							"VERBOSE");
-	else if (Matches2("EXPLAIN", "VERBOSE") ||
+	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 (Matches1("FETCH|MOVE"))
+	if (Matches1("FETCH|MOVE"))
 		COMPLETE_WITH_LIST4("ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE");
 	/* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
-	else if (Matches2("FETCH|MOVE", MatchAny))
+	if (Matches2("FETCH|MOVE", MatchAny))
 		COMPLETE_WITH_LIST3("ALL", "NEXT", "PRIOR");
 
 	/*
@@ -2832,30 +2529,30 @@ 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 (Matches3("FETCH|MOVE", MatchAny, MatchAny))
+	if (Matches3("FETCH|MOVE", MatchAny, MatchAny))
 		COMPLETE_WITH_LIST2("FROM", "IN");
 
 /* FOREIGN DATA WRAPPER */
 	/* applies in ALTER/DROP FDW and in CREATE SERVER */
-	else if (TailMatches3("FOREIGN", "DATA", "WRAPPER") &&
+	if (TailMatches3("FOREIGN", "DATA", "WRAPPER") &&
 			 !TailMatches4("CREATE", MatchAny, MatchAny, MatchAny))
 		COMPLETE_WITH_QUERY(Query_for_list_of_fdws, "");
 	/* applies in CREATE SERVER */
-	else if (TailMatches4("FOREIGN", "DATA", "WRAPPER", MatchAny) &&
+	if (TailMatches4("FOREIGN", "DATA", "WRAPPER", MatchAny) &&
 			 HeadMatches2("CREATE", "SERVER"))
 		COMPLETE_WITH_CONST("OPTIONS");
 
 /* FOREIGN TABLE */
-	else if (TailMatches2("FOREIGN", "TABLE"))
+	if (TailMatches2("FOREIGN", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, "");
 
 /* FOREIGN SERVER */
-	else if (TailMatches2("FOREIGN", "SERVER"))
+	if (TailMatches2("FOREIGN", "SERVER"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_servers, "");
 
 /* GRANT && REVOKE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	/* Complete GRANT/REVOKE with a list of roles and privileges */
-	else if (TailMatches1("GRANT|REVOKE"))
+	if (TailMatches1("GRANT|REVOKE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles,
 			ADDLIST13("SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE",
 					  "REFERENCES", "TRIGGER", "CREATE", "CONNECT", "TEMPORARY",
@@ -2865,11 +2562,11 @@ 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))
+	if (TailMatches2("GRANT|REVOKE", MatchAny))
 	{
 		if (TailMatches1("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|ALL"))
 			COMPLETE_WITH_CONST("ON");
-		else if (TailMatches2("GRANT", MatchAny))
+		if (TailMatches2("GRANT", MatchAny))
 			COMPLETE_WITH_CONST("TO");
 		else
 			COMPLETE_WITH_CONST("FROM");
@@ -2886,7 +2583,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"))
+	if (TailMatches3("GRANT|REVOKE", MatchAny, "ON"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf,
 			   ADDLIST15("ALL FUNCTIONS IN SCHEMA",
 						 "ALL SEQUENCES IN SCHEMA",
@@ -2904,11 +2601,11 @@ psql_completion(const char *text, int start, int end)
 						 "TABLESPACE",
 						 "TYPE"));
 
-	else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "ALL"))
+	if (TailMatches4("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"))
+	if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"))
 		COMPLETE_WITH_LIST2("DATA WRAPPER", "SERVER");
 
 	/*
@@ -2917,27 +2614,27 @@ psql_completion(const char *text, int start, int end)
 	 *
 	 * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
 	 */
-	else if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", MatchAny))
+	if (TailMatches4("GRANT|REVOKE", MatchAny, "ON", MatchAny))
 	{
 		if (TailMatches1("DATABASE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_databases, "");
-		else if (TailMatches1("DOMAIN"))
+		if (TailMatches1("DOMAIN"))
 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, "");
-		else if (TailMatches1("FUNCTION"))
+		if (TailMatches1("FUNCTION"))
 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, "");
-		else if (TailMatches1("LANGUAGE"))
+		if (TailMatches1("LANGUAGE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_languages, "");
-		else if (TailMatches1("SCHEMA"))
+		if (TailMatches1("SCHEMA"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_schemas, "");
-		else if (TailMatches1("SEQUENCE"))
+		if (TailMatches1("SEQUENCE"))
 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, "");
-		else if (TailMatches1("TABLE"))
+		if (TailMatches1("TABLE"))
 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, "");
-		else if (TailMatches1("TABLESPACE"))
+		if (TailMatches1("TABLESPACE"))
 			COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces, "");
-		else if (TailMatches1("TYPE"))
+		if (TailMatches1("TYPE"))
 			COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, "");
-		else if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
+		if (TailMatches4("GRANT", MatchAny, MatchAny, MatchAny))
 			COMPLETE_WITH_CONST("TO");
 		else
 			COMPLETE_WITH_CONST("FROM");
@@ -2947,18 +2644,18 @@ psql_completion(const char *text, int start, int end)
 	 * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
 	 * CURRENT_USER, or SESSION_USER.
 	 */
-	else if ((HeadMatches1("GRANT") && TailMatches1("TO")) ||
+	if ((HeadMatches1("GRANT") && TailMatches1("TO")) ||
 			 (HeadMatches1("REVOKE") && TailMatches1("FROM")))
 		COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles, "");
 
 	/* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
-	else if (HeadMatches1("GRANT") && TailMatches3("ON", MatchAny, MatchAny))
+	if (HeadMatches1("GRANT") && TailMatches3("ON", MatchAny, MatchAny))
 		COMPLETE_WITH_CONST("TO");
-	else if (HeadMatches1("REVOKE") && TailMatches3("ON", MatchAny, MatchAny))
+	if (HeadMatches1("REVOKE") && TailMatches3("ON", MatchAny, MatchAny))
 		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))
+	if (TailMatches8("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny))
 	{
 		if (TailMatches8("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
 			COMPLETE_WITH_CONST("TO");
@@ -2967,7 +2664,7 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
-	else if (TailMatches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+	if (TailMatches7("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
 	{
 		if (TailMatches7("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
 			COMPLETE_WITH_CONST("TO");
@@ -2976,7 +2673,7 @@ psql_completion(const char *text, int start, int end)
 	}
 
 	/* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
-	else if (TailMatches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
+	if (TailMatches6("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
 	{
 		if (TailMatches6("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
 			COMPLETE_WITH_CONST("TO");
@@ -2985,62 +2682,62 @@ psql_completion(const char *text, int start, int end)
 	}
 
 /* GROUP BY */
-	else if (TailMatches3("FROM", MatchAny, "GROUP"))
+	if (TailMatches3("FROM", MatchAny, "GROUP"))
 		COMPLETE_WITH_CONST("BY");
 
 /* IMPORT FOREIGN SCHEMA */
-	else if (Matches1("IMPORT"))
+	if (Matches1("IMPORT"))
 		COMPLETE_WITH_CONST("FOREIGN SCHEMA");
-	else if (Matches2("IMPORT", "FOREIGN"))
+	if (Matches2("IMPORT", "FOREIGN"))
 		COMPLETE_WITH_CONST("SCHEMA");
 
 /* INSERT --- can be inside EXPLAIN, RULE, etc */
 	/* Complete INSERT with "INTO" */
-	else if (TailMatches1("INSERT"))
+	if (TailMatches1("INSERT"))
 		COMPLETE_WITH_CONST("INTO");
 	/* Complete INSERT INTO with table names */
-	else if (TailMatches2("INSERT", "INTO"))
+	if (TailMatches2("INSERT", "INTO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, "");
 	/* Complete "INSERT INTO <table> (" with attribute names */
-	else if (TailMatches4("INSERT", "INTO", MatchAny, "("))
+	if (TailMatches4("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))
+	if (TailMatches3("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) &&
+	if (TailMatches4("INSERT", "INTO", MatchAny, MatchAny) &&
 			 ends_with(prev_wd, ')'))
 		COMPLETE_WITH_LIST3("SELECT", "TABLE", "VALUES");
 
 	/* Insert an open parenthesis after "VALUES" */
-	else if (TailMatches1("VALUES") && !TailMatches2("DEFAULT", "VALUES"))
+	if (TailMatches1("VALUES") && !TailMatches2("DEFAULT", "VALUES"))
 		COMPLETE_WITH_CONST("(");
 
 /* LOCK */
 	/* Complete LOCK [TABLE] with a list of tables */
-	else if (Matches1("LOCK"))
+	if (Matches1("LOCK"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables,
 								   ADDLIST1("TABLE"));
-	else if (Matches2("LOCK", "TABLE"))
+	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 (Matches2("LOCK", MatchAnyExcept("TABLE")) ||
+	if (Matches2("LOCK", MatchAnyExcept("TABLE")) ||
 			 Matches3("LOCK", "TABLE", MatchAny))
 		COMPLETE_WITH_CONST("IN");
 
 	/* Complete LOCK [TABLE] <table> IN with a lock mode */
-	else if (Matches3("LOCK", MatchAny, "IN") ||
+	if (Matches3("LOCK", MatchAny, "IN") ||
 			 Matches4("LOCK", "TABLE", MatchAny, "IN"))
 		COMPLETE_WITH_LIST8("ACCESS SHARE MODE",
 							"ROW SHARE MODE", "ROW EXCLUSIVE MODE",
@@ -3049,25 +2746,25 @@ psql_completion(const char *text, int start, int end)
 							"EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
 
 /* NOTIFY --- can be inside EXPLAIN, RULE, etc */
-	else if (TailMatches1("NOTIFY"))
+	if (TailMatches1("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 */
-	else if (TailMatches1("OPTIONS"))
+	if (TailMatches1("OPTIONS"))
 		COMPLETE_WITH_CONST("(");
 
 /* OWNER TO  - complete with available roles */
-	else if (TailMatches2("OWNER", "TO"))
+	if (TailMatches2("OWNER", "TO"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 
 /* ORDER BY */
-	else if (TailMatches3("FROM", MatchAny, "ORDER"))
+	if (TailMatches3("FROM", MatchAny, "ORDER"))
 		COMPLETE_WITH_CONST("BY");
-	else if (TailMatches4("FROM", MatchAny, "ORDER", "BY"))
+	if (TailMatches4("FROM", MatchAny, "ORDER", "BY"))
 		COMPLETE_WITH_ATTR(prev3_wd, "");
 
 /* PREPARE xx AS */
-	else if (Matches3("PREPARE", MatchAny, "AS"))
+	if (Matches3("PREPARE", MatchAny, "AS"))
 		COMPLETE_WITH_LIST4("SELECT", "UPDATE", "INSERT", "DELETE FROM");
 
 /*
@@ -3076,60 +2773,60 @@ psql_completion(const char *text, int start, int end)
  */
 
 /* REASSIGN OWNED BY xxx TO yyy */
-	else if (Matches1("REASSIGN"))
+	if (Matches1("REASSIGN"))
 		COMPLETE_WITH_CONST("OWNED BY");
-	else if (Matches2("REASSIGN", "OWNED"))
+	if (Matches2("REASSIGN", "OWNED"))
 		COMPLETE_WITH_CONST("BY");
-	else if (Matches3("REASSIGN", "OWNED", "BY"))
+	if (Matches3("REASSIGN", "OWNED", "BY"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
-	else if (Matches4("REASSIGN", "OWNED", "BY", MatchAny))
+	if (Matches4("REASSIGN", "OWNED", "BY", MatchAny))
 		COMPLETE_WITH_CONST("TO");
-	else if (Matches5("REASSIGN", "OWNED", "BY", MatchAny, "TO"))
+	if (Matches5("REASSIGN", "OWNED", "BY", MatchAny, "TO"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 
 /* REFRESH MATERIALIZED VIEW */
-	else if (Matches1("REFRESH"))
+	if (Matches1("REFRESH"))
 		COMPLETE_WITH_CONST("MATERIALIZED VIEW");
-	else if (Matches2("REFRESH", "MATERIALIZED"))
+	if (Matches2("REFRESH", "MATERIALIZED"))
 		COMPLETE_WITH_CONST("VIEW");
-	else if (Matches3("REFRESH", "MATERIALIZED", "VIEW"))
+	if (Matches3("REFRESH", "MATERIALIZED", "VIEW"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews,
 								   ADDLIST1("CONCURRENTLY"));
-	else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
+	if (Matches4("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
-	else if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
+	if (Matches4("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
 		COMPLETE_WITH_CONST("WITH");
-	else if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
+	if (Matches5("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
 		COMPLETE_WITH_CONST("WITH");
-	else if (Matches5("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"))
+	if (Matches5("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"))
 		COMPLETE_WITH_LIST2("NO DATA", "DATA");
-	else if (Matches6("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"))
+	if (Matches6("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"))
 		COMPLETE_WITH_LIST2("NO DATA", "DATA");
-	else if (Matches6("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"))
+	if (Matches6("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"))
 		COMPLETE_WITH_CONST("DATA");
-	else if (Matches7("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO"))
+	if (Matches7("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO"))
 		COMPLETE_WITH_CONST("DATA");
 
 /* REINDEX */
-	else if (Matches1("REINDEX"))
+	if (Matches1("REINDEX"))
 		COMPLETE_WITH_LIST5("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
-	else if (Matches2("REINDEX", "TABLE"))
+	if (Matches2("REINDEX", "TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "");
-	else if (Matches2("REINDEX", "INDEX"))
+	if (Matches2("REINDEX", "INDEX"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "");
-	else if (Matches2("REINDEX", "SCHEMA"))
+	if (Matches2("REINDEX", "SCHEMA"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas, "");
-	else if (Matches2("REINDEX", "SYSTEM|DATABASE"))
+	if (Matches2("REINDEX", "SYSTEM|DATABASE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_databases, "");
 
 /* SECURITY LABEL */
-	else if (Matches1("SECURITY"))
+	if (Matches1("SECURITY"))
 		COMPLETE_WITH_CONST("LABEL");
-	else if (Matches2("SECURITY", "LABEL"))
+	if (Matches2("SECURITY", "LABEL"))
 		COMPLETE_WITH_LIST2("ON", "FOR");
-	else if (Matches4("SECURITY", "LABEL", "FOR", MatchAny))
+	if (Matches4("SECURITY", "LABEL", "FOR", MatchAny))
 		COMPLETE_WITH_CONST("ON");
-	else if (Matches3("SECURITY", "LABEL", "ON") ||
+	if (Matches3("SECURITY", "LABEL", "ON") ||
 			 Matches5("SECURITY", "LABEL", "FOR", MatchAny, "ON"))
 	{
 		static const char *const list_SECURITY_LABEL[] =
@@ -3140,7 +2837,7 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST(list_SECURITY_LABEL);
 	}
-	else if (Matches5("SECURITY", "LABEL", "ON", MatchAny, MatchAny))
+	if (Matches5("SECURITY", "LABEL", "ON", MatchAny, MatchAny))
 		COMPLETE_WITH_CONST("IS");
 
 /* SELECT */
@@ -3148,71 +2845,55 @@ psql_completion(const char *text, int start, int end)
 
 /* SET, RESET, SHOW */
 	/* Complete with a variable name */
-	else if (TailMatches1("SET|RESET") && !TailMatches3("UPDATE", MatchAny, "SET"))
+	if (TailMatches1("SET|RESET") && !TailMatches3("UPDATE", MatchAny, "SET"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_set_vars, "");
-	else if (Matches1("SHOW"))
+	if (Matches1("SHOW"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_show_vars, "");
 	/* Complete "SET TRANSACTION" */
-	else if (Matches2("SET", "TRANSACTION"))
-		COMPLETE_WITH_LIST5("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
-	else if (Matches2("BEGIN|START", "TRANSACTION") ||
+	if (Matches2("SET|BEGIN|START", "TRANSACTION") ||
 			 Matches2("BEGIN", "WORK") ||
-			 Matches1("BEGIN") ||
 		  Matches5("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION"))
-		COMPLETE_WITH_LIST4("ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
-	else if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "NOT") ||
-			 Matches2("BEGIN", "NOT") ||
-			 Matches6("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "NOT"))
-		COMPLETE_WITH_CONST("DEFERRABLE");
-	else if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") ||
-			 Matches2("BEGIN", "ISOLATION") ||
+		COMPLETE_WITH_LIST2("ISOLATION LEVEL", "READ");
+	if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") ||
 			 Matches6("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION"))
 		COMPLETE_WITH_CONST("LEVEL");
-	else if (Matches4("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL") ||
-			 Matches3("BEGIN", "ISOLATION", "LEVEL") ||
-			 Matches7("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL"))
+	if (Matches4("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL"))
 		COMPLETE_WITH_LIST3("READ", "REPEATABLE READ", "SERIALIZABLE");
-	else if (Matches5("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ") ||
-			 Matches4("BEGIN", "ISOLATION", "LEVEL", "READ") ||
-			 Matches8("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "READ"))
+	if (Matches5("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ"))
 		COMPLETE_WITH_LIST2("UNCOMMITTED", "COMMITTED");
-	else if (Matches5("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE") ||
-			 Matches4("BEGIN", "ISOLATION", "LEVEL", "REPEATABLE") ||
-			 Matches8("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "REPEATABLE"))
+	if (Matches5("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE"))
 		COMPLETE_WITH_CONST("READ");
-	else if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "READ") ||
-			 Matches2("BEGIN", "READ") ||
-			 Matches6("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "READ"))
+	if (Matches3("SET|BEGIN|START", "TRANSACTION|WORK", "READ"))
 		COMPLETE_WITH_LIST2("ONLY", "WRITE");
 	/* SET CONSTRAINTS */
-	else if (Matches2("SET", "CONSTRAINTS"))
+	if (Matches2("SET", "CONSTRAINTS"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_constraints_with_schema,
 								   ADDLIST1("ALL"));
 	/* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
-	else if (Matches3("SET", "CONSTRAINTS", MatchAny))
+	if (Matches3("SET", "CONSTRAINTS", MatchAny))
 		COMPLETE_WITH_LIST2("DEFERRED", "IMMEDIATE");
 	/* Complete SET ROLE */
-	else if (Matches2("SET", "ROLE"))
+	if (Matches2("SET", "ROLE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 	/* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
-	else if (Matches2("SET", "SESSION"))
+	if (Matches2("SET", "SESSION"))
 		COMPLETE_WITH_LIST2("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
 	/* Complete SET SESSION AUTHORIZATION with username */
-	else if (Matches3("SET", "SESSION", "AUTHORIZATION"))
+	if (Matches3("SET", "SESSION", "AUTHORIZATION"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles,
 							ADDLIST1("DEFAULT"));
 	/* Complete RESET SESSION with AUTHORIZATION */
-	else if (Matches2("RESET", "SESSION"))
+	if (Matches2("RESET", "SESSION"))
 		COMPLETE_WITH_CONST("AUTHORIZATION");
 	/* Complete SET <var> with "TO" */
-	else if (Matches2("SET", MatchAny))
+	if (Matches2("SET", MatchAny))
 		COMPLETE_WITH_CONST("TO");
 	/* Complete ALTER DATABASE|FUNCTION|ROLE|USER ... SET <name> */
-	else if (HeadMatches2("ALTER", "DATABASE|FUNCTION|ROLE|USER") &&
+	if (HeadMatches2("ALTER", "DATABASE|FUNCTION|ROLE|USER") &&
 			 TailMatches2("SET", MatchAny))
 		COMPLETE_WITH_LIST2("FROM CURRENT", "TO");
 	/* Suggest possible variable values */
-	else if (TailMatches3("SET", MatchAny, "TO|="))
+	if (TailMatches3("SET", MatchAny, "TO|="))
 	{
 		/* special cased code for individual GUCs */
 		if (TailMatches2("DateStyle", "TO|="))
@@ -3225,7 +2906,7 @@ psql_completion(const char *text, int start, int end)
 
 			COMPLETE_WITH_LIST(my_list);
 		}
-		else if (TailMatches2("search_path", "TO|="))
+		if (TailMatches2("search_path", "TO|="))
 			COMPLETE_WITH_QUERY(Query_for_list_of_schemas
 								" AND nspname not like 'pg\\_toast%%' "
 								" AND nspname not like 'pg\\_temp%%' ",
@@ -3233,103 +2914,108 @@ psql_completion(const char *text, int start, int end)
 		else
 		{
 			/* generic, type based, GUC support */
-			char	   *guctype = get_guctype(prev2_wd);
+			char		guctype[6];
 
-			if (guctype && strcmp(guctype, "enum") == 0)
+			/*
+			 * The value guc_guctype returned is 
+			 */
+			if (get_guctype(guctype, 6, prev2_wd))
 			{
-				char		querybuf[1024];
+				if (strcmp(guctype, "enum") == 0)
+				{
+					char		querybuf[1024];
 
-				snprintf(querybuf, sizeof(querybuf), Query_for_enum, prev2_wd);
-				COMPLETE_WITH_QUERY(querybuf, "");
+					snprintf(querybuf, sizeof(querybuf),
+							 Query_for_enum, prev2_wd);
+					COMPLETE_WITH_QUERY(querybuf, "");
+				}
+				if (strcmp(guctype, "bool") == 0)
+					COMPLETE_WITH_LIST9("on", "off", "true", "false",
+										"yes", "no", "1", "0", "DEFAULT");
 			}
-			else if (guctype && strcmp(guctype, "bool") == 0)
-				COMPLETE_WITH_LIST9("on", "off", "true", "false", "yes", "no",
-									"1", "0", "DEFAULT");
 			else
 				COMPLETE_WITH_CONST("DEFAULT");
-
-			if (guctype)
-				free(guctype);
 		}
 	}
 
 /* START TRANSACTION */
-	else if (Matches1("START"))
+	if (Matches1("START"))
 		COMPLETE_WITH_CONST("TRANSACTION");
 
 /* TABLE, but not TABLE embedded in other commands */
-	else if (Matches1("TABLE"))
+	if (Matches1("TABLE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, "");
 
 /* TABLESAMPLE */
-	else if (TailMatches1("TABLESAMPLE"))
+	if (TailMatches1("TABLESAMPLE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods, "");
-	else if (TailMatches2("TABLESAMPLE", MatchAny))
+	if (TailMatches2("TABLESAMPLE", MatchAny))
 		COMPLETE_WITH_CONST("(");
 
 /* TRUNCATE */
-	else if (Matches1("TRUNCATE"))
+	if (Matches1("TRUNCATE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
 
 /* UNLISTEN */
-	else if (Matches1("UNLISTEN"))
+	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 --- can be inside EXPLAIN, RULE, etc */
 	/* If prev. word is UPDATE suggest a list of tables */
-	else if (TailMatches1("UPDATE"))
+	if (TailMatches1("UPDATE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables, "");
 	/* Complete UPDATE <table> with "SET" */
-	else if (TailMatches2("UPDATE", MatchAny))
+	if (TailMatches2("UPDATE", MatchAny))
 		COMPLETE_WITH_CONST("SET");
 	/* Complete UPDATE <table> SET with list of attributes */
-	else if (TailMatches3("UPDATE", MatchAny, "SET"))
+	if (TailMatches3("UPDATE", MatchAny, "SET"))
 		COMPLETE_WITH_ATTR(prev2_wd, "");
 	/* UPDATE <table> SET <attr> = */
-	else if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
+	if (TailMatches4("UPDATE", MatchAny, "SET", MatchAny))
 		COMPLETE_WITH_CONST("=");
 
 /* USER MAPPING */
-	else if (Matches3("ALTER|CREATE", "USER", "MAPPING"))
+	if (Matches3("ALTER|CREATE", "USER", "MAPPING"))
 		COMPLETE_WITH_CONST("FOR");
-	else if (Matches3("DROP", "USER", "MAPPING"))
+	if (Matches3("DROP", "USER", "MAPPING"))
 		COMPLETE_WITH_LIST2("FOR", "IF EXISTS FOR");
-	else if (HeadMatches3("DROP", "USER", "MAPPING") &&
-			 MidMatchAndRemove2(3, "IF", "EXISTS") &&
-			 false) {} /* FALL THROUGH */
-	else if (Matches4("CREATE", "USER", "MAPPING", "FOR"))
+	if (HeadMatches3("DROP", "USER", "MAPPING") &&
+		MidMatches2(4, "IF", "EXISTS"))
+		COLLAPSE(4, 2);
+
+	if (Matches4("CREATE", "USER", "MAPPING", "FOR"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles,
 							ADDLIST3("CURRENT_USER", "PUBLIC", "USER"));
-	else if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR"))
+	if (Matches4("ALTER|DROP", "USER", "MAPPING", "FOR"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings, "");
-	else if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
+	if (Matches5("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
 		COMPLETE_WITH_CONST("SERVER");
-	else if (Matches7("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny))
+	if (Matches7("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny))
 		COMPLETE_WITH_CONST("OPTIONS");
 
 /*
  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] [ table ]
  * VACUUM [ FULL | FREEZE ] [ VERBOSE ] ANALYZE [ table [ (column [, ...] ) ] ]
  */
-	else if (Matches1("VACUUM"))
+	if (Matches1("VACUUM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 		   ADDLIST4("FULL", "FREEZE", "ANALYZE", "VERBOSE"));
-	else if (Matches2("VACUUM", "FULL|FREEZE"))
+	if (Matches2("VACUUM", "FULL|FREEZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 		   ADDLIST2("ANALYZE", "VERBOSE"));
-	else if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE"))
+	if (Matches3("VACUUM", "FULL|FREEZE", "ANALYZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 		   ADDLIST1("VERBOSE"));
-	else if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE"))
+	if (Matches3("VACUUM", "FULL|FREEZE", "VERBOSE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 		   ADDLIST1("ANALYZE"));
-	else if (Matches2("VACUUM", "VERBOSE"))
+	if (Matches2("VACUUM", "VERBOSE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 		   ADDLIST1("ANALYZE"));
-	else if (Matches2("VACUUM", "ANALYZE"))
+	if (Matches2("VACUUM", "ANALYZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm,
 		   ADDLIST1("VERBOSE"));
-	else if (HeadMatches1("VACUUM"))
+	if (HeadMatches1("VACUUM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tm, "");
 
 /* WITH [RECURSIVE] */
@@ -3338,114 +3024,114 @@ psql_completion(const char *text, int start, int end)
 	 * Only match when WITH is the first word, as WITH may appear in many
 	 * other contexts.
 	 */
-	else if (Matches1("WITH"))
+	if (Matches1("WITH"))
 		COMPLETE_WITH_CONST("RECURSIVE");
 
 /* ANALYZE */
 	/* Complete with list of tables */
-	else if (Matches1("ANALYZE"))
+	if (Matches1("ANALYZE"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tmf, "");
 
 /* WHERE */
 	/* Simple case of the word before the where being the table name */
-	else if (TailMatches2(MatchAny, "WHERE"))
+	if (TailMatches2(MatchAny, "WHERE"))
 		COMPLETE_WITH_ATTR(prev2_wd, "");
 
 /* ... FROM ... */
 /* TODO: also include SRF ? */
-	else if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM"))
+	if (TailMatches1("FROM") && !Matches3("COPY|\\copy", MatchAny, "FROM"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, "");
 
 /* ... JOIN ... */
-	else if (TailMatches1("JOIN"))
+	if (TailMatches1("JOIN"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, "");
 
 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
-	else if (TailMatchesCS1("\\?"))
+	if (TailMatchesCS1("\\?"))
 		COMPLETE_WITH_LIST_CS3("commands", "options", "variables");
-	else if (TailMatchesCS1("\\connect|\\c"))
+	if (TailMatchesCS1("\\connect|\\c"))
 	{
 		if (!recognized_connection_string(text))
 			COMPLETE_WITH_QUERY(Query_for_list_of_databases, "");
 	}
-	else if (TailMatchesCS2("\\connect|\\c", MatchAny))
+	if (TailMatchesCS2("\\connect|\\c", MatchAny))
 	{
 		if (!recognized_connection_string(prev_wd))
 			COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
 	}
-	else if (TailMatchesCS1("\\da*"))
+	if (TailMatchesCS1("\\da*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, "");
-	else if (TailMatchesCS1("\\dA*"))
+	if (TailMatchesCS1("\\dA*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_access_methods, "");
-	else if (TailMatchesCS1("\\db*"))
+	if (TailMatchesCS1("\\db*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces, "");
-	else if (TailMatchesCS1("\\dD*"))
+	if (TailMatchesCS1("\\dD*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains, "");
-	else if (TailMatchesCS1("\\des*"))
+	if (TailMatchesCS1("\\des*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_servers, "");
-	else if (TailMatchesCS1("\\deu*"))
+	if (TailMatchesCS1("\\deu*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings, "");
-	else if (TailMatchesCS1("\\dew*"))
+	if (TailMatchesCS1("\\dew*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_fdws, "");
-	else if (TailMatchesCS1("\\df*"))
+	if (TailMatchesCS1("\\df*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, "");
 
-	else if (TailMatchesCS1("\\dFd*"))
+	if (TailMatchesCS1("\\dFd*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_dictionaries, "");
-	else if (TailMatchesCS1("\\dFp*"))
+	if (TailMatchesCS1("\\dFp*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_parsers, "");
-	else if (TailMatchesCS1("\\dFt*"))
+	if (TailMatchesCS1("\\dFt*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_templates, "");
 	/* must be at end of \dF alternatives: */
-	else if (TailMatchesCS1("\\dF*"))
+	if (TailMatchesCS1("\\dF*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_ts_configurations, "");
 
-	else if (TailMatchesCS1("\\di*"))
+	if (TailMatchesCS1("\\di*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes, "");
-	else if (TailMatchesCS1("\\dL*"))
+	if (TailMatchesCS1("\\dL*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_languages, "");
-	else if (TailMatchesCS1("\\dn*"))
+	if (TailMatchesCS1("\\dn*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_schemas, "");
-	else if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z"))
+	if (TailMatchesCS1("\\dp") || TailMatchesCS1("\\z"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tsvmf, "");
-	else if (TailMatchesCS1("\\ds*"))
+	if (TailMatchesCS1("\\ds*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences, "");
-	else if (TailMatchesCS1("\\dt*"))
+	if (TailMatchesCS1("\\dt*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "");
-	else if (TailMatchesCS1("\\dT*"))
+	if (TailMatchesCS1("\\dT*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, "");
-	else if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*"))
+	if (TailMatchesCS1("\\du*") || TailMatchesCS1("\\dg*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
-	else if (TailMatchesCS1("\\dv*"))
+	if (TailMatchesCS1("\\dv*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, "");
-	else if (TailMatchesCS1("\\dx*"))
+	if (TailMatchesCS1("\\dx*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_extensions, "");
-	else if (TailMatchesCS1("\\dm*"))
+	if (TailMatchesCS1("\\dm*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews, "");
-	else if (TailMatchesCS1("\\dE*"))
+	if (TailMatchesCS1("\\dE*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables, "");
-	else if (TailMatchesCS1("\\dy*"))
+	if (TailMatchesCS1("\\dy*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers, "");
 
 	/* must be at end of \d alternatives: */
-	else if (TailMatchesCS1("\\d*"))
+	if (TailMatchesCS1("\\d*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations, "");
 
-	else if (TailMatchesCS1("\\ef"))
+	if (TailMatchesCS1("\\ef"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, "");
-	else if (TailMatchesCS1("\\ev"))
+	if (TailMatchesCS1("\\ev"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, "");
 
-	else if (TailMatchesCS1("\\encoding"))
+	if (TailMatchesCS1("\\encoding"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_encodings, "");
-	else if (TailMatchesCS1("\\h") || TailMatchesCS1("\\help"))
+	if (TailMatchesCS1("\\h") || TailMatchesCS1("\\help"))
 		COMPLETE_WITH_LIST(sql_commands);
-	else if (TailMatchesCS1("\\l*") && !TailMatchesCS1("\\lo*"))
+	if (TailMatchesCS1("\\l*") && !TailMatchesCS1("\\lo*"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_databases, "");
-	else if (TailMatchesCS1("\\password"))
+	if (TailMatchesCS1("\\password"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles, "");
-	else if (TailMatchesCS1("\\pset"))
+	if (TailMatchesCS1("\\pset"))
 	{
 		static const char *const my_list[] =
 		{"border", "columns", "expanded", "fieldsep", "fieldsep_zero",
@@ -3456,7 +3142,7 @@ psql_completion(const char *text, int start, int end)
 
 		COMPLETE_WITH_LIST_CS(my_list);
 	}
-	else if (TailMatchesCS2("\\pset", MatchAny))
+	if (TailMatchesCS2("\\pset", MatchAny))
 	{
 		if (TailMatchesCS1("format"))
 		{
@@ -3466,53 +3152,53 @@ psql_completion(const char *text, int start, int end)
 
 			COMPLETE_WITH_LIST_CS(my_list);
 		}
-		else if (TailMatchesCS1("linestyle"))
+		if (TailMatchesCS1("linestyle"))
 			COMPLETE_WITH_LIST_CS3("ascii", "old-ascii", "unicode");
-		else if (TailMatchesCS1("unicode_border_linestyle|"
+		if (TailMatchesCS1("unicode_border_linestyle|"
 								"unicode_column_linestyle|"
 								"unicode_header_linestyle"))
 			COMPLETE_WITH_LIST_CS2("single", "double");
 	}
-	else if (TailMatchesCS1("\\unset"))
+	if (TailMatchesCS1("\\unset"))
 	{
-		matches = complete_from_variables(text, "", "", true);
+		return complete_from_variables(text, "", "", true);
 	}
-	else if (TailMatchesCS1("\\set"))
+	if (TailMatchesCS1("\\set"))
 	{
-		matches = complete_from_variables(text, "", "", false);
+		return complete_from_variables(text, "", "", false);
 	}
-	else if (TailMatchesCS2("\\set", MatchAny))
+	if (TailMatchesCS2("\\set", MatchAny))
 	{
 		if (TailMatchesCS1("AUTOCOMMIT|ON_ERROR_STOP|QUIET|"
 						   "SINGLELINE|SINGLESTEP"))
 			COMPLETE_WITH_LIST_CS2("on", "off");
-		else if (TailMatchesCS1("COMP_KEYWORD_CASE"))
+		if (TailMatchesCS1("COMP_KEYWORD_CASE"))
 			COMPLETE_WITH_LIST_CS4("lower", "upper",
 								   "preserve-lower", "preserve-upper");
-		else if (TailMatchesCS1("ECHO"))
+		if (TailMatchesCS1("ECHO"))
 			COMPLETE_WITH_LIST_CS4("errors", "queries", "all", "none");
-		else if (TailMatchesCS1("ECHO_HIDDEN"))
+		if (TailMatchesCS1("ECHO_HIDDEN"))
 			COMPLETE_WITH_LIST_CS3("noexec", "off", "on");
-		else if (TailMatchesCS1("HISTCONTROL"))
+		if (TailMatchesCS1("HISTCONTROL"))
 			COMPLETE_WITH_LIST_CS4("ignorespace", "ignoredups",
 								   "ignoreboth", "none");
-		else if (TailMatchesCS1("ON_ERROR_ROLLBACK"))
+		if (TailMatchesCS1("ON_ERROR_ROLLBACK"))
 			COMPLETE_WITH_LIST_CS3("on", "off", "interactive");
-		else if (TailMatchesCS1("SHOW_CONTEXT"))
+		if (TailMatchesCS1("SHOW_CONTEXT"))
 			COMPLETE_WITH_LIST_CS3("never", "errors", "always");
-		else if (TailMatchesCS1("VERBOSITY"))
+		if (TailMatchesCS1("VERBOSITY"))
 			COMPLETE_WITH_LIST_CS3("default", "verbose", "terse");
 	}
-	else if (TailMatchesCS1("\\sf*"))
+	if (TailMatchesCS1("\\sf*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, "");
-	else if (TailMatchesCS1("\\sv*"))
+	if (TailMatchesCS1("\\sv*"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, "");
-	else if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|"
+	if (TailMatchesCS1("\\cd|\\e|\\edit|\\g|\\i|\\include|"
 							"\\ir|\\include_relative|\\o|\\out|"
 							"\\s|\\w|\\write|\\lo_import"))
 	{
-		SET_COMP_CHARP("\\");
-		matches = completion_matches(text, complete_from_files);
+		SET_COMPLETION_CHARP("\\");
+		return completion_matches(text, complete_from_files);
 	}
 
 	/*
@@ -3520,41 +3206,9 @@ psql_completion(const char *text, int start, int end)
 	 * check if that was the previous word. If so, execute the query to get a
 	 * list of them.
 	 */
-	else
-	{
-		const pgsql_thing_t *ent = find_thing_entry(prev_wd);
-
-		if (ent)
-		{
-			if (ent->query)
-				COMPLETE_WITH_QUERY(ent->query, "");
-			else if (ent->squery)
-				COMPLETE_WITH_SCHEMA_QUERY(*ent->squery, "");
-		}
-	}
-
-	/*
-	 * If we still don't have anything to match we have to fabricate some sort
-	 * of default list. If we were to just return NULL, readline automatically
-	 * attempts filename completion, and that's usually no good.
-	 */
-	if (matches == NULL)
-	{
-		COMPLETE_WITH_CONST("");
-#ifdef HAVE_RL_COMPLETION_APPEND_CHARACTER
-		rl_completion_append_character = '\0';
-#endif
-	}
-
-	/* free storage */
-	free(previous_words);
-	free(words_buffer);
-
-	/* Return our Grand List O' Matches */
-	return matches;
+	COMPLETE_THING(-1);
 }
 
-
 /*
  * GENERATOR FUNCTIONS
  *
@@ -4320,14 +3974,15 @@ get_previous_words(int point, char **buffer, int *nwords)
  * Returns NULL if the variable is unknown. Otherwise the returned string,
  * containing the type, has to be freed.
  */
-static char *
-get_guctype(const char *varname)
+static bool
+get_guctype(char *buf, size_t len, const char *varname)
 {
 	PQExpBufferData query_buffer;
 	char	   *e_varname;
 	PGresult   *result;
-	char	   *guctype = NULL;
+	bool		success = true;
 
+	memset(buf, 0, len);
 	e_varname = escape_string(varname);
 
 	initPQExpBuffer(&query_buffer);
@@ -4341,11 +3996,12 @@ get_guctype(const char *varname)
 	free(e_varname);
 
 	if (PQresultStatus(result) == PGRES_TUPLES_OK && PQntuples(result) > 0)
-		guctype = pg_strdup(PQgetvalue(result, 0, 0));
-
+		strncpy(buf, PQgetvalue(result, 0, 0), len - 1);
+	else
+		success = false;
 	PQclear(result);
 
-	return guctype;
+	return success;
 }
 
 /*
-- 
2.9.2

