Обсуждение: Converting tab-complete.c's else-if chain to a switch

Поиск
Список
Период
Сортировка

Converting tab-complete.c's else-if chain to a switch

От
Tom Lane
Дата:
Per discussion elsewhere [1], I've been looking at $SUBJECT.
I have a very crude Perl hack (too ugly to show yet ;-)) that
produces output along the lines of


Existing code:

/* CREATE STATISTICS <name> */
    else if (Matches("CREATE", "STATISTICS", MatchAny))
        COMPLETE_WITH("(", "ON");
    else if (Matches("CREATE", "STATISTICS", MatchAny, "("))
        COMPLETE_WITH("ndistinct", "dependencies", "mcv");
    else if (Matches("CREATE", "STATISTICS", MatchAny, "(*)"))
        COMPLETE_WITH("ON");
    else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
             TailMatches("FROM"))
        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);


Generated tables:

enum TCPatternID
{
    ...
    M_CREATE_STATISTICS_ANY,
    M_CREATE_STATISTICS_ANY_LPAREN,
    M_CREATE_STATISTICS_ANY_PARENSTARPAREN,
    HM_CREATE_STATISTICS_ANY,
    ...
};

enum TCPatternKind
{
    Match,
    MatchCS,
    HeadMatch,
    HeadMatchCS,
    TailMatch,
    TailMatchCS,
};

typedef struct
{
    enum TCPatternID id;
    enum TCPatternKind kind;
    int         nwords;
    const char *const *words;
} TCPattern;

#define TCPAT(id, kind, ...) \
    { (id), (kind), VA_ARGS_NARGS(__VA_ARGS__), \
      (const char * const []) { __VA_ARGS__ } }

static const TCPattern tcpatterns[] =
{
    ...
    TCPAT(M_CREATE_STATISTICS_ANY,
          Match, "CREATE", "STATISTICS", MatchAny),
    TCPAT(M_CREATE_STATISTICS_ANY_LPAREN,
          Match, "CREATE", "STATISTICS", MatchAny, "("),
    TCPAT(M_CREATE_STATISTICS_ANY_PARENSTARPAREN,
          Match, "CREATE", "STATISTICS", MatchAny, "(*)"),
    TCPAT(HM_CREATE_STATISTICS_ANY,
          HeadMatch, "CREATE", "STATISTICS", MatchAny),
    ...
};


Converted code:

/* CREATE STATISTICS <name> */
    case M_CREATE_STATISTICS_ANY:
        COMPLETE_WITH("(", "ON");
        break;
    case M_CREATE_STATISTICS_ANY_LPAREN:
        COMPLETE_WITH("ndistinct", "dependencies", "mcv");
        break;
    case M_CREATE_STATISTICS_ANY_PARENSTARPAREN:
        COMPLETE_WITH("ON");
        break;
    case HM_CREATE_STATISTICS_ANY:
        if (TailMatches("FROM"))
            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
        break;


The idea (I've not actually written this part yet) is that an
outer loop would iterate through the table entries and invoke
the appropriate switch case for any successful match.  As soon
as the switch code sets "matches" non-NULL (it might not, as in
the last case in the example), we can exit.

While this clearly can be made to work, it doesn't seem like the
result will be very maintainable.  You have to invent a single-use
enum label, and the actual definition of the primary match pattern
is far away from the code using it, and we have two fundamentally
different ways of writing the same pattern test (since we'll still
need some instances of direct calls to TailMatches and friends,
as in the last example case).

What I'm thinking about doing instead of pursuing this exact
implementation idea is that we should create a preprocessor
to deal with building the table.  I'm envisioning that the
new source code will look like


/* CREATE STATISTICS <name> */
    case Matches("CREATE", "STATISTICS", MatchAny):
        COMPLETE_WITH("(", "ON");
        break;
    case Matches("CREATE", "STATISTICS", MatchAny, "("):
        COMPLETE_WITH("ndistinct", "dependencies", "mcv");
        break;
    case Matches("CREATE", "STATISTICS", MatchAny, "(*)"):
        COMPLETE_WITH("ON");
        break;
    case HeadMatches("CREATE", "STATISTICS", MatchAny):
        if (TailMatches("FROM"))
            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
        break;


Of course this isn't legal C, since the case labels are not
constant expressions.  The preprocessing script would look
for case labels that look like this, generate the appropriate
table entries, and replace the case labels with mechanically-
generated ID codes that don't need to be particularly human
readable.  On the downside, YA preprocessor and mini-language
isn't much fun; but at least this is a lot closer to real C
than some of the other things we've invented.

(Further down the line, we can look into improvements such as
avoiding duplicate string comparisons; but that can be done behind
the scenes, without messing with this source-code notation.)

Does anyone particularly hate this plan, or have a better idea?

BTW, we have quite a few instances of code like the aforementioned

    else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
             TailMatches("FROM"))

I'm thinking we should invent a Matches() option "MatchAnyN",
which could appear at most once and would represent an automatic
match to zero or more words appearing between the head part and
the tail part.  Then this could be transformed to

    else if (Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"))

the advantage being that more of the pattern can be embedded in the
table and we need fewer bits of ad-hoc logic.  Maybe this'd be worth
doing even if we don't go forward with the switch conversion.

            regards, tom lane

[1] https://www.postgresql.org/message-id/1203570.1720470608%40sss.pgh.pa.us



Re: Converting tab-complete.c's else-if chain to a switch

От
Tom Lane
Дата:
I wrote:
> Per discussion elsewhere [1], I've been looking at $SUBJECT.

Here's a finished patchset to perform this change.  I don't
believe it has any material effect on runtime behavior or
performance, but it does achieve what Andres anticipated
would happen: the compile time for tab-complete.c drops
considerably.  With gcc 8.5.0 I see the time drop from
about 3 seconds to about 0.7.  The win is less noticeable
with clang version 15.0 on a Mac M1: from about 0.7s to 0.45s.
Of course the main point is the hope that it will work at all
with MSVC, but I'm not in a position to test that.

The most painful part of this is all the changes like

         COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME", "SET",
                       "VALIDATE CONSTRAINT");
+            break;
     /* ALTER DOMAIN <sth> DROP */
-    else if (Matches("ALTER", "DOMAIN", MatchAny, "DROP"))
+        case Matches("ALTER", "DOMAIN", MatchAny, "DROP"):
         COMPLETE_WITH("CONSTRAINT", "DEFAULT", "NOT NULL");
+            break;
     /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */

I despaired of doing that accurately by hand, so I wrote
a Perl script to do it.  The script can cope with all but
about three of the existing tests; those have to be manually
modified before running the script, and then the actual
"switch()" has to be inserted afterwards.  There won't be
any need to commit the script, since it's a one-time tool,
but I've included it for documentation's sake, along with
the "pre-convert.diff" and "post-convert.diff" manual patch
steps that are required to construct the complete 0004 patch.

I'm going to add this to the September CF, but I'd really
rather get it reviewed and committed pretty quickly.
Even with the helper script, I'm not eager to have to
rebase this a bunch of times.

            regards, tom lane

From bee175ee685cdd76daafeac8c20919a74d9327a8 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 13 Jul 2024 11:58:14 -0400
Subject: [PATCH v1 1/5] Invent "MatchAnyN" option for tab-complete.c's
 Matches/MatchesCS.

This argument matches any number (including zero) of previous words.
Use it to replace the common coding pattern

    if (HeadMatches("A", "B") && TailMatches("X", "Y"))

with

    if (Matches("A", "B", MatchAnyN, "X", "Y"))

In itself this feature doesn't do much except (arguably) make the
code slightly shorter and more readable.  However, it reduces the
number of complex if-condition patterns that have to be dealt with
in the next commits in this series.

While here, restructure the *Matches implementation functions so
that the actual work is done in functions that take a char **
array of pattern strings, and the versions taking variadic arguments
are thin wrappers around the array ones.  This simplifies the
new Matches logic considerably.  At the end of this patch series,
the array functions will be the only ones that are material to
performance, so having the variadic ones be wrappers makes sense.
---
 src/bin/psql/tab-complete.c | 400 +++++++++++++++++++++++-------------
 1 file changed, 258 insertions(+), 142 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index d453e224d9..2878506c30 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1446,10 +1446,12 @@ initialize_readline(void)
  *
  * For readability, callers should use the macros MatchAny and MatchAnyExcept
  * to invoke those two special cases for 'pattern'.  (But '|' and '*' must
- * just be written directly in patterns.)
+ * just be written directly in patterns.)  There is also MatchAnyN, but that
+ * is supported only in Matches/MatchesCS and is not handled here.
  */
 #define MatchAny  NULL
 #define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""

 static bool
 word_matches(const char *pattern,
@@ -1514,107 +1516,196 @@ word_matches(const char *pattern,
 }

 /*
- * Implementation of TailMatches and TailMatchesCS macros: do the last N words
- * in previous_words match the variadic arguments?
+ * Implementation of TailMatches and TailMatchesCS tests: do the last N words
+ * in previous_words match the pattern arguments?
  *
  * The array indexing might look backwards, but remember that
  * previous_words[0] contains the *last* word on the line, not the first.
  */
 static bool
-TailMatchesImpl(bool case_sensitive,
-                int previous_words_count, char **previous_words,
-                int narg,...)
+TailMatchesArray(bool case_sensitive,
+                 int previous_words_count, char **previous_words,
+                 int narg, const char *const *args)
 {
-    va_list        args;
-
     if (previous_words_count < narg)
         return false;

-    va_start(args, narg);
-
     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

         if (!word_matches(arg, previous_words[narg - argno - 1],
                           case_sensitive))
-        {
-            va_end(args);
             return false;
-        }
     }

-    va_end(args);
-
     return true;
 }

 /*
- * Implementation of Matches and MatchesCS macros: do all of the words
- * in previous_words match the variadic arguments?
+ * As above, but the pattern is passed as a variadic argument list.
  */
 static bool
-MatchesImpl(bool case_sensitive,
-            int previous_words_count, char **previous_words,
-            int narg,...)
+TailMatchesImpl(bool case_sensitive,
+                int previous_words_count, char **previous_words,
+                int narg,...)
 {
+    const char *argarray[64];
     va_list        args;

-    if (previous_words_count != narg)
+    Assert(narg <= lengthof(argarray));
+
+    if (previous_words_count < narg)
         return false;

     va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);
+
+    return TailMatchesArray(case_sensitive,
+                            previous_words_count, previous_words,
+                            narg, argarray);
+}
+
+/*
+ * Implementation of HeadMatches and HeadMatchesCS tests: do the first N
+ * words in previous_words match the pattern arguments?
+ */
+static bool
+HeadMatchesArray(bool case_sensitive,
+                 int previous_words_count, char **previous_words,
+                 int narg, const char *const *args)
+{
+    if (previous_words_count < narg)
+        return false;

     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

-        if (!word_matches(arg, previous_words[narg - argno - 1],
+        if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
                           case_sensitive))
-        {
-            va_end(args);
             return false;
-        }
     }

-    va_end(args);
-
     return true;
 }

 /*
- * Implementation of HeadMatches and HeadMatchesCS macros: do the first N
- * words in previous_words match the variadic arguments?
+ * As above, but the pattern is passed as a variadic argument list.
  */
 static bool
 HeadMatchesImpl(bool case_sensitive,
                 int previous_words_count, char **previous_words,
                 int narg,...)
 {
+    const char *argarray[64];
     va_list        args;

+    Assert(narg <= lengthof(argarray));
+
     if (previous_words_count < narg)
         return false;

     va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);

+    return HeadMatchesArray(case_sensitive,
+                            previous_words_count, previous_words,
+                            narg, argarray);
+}
+
+/*
+ * Implementation of Matches and MatchesCS tests: do all of the words
+ * in previous_words match the pattern arguments?
+ *
+ * This supports an additional kind of wildcard: MatchAnyN (represented as "")
+ * can match any number of words, including zero, in the middle of the list.
+ */
+static bool
+MatchesArray(bool case_sensitive,
+             int previous_words_count, char **previous_words,
+             int narg, const char *const *args)
+{
+    int            match_any_pos = -1;
+
+    /* Even with MatchAnyN, there must be at least N-1 words */
+    if (previous_words_count < narg - 1)
+        return false;
+
+    /* Check for MatchAnyN */
     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

-        if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
-                          case_sensitive))
+        if (arg != NULL && arg[0] == '\0')
         {
-            va_end(args);
-            return false;
+            match_any_pos = argno;
+            break;
         }
     }

-    va_end(args);
+    if (match_any_pos < 0)
+    {
+        /* Standard case without MatchAnyN */
+        if (previous_words_count != narg)
+            return false;
+
+        /* Either Head or Tail match will do for the rest */
+        if (!HeadMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              narg, args))
+            return false;
+    }
+    else
+    {
+        /* Match against head */
+        if (!HeadMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              match_any_pos, args))
+            return false;
+
+        /* Match against tail */
+        if (!TailMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              narg - match_any_pos - 1,
+                              args + match_any_pos + 1))
+            return false;
+    }

     return true;
 }

+/*
+ * As above, but the pattern is passed as a variadic argument list.
+ */
+static bool
+MatchesImpl(bool case_sensitive,
+            int previous_words_count, char **previous_words,
+            int narg,...)
+{
+    const char *argarray[64];
+    va_list        args;
+
+    Assert(narg <= lengthof(argarray));
+
+    /* Even with MatchAnyN, there must be at least N-1 words */
+    if (previous_words_count < narg - 1)
+        return false;
+
+    va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);
+
+    return MatchesArray(case_sensitive,
+                        previous_words_count, previous_words,
+                        narg, argarray);
+}
+
 /*
  * Check if the final character of 's' is 'c'.
  */
@@ -1906,9 +1997,9 @@ psql_completion(const char *text, int start, int end)
      * "ALTER PUBLICATION <name> ADD TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
         COMPLETE_WITH("(");
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("WHERE", "("))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
         COMPLETE_WITH_ATTR(prev3_wd);
     else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
              !TailMatches("WHERE", "(*)"))
@@ -1926,7 +2017,7 @@ psql_completion(const char *text, int start, int end)
                                  " AND nspname NOT LIKE E'pg\\\\_%%'",
                                  "CURRENT_SCHEMA");
     /* ALTER PUBLICATION <name> SET ( */
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("SET", "("))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "("))
         COMPLETE_WITH("publish", "publish_via_partition_root");
     /* ALTER SUBSCRIPTION <name> */
     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny))
@@ -1934,36 +2025,34 @@ psql_completion(const char *text, int start, int end)
                       "RENAME TO", "REFRESH PUBLICATION", "SET", "SKIP (",
                       "ADD PUBLICATION", "DROP PUBLICATION");
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("REFRESH", "PUBLICATION"))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"))
         COMPLETE_WITH("WITH (");
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("REFRESH", "PUBLICATION", "WITH", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("))
         COMPLETE_WITH("copy_data");
     /* ALTER SUBSCRIPTION <name> SET */
     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"))
         COMPLETE_WITH("(", "PUBLICATION");
     /* ALTER SUBSCRIPTION <name> SET ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "("))
         COMPLETE_WITH("binary", "disable_on_error", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
                       "streaming", "synchronous_commit");
     /* ALTER SUBSCRIPTION <name> SKIP ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SKIP", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "("))
         COMPLETE_WITH("lsn");
     /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "PUBLICATION"))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "PUBLICATION"))
     {
         /* complete with nothing here as this refers to remote publications */
     }
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
+                     "ADD|DROP|SET", "PUBLICATION", MatchAny))
         COMPLETE_WITH("WITH (");
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
+                     "ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
         COMPLETE_WITH("copy_data", "refresh");

     /* ALTER SCHEMA <name> */
@@ -2726,7 +2815,7 @@ psql_completion(const char *text, int start, int end)
         else if (TailMatches("VERBOSE|SKIP_LOCKED"))
             COMPLETE_WITH("ON", "OFF");
     }
-    else if (HeadMatches("ANALYZE") && TailMatches("("))
+    else if (Matches("ANALYZE", MatchAnyN, "("))
         /* "ANALYZE (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
     else if (HeadMatches("ANALYZE"))
@@ -3178,11 +3267,11 @@ psql_completion(const char *text, int start, int end)
      * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
         COMPLETE_WITH("(");
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE", "("))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
         COMPLETE_WITH_ATTR(prev3_wd);
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE", "(*)"))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "(*)"))
         COMPLETE_WITH(" WITH (");

     /*
@@ -3195,7 +3284,7 @@ psql_completion(const char *text, int start, int end)
     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny) &&
(!ends_with(prev_wd,','))) 
         COMPLETE_WITH("WITH (");
     /* Complete "CREATE PUBLICATION <name> [...] WITH" */
-    else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))
+    else if (Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "("))
         COMPLETE_WITH("publish", "publish_via_partition_root");

 /* CREATE RULE */
@@ -3259,8 +3348,7 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH("ndistinct", "dependencies", "mcv");
     else if (Matches("CREATE", "STATISTICS", MatchAny, "(*)"))
         COMPLETE_WITH("ON");
-    else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
-             TailMatches("FROM"))
+    else if (Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);

 /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
@@ -3358,10 +3446,10 @@ psql_completion(const char *text, int start, int end)
     {
         /* complete with nothing here as this refers to remote publications */
     }
-    else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("PUBLICATION", MatchAny))
+    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny))
         COMPLETE_WITH("WITH (");
     /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
-    else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("WITH", "("))
+    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "("))
         COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
                       "disable_on_error", "enabled", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
@@ -3415,9 +3503,10 @@ psql_completion(const char *text, int start, int end)
     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON") ||
              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("ON", MatchAny))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "ON", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "ON", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
@@ -3426,76 +3515,108 @@ psql_completion(const char *text, int start, int end)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
                           "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("DEFERRABLE") || TailMatches("INITIALLY", "IMMEDIATE|DEFERRED")))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "DEFERRABLE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "DEFERRABLE") ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "INITIALLY", "IMMEDIATE|DEFERRED") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "INITIALLY", "IMMEDIATE|DEFERRED"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("REFERENCING"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING"))
         COMPLETE_WITH("OLD TABLE", "NEW TABLE");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("OLD|NEW", "TABLE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "OLD|NEW", "TABLE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "OLD|NEW", "TABLE"))
         COMPLETE_WITH("AS");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "NEW", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("FOR"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR"))
         COMPLETE_WITH("EACH", "ROW", "STATEMENT");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("FOR", "EACH"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH"))
         COMPLETE_WITH("ROW", "STATEMENT");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("FOR", "EACH", "ROW|STATEMENT") ||
-              TailMatches("FOR", "ROW|STATEMENT")))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH", "ROW|STATEMENT") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH", "ROW|STATEMENT") ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "ROW|STATEMENT") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "ROW|STATEMENT"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("WHEN", "(*)"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "WHEN", "(*)") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "WHEN", "(*)"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
@@ -3507,18 +3628,20 @@ psql_completion(const char *text, int start, int end)
      * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
      * PROCEDURE|FUNCTION.
      */
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("EXECUTE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "EXECUTE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "EXECUTE"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FUNCTION");
         else
             COMPLETE_WITH("PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "EXECUTE", "FUNCTION|PROCEDURE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "EXECUTE", "FUNCTION|PROCEDURE"))
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);

 /* CREATE ROLE,USER,GROUP <name> */
@@ -3651,16 +3774,14 @@ psql_completion(const char *text, int start, int end)
         else
             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
     }
-    else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
-             TailMatches("WHEN|AND", MatchAny, "IN", "(*)"))
+    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "WHEN|AND", MatchAny, "IN", "(*)"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
         else
             COMPLETE_WITH("EXECUTE PROCEDURE");
     }
-    else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
-             TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
+    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"))
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);

 /* DEALLOCATE */
@@ -3685,27 +3806,27 @@ psql_completion(const char *text, int start, int end)
      * provides, like the syntax of DECLARE command in the documentation
      * indicates.
      */
-    else if (HeadMatches("DECLARE") && TailMatches("BINARY"))
+    else if (Matches("DECLARE", MatchAnyN, "BINARY"))
         COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
-    else if (HeadMatches("DECLARE") && TailMatches("ASENSITIVE|INSENSITIVE"))
+    else if (Matches("DECLARE", MatchAnyN, "ASENSITIVE|INSENSITIVE"))
         COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
-    else if (HeadMatches("DECLARE") && TailMatches("SCROLL"))
+    else if (Matches("DECLARE", MatchAnyN, "SCROLL"))
         COMPLETE_WITH("CURSOR");
     /* Complete DECLARE ... [options] NO with SCROLL */
-    else if (HeadMatches("DECLARE") && TailMatches("NO"))
+    else if (Matches("DECLARE", MatchAnyN, "NO"))
         COMPLETE_WITH("SCROLL");

     /*
      * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and
      * FOR
      */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR"))
         COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
     /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT"))
         COMPLETE_WITH("HOLD");
     /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT", "HOLD"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT", "HOLD"))
         COMPLETE_WITH("FOR");

 /* DELETE --- can be inside EXPLAIN, RULE, etc */
@@ -3930,8 +4051,7 @@ psql_completion(const char *text, int start, int end)
                                  "FROM",
                                  "IN");
     /* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
-    else if (HeadMatches("FETCH|MOVE") &&
-             TailMatches("FROM|IN"))
+    else if (Matches("FETCH|MOVE", MatchAnyN, "FROM|IN"))
         COMPLETE_WITH_QUERY(Query_for_list_of_cursors);

 /* FOREIGN DATA WRAPPER */
@@ -3940,8 +4060,7 @@ psql_completion(const char *text, int start, int end)
              !TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
     /* applies in CREATE SERVER */
-    else if (TailMatches("FOREIGN", "DATA", "WRAPPER", MatchAny) &&
-             HeadMatches("CREATE", "SERVER"))
+    else if (Matches("CREATE", "SERVER", MatchAnyN, "FOREIGN", "DATA", "WRAPPER", MatchAny))
         COMPLETE_WITH("OPTIONS");

 /* FOREIGN TABLE */
@@ -4128,44 +4247,43 @@ psql_completion(const char *text, int start, int end)
      * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
      * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
      */
-    else if ((HeadMatches("GRANT") && TailMatches("TO")) ||
-             (HeadMatches("REVOKE") && TailMatches("FROM")))
+    else if (Matches("GRANT", MatchAnyN, "TO") ||
+             Matches("REVOKE", MatchAnyN, "FROM"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);

     /*
      * Offer grant options after that.
      */
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny))
         COMPLETE_WITH("WITH ADMIN",
                       "WITH INHERIT",
                       "WITH SET",
                       "WITH GRANT OPTION",
                       "GRANTED BY");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH"))
         COMPLETE_WITH("ADMIN",
                       "INHERIT",
                       "SET",
                       "GRANT OPTION");
-    else if (HeadMatches("GRANT") &&
-             (TailMatches("TO", MatchAny, "WITH", "ADMIN|INHERIT|SET")))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", "ADMIN|INHERIT|SET"))
         COMPLETE_WITH("OPTION", "TRUE", "FALSE");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION"))
         COMPLETE_WITH("GRANTED BY");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
     /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
-    else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
+    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO|FROM"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
     /* Offer WITH GRANT OPTION after that */
-    else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO", MatchAny))
+    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny))
         COMPLETE_WITH("WITH GRANT OPTION");
     /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
-    else if (HeadMatches("GRANT") && TailMatches("ON", MatchAny, MatchAny))
+    else if (Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny))
         COMPLETE_WITH("TO");
-    else if (HeadMatches("REVOKE") && TailMatches("ON", MatchAny, MatchAny))
+    else if (Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny))
         COMPLETE_WITH("FROM");

     /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
@@ -4280,7 +4398,7 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH("IN", "NOWAIT");

     /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
-    else if (HeadMatches("LOCK") && TailMatches("IN"))
+    else if (Matches("LOCK", MatchAnyN, "IN"))
         COMPLETE_WITH("ACCESS SHARE MODE",
                       "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
                       "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
@@ -4291,16 +4409,16 @@ psql_completion(const char *text, int start, int end)
      * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of lock
      * mode
      */
-    else if (HeadMatches("LOCK") && TailMatches("IN", "ACCESS|ROW"))
+    else if (Matches("LOCK", MatchAnyN, "IN", "ACCESS|ROW"))
         COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");

     /* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
-    else if (HeadMatches("LOCK") && TailMatches("IN", "SHARE"))
+    else if (Matches("LOCK", MatchAnyN, "IN", "SHARE"))
         COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
                       "UPDATE EXCLUSIVE MODE");

     /* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
-    else if (HeadMatches("LOCK") && TailMatches("MODE"))
+    else if (Matches("LOCK", MatchAnyN, "MODE"))
         COMPLETE_WITH("NOWAIT");

 /* MERGE --- can be inside EXPLAIN */
@@ -4605,9 +4723,7 @@ psql_completion(const char *text, int start, int end)
      * Complete ALTER DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER ... SET
      * <name>
      */
-    else if (HeadMatches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER") &&
-             TailMatches("SET", MatchAny) &&
-             !TailMatches("SCHEMA"))
+    else if (Matches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER", MatchAnyN, "SET",
MatchAnyExcept("SCHEMA")))
         COMPLETE_WITH("FROM CURRENT", "TO");

     /*
@@ -4683,13 +4799,13 @@ psql_completion(const char *text, int start, int end)
     else if (Matches("TRUNCATE", "TABLE"))
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
                                         "ONLY");
-    else if (HeadMatches("TRUNCATE") && TailMatches("ONLY"))
+    else if (Matches("TRUNCATE", MatchAnyN, "ONLY"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
     else if (Matches("TRUNCATE", MatchAny) ||
              Matches("TRUNCATE", "TABLE|ONLY", MatchAny) ||
              Matches("TRUNCATE", "TABLE", "ONLY", MatchAny))
         COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
-    else if (HeadMatches("TRUNCATE") && TailMatches("IDENTITY"))
+    else if (Matches("TRUNCATE", MatchAnyN, "IDENTITY"))
         COMPLETE_WITH("CASCADE", "RESTRICT");

 /* UNLISTEN */
@@ -4770,7 +4886,7 @@ psql_completion(const char *text, int start, int end)
         else if (TailMatches("INDEX_CLEANUP"))
             COMPLETE_WITH("AUTO", "ON", "OFF");
     }
-    else if (HeadMatches("VACUUM") && TailMatches("("))
+    else if (Matches("VACUUM", MatchAnyN, "("))
         /* "VACUUM (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
     else if (HeadMatches("VACUUM"))
--
2.43.5

From 6fb82cb4e9521d1c6086c4848cf1733c4009c0cc Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 13 Jul 2024 12:11:21 -0400
Subject: [PATCH v1 2/5] Split out most of psql_completion into a separate
 function.

This is in preparation for converting the giant else-if chain
into a switch statement with a loop around it.  If we keep the
code where it is, it'll need several additional tab stops of
indentation; but as a separate function there'll be at most
one more tab stop.

The first and last couple of bits of psql_completion are not
based on HeadMatches/TailMatches/Matches tests, so they stay
where they are; they won't become part of the switch.
---
 src/bin/psql/tab-complete.c | 163 +++++++++++++++++++++---------------
 1 file changed, 97 insertions(+), 66 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2878506c30..b628cc3999 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1195,6 +1195,20 @@ static const VersionedQuery Query_for_list_of_subscriptions[] = {
     {0, NULL}
 };

+ /* Known command-starting keywords. */
+static const char *const sql_commands[] = {
+    "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
+    "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
+    "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
+    "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
+    "MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
+    "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
+    "RESET", "REVOKE", "ROLLBACK",
+    "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
+    "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
+    NULL
+};
+
 /*
  * This is a list of all "things" in Pgsql, which can show up after CREATE or
  * DROP; and there is also a query to get a list of them.
@@ -1340,6 +1354,9 @@ static const char *const view_optional_parameters[] = {

 /* Forward declaration of functions */
 static char **psql_completion(const char *text, int start, int end);
+static char **match_previous_words(const char *text, int start, int end,
+                                   char **previous_words,
+                                   int previous_words_count);
 static char *create_command_generator(const char *text, int state);
 static char *drop_command_generator(const char *text, int state);
 static char *alter_command_generator(const char *text, int state);
@@ -1787,20 +1804,6 @@ psql_completion(const char *text, int start, int end)
     HeadMatchesImpl(true, previous_words_count, previous_words, \
                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)

-    /* Known command-starting keywords. */
-    static const char *const sql_commands[] = {
-        "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
-        "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
-        "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
-        "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
-        "MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
-        "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
-        "RESET", "REVOKE", "ROLLBACK",
-        "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
-        "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
-        NULL
-    };
-
     /* psql's backslash commands. */
     static const char *const backslash_commands[] = {
         "\\a",
@@ -1887,6 +1890,86 @@ psql_completion(const char *text, int start, int end)
     else if (previous_words_count == 0)
         COMPLETE_WITH_LIST(sql_commands);

+    /* Else try completions based on matching patterns of previous words */
+    else if ((matches = match_previous_words(text, start, end,
+                                             previous_words,
+                                             previous_words_count)) != NULL)
+         /* skip */ ;
+
+    /*
+     * Finally, we look through the list of "things", such as TABLE, INDEX and
+     * check if that was the previous word. If so, execute the query to get a
+     * list of them.
+     */
+    else
+    {
+        const pgsql_thing_t *wac;
+
+        for (wac = words_after_create; wac->name != NULL; wac++)
+        {
+            if (pg_strcasecmp(prev_wd, wac->name) == 0)
+            {
+                if (wac->query)
+                    COMPLETE_WITH_QUERY_LIST(wac->query,
+                                             wac->keywords);
+                else if (wac->vquery)
+                    COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
+                                                       wac->keywords);
+                else if (wac->squery)
+                    COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
+                                                              wac->keywords);
+                break;
+            }
+        }
+    }
+
+    /*
+     * 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(true, "");
+        /* Also, prevent Readline from appending stuff to the non-match */
+        rl_completion_append_character = '\0';
+#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
+        rl_completion_suppress_quote = 1;
+#endif
+    }
+
+    /* free storage */
+    free(previous_words);
+    free(words_buffer);
+    free(text_copy);
+    free(completion_ref_object);
+    completion_ref_object = NULL;
+    free(completion_ref_schema);
+    completion_ref_schema = NULL;
+
+    /* Return our Grand List O' Matches */
+    return matches;
+}
+
+/*
+ * Subroutine to try matches based on previous_words.
+ *
+ * This is split out of psql_completion() simply to reduce code
+ * indentation level.
+ *
+ * Returns a matches list, or NULL if no match.
+ */
+static char **
+match_previous_words(const char *text, int start, int end,
+                     char **previous_words, int previous_words_count)
+{
+    /* This is the variable we'll return. */
+    char      **matches = NULL;
+
+    /* Dummy, to be replaced by switch */
+    if (0)
+        ;
+
 /* CREATE */
     /* complete with something you can create */
     else if (TailMatches("CREATE"))
@@ -5140,58 +5223,6 @@ psql_completion(const char *text, int start, int end)
         matches = rl_completion_matches(text, complete_from_files);
     }

-    /*
-     * Finally, we look through the list of "things", such as TABLE, INDEX and
-     * check if that was the previous word. If so, execute the query to get a
-     * list of them.
-     */
-    else
-    {
-        const pgsql_thing_t *wac;
-
-        for (wac = words_after_create; wac->name != NULL; wac++)
-        {
-            if (pg_strcasecmp(prev_wd, wac->name) == 0)
-            {
-                if (wac->query)
-                    COMPLETE_WITH_QUERY_LIST(wac->query,
-                                             wac->keywords);
-                else if (wac->vquery)
-                    COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
-                                                       wac->keywords);
-                else if (wac->squery)
-                    COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
-                                                              wac->keywords);
-                break;
-            }
-        }
-    }
-
-    /*
-     * 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(true, "");
-        /* Also, prevent Readline from appending stuff to the non-match */
-        rl_completion_append_character = '\0';
-#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
-        rl_completion_suppress_quote = 1;
-#endif
-    }
-
-    /* free storage */
-    free(previous_words);
-    free(words_buffer);
-    free(text_copy);
-    free(completion_ref_object);
-    completion_ref_object = NULL;
-    free(completion_ref_schema);
-    completion_ref_schema = NULL;
-
-    /* Return our Grand List O' Matches */
     return matches;
 }

--
2.43.5

From 3e61f8b43b1c64bf0d27f508b2804f5080358a9b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 13 Jul 2024 12:24:30 -0400
Subject: [PATCH v1 3/5] Install preprocessor infrastructure for
 tab-complete.c.

Rename tab-complete.c to tab-complete.in.c, create the preprocessor
script gen_tabcomplete.pl, and install Makefile/meson.build rules
to create tab-complete.c from tab-complete.in.c.

Although this is the real preprocessor script, it makes no
meaningful changes to tab-complete.in.c right now, for lack of
any source lines that would trigger changes.  (A boilerplate
header comment is added, but that's about it.)  So the result of
this step still compiles and passes tests.
---
 src/bin/psql/.gitignore                       |   1 +
 src/bin/psql/Makefile                         |   5 +-
 src/bin/psql/gen_tabcomplete.pl               | 179 ++++++++++++++++++
 src/bin/psql/meson.build                      |  15 +-
 .../{tab-complete.c => tab-complete.in.c}     |   5 +-
 5 files changed, 202 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/psql/gen_tabcomplete.pl
 rename src/bin/psql/{tab-complete.c => tab-complete.in.c} (99%)

diff --git a/src/bin/psql/.gitignore b/src/bin/psql/.gitignore
index 10b6dd3a6b..7272f6e35d 100644
--- a/src/bin/psql/.gitignore
+++ b/src/bin/psql/.gitignore
@@ -1,4 +1,5 @@
 /psqlscanslash.c
+/tab-complete.c
 /sql_help.h
 /sql_help.c
 /psql
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index 374c4c3ab8..62636d2663 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -62,6 +62,9 @@ psqlscanslash.c: FLEXFLAGS = -Cfe -p -p
 psqlscanslash.c: FLEX_NO_BACKUP=yes
 psqlscanslash.c: FLEX_FIX_WARNING=yes

+tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
+    $(PERL) $^ --outfile $@
+
 install: all installdirs
     $(INSTALL_PROGRAM) psql$(X) '$(DESTDIR)$(bindir)/psql$(X)'
     $(INSTALL_DATA) $(srcdir)/psqlrc.sample '$(DESTDIR)$(datadir)/psqlrc.sample'
@@ -75,7 +78,7 @@ uninstall:
 clean distclean:
     rm -f psql$(X) $(OBJS) lex.backup
     rm -rf tmp_check
-    rm -f sql_help.h sql_help.c psqlscanslash.c
+    rm -f sql_help.h sql_help.c psqlscanslash.c tab-complete.c

 check:
     $(prove_check)
diff --git a/src/bin/psql/gen_tabcomplete.pl b/src/bin/psql/gen_tabcomplete.pl
new file mode 100644
index 0000000000..571708a876
--- /dev/null
+++ b/src/bin/psql/gen_tabcomplete.pl
@@ -0,0 +1,179 @@
+#----------------------------------------------------------------------
+#
+# gen_tabcomplete.pl
+#    Perl script that transforms tab-complete.in.c to tab-complete.c.
+#
+# The input is a C file that contains some case labels that are not
+# constants, but look like function calls, for example:
+#    case Matches("A", "B"):
+# The function name can be any one of Matches, HeadMatches, TailMatches,
+# MatchesCS, HeadMatchesCS, or TailMatchesCS.  The argument(s) must be
+# string literals or macros that expand to string literals or NULL.
+# These lines are replaced by "case N:" where N is a unique number
+# for each such case label.  (For convenience, we use the line number
+# of the case label, although other values would work just as well.)
+#
+# In addition, there must be one input line that reads
+#    /* Insert tab-completion pattern data here. */
+# This line is replaced in the output file by macro calls, one for each
+# replaced case label.  The output for the above example would be
+#    TCPAT(N, Match, "A", "B"),
+# where N is the replacement case label, "Match" is the original function
+# name shorn of "es", and the rest are the function arguments.
+# The tab-completion data line must appear before any replaceable
+# case labels, and should appear before any executable code.
+#
+#
+# Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/bin/psql/gen_tabcomplete.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings FATAL => 'all';
+use Getopt::Long;
+
+my $outfile = '';
+
+GetOptions('outfile=s' => \$outfile) or die "$0: wrong arguments";
+
+open my $infh, '<', $ARGV[0]
+  or die "$0: could not open input file '$ARGV[0]': $!\n";
+
+my $outfh;
+if ($outfile)
+{
+    open $outfh, '>', $outfile
+      or die "$0: could not open output file '$outfile': $!\n";
+}
+else
+{
+    $outfh = *STDOUT;
+}
+
+# Opening boilerplate for output file.
+printf $outfh <<EOM;
+/*-------------------------------------------------------------------------
+ *
+ * tab-complete.c
+ *    Preprocessed tab-completion code.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *  ******************************
+ *  *** DO NOT EDIT THIS FILE! ***
+ *  ******************************
+ *
+ *  It has been GENERATED by src/bin/psql/gen_tabcomplete.pl
+ *
+ *-------------------------------------------------------------------------
+ */
+
+EOM
+
+# Scan input file until we find the data-replacement label line.
+# Dump what we scan directly into the output file.
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* Insert tab-completion pattern data here\. \*/\s*$|;
+    print $outfh "$_\n";
+}
+
+# Remember next input line number for later.
+my $post_data_line_no = $. + 1;
+
+# Scan input file, looking for replaceable case labels.
+# Add the generated table-contents macros to $table_data,
+# and add the replacement labels to $output_code.
+# Lines that aren't replaceable labels go to $output_code verbatim.
+my $table_data = '';
+my $output_code = '';
+# Data extracted from current case label:
+my $in_label = 0;
+my $head;
+my $cs;
+my $rest;
+my $lineno;
+my $nlines;
+
+while (<$infh>)
+{
+    chomp;
+    if ($in_label)
+    {
+        # Append multiple lines to current case label.
+        s/^\s+//;
+        $rest .= ' ' . $_;
+        $nlines++;
+        if ($rest =~ m/\)\s*:\s*$/)
+        {
+            process_case_label($head, $cs, $rest, $lineno, $nlines);
+            $in_label = 0;
+        }
+    }
+    elsif (m/^\s*case\s+(Head|Tail|)Matches(CS|)\((.*)$/)
+    {
+        $head = $1;
+        $cs = $2;
+        $rest = $3;
+        $lineno = $.;
+        $nlines = 1;
+        if ($rest =~ m/\)\s*:\s*$/)
+        {
+            process_case_label($head, $cs, $rest, $lineno, $nlines);
+        }
+        else
+        {
+            $in_label = 1;
+        }
+    }
+    else
+    {
+        $output_code .= $_ . "\n";
+    }
+}
+
+# Dump out the table data.
+print $outfh $table_data;
+
+# Adjust line numbering so that the rest of the file matches the
+# line numbering of the original, to simplify debugging.
+print $outfh "#line ${post_data_line_no} \"tab-complete.in.c\"\n";
+
+# Dump out the modified code, and we're done!
+print $outfh $output_code;
+
+close($infh);
+close($outfh);
+
+
+sub process_case_label
+{
+    my ($head, $cs, $rest, $lineno, $nlines) = @_;
+
+    # We already know $rest ends with "):".
+    $rest =~ s/\)\s*:\s*$//;
+
+    $table_data .= "\tTCPAT(${lineno}, ${head}Match${cs}, ${rest}),\n";
+
+    $output_code .= "\t\tcase $lineno:";
+
+    # Generate the same number of lines as in the input, so that line
+    # numbers stay in sync for debugging purposes.
+    $output_code .= "\n" x $nlines;
+}
+
+sub usage
+{
+    die <<EOM;
+Usage: gen_tabcomplete.pl [--outfile/-o <path>] input_file
+    --outfile       Output file (default is stdout)
+
+gen_tabcomplete.pl transforms tab-complete.in.c to tab-complete.c.
+EOM
+}
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f3a6392138..6ee6c6fd52 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -13,7 +13,6 @@ psql_sources = files(
   'prompt.c',
   'startup.c',
   'stringutils.c',
-  'tab-complete.c',
   'variables.c',
 )

@@ -24,6 +23,20 @@ psqlscanslash = custom_target('psqlscanslash',
 generated_sources += psqlscanslash
 psql_sources += psqlscanslash

+tabcomplete = custom_target('tabcomplete',
+  input: 'tab-complete.in.c',
+  output: 'tab-complete.c',
+  command: [
+    perl, files('gen_tabcomplete.pl'), files('tab-complete.in.c'),
+    '--outfile', '@OUTPUT@',
+    '@INPUT@',
+  ],
+  install: true,
+  install_dir: dir_include_server / 'utils',
+)
+generated_sources += tabcomplete
+psql_sources += tabcomplete
+
 sql_help = custom_target('psql_help',
   output: ['sql_help.c', 'sql_help.h'],
   depfile: 'sql_help.dep',
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.in.c
similarity index 99%
rename from src/bin/psql/tab-complete.c
rename to src/bin/psql/tab-complete.in.c
index b628cc3999..c92fc729be 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3,7 +3,10 @@
  *
  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
  *
- * src/bin/psql/tab-complete.c
+ * src/bin/psql/tab-complete.in.c
+ *
+ * NOTE: this file must be passed through gen_tabcomplete.pl
+ * to produce compilable code.
  */

 /*----------------------------------------------------------------------
--
2.43.5

From 20f66b8aaf336a2a2344a463ae8fd96740b04893 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 13 Jul 2024 12:41:33 -0400
Subject: [PATCH v1 4/5] Convert tab-complete's long else-if chain to a switch
 statement.

The initial HeadMatches/TailMatches/Matches test in each else-if arm
is now performed in a table-driven loop.  Where we get a match, the
corresponding switch case is invoked to see if the match succeeds.
(It might not, if there were additional conditions in the original
else-if test.)

99% of the mind-numbingly repetitious changes inside
match_previous_words were performed by a Perl script, which isn't
proposed for commit, but I'll include it in the email thread.

The total number of string comparisons done is just about the
same as it was in the previous coding; however, now that we
have table-driven logic underlying the handmade rules, there
is room to improve that.  For now I haven't bothered because
tab completion is still plenty fast enough for human use.
If the number of rules keeps increasing, we might someday
need to do more in that area.

The immediate benefit of all this thrashing is that C compilers
frequently don't deal well with long else-if chains.  On gcc 8.5.0,
this reduces the compile time of tab-complete.c by about a factor of
four, while MSVC is reported to crash outright with the previous
coding.
---
 src/bin/psql/tab-complete.in.c | 2850 +++++++++++++++++++-------------
 1 file changed, 1726 insertions(+), 1124 deletions(-)

diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index c92fc729be..a3bd6b7970 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -1304,6 +1304,47 @@ static const pgsql_thing_t words_after_create[] = {
     {NULL}                        /* end of list */
 };

+/*
+ * The tcpatterns[] table provides the initial pattern-match rule for each
+ * switch case in match_previous_words().  The contents of the table
+ * are constructed by gen_tabcomplete.pl.
+ */
+
+/* Basic match rules appearing in tcpatterns[].kind */
+enum TCPatternKind
+{
+    Match,
+    MatchCS,
+    HeadMatch,
+    HeadMatchCS,
+    TailMatch,
+    TailMatchCS,
+};
+
+/* Things besides string literals that can appear in tcpatterns[].words */
+#define MatchAny  NULL
+#define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""
+
+/* One entry in tcpatterns[] */
+typedef struct
+{
+    int            id;                /* case label used in match_previous_words */
+    enum TCPatternKind kind;    /* match kind, see above */
+    int            nwords;            /* length of words[] array */
+    const char *const *words;    /* array of match words */
+} TCPattern;
+
+/* Macro emitted by gen_tabcomplete.pl to fill a tcpatterns[] entry */
+#define TCPAT(id, kind, ...) \
+    { (id), (kind), VA_ARGS_NARGS(__VA_ARGS__), \
+      (const char * const []) { __VA_ARGS__ } }
+
+static const TCPattern tcpatterns[] =
+{
+    /* Insert tab-completion pattern data here. */
+};
+
 /* Storage parameters for CREATE TABLE and ALTER TABLE */
 static const char *const table_storage_parameters[] = {
     "autovacuum_analyze_scale_factor",
@@ -1357,7 +1398,8 @@ static const char *const view_optional_parameters[] = {

 /* Forward declaration of functions */
 static char **psql_completion(const char *text, int start, int end);
-static char **match_previous_words(const char *text, int start, int end,
+static char **match_previous_words(int pattern_id,
+                                   const char *text, int start, int end,
                                    char **previous_words,
                                    int previous_words_count);
 static char *create_command_generator(const char *text, int state);
@@ -1469,10 +1511,6 @@ initialize_readline(void)
  * just be written directly in patterns.)  There is also MatchAnyN, but that
  * is supported only in Matches/MatchesCS and is not handled here.
  */
-#define MatchAny  NULL
-#define MatchAnyExcept(pattern)  ("!" pattern)
-#define MatchAnyN ""
-
 static bool
 word_matches(const char *pattern,
              const char *word,
@@ -1894,17 +1932,78 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH_LIST(sql_commands);

     /* Else try completions based on matching patterns of previous words */
-    else if ((matches = match_previous_words(text, start, end,
+    else
+    {
+        /*
+         * For now, we have to try the patterns in the order they are stored
+         * (matching the order of switch cases in match_previous_words),
+         * because some of the logic in match_previous_words assumes that
+         * previous matches have been eliminated.  This is fairly
+         * unprincipled, and it is likely that there are undesirable as well
+         * as desirable interactions hidden in the order of the pattern
+         * checks.  TODO: think about a better way to manage that.
+         */
+        for (int tindx = 0; tindx < lengthof(tcpatterns); tindx++)
+        {
+            const TCPattern *tcpat = tcpatterns + tindx;
+            bool        match = false;
+
+            switch (tcpat->kind)
+            {
+                case Match:
+                    match = MatchesArray(false,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case MatchCS:
+                    match = MatchesArray(true,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatch:
+                    match = HeadMatchesArray(false,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatchCS:
+                    match = HeadMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatch:
+                    match = TailMatchesArray(false,
+                                             previous_words_count,
                                              previous_words,
-                                             previous_words_count)) != NULL)
-         /* skip */ ;
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatchCS:
+                    match = TailMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+            }
+            if (match)
+            {
+                matches = match_previous_words(tcpat->id, text, start, end,
+                                               previous_words,
+                                               previous_words_count);
+                if (matches != NULL)
+                    break;
+            }
+        }
+    }

     /*
      * Finally, we look through the list of "things", such as TABLE, INDEX and
      * check if that was the previous word. If so, execute the query to get a
      * list of them.
      */
-    else
+    if (matches == NULL)
     {
         const pgsql_thing_t *wac;

@@ -1963,20 +2062,19 @@ psql_completion(const char *text, int start, int end)
  * Returns a matches list, or NULL if no match.
  */
 static char **
-match_previous_words(const char *text, int start, int end,
+match_previous_words(int pattern_id,
+                     const char *text, int start, int end,
                      char **previous_words, int previous_words_count)
 {
     /* This is the variable we'll return. */
     char      **matches = NULL;

-    /* Dummy, to be replaced by switch */
-    if (0)
-        ;
-
+    /* Examine the pattern identified by the caller. */
+    switch (pattern_id)
+    {
 /* CREATE */
     /* complete with something you can create */
-    else if (TailMatches("CREATE"))
-    {
+        case TailMatches("CREATE"):
         /* only some object types can be created as part of CREATE SCHEMA */
         if (HeadMatches("CREATE", "SCHEMA"))
             COMPLETE_WITH("TABLE", "VIEW", "INDEX", "SEQUENCE", "TRIGGER",
@@ -1984,97 +2082,110 @@ match_previous_words(const char *text, int start, int end,
                           "UNIQUE", "UNLOGGED");
         else
             matches = rl_completion_matches(text, create_command_generator);
-    }
+            break;
     /* complete with something you can create or replace */
-    else if (TailMatches("CREATE", "OR", "REPLACE"))
+        case TailMatches("CREATE", "OR", "REPLACE"):
         COMPLETE_WITH("FUNCTION", "PROCEDURE", "LANGUAGE", "RULE", "VIEW",
                       "AGGREGATE", "TRANSFORM", "TRIGGER");
+            break;

 /* DROP, but not DROP embedded in other commands */
     /* complete with something you can drop */
-    else if (Matches("DROP"))
+        case Matches("DROP"):
         matches = rl_completion_matches(text, drop_command_generator);
+            break;

 /* ALTER */

     /* ALTER TABLE */
-    else if (Matches("ALTER", "TABLE"))
+        case Matches("ALTER", "TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
                                         "ALL IN TABLESPACE");
+            break;

     /* ALTER something */
-    else if (Matches("ALTER"))
+        case Matches("ALTER"):
         matches = rl_completion_matches(text, alter_command_generator);
+            break;
     /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
-    else if (TailMatches("ALL", "IN", "TABLESPACE", MatchAny))
+        case TailMatches("ALL", "IN", "TABLESPACE", MatchAny):
         COMPLETE_WITH("SET TABLESPACE", "OWNED BY");
+            break;
     /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */
-    else if (TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY"))
+        case TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            break;
     /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */
-    else if (TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny))
+        case TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny):
         COMPLETE_WITH("SET TABLESPACE");
+            break;
     /* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> */
-    else if (Matches("ALTER", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
+        case Matches("ALTER", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny):
         COMPLETE_WITH("(");
+            break;
     /* ALTER AGGREGATE <name> (...) */
-    else if (Matches("ALTER", "AGGREGATE", MatchAny, MatchAny))
-    {
+        case Matches("ALTER", "AGGREGATE", MatchAny, MatchAny):
         if (ends_with(prev_wd, ')'))
             COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
         else
             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-    }
+            break;
     /* ALTER FUNCTION <name> (...) */
-    else if (Matches("ALTER", "FUNCTION", MatchAny, MatchAny))
-    {
+        case Matches("ALTER", "FUNCTION", MatchAny, MatchAny):
         if (ends_with(prev_wd, ')'))
             COMPLETE_WITH(Alter_function_options);
         else
             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-    }
+            break;
     /* ALTER PROCEDURE <name> (...) */
-    else if (Matches("ALTER", "PROCEDURE", MatchAny, MatchAny))
-    {
+        case Matches("ALTER", "PROCEDURE", MatchAny, MatchAny):
         if (ends_with(prev_wd, ')'))
             COMPLETE_WITH(Alter_procedure_options);
         else
             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-    }
+            break;
     /* ALTER ROUTINE <name> (...) */
-    else if (Matches("ALTER", "ROUTINE", MatchAny, MatchAny))
-    {
+        case Matches("ALTER", "ROUTINE", MatchAny, MatchAny):
         if (ends_with(prev_wd, ')'))
             COMPLETE_WITH(Alter_routine_options);
         else
             COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-    }
+            break;
     /* ALTER FUNCTION|ROUTINE <name> (...) PARALLEL */
-    else if (Matches("ALTER", "FUNCTION|ROUTINE", MatchAny, MatchAny, "PARALLEL"))
+        case Matches("ALTER", "FUNCTION|ROUTINE", MatchAny, MatchAny, "PARALLEL"):
         COMPLETE_WITH("RESTRICTED", "SAFE", "UNSAFE");
+            break;
     /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) [EXTERNAL] SECURITY */
-    else if (Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "SECURITY") ||
-             Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "EXTERNAL", "SECURITY"))
+        case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "SECURITY"):
+        case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "EXTERNAL", "SECURITY"):
         COMPLETE_WITH("DEFINER", "INVOKER");
+            break;
     /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) RESET */
-    else if (Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "RESET"))
+        case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "RESET"):
         COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
                                           "ALL");
+            break;
     /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) SET */
-    else if (Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "SET"))
+        case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "SET"):
         COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
                                           "SCHEMA");
+            break;

     /* ALTER PUBLICATION <name> */
-    else if (Matches("ALTER", "PUBLICATION", MatchAny))
+        case Matches("ALTER", "PUBLICATION", MatchAny):
         COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
+            break;
     /* ALTER PUBLICATION <name> ADD */
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
+        case Matches("ALTER", "PUBLICATION", MatchAny, "ADD"):
         COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
-             (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
-              ends_with(prev_wd, ',')))
+            break;
+        case Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
+        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;
+        case HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
+        if (ends_with(prev_wd, ','))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;

     /*
      * "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with
@@ -2083,235 +2194,280 @@ match_previous_words(const char *text, int start, int end,
      * "ALTER PUBLICATION <name> ADD TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
+        case Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"):
         COMPLETE_WITH("(");
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
+            break;
+        case Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("):
         COMPLETE_WITH_ATTR(prev3_wd);
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
-             !TailMatches("WHERE", "(*)"))
+            break;
+        case HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
+        if (!TailMatches("WHERE", "(*)"))
         COMPLETE_WITH(",", "WHERE (");
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"))
+            break;
+        case HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
         COMPLETE_WITH(",");
+            break;
     /* ALTER PUBLICATION <name> DROP */
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "DROP"))
+        case Matches("ALTER", "PUBLICATION", MatchAny, "DROP"):
         COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
+            break;
     /* ALTER PUBLICATION <name> SET */
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "SET"))
+        case Matches("ALTER", "PUBLICATION", MatchAny, "SET"):
         COMPLETE_WITH("(", "TABLES IN SCHEMA", "TABLE");
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "TABLES", "IN", "SCHEMA"))
+            break;
+        case Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "TABLES", "IN", "SCHEMA"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
                                  " AND nspname NOT LIKE E'pg\\\\_%%'",
                                  "CURRENT_SCHEMA");
+            break;
     /* ALTER PUBLICATION <name> SET ( */
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "("))
+        case Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "("):
         COMPLETE_WITH("publish", "publish_via_partition_root");
+            break;
     /* ALTER SUBSCRIPTION <name> */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny):
         COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO",
                       "RENAME TO", "REFRESH PUBLICATION", "SET", "SKIP (",
                       "ADD PUBLICATION", "DROP PUBLICATION");
+            break;
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"):
         COMPLETE_WITH("WITH (");
+            break;
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("):
         COMPLETE_WITH("copy_data");
+            break;
     /* ALTER SUBSCRIPTION <name> SET */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"):
         COMPLETE_WITH("(", "PUBLICATION");
+            break;
     /* ALTER SUBSCRIPTION <name> SET ( */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "("))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "("):
         COMPLETE_WITH("binary", "disable_on_error", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
                       "streaming", "synchronous_commit");
+            break;
     /* ALTER SUBSCRIPTION <name> SKIP ( */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "("))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "("):
         COMPLETE_WITH("lsn");
+            break;
     /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "PUBLICATION"))
-    {
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "PUBLICATION"):
         /* complete with nothing here as this refers to remote publications */
-    }
+            break;
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
-                     "ADD|DROP|SET", "PUBLICATION", MatchAny))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "ADD|DROP|SET", "PUBLICATION", MatchAny):
         COMPLETE_WITH("WITH (");
+            break;
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
-    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
-                     "ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
+        case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH",
"("):
         COMPLETE_WITH("copy_data", "refresh");
+            break;

     /* ALTER SCHEMA <name> */
-    else if (Matches("ALTER", "SCHEMA", MatchAny))
+        case Matches("ALTER", "SCHEMA", MatchAny):
         COMPLETE_WITH("OWNER TO", "RENAME TO");
+            break;

     /* ALTER COLLATION <name> */
-    else if (Matches("ALTER", "COLLATION", MatchAny))
+        case Matches("ALTER", "COLLATION", MatchAny):
         COMPLETE_WITH("OWNER TO", "REFRESH VERSION", "RENAME TO", "SET SCHEMA");
+            break;

     /* ALTER CONVERSION <name> */
-    else if (Matches("ALTER", "CONVERSION", MatchAny))
+        case Matches("ALTER", "CONVERSION", MatchAny):
         COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
+            break;

     /* ALTER DATABASE <name> */
-    else if (Matches("ALTER", "DATABASE", MatchAny))
+        case Matches("ALTER", "DATABASE", MatchAny):
         COMPLETE_WITH("RESET", "SET", "OWNER TO", "REFRESH COLLATION VERSION", "RENAME TO",
                       "IS_TEMPLATE", "ALLOW_CONNECTIONS",
                       "CONNECTION LIMIT");
+            break;

     /* ALTER DATABASE <name> SET TABLESPACE */
-    else if (Matches("ALTER", "DATABASE", MatchAny, "SET", "TABLESPACE"))
+        case Matches("ALTER", "DATABASE", MatchAny, "SET", "TABLESPACE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            break;

     /* ALTER EVENT TRIGGER */
-    else if (Matches("ALTER", "EVENT", "TRIGGER"))
+        case Matches("ALTER", "EVENT", "TRIGGER"):
         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            break;

     /* ALTER EVENT TRIGGER <name> */
-    else if (Matches("ALTER", "EVENT", "TRIGGER", MatchAny))
+        case Matches("ALTER", "EVENT", "TRIGGER", MatchAny):
         COMPLETE_WITH("DISABLE", "ENABLE", "OWNER TO", "RENAME TO");
+            break;

     /* ALTER EVENT TRIGGER <name> ENABLE */
-    else if (Matches("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"))
+        case Matches("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"):
         COMPLETE_WITH("REPLICA", "ALWAYS");
+            break;

     /* ALTER EXTENSION <name> */
-    else if (Matches("ALTER", "EXTENSION", MatchAny))
+        case Matches("ALTER", "EXTENSION", MatchAny):
         COMPLETE_WITH("ADD", "DROP", "UPDATE", "SET SCHEMA");
+            break;

     /* ALTER EXTENSION <name> ADD|DROP */
-    else if (Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP"))
+        case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP"):
         COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
                       "CONVERSION", "DOMAIN", "EVENT TRIGGER", "FOREIGN",
                       "FUNCTION", "MATERIALIZED VIEW", "OPERATOR",
                       "LANGUAGE", "PROCEDURE", "ROUTINE", "SCHEMA",
                       "SEQUENCE", "SERVER", "TABLE", "TEXT SEARCH",
                       "TRANSFORM FOR", "TYPE", "VIEW");
+            break;

     /* ALTER EXTENSION <name> ADD|DROP FOREIGN */
-    else if (Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "FOREIGN"))
+        case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "FOREIGN"):
         COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            break;

     /* ALTER EXTENSION <name> ADD|DROP OPERATOR */
-    else if (Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "OPERATOR"))
+        case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "OPERATOR"):
         COMPLETE_WITH("CLASS", "FAMILY");
+            break;

     /* ALTER EXTENSION <name> ADD|DROP TEXT SEARCH */
-    else if (Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "TEXT", "SEARCH"))
+        case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "TEXT", "SEARCH"):
         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            break;

     /* ALTER EXTENSION <name> UPDATE */
-    else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE"))
+        case Matches("ALTER", "EXTENSION", MatchAny, "UPDATE"):
         COMPLETE_WITH("TO");
+            break;

     /* ALTER EXTENSION <name> UPDATE TO */
-    else if (Matches("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"))
-    {
+        case Matches("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
-    }
+            break;

     /* ALTER FOREIGN */
-    else if (Matches("ALTER", "FOREIGN"))
+        case Matches("ALTER", "FOREIGN"):
         COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            break;

     /* ALTER FOREIGN DATA WRAPPER <name> */
-    else if (Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+        case Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny):
         COMPLETE_WITH("HANDLER", "VALIDATOR", "NO",
                       "OPTIONS", "OWNER TO", "RENAME TO");
-    else if (Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny, "NO"))
+            break;
+        case Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny, "NO"):
         COMPLETE_WITH("HANDLER", "VALIDATOR");
+            break;

     /* ALTER FOREIGN TABLE <name> */
-    else if (Matches("ALTER", "FOREIGN", "TABLE", MatchAny))
+        case Matches("ALTER", "FOREIGN", "TABLE", MatchAny):
         COMPLETE_WITH("ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE",
                       "INHERIT", "NO INHERIT", "OPTIONS", "OWNER TO",
                       "RENAME", "SET", "VALIDATE CONSTRAINT");
+            break;

     /* ALTER INDEX */
-    else if (Matches("ALTER", "INDEX"))
+        case Matches("ALTER", "INDEX"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
                                         "ALL IN TABLESPACE");
+            break;
     /* ALTER INDEX <name> */
-    else if (Matches("ALTER", "INDEX", MatchAny))
+        case Matches("ALTER", "INDEX", MatchAny):
         COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET",
                       "RESET", "ATTACH PARTITION",
                       "DEPENDS ON EXTENSION", "NO DEPENDS ON EXTENSION");
-    else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH"))
+            break;
+        case Matches("ALTER", "INDEX", MatchAny, "ATTACH"):
         COMPLETE_WITH("PARTITION");
-    else if (Matches("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION"))
+            break;
+        case Matches("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
+            break;
     /* ALTER INDEX <name> ALTER */
-    else if (Matches("ALTER", "INDEX", MatchAny, "ALTER"))
+        case Matches("ALTER", "INDEX", MatchAny, "ALTER"):
         COMPLETE_WITH("COLUMN");
+            break;
     /* ALTER INDEX <name> ALTER COLUMN */
-    else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN"))
-    {
+        case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(Query_for_list_of_attribute_numbers);
-    }
+            break;
     /* ALTER INDEX <name> ALTER COLUMN <colnum> */
-    else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny))
+        case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny):
         COMPLETE_WITH("SET STATISTICS");
+            break;
     /* ALTER INDEX <name> ALTER COLUMN <colnum> SET */
-    else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET"))
+        case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET"):
         COMPLETE_WITH("STATISTICS");
+            break;
     /* ALTER INDEX <name> ALTER COLUMN <colnum> SET STATISTICS */
-    else if (Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS"))
-    {
+        case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS"):
         /* Enforce no completion here, as an integer has to be specified */
-    }
+            break;
     /* ALTER INDEX <name> SET */
-    else if (Matches("ALTER", "INDEX", MatchAny, "SET"))
+        case Matches("ALTER", "INDEX", MatchAny, "SET"):
         COMPLETE_WITH("(", "TABLESPACE");
+            break;
     /* ALTER INDEX <name> RESET */
-    else if (Matches("ALTER", "INDEX", MatchAny, "RESET"))
+        case Matches("ALTER", "INDEX", MatchAny, "RESET"):
         COMPLETE_WITH("(");
+            break;
     /* ALTER INDEX <foo> SET|RESET ( */
-    else if (Matches("ALTER", "INDEX", MatchAny, "RESET", "("))
+        case Matches("ALTER", "INDEX", MatchAny, "RESET", "("):
         COMPLETE_WITH("fillfactor",
                       "deduplicate_items",    /* BTREE */
                       "fastupdate", "gin_pending_list_limit",    /* GIN */
                       "buffering",    /* GiST */
                       "pages_per_range", "autosummarize"    /* BRIN */
             );
-    else if (Matches("ALTER", "INDEX", MatchAny, "SET", "("))
+            break;
+        case Matches("ALTER", "INDEX", MatchAny, "SET", "("):
         COMPLETE_WITH("fillfactor =",
                       "deduplicate_items =",    /* BTREE */
                       "fastupdate =", "gin_pending_list_limit =",    /* GIN */
                       "buffering =",    /* GiST */
                       "pages_per_range =", "autosummarize ="    /* BRIN */
             );
-    else if (Matches("ALTER", "INDEX", MatchAny, "NO", "DEPENDS"))
+            break;
+        case Matches("ALTER", "INDEX", MatchAny, "NO", "DEPENDS"):
         COMPLETE_WITH("ON EXTENSION");
-    else if (Matches("ALTER", "INDEX", MatchAny, "DEPENDS"))
+            break;
+        case Matches("ALTER", "INDEX", MatchAny, "DEPENDS"):
         COMPLETE_WITH("ON EXTENSION");
+            break;

     /* ALTER LANGUAGE <name> */
-    else if (Matches("ALTER", "LANGUAGE", MatchAny))
+        case Matches("ALTER", "LANGUAGE", MatchAny):
         COMPLETE_WITH("OWNER TO", "RENAME TO");
+            break;

     /* ALTER LARGE OBJECT <oid> */
-    else if (Matches("ALTER", "LARGE", "OBJECT", MatchAny))
+        case Matches("ALTER", "LARGE", "OBJECT", MatchAny):
         COMPLETE_WITH("OWNER TO");
+            break;

     /* ALTER MATERIALIZED VIEW */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW"))
+        case Matches("ALTER", "MATERIALIZED", "VIEW"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
                                         "ALL IN TABLESPACE");
+            break;

     /* ALTER USER,ROLE <name> */
-    else if (Matches("ALTER", "USER|ROLE", MatchAny) &&
-             !TailMatches("USER", "MAPPING"))
+        case Matches("ALTER", "USER|ROLE", MatchAny):
+        if (!TailMatches("USER", "MAPPING"))
         COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
                       "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
                       "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
                       "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
                       "VALID UNTIL", "WITH");
+            break;

     /* ALTER USER,ROLE <name> WITH */
-    else if (Matches("ALTER", "USER|ROLE", MatchAny, "WITH"))
+        case Matches("ALTER", "USER|ROLE", MatchAny, "WITH"):
         /* Similar to the above, but don't complete "WITH" again. */
         COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
                       "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
@@ -2319,210 +2475,254 @@ match_previous_words(const char *text, int start, int end,
                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
                       "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
                       "VALID UNTIL");
+            break;

     /* ALTER DEFAULT PRIVILEGES */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES"))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES"):
         COMPLETE_WITH("FOR", "GRANT", "IN SCHEMA", "REVOKE");
+            break;
     /* ALTER DEFAULT PRIVILEGES FOR */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR"))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR"):
         COMPLETE_WITH("ROLE");
+            break;
     /* ALTER DEFAULT PRIVILEGES IN */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN"))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN"):
         COMPLETE_WITH("SCHEMA");
+            break;
     /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER",
-                     MatchAny))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny):
         COMPLETE_WITH("GRANT", "REVOKE", "IN SCHEMA");
+            break;
     /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA",
-                     MatchAny))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny):
         COMPLETE_WITH("GRANT", "REVOKE", "FOR ROLE");
+            break;
     /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA",
-                     MatchAny, "FOR"))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny, "FOR"):
         COMPLETE_WITH("ROLE");
+            break;
     /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... IN SCHEMA ... */
     /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR ROLE|USER ... */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER",
-                     MatchAny, "IN", "SCHEMA", MatchAny) ||
-             Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA",
-                     MatchAny, "FOR", "ROLE|USER", MatchAny))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny, "IN", "SCHEMA", MatchAny):
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny, "FOR", "ROLE|USER", MatchAny):
         COMPLETE_WITH("GRANT", "REVOKE");
+            break;
     /* ALTER DOMAIN <name> */
-    else if (Matches("ALTER", "DOMAIN", MatchAny))
+        case Matches("ALTER", "DOMAIN", MatchAny):
         COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME", "SET",
                       "VALIDATE CONSTRAINT");
+            break;
     /* ALTER DOMAIN <sth> DROP */
-    else if (Matches("ALTER", "DOMAIN", MatchAny, "DROP"))
+        case Matches("ALTER", "DOMAIN", MatchAny, "DROP"):
         COMPLETE_WITH("CONSTRAINT", "DEFAULT", "NOT NULL");
+            break;
     /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
-    else if (Matches("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"))
-    {
+        case Matches("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_type);
-    }
+            break;
     /* ALTER DOMAIN <sth> RENAME */
-    else if (Matches("ALTER", "DOMAIN", MatchAny, "RENAME"))
+        case Matches("ALTER", "DOMAIN", MatchAny, "RENAME"):
         COMPLETE_WITH("CONSTRAINT", "TO");
+            break;
     /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
-    else if (Matches("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny))
+        case Matches("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny):
         COMPLETE_WITH("TO");
+            break;

     /* ALTER DOMAIN <sth> SET */
-    else if (Matches("ALTER", "DOMAIN", MatchAny, "SET"))
+        case Matches("ALTER", "DOMAIN", MatchAny, "SET"):
         COMPLETE_WITH("DEFAULT", "NOT NULL", "SCHEMA");
+            break;
     /* ALTER SEQUENCE <name> */
-    else if (Matches("ALTER", "SEQUENCE", MatchAny))
+        case Matches("ALTER", "SEQUENCE", MatchAny):
         COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART",
                       "START", "NO", "CACHE", "CYCLE", "SET", "OWNED BY",
                       "OWNER TO", "RENAME TO");
+            break;
     /* ALTER SEQUENCE <name> AS */
-    else if (TailMatches("ALTER", "SEQUENCE", MatchAny, "AS"))
+        case TailMatches("ALTER", "SEQUENCE", MatchAny, "AS"):
         COMPLETE_WITH_CS("smallint", "integer", "bigint");
+            break;
     /* ALTER SEQUENCE <name> NO */
-    else if (Matches("ALTER", "SEQUENCE", MatchAny, "NO"))
+        case Matches("ALTER", "SEQUENCE", MatchAny, "NO"):
         COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+            break;
     /* ALTER SEQUENCE <name> SET */
-    else if (Matches("ALTER", "SEQUENCE", MatchAny, "SET"))
+        case Matches("ALTER", "SEQUENCE", MatchAny, "SET"):
         COMPLETE_WITH("SCHEMA", "LOGGED", "UNLOGGED");
+            break;
     /* ALTER SERVER <name> */
-    else if (Matches("ALTER", "SERVER", MatchAny))
+        case Matches("ALTER", "SERVER", MatchAny):
         COMPLETE_WITH("VERSION", "OPTIONS", "OWNER TO", "RENAME TO");
+            break;
     /* ALTER SERVER <name> VERSION <version> */
-    else if (Matches("ALTER", "SERVER", MatchAny, "VERSION", MatchAny))
+        case Matches("ALTER", "SERVER", MatchAny, "VERSION", MatchAny):
         COMPLETE_WITH("OPTIONS");
+            break;
     /* ALTER SYSTEM SET, RESET, RESET ALL */
-    else if (Matches("ALTER", "SYSTEM"))
+        case Matches("ALTER", "SYSTEM"):
         COMPLETE_WITH("SET", "RESET");
-    else if (Matches("ALTER", "SYSTEM", "SET|RESET"))
+            break;
+        case Matches("ALTER", "SYSTEM", "SET|RESET"):
         COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_set_vars,
                                           "ALL");
-    else if (Matches("ALTER", "SYSTEM", "SET", MatchAny))
+            break;
+        case Matches("ALTER", "SYSTEM", "SET", MatchAny):
         COMPLETE_WITH("TO");
+            break;
     /* ALTER VIEW <name> */
-    else if (Matches("ALTER", "VIEW", MatchAny))
+        case Matches("ALTER", "VIEW", MatchAny):
         COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", "RESET", "SET");
+            break;
     /* ALTER VIEW xxx RENAME */
-    else if (Matches("ALTER", "VIEW", MatchAny, "RENAME"))
+        case Matches("ALTER", "VIEW", MatchAny, "RENAME"):
         COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
-    else if (Matches("ALTER", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"))
+            break;
+        case Matches("ALTER", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"):
         COMPLETE_WITH_ATTR(prev3_wd);
+            break;
     /* ALTER VIEW xxx ALTER [ COLUMN ] yyy */
-    else if (Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny) ||
-             Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny))
+        case Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny):
+        case Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny):
         COMPLETE_WITH("SET DEFAULT", "DROP DEFAULT");
+            break;
     /* ALTER VIEW xxx RENAME yyy */
-    else if (Matches("ALTER", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")))
+        case Matches("ALTER", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")):
         COMPLETE_WITH("TO");
+            break;
     /* ALTER VIEW xxx RENAME COLUMN yyy */
-    else if (Matches("ALTER", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")))
+        case Matches("ALTER", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")):
         COMPLETE_WITH("TO");
+            break;
     /* ALTER VIEW xxx RESET ( */
-    else if (Matches("ALTER", "VIEW", MatchAny, "RESET"))
+        case Matches("ALTER", "VIEW", MatchAny, "RESET"):
         COMPLETE_WITH("(");
+            break;
     /* Complete ALTER VIEW xxx SET with "(" or "SCHEMA" */
-    else if (Matches("ALTER", "VIEW", MatchAny, "SET"))
+        case Matches("ALTER", "VIEW", MatchAny, "SET"):
         COMPLETE_WITH("(", "SCHEMA");
+            break;
     /* ALTER VIEW xxx SET|RESET ( yyy [= zzz] ) */
-    else if (Matches("ALTER", "VIEW", MatchAny, "SET|RESET", "("))
+        case Matches("ALTER", "VIEW", MatchAny, "SET|RESET", "("):
         COMPLETE_WITH_LIST(view_optional_parameters);
-    else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", MatchAny))
+            break;
+        case Matches("ALTER", "VIEW", MatchAny, "SET", "(", MatchAny):
         COMPLETE_WITH("=");
-    else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", "check_option", "="))
+            break;
+        case Matches("ALTER", "VIEW", MatchAny, "SET", "(", "check_option", "="):
         COMPLETE_WITH("local", "cascaded");
-    else if (Matches("ALTER", "VIEW", MatchAny, "SET", "(", "security_barrier|security_invoker", "="))
+            break;
+        case Matches("ALTER", "VIEW", MatchAny, "SET", "(", "security_barrier|security_invoker", "="):
         COMPLETE_WITH("true", "false");
+            break;

     /* ALTER MATERIALIZED VIEW <name> */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny))
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny):
         COMPLETE_WITH("ALTER COLUMN", "CLUSTER ON", "DEPENDS ON EXTENSION",
                       "NO DEPENDS ON EXTENSION", "OWNER TO", "RENAME",
                       "RESET (", "SET");
+            break;
     /* ALTER MATERIALIZED VIEW xxx RENAME */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME"))
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME"):
         COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"))
+            break;
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"):
         COMPLETE_WITH_ATTR(prev3_wd);
+            break;
     /* ALTER MATERIALIZED VIEW xxx RENAME yyy */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")))
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")):
         COMPLETE_WITH("TO");
+            break;
     /* ALTER MATERIALIZED VIEW xxx RENAME COLUMN yyy */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")))
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")):
         COMPLETE_WITH("TO");
+            break;
     /* ALTER MATERIALIZED VIEW xxx SET */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET"))
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET"):
         COMPLETE_WITH("(", "ACCESS METHOD", "SCHEMA", "TABLESPACE", "WITHOUT CLUSTER");
+            break;
     /* ALTER MATERIALIZED VIEW xxx SET ACCESS METHOD */
-    else if (Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET", "ACCESS", "METHOD"))
+        case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET", "ACCESS", "METHOD"):
         COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
+            break;

     /* ALTER POLICY <name> */
-    else if (Matches("ALTER", "POLICY"))
+        case Matches("ALTER", "POLICY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+            break;
     /* ALTER POLICY <name> ON */
-    else if (Matches("ALTER", "POLICY", MatchAny))
+        case Matches("ALTER", "POLICY", MatchAny):
         COMPLETE_WITH("ON");
+            break;
     /* ALTER POLICY <name> ON <table> */
-    else if (Matches("ALTER", "POLICY", MatchAny, "ON"))
-    {
+        case Matches("ALTER", "POLICY", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
-    }
+            break;
     /* ALTER POLICY <name> ON <table> - show options */
-    else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny))
+        case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("RENAME TO", "TO", "USING (", "WITH CHECK (");
+            break;
     /* ALTER POLICY <name> ON <table> TO <role> */
-    else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"))
+        case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
+            break;
     /* ALTER POLICY <name> ON <table> USING ( */
-    else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"))
+        case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"):
         COMPLETE_WITH("(");
+            break;
     /* ALTER POLICY <name> ON <table> WITH CHECK ( */
-    else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
+        case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"):
         COMPLETE_WITH("(");
+            break;

     /* ALTER RULE <name>, add ON */
-    else if (Matches("ALTER", "RULE", MatchAny))
+        case Matches("ALTER", "RULE", MatchAny):
         COMPLETE_WITH("ON");
+            break;

     /* If we have ALTER RULE <name> ON, then add the correct tablename */
-    else if (Matches("ALTER", "RULE", MatchAny, "ON"))
-    {
+        case Matches("ALTER", "RULE", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
-    }
+            break;

     /* ALTER RULE <name> ON <name> */
-    else if (Matches("ALTER", "RULE", MatchAny, "ON", MatchAny))
+        case Matches("ALTER", "RULE", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("RENAME TO");
+            break;

     /* ALTER STATISTICS <name> */
-    else if (Matches("ALTER", "STATISTICS", MatchAny))
+        case Matches("ALTER", "STATISTICS", MatchAny):
         COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA", "SET STATISTICS");
+            break;
     /* ALTER STATISTICS <name> SET */
-    else if (Matches("ALTER", "STATISTICS", MatchAny, "SET"))
+        case Matches("ALTER", "STATISTICS", MatchAny, "SET"):
         COMPLETE_WITH("SCHEMA", "STATISTICS");
+            break;

     /* ALTER TRIGGER <name>, add ON */
-    else if (Matches("ALTER", "TRIGGER", MatchAny))
+        case Matches("ALTER", "TRIGGER", MatchAny):
         COMPLETE_WITH("ON");
+            break;

-    else if (Matches("ALTER", "TRIGGER", MatchAny, "ON"))
-    {
+        case Matches("ALTER", "TRIGGER", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
-    }
+            break;

     /* ALTER TRIGGER <name> ON <name> */
-    else if (Matches("ALTER", "TRIGGER", MatchAny, "ON", MatchAny))
+        case Matches("ALTER", "TRIGGER", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("RENAME TO", "DEPENDS ON EXTENSION",
                       "NO DEPENDS ON EXTENSION");
+            break;

     /*
      * If we detect ALTER TABLE <name>, suggest sub commands
      */
-    else if (Matches("ALTER", "TABLE", MatchAny))
+        case Matches("ALTER", "TABLE", MatchAny):
         COMPLETE_WITH("ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP",
                       "ENABLE", "INHERIT", "NO", "RENAME", "RESET",
                       "OWNER TO", "SET", "VALIDATE CONSTRAINT",
@@ -2530,366 +2730,412 @@ match_previous_words(const char *text, int start, int end,
                       "DETACH PARTITION", "FORCE ROW LEVEL SECURITY",
                       "SPLIT PARTITION", "MERGE PARTITIONS (",
                       "OF", "NOT OF");
+            break;
     /* ALTER TABLE xxx ADD */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ADD"):
         /* make sure to keep this list and the !Matches() below in sync */
         COMPLETE_WITH("COLUMN", "CONSTRAINT", "CHECK", "UNIQUE", "PRIMARY KEY",
                       "EXCLUDE", "FOREIGN KEY");
-    }
+            break;
     /* ALTER TABLE xxx ADD [COLUMN] yyy */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny) ||
-             (Matches("ALTER", "TABLE", MatchAny, "ADD", MatchAny) &&
-              !Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny):
+        case Matches("ALTER", "TABLE", MatchAny, "ADD",
MatchAnyExcept("COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            break;
     /* ALTER TABLE xxx ADD CONSTRAINT yyy */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny))
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny):
         COMPLETE_WITH("CHECK", "UNIQUE", "PRIMARY KEY", "EXCLUDE", "FOREIGN KEY");
+            break;
     /* ALTER TABLE xxx ADD [CONSTRAINT yyy] (PRIMARY KEY|UNIQUE) */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY") ||
-             Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE") ||
-             Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "PRIMARY", "KEY") ||
-             Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "UNIQUE"))
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY"):
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE"):
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "PRIMARY", "KEY"):
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "UNIQUE"):
         COMPLETE_WITH("(", "USING INDEX");
+            break;
     /* ALTER TABLE xxx ADD PRIMARY KEY USING INDEX */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY", "USING", "INDEX"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY", "USING", "INDEX"):
         set_completion_reference(prev6_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
-    }
+            break;
     /* ALTER TABLE xxx ADD UNIQUE USING INDEX */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE", "USING", "INDEX"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE", "USING", "INDEX"):
         set_completion_reference(prev5_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
-    }
+            break;
     /* ALTER TABLE xxx ADD CONSTRAINT yyy PRIMARY KEY USING INDEX */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny,
-                     "PRIMARY", "KEY", "USING", "INDEX"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "PRIMARY", "KEY", "USING", "INDEX"):
         set_completion_reference(prev8_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
-    }
+            break;
     /* ALTER TABLE xxx ADD CONSTRAINT yyy UNIQUE USING INDEX */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny,
-                     "UNIQUE", "USING", "INDEX"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "UNIQUE", "USING", "INDEX"):
         set_completion_reference(prev7_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
-    }
+            break;
     /* ALTER TABLE xxx ENABLE */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE"))
+        case Matches("ALTER", "TABLE", MatchAny, "ENABLE"):
         COMPLETE_WITH("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE",
                       "TRIGGER");
-    else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"):
         COMPLETE_WITH("RULE", "TRIGGER");
-    else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"):
         set_completion_reference(prev4_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"):
         set_completion_reference(prev4_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
-    }
+            break;
     /* ALTER TABLE xxx INHERIT */
-    else if (Matches("ALTER", "TABLE", MatchAny, "INHERIT"))
+        case Matches("ALTER", "TABLE", MatchAny, "INHERIT"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;
     /* ALTER TABLE xxx NO */
-    else if (Matches("ALTER", "TABLE", MatchAny, "NO"))
+        case Matches("ALTER", "TABLE", MatchAny, "NO"):
         COMPLETE_WITH("FORCE ROW LEVEL SECURITY", "INHERIT");
+            break;
     /* ALTER TABLE xxx NO INHERIT */
-    else if (Matches("ALTER", "TABLE", MatchAny, "NO", "INHERIT"))
+        case Matches("ALTER", "TABLE", MatchAny, "NO", "INHERIT"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;
     /* ALTER TABLE xxx DISABLE */
-    else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE"))
+        case Matches("ALTER", "TABLE", MatchAny, "DISABLE"):
         COMPLETE_WITH("ROW LEVEL SECURITY", "RULE", "TRIGGER");
-    else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
-    }
+            break;

     /* ALTER TABLE xxx ALTER */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER"):
         COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT");
+            break;

     /* ALTER TABLE xxx RENAME */
-    else if (Matches("ALTER", "TABLE", MatchAny, "RENAME"))
+        case Matches("ALTER", "TABLE", MatchAny, "RENAME"):
         COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT", "TO");
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"):
         COMPLETE_WITH_ATTR(prev3_wd);
+            break;

     /* ALTER TABLE xxx RENAME yyy */
-    else if (Matches("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")))
+        case Matches("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")):
         COMPLETE_WITH("TO");
+            break;

     /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
-    else if (Matches("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")))
+        case Matches("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")):
         COMPLETE_WITH("TO");
+            break;

     /* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
-    else if (Matches("ALTER", "TABLE", MatchAny, "DROP"))
+        case Matches("ALTER", "TABLE", MatchAny, "DROP"):
         COMPLETE_WITH("COLUMN", "CONSTRAINT");
+            break;
     /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
-    else if (Matches("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"))
+        case Matches("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"):
         COMPLETE_WITH_ATTR(prev3_wd);
+            break;
     /* ALTER TABLE <sth> ALTER|DROP|RENAME CONSTRAINT <constraint> */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME", "CONSTRAINT"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME", "CONSTRAINT"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table);
-    }
+            break;
     /* ALTER TABLE <sth> VALIDATE CONSTRAINT <non-validated constraint> */
-    else if (Matches("ALTER", "TABLE", MatchAny, "VALIDATE", "CONSTRAINT"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "VALIDATE", "CONSTRAINT"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table_not_validated);
-    }
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny) ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny):
         COMPLETE_WITH("TYPE", "SET", "RESET", "RESTART", "ADD", "DROP");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> ADD */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD"):
         COMPLETE_WITH("GENERATED");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED"):
         COMPLETE_WITH("ALWAYS", "BY DEFAULT");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED", "ALWAYS") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED", "ALWAYS") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED", "BY", "DEFAULT") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED", "BY", "DEFAULT"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED", "ALWAYS"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED", "ALWAYS"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED", "BY", "DEFAULT"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED", "BY", "DEFAULT"):
         COMPLETE_WITH("AS IDENTITY");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"):
         COMPLETE_WITH("(", "COMPRESSION", "DATA TYPE", "DEFAULT", "EXPRESSION", "GENERATED", "NOT NULL",
                       "STATISTICS", "STORAGE",
         /* a subset of ALTER SEQUENCE options */
                       "INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "(") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "("):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("):
         COMPLETE_WITH("n_distinct", "n_distinct_inherited");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET COMPRESSION */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION"):
         COMPLETE_WITH("DEFAULT", "PGLZ", "LZ4");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION"):
         COMPLETE_WITH("AS");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION AS */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION", "AS") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION", "AS"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION", "AS"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION", "AS"):
         COMPLETE_WITH("(");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET GENERATED */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "GENERATED") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "GENERATED"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "GENERATED"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "GENERATED"):
         COMPLETE_WITH("ALWAYS", "BY DEFAULT");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET NO */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "NO") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "NO"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "NO"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "NO"):
         COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"):
         COMPLETE_WITH("DEFAULT", "PLAIN", "EXTERNAL", "EXTENDED", "MAIN");
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> SET STATISTICS */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STATISTICS"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STATISTICS"):
         /* Enforce no completion here, as an integer has to be specified */
-    }
+            break;
     /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP") ||
-             Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "DROP"))
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP"):
+        case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "DROP"):
         COMPLETE_WITH("DEFAULT", "EXPRESSION", "IDENTITY", "NOT NULL");
-    else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "CLUSTER"):
         COMPLETE_WITH("ON");
-    else if (Matches("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
-    }
+            break;
     /* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
-    else if (Matches("ALTER", "TABLE", MatchAny, "SET"))
+        case Matches("ALTER", "TABLE", MatchAny, "SET"):
         COMPLETE_WITH("(", "ACCESS METHOD", "LOGGED", "SCHEMA",
                       "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT");
+            break;

     /*
      * If we have ALTER TABLE <sth> SET ACCESS METHOD provide a list of table
      * AMs.
      */
-    else if (Matches("ALTER", "TABLE", MatchAny, "SET", "ACCESS", "METHOD"))
+        case Matches("ALTER", "TABLE", MatchAny, "SET", "ACCESS", "METHOD"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_table_access_methods,
                                  "DEFAULT");
+            break;

     /*
      * If we have ALTER TABLE <sth> SET TABLESPACE provide a list of
      * tablespaces
      */
-    else if (Matches("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"))
+        case Matches("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            break;
     /* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
-    else if (Matches("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"))
+        case Matches("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"):
         COMPLETE_WITH("CLUSTER", "OIDS");
+            break;
     /* ALTER TABLE <foo> RESET */
-    else if (Matches("ALTER", "TABLE", MatchAny, "RESET"))
+        case Matches("ALTER", "TABLE", MatchAny, "RESET"):
         COMPLETE_WITH("(");
+            break;
     /* ALTER TABLE <foo> SET|RESET ( */
-    else if (Matches("ALTER", "TABLE", MatchAny, "SET|RESET", "("))
+        case Matches("ALTER", "TABLE", MatchAny, "SET|RESET", "("):
         COMPLETE_WITH_LIST(table_storage_parameters);
-    else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"))
-    {
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"):
         set_completion_reference(prev5_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"):
         COMPLETE_WITH("INDEX");
-    else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"):
         COMPLETE_WITH("FULL", "NOTHING", "DEFAULT", "USING");
-    else if (Matches("ALTER", "TABLE", MatchAny, "REPLICA"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "REPLICA"):
         COMPLETE_WITH("IDENTITY");
+            break;

     /*
      * If we have ALTER TABLE <foo> ATTACH PARTITION, provide a list of
      * tables.
      */
-    else if (Matches("ALTER", "TABLE", MatchAny, "ATTACH", "PARTITION"))
+        case Matches("ALTER", "TABLE", MatchAny, "ATTACH", "PARTITION"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;
     /* Limited completion support for partition bound specification */
-    else if (TailMatches("ATTACH", "PARTITION", MatchAny))
+        case TailMatches("ATTACH", "PARTITION", MatchAny):
         COMPLETE_WITH("FOR VALUES", "DEFAULT");
-    else if (TailMatches("FOR", "VALUES"))
+            break;
+        case TailMatches("FOR", "VALUES"):
         COMPLETE_WITH("FROM (", "IN (", "WITH (");
+            break;

     /*
      * If we have ALTER TABLE <foo> DETACH|SPLIT PARTITION, provide a list of
      * partitions of <foo>.
      */
-    else if (Matches("ALTER", "TABLE", MatchAny, "DETACH|SPLIT", "PARTITION"))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "DETACH|SPLIT", "PARTITION"):
         set_completion_reference(prev3_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny):
         COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
+            break;

     /* ALTER TABLE <name> SPLIT PARTITION <name> */
-    else if (Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny))
+        case Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny):
         COMPLETE_WITH("INTO ( PARTITION");
+            break;

     /* ALTER TABLE <name> MERGE PARTITIONS ( */
-    else if (Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "("))
-    {
+        case Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "("):
         set_completion_reference(prev4_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
-    }
-    else if (Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "(*)"))
+            break;
+        case Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "(*)"):
         COMPLETE_WITH("INTO");
+            break;

     /* ALTER TABLE <name> OF */
-    else if (Matches("ALTER", "TABLE", MatchAny, "OF"))
+        case Matches("ALTER", "TABLE", MatchAny, "OF"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
+            break;

     /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
-    else if (Matches("ALTER", "TABLESPACE", MatchAny))
+        case Matches("ALTER", "TABLESPACE", MatchAny):
         COMPLETE_WITH("RENAME TO", "OWNER TO", "SET", "RESET");
+            break;
     /* ALTER TABLESPACE <foo> SET|RESET */
-    else if (Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET"))
+        case Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET"):
         COMPLETE_WITH("(");
+            break;
     /* ALTER TABLESPACE <foo> SET|RESET ( */
-    else if (Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("))
+        case Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("):
         COMPLETE_WITH("seq_page_cost", "random_page_cost",
                       "effective_io_concurrency", "maintenance_io_concurrency");
+            break;

     /* ALTER TEXT SEARCH */
-    else if (Matches("ALTER", "TEXT", "SEARCH"))
+        case Matches("ALTER", "TEXT", "SEARCH"):
         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-    else if (Matches("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny))
+            break;
+        case Matches("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny):
         COMPLETE_WITH("RENAME TO", "SET SCHEMA");
-    else if (Matches("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny))
+            break;
+        case Matches("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny):
         COMPLETE_WITH("(", "OWNER TO", "RENAME TO", "SET SCHEMA");
-    else if (Matches("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny))
+            break;
+        case Matches("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny):
         COMPLETE_WITH("ADD MAPPING FOR", "ALTER MAPPING",
                       "DROP MAPPING FOR",
                       "OWNER TO", "RENAME TO", "SET SCHEMA");
+            break;

     /* complete ALTER TYPE <foo> with actions */
-    else if (Matches("ALTER", "TYPE", MatchAny))
+        case Matches("ALTER", "TYPE", MatchAny):
         COMPLETE_WITH("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE",
                       "DROP ATTRIBUTE",
                       "OWNER TO", "RENAME", "SET SCHEMA", "SET (");
+            break;
     /* complete ALTER TYPE <foo> ADD with actions */
-    else if (Matches("ALTER", "TYPE", MatchAny, "ADD"))
+        case Matches("ALTER", "TYPE", MatchAny, "ADD"):
         COMPLETE_WITH("ATTRIBUTE", "VALUE");
+            break;
     /* ALTER TYPE <foo> RENAME    */
-    else if (Matches("ALTER", "TYPE", MatchAny, "RENAME"))
+        case Matches("ALTER", "TYPE", MatchAny, "RENAME"):
         COMPLETE_WITH("ATTRIBUTE", "TO", "VALUE");
+            break;
     /* ALTER TYPE xxx RENAME (ATTRIBUTE|VALUE) yyy */
-    else if (Matches("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE|VALUE", MatchAny))
+        case Matches("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE|VALUE", MatchAny):
         COMPLETE_WITH("TO");
+            break;

     /*
      * If we have ALTER TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list
      * of attributes
      */
-    else if (Matches("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"))
+        case Matches("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"):
         COMPLETE_WITH_ATTR(prev3_wd);
+            break;
     /* ALTER TYPE ALTER ATTRIBUTE <foo> */
-    else if (Matches("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny))
+        case Matches("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny):
         COMPLETE_WITH("TYPE");
+            break;
     /* complete ALTER TYPE <sth> RENAME VALUE with list of enum values */
-    else if (Matches("ALTER", "TYPE", MatchAny, "RENAME", "VALUE"))
+        case Matches("ALTER", "TYPE", MatchAny, "RENAME", "VALUE"):
         COMPLETE_WITH_ENUM_VALUE(prev3_wd);
+            break;
     /* ALTER TYPE <foo> SET */
-    else if (Matches("ALTER", "TYPE", MatchAny, "SET"))
+        case Matches("ALTER", "TYPE", MatchAny, "SET"):
         COMPLETE_WITH("(", "SCHEMA");
+            break;
     /* complete ALTER TYPE <foo> SET ( with settable properties */
-    else if (Matches("ALTER", "TYPE", MatchAny, "SET", "("))
+        case Matches("ALTER", "TYPE", MatchAny, "SET", "("):
         COMPLETE_WITH("ANALYZE", "RECEIVE", "SEND", "STORAGE", "SUBSCRIPT",
                       "TYPMOD_IN", "TYPMOD_OUT");
+            break;

     /* complete ALTER GROUP <foo> */
-    else if (Matches("ALTER", "GROUP", MatchAny))
+        case Matches("ALTER", "GROUP", MatchAny):
         COMPLETE_WITH("ADD USER", "DROP USER", "RENAME TO");
+            break;
     /* complete ALTER GROUP <foo> ADD|DROP with USER */
-    else if (Matches("ALTER", "GROUP", MatchAny, "ADD|DROP"))
+        case Matches("ALTER", "GROUP", MatchAny, "ADD|DROP"):
         COMPLETE_WITH("USER");
+            break;
     /* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
-    else if (Matches("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"))
+        case Matches("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            break;

 /*
  * ANALYZE [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
  * ANALYZE [ VERBOSE ] [ table_and_columns [, ...] ]
  */
-    else if (Matches("ANALYZE"))
+        case Matches("ANALYZE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,
                                         "VERBOSE");
-    else if (HeadMatches("ANALYZE", "(*") &&
-             !HeadMatches("ANALYZE", "(*)"))
+            break;
+        case HeadMatches("ANALYZE", "(*"):
+        if (!HeadMatches("ANALYZE", "(*)"))
     {
         /*
          * This fires if we're in an unfinished parenthesized option list.
@@ -2901,60 +3147,75 @@ match_previous_words(const char *text, int start, int end,
         else if (TailMatches("VERBOSE|SKIP_LOCKED"))
             COMPLETE_WITH("ON", "OFF");
     }
-    else if (Matches("ANALYZE", MatchAnyN, "("))
+            break;
+        case Matches("ANALYZE", MatchAnyN, "("):
         /* "ANALYZE (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
-    else if (HeadMatches("ANALYZE"))
+            break;
+        case HeadMatches("ANALYZE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables);
+            break;

 /* BEGIN */
-    else if (Matches("BEGIN"))
+        case Matches("BEGIN"):
         COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
+            break;
 /* END, ABORT */
-    else if (Matches("END|ABORT"))
+        case Matches("END|ABORT"):
         COMPLETE_WITH("AND", "WORK", "TRANSACTION");
+            break;
 /* COMMIT */
-    else if (Matches("COMMIT"))
+        case Matches("COMMIT"):
         COMPLETE_WITH("AND", "WORK", "TRANSACTION", "PREPARED");
+            break;
 /* RELEASE SAVEPOINT */
-    else if (Matches("RELEASE"))
+        case Matches("RELEASE"):
         COMPLETE_WITH("SAVEPOINT");
+            break;
 /* ROLLBACK */
-    else if (Matches("ROLLBACK"))
+        case Matches("ROLLBACK"):
         COMPLETE_WITH("AND", "WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
-    else if (Matches("ABORT|END|COMMIT|ROLLBACK", "AND"))
+            break;
+        case Matches("ABORT|END|COMMIT|ROLLBACK", "AND"):
         COMPLETE_WITH("CHAIN");
+            break;
 /* CALL */
-    else if (Matches("CALL"))
+        case Matches("CALL"):
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
-    else if (Matches("CALL", MatchAny))
+            break;
+        case Matches("CALL", MatchAny):
         COMPLETE_WITH("(");
+            break;
 /* CLOSE */
-    else if (Matches("CLOSE"))
+        case Matches("CLOSE"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
                                  "ALL");
+            break;
 /* CLUSTER */
-    else if (Matches("CLUSTER"))
+        case Matches("CLUSTER"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,
                                         "VERBOSE");
-    else if (Matches("CLUSTER", "VERBOSE") ||
-             Matches("CLUSTER", "(*)"))
+            break;
+        case Matches("CLUSTER", "VERBOSE"):
+        case Matches("CLUSTER", "(*)"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables);
+            break;
     /* If we have CLUSTER <sth>, then add "USING" */
-    else if (Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON|(|(*)")))
+        case Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON|(|(*)")):
         COMPLETE_WITH("USING");
+            break;
     /* If we have CLUSTER VERBOSE <sth>, then add "USING" */
-    else if (Matches("CLUSTER", "VERBOSE|(*)", MatchAny))
+        case Matches("CLUSTER", "VERBOSE|(*)", MatchAny):
         COMPLETE_WITH("USING");
+            break;
     /* If we have CLUSTER <sth> USING, then add the index as well */
-    else if (Matches("CLUSTER", MatchAny, "USING") ||
-             Matches("CLUSTER", "VERBOSE|(*)", MatchAny, "USING"))
-    {
+        case Matches("CLUSTER", MatchAny, "USING"):
+        case Matches("CLUSTER", "VERBOSE|(*)", MatchAny, "USING"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
-    }
-    else if (HeadMatches("CLUSTER", "(*") &&
-             !HeadMatches("CLUSTER", "(*)"))
+            break;
+        case HeadMatches("CLUSTER", "(*"):
+        if (!HeadMatches("CLUSTER", "(*)"))
     {
         /*
          * This fires if we're in an unfinished parenthesized option list.
@@ -2964,11 +3225,13 @@ match_previous_words(const char *text, int start, int end,
         if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
             COMPLETE_WITH("VERBOSE");
     }
+            break;

 /* COMMENT */
-    else if (Matches("COMMENT"))
+        case Matches("COMMENT"):
         COMPLETE_WITH("ON");
-    else if (Matches("COMMENT", "ON"))
+            break;
+        case Matches("COMMENT", "ON"):
         COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
                       "COLUMN", "CONSTRAINT", "CONVERSION", "DATABASE",
                       "DOMAIN", "EXTENSION", "EVENT TRIGGER",
@@ -2980,77 +3243,94 @@ match_previous_words(const char *text, int start, int end,
                       "STATISTICS", "SUBSCRIPTION", "TABLE",
                       "TABLESPACE", "TEXT SEARCH", "TRANSFORM FOR",
                       "TRIGGER", "TYPE", "VIEW");
-    else if (Matches("COMMENT", "ON", "ACCESS", "METHOD"))
+            break;
+        case Matches("COMMENT", "ON", "ACCESS", "METHOD"):
         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
-    else if (Matches("COMMENT", "ON", "CONSTRAINT"))
+            break;
+        case Matches("COMMENT", "ON", "CONSTRAINT"):
         COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
-    else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny))
+            break;
+        case Matches("COMMENT", "ON", "CONSTRAINT", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"))
-    {
+            break;
+        case Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables_for_constraint,
                                         "DOMAIN");
-    }
-    else if (Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON", "DOMAIN"))
+            break;
+        case Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON", "DOMAIN"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
-    else if (Matches("COMMENT", "ON", "EVENT", "TRIGGER"))
+            break;
+        case Matches("COMMENT", "ON", "EVENT", "TRIGGER"):
         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
-    else if (Matches("COMMENT", "ON", "FOREIGN"))
+            break;
+        case Matches("COMMENT", "ON", "FOREIGN"):
         COMPLETE_WITH("DATA WRAPPER", "TABLE");
-    else if (Matches("COMMENT", "ON", "FOREIGN", "TABLE"))
+            break;
+        case Matches("COMMENT", "ON", "FOREIGN", "TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
-    else if (Matches("COMMENT", "ON", "MATERIALIZED", "VIEW"))
+            break;
+        case Matches("COMMENT", "ON", "MATERIALIZED", "VIEW"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
-    else if (Matches("COMMENT", "ON", "POLICY"))
+            break;
+        case Matches("COMMENT", "ON", "POLICY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_policies);
-    else if (Matches("COMMENT", "ON", "POLICY", MatchAny))
+            break;
+        case Matches("COMMENT", "ON", "POLICY", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("COMMENT", "ON", "POLICY", MatchAny, "ON"))
-    {
+            break;
+        case Matches("COMMENT", "ON", "POLICY", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
-    }
-    else if (Matches("COMMENT", "ON", "PROCEDURAL", "LANGUAGE"))
+            break;
+        case Matches("COMMENT", "ON", "PROCEDURAL", "LANGUAGE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-    else if (Matches("COMMENT", "ON", "RULE", MatchAny))
+            break;
+        case Matches("COMMENT", "ON", "RULE", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("COMMENT", "ON", "RULE", MatchAny, "ON"))
-    {
+            break;
+        case Matches("COMMENT", "ON", "RULE", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
-    }
-    else if (Matches("COMMENT", "ON", "TEXT", "SEARCH"))
+            break;
+        case Matches("COMMENT", "ON", "TEXT", "SEARCH"):
         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-    else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "CONFIGURATION"))
+            break;
+        case Matches("COMMENT", "ON", "TEXT", "SEARCH", "CONFIGURATION"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
-    else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "DICTIONARY"))
+            break;
+        case Matches("COMMENT", "ON", "TEXT", "SEARCH", "DICTIONARY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
-    else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "PARSER"))
+            break;
+        case Matches("COMMENT", "ON", "TEXT", "SEARCH", "PARSER"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
-    else if (Matches("COMMENT", "ON", "TEXT", "SEARCH", "TEMPLATE"))
+            break;
+        case Matches("COMMENT", "ON", "TEXT", "SEARCH", "TEMPLATE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
-    else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR"))
+            break;
+        case Matches("COMMENT", "ON", "TRANSFORM", "FOR"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny))
+            break;
+        case Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny):
         COMPLETE_WITH("LANGUAGE");
-    else if (Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
-    {
+            break;
+        case Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-    }
-    else if (Matches("COMMENT", "ON", "TRIGGER", MatchAny))
+            break;
+        case Matches("COMMENT", "ON", "TRIGGER", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("COMMENT", "ON", "TRIGGER", MatchAny, "ON"))
-    {
+            break;
+        case Matches("COMMENT", "ON", "TRIGGER", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
-    }
-    else if (Matches("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")) ||
-             Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")) ||
-             Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")) ||
-             Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")))
+            break;
+        case Matches("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")):
+        case Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")):
+        case Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")):
+        case Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")):
         COMPLETE_WITH("IS");
+            break;

 /* COPY */

@@ -3058,77 +3338,89 @@ match_previous_words(const char *text, int start, int end,
      * If we have COPY, offer list of tables or "(" (Also cover the analogous
      * backslash command).
      */
-    else if (Matches("COPY|\\copy"))
+        case Matches("COPY|\\copy"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables, "(");
+            break;
     /* Complete COPY ( with legal query commands */
-    else if (Matches("COPY|\\copy", "("))
+        case Matches("COPY|\\copy", "("):
         COMPLETE_WITH("SELECT", "TABLE", "VALUES", "INSERT INTO", "UPDATE", "DELETE FROM", "WITH");
+            break;
     /* Complete COPY <sth> */
-    else if (Matches("COPY|\\copy", MatchAny))
+        case Matches("COPY|\\copy", MatchAny):
         COMPLETE_WITH("FROM", "TO");
+            break;
     /* Complete COPY <sth> FROM|TO with filename */
-    else if (Matches("COPY", MatchAny, "FROM|TO"))
-    {
+        case Matches("COPY", MatchAny, "FROM|TO"):
         completion_charp = "";
         completion_force_quote = true;    /* COPY requires quoted filename */
         matches = rl_completion_matches(text, complete_from_files);
-    }
-    else if (Matches("\\copy", MatchAny, "FROM|TO"))
-    {
+            break;
+        case Matches("\\copy", MatchAny, "FROM|TO"):
         completion_charp = "";
         completion_force_quote = false;
         matches = rl_completion_matches(text, complete_from_files);
-    }
+            break;

     /* Complete COPY <sth> TO <sth> */
-    else if (Matches("COPY|\\copy", MatchAny, "TO", MatchAny))
+        case Matches("COPY|\\copy", MatchAny, "TO", MatchAny):
         COMPLETE_WITH("WITH (");
+            break;

     /* Complete COPY <sth> FROM <sth> */
-    else if (Matches("COPY|\\copy", MatchAny, "FROM", MatchAny))
+        case Matches("COPY|\\copy", MatchAny, "FROM", MatchAny):
         COMPLETE_WITH("WITH (", "WHERE");
+            break;

     /* Complete COPY <sth> FROM|TO filename WITH ( */
-    else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("))
+        case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("):
         COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
                       "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
                       "FORCE_NOT_NULL", "FORCE_NULL", "ENCODING", "DEFAULT",
                       "ON_ERROR", "LOG_VERBOSITY");
+            break;

     /* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
-    else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "FORMAT"))
+        case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "FORMAT"):
         COMPLETE_WITH("binary", "csv", "text");
+            break;

     /* Complete COPY <sth> FROM filename WITH (ON_ERROR */
-    else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "ON_ERROR"))
+        case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "ON_ERROR"):
         COMPLETE_WITH("stop", "ignore");
+            break;

     /* Complete COPY <sth> FROM filename WITH (LOG_VERBOSITY */
-    else if (Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "LOG_VERBOSITY"))
+        case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "LOG_VERBOSITY"):
         COMPLETE_WITH("default", "verbose");
+            break;

     /* Complete COPY <sth> FROM <sth> WITH (<options>) */
-    else if (Matches("COPY|\\copy", MatchAny, "FROM", MatchAny, "WITH", MatchAny))
+        case Matches("COPY|\\copy", MatchAny, "FROM", MatchAny, "WITH", MatchAny):
         COMPLETE_WITH("WHERE");
+            break;

     /* CREATE ACCESS METHOD */
     /* Complete "CREATE ACCESS METHOD <name>" */
-    else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny))
+        case Matches("CREATE", "ACCESS", "METHOD", MatchAny):
         COMPLETE_WITH("TYPE");
+            break;
     /* Complete "CREATE ACCESS METHOD <name> TYPE" */
-    else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"))
+        case Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"):
         COMPLETE_WITH("INDEX", "TABLE");
+            break;
     /* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
-    else if (Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny))
+        case Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny):
         COMPLETE_WITH("HANDLER");
+            break;

     /* CREATE COLLATION */
-    else if (Matches("CREATE", "COLLATION", MatchAny))
+        case Matches("CREATE", "COLLATION", MatchAny):
         COMPLETE_WITH("(", "FROM");
-    else if (Matches("CREATE", "COLLATION", MatchAny, "FROM"))
+            break;
+        case Matches("CREATE", "COLLATION", MatchAny, "FROM"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
-    else if (HeadMatches("CREATE", "COLLATION", MatchAny, "(*"))
-    {
+            break;
+        case HeadMatches("CREATE", "COLLATION", MatchAny, "(*"):
         if (TailMatches("(|*,"))
             COMPLETE_WITH("LOCALE =", "LC_COLLATE =", "LC_CTYPE =",
                           "PROVIDER =", "DETERMINISTIC =");
@@ -3136,410 +3428,499 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH("libc", "icu");
         else if (TailMatches("DETERMINISTIC", "="))
             COMPLETE_WITH("true", "false");
-    }
+            break;

     /* CREATE DATABASE */
-    else if (Matches("CREATE", "DATABASE", MatchAny))
+        case Matches("CREATE", "DATABASE", MatchAny):
         COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
                       "IS_TEMPLATE", "STRATEGY",
                       "ALLOW_CONNECTIONS", "CONNECTION LIMIT",
                       "LC_COLLATE", "LC_CTYPE", "LOCALE", "OID",
                       "LOCALE_PROVIDER", "ICU_LOCALE");
+            break;

-    else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
+        case Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
-    else if (Matches("CREATE", "DATABASE", MatchAny, "STRATEGY"))
+            break;
+        case Matches("CREATE", "DATABASE", MatchAny, "STRATEGY"):
         COMPLETE_WITH("WAL_LOG", "FILE_COPY");
+            break;

     /* CREATE DOMAIN */
-    else if (Matches("CREATE", "DOMAIN", MatchAny))
+        case Matches("CREATE", "DOMAIN", MatchAny):
         COMPLETE_WITH("AS");
-    else if (Matches("CREATE", "DOMAIN", MatchAny, "AS"))
+            break;
+        case Matches("CREATE", "DOMAIN", MatchAny, "AS"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny))
+            break;
+        case Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny):
         COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
                       "NOT NULL", "NULL", "CHECK (");
-    else if (Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"))
+            break;
+        case Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
+            break;

     /* CREATE EXTENSION */
     /* Complete with available extensions rather than installed ones. */
-    else if (Matches("CREATE", "EXTENSION"))
+        case Matches("CREATE", "EXTENSION"):
         COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
+            break;
     /* CREATE EXTENSION <name> */
-    else if (Matches("CREATE", "EXTENSION", MatchAny))
+        case Matches("CREATE", "EXTENSION", MatchAny):
         COMPLETE_WITH("WITH SCHEMA", "CASCADE", "VERSION");
+            break;
     /* CREATE EXTENSION <name> VERSION */
-    else if (Matches("CREATE", "EXTENSION", MatchAny, "VERSION"))
-    {
+        case Matches("CREATE", "EXTENSION", MatchAny, "VERSION"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
-    }
+            break;

     /* CREATE FOREIGN */
-    else if (Matches("CREATE", "FOREIGN"))
+        case Matches("CREATE", "FOREIGN"):
         COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            break;

     /* CREATE FOREIGN DATA WRAPPER */
-    else if (Matches("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny))
+        case Matches("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny):
         COMPLETE_WITH("HANDLER", "VALIDATOR", "OPTIONS");
+            break;

     /* CREATE FOREIGN TABLE */
-    else if (Matches("CREATE", "FOREIGN", "TABLE", MatchAny))
+        case Matches("CREATE", "FOREIGN", "TABLE", MatchAny):
         COMPLETE_WITH("(", "PARTITION OF");
+            break;

     /* CREATE INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */
     /* First off we complete CREATE UNIQUE with "INDEX" */
-    else if (TailMatches("CREATE", "UNIQUE"))
+        case TailMatches("CREATE", "UNIQUE"):
         COMPLETE_WITH("INDEX");
+            break;

     /*
      * If we have CREATE|UNIQUE INDEX, then add "ON", "CONCURRENTLY", and
      * existing indexes
      */
-    else if (TailMatches("CREATE|UNIQUE", "INDEX"))
+        case TailMatches("CREATE|UNIQUE", "INDEX"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
                                         "ON", "CONCURRENTLY");
+            break;

     /*
      * Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of relations
      * that indexes can be created on
      */
-    else if (TailMatches("INDEX|CONCURRENTLY", MatchAny, "ON") ||
-             TailMatches("INDEX|CONCURRENTLY", "ON"))
+        case TailMatches("INDEX|CONCURRENTLY", MatchAny, "ON"):
+        case TailMatches("INDEX|CONCURRENTLY", "ON"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
+            break;

     /*
      * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing
      * indexes
      */
-    else if (TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY"))
+        case TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
                                         "ON");
+            break;
     /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
-    else if (TailMatches("CREATE|UNIQUE", "INDEX", MatchAny) ||
-             TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny))
+        case TailMatches("CREATE|UNIQUE", "INDEX", MatchAny):
+        case TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny):
         COMPLETE_WITH("ON");
+            break;

     /*
      * Complete INDEX <name> ON <table> with a list of table columns (which
      * should really be in parens)
      */
-    else if (TailMatches("INDEX", MatchAny, "ON", MatchAny) ||
-             TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny))
+        case TailMatches("INDEX", MatchAny, "ON", MatchAny):
+        case TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny):
         COMPLETE_WITH("(", "USING");
-    else if (TailMatches("INDEX", MatchAny, "ON", MatchAny, "(") ||
-             TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny, "("))
+            break;
+        case TailMatches("INDEX", MatchAny, "ON", MatchAny, "("):
+        case TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny, "("):
         COMPLETE_WITH_ATTR(prev2_wd);
+            break;
     /* same if you put in USING */
-    else if (TailMatches("ON", MatchAny, "USING", MatchAny, "("))
+        case TailMatches("ON", MatchAny, "USING", MatchAny, "("):
         COMPLETE_WITH_ATTR(prev4_wd);
+            break;
     /* Complete USING with an index method */
-    else if (TailMatches("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING") ||
-             TailMatches("INDEX", MatchAny, "ON", MatchAny, "USING") ||
-             TailMatches("INDEX", "ON", MatchAny, "USING"))
+        case TailMatches("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING"):
+        case TailMatches("INDEX", MatchAny, "ON", MatchAny, "USING"):
+        case TailMatches("INDEX", "ON", MatchAny, "USING"):
         COMPLETE_WITH_QUERY(Query_for_list_of_index_access_methods);
-    else if (TailMatches("ON", MatchAny, "USING", MatchAny) &&
-             !TailMatches("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) &&
-             !TailMatches("FOR", MatchAny, MatchAny, MatchAny))
+            break;
+        case TailMatches("ON", MatchAny, "USING", MatchAny):
+        if (!TailMatches("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) && !TailMatches("FOR", MatchAny,
MatchAny,MatchAny)) 
         COMPLETE_WITH("(");
+            break;

     /* CREATE OR REPLACE */
-    else if (Matches("CREATE", "OR"))
+        case Matches("CREATE", "OR"):
         COMPLETE_WITH("REPLACE");
+            break;

     /* CREATE POLICY */
     /* Complete "CREATE POLICY <name> ON" */
-    else if (Matches("CREATE", "POLICY", MatchAny))
+        case Matches("CREATE", "POLICY", MatchAny):
         COMPLETE_WITH("ON");
+            break;
     /* Complete "CREATE POLICY <name> ON <table>" */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;
     /* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("AS", "FOR", "TO", "USING (", "WITH CHECK (");
+            break;
     /* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS"):
         COMPLETE_WITH("PERMISSIVE", "RESTRICTIVE");
+            break;

     /*
      * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
      * FOR|TO|USING|WITH CHECK
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny):
         COMPLETE_WITH("FOR", "TO", "USING", "WITH CHECK");
+            break;
     /* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"):
         COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
+            break;
     /* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"):
         COMPLETE_WITH("TO", "WITH CHECK (");
+            break;
     /* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE TO|USING" */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"):
         COMPLETE_WITH("TO", "USING (");
+            break;
     /* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"):
         COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
+            break;
     /* Complete "CREATE POLICY <name> ON <table> TO <role>" */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
+            break;
     /* Complete "CREATE POLICY <name> ON <table> USING (" */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"):
         COMPLETE_WITH("(");
+            break;

     /*
      * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
      * ALL|SELECT|INSERT|UPDATE|DELETE
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR"):
         COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
+            break;

     /*
      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
      * INSERT TO|WITH CHECK"
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "INSERT"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "INSERT"):
         COMPLETE_WITH("TO", "WITH CHECK (");
+            break;

     /*
      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
      * SELECT|DELETE TO|USING"
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "SELECT|DELETE"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "SELECT|DELETE"):
         COMPLETE_WITH("TO", "USING (");
+            break;

     /*
      * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
      * ALL|UPDATE TO|USING|WITH CHECK
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "ALL|UPDATE"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "ALL|UPDATE"):
         COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
+            break;

     /*
      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE TO
      * <role>"
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "TO"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "TO"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
+            break;

     /*
      * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
      * USING ("
      */
-    else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "USING"))
+        case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "USING"):
         COMPLETE_WITH("(");
+            break;


 /* CREATE PUBLICATION */
-    else if (Matches("CREATE", "PUBLICATION", MatchAny))
+        case Matches("CREATE", "PUBLICATION", MatchAny):
         COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR TABLES IN SCHEMA", "WITH (");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR"))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR"):
         COMPLETE_WITH("TABLE", "ALL TABLES", "TABLES IN SCHEMA");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"):
         COMPLETE_WITH("TABLES");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"):
         COMPLETE_WITH("WITH (");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES"))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES"):
         COMPLETE_WITH("IN SCHEMA");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny) && !ends_with(prev_wd, ','))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny):
+        if (!ends_with(prev_wd, ','))
         COMPLETE_WITH("WHERE (", "WITH (");
+            break;
     /* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"))
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;

     /*
      * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
+        case Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"):
         COMPLETE_WITH("(");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("):
         COMPLETE_WITH_ATTR(prev3_wd);
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "(*)"))
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "(*)"):
         COMPLETE_WITH(" WITH (");
+            break;

     /*
      * Complete "CREATE PUBLICATION <name> FOR TABLES IN SCHEMA <schema>, ..."
      */
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA"))
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
                                  " AND nspname NOT LIKE E'pg\\\\_%%'",
                                  "CURRENT_SCHEMA");
-    else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny) &&
(!ends_with(prev_wd,','))) 
+            break;
+        case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny):
+        if ((!ends_with(prev_wd, ',')))
         COMPLETE_WITH("WITH (");
+            break;
     /* Complete "CREATE PUBLICATION <name> [...] WITH" */
-    else if (Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "("))
+        case Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "("):
         COMPLETE_WITH("publish", "publish_via_partition_root");
+            break;

 /* CREATE RULE */
     /* Complete "CREATE [ OR REPLACE ] RULE <sth>" with "AS ON" */
-    else if (Matches("CREATE", "RULE", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny))
+        case Matches("CREATE", "RULE", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny):
         COMPLETE_WITH("AS ON");
+            break;
     /* Complete "CREATE [ OR REPLACE ] RULE <sth> AS" with "ON" */
-    else if (Matches("CREATE", "RULE", MatchAny, "AS") ||
-             Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS"))
+        case Matches("CREATE", "RULE", MatchAny, "AS"):
+        case Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS"):
         COMPLETE_WITH("ON");
+            break;

     /*
      * Complete "CREATE [ OR REPLACE ] RULE <sth> AS ON" with
      * SELECT|UPDATE|INSERT|DELETE
      */
-    else if (Matches("CREATE", "RULE", MatchAny, "AS", "ON") ||
-             Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS", "ON"))
+        case Matches("CREATE", "RULE", MatchAny, "AS", "ON"):
+        case Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS", "ON"):
         COMPLETE_WITH("SELECT", "UPDATE", "INSERT", "DELETE");
+            break;
     /* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */
-    else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE"))
+        case TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE"):
         COMPLETE_WITH("TO");
+            break;
     /* Complete "AS ON <sth> TO" with a table name */
-    else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
+        case TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;

 /* CREATE SCHEMA [ <name> ] [ AUTHORIZATION ] */
-    else if (Matches("CREATE", "SCHEMA"))
+        case Matches("CREATE", "SCHEMA"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
                                  "AUTHORIZATION");
-    else if (Matches("CREATE", "SCHEMA", "AUTHORIZATION") ||
-             Matches("CREATE", "SCHEMA", MatchAny, "AUTHORIZATION"))
+            break;
+        case Matches("CREATE", "SCHEMA", "AUTHORIZATION"):
+        case Matches("CREATE", "SCHEMA", MatchAny, "AUTHORIZATION"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_owner_roles);
-    else if (Matches("CREATE", "SCHEMA", "AUTHORIZATION", MatchAny) ||
-             Matches("CREATE", "SCHEMA", MatchAny, "AUTHORIZATION", MatchAny))
+            break;
+        case Matches("CREATE", "SCHEMA", "AUTHORIZATION", MatchAny):
+        case Matches("CREATE", "SCHEMA", MatchAny, "AUTHORIZATION", MatchAny):
         COMPLETE_WITH("CREATE", "GRANT");
-    else if (Matches("CREATE", "SCHEMA", MatchAny))
+            break;
+        case Matches("CREATE", "SCHEMA", MatchAny):
         COMPLETE_WITH("AUTHORIZATION", "CREATE", "GRANT");
+            break;

 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
-    else if (TailMatches("CREATE", "SEQUENCE", MatchAny) ||
-             TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
+        case TailMatches("CREATE", "SEQUENCE", MatchAny):
+        case TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny):
         COMPLETE_WITH("AS", "INCREMENT BY", "MINVALUE", "MAXVALUE", "NO",
                       "CACHE", "CYCLE", "OWNED BY", "START WITH");
-    else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"))
+            break;
+        case TailMatches("CREATE", "SEQUENCE", MatchAny, "AS"):
+        case TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"):
         COMPLETE_WITH_CS("smallint", "integer", "bigint");
-    else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "NO") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
+            break;
+        case TailMatches("CREATE", "SEQUENCE", MatchAny, "NO"):
+        case TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"):
         COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+            break;

 /* CREATE SERVER <name> */
-    else if (Matches("CREATE", "SERVER", MatchAny))
+        case Matches("CREATE", "SERVER", MatchAny):
         COMPLETE_WITH("TYPE", "VERSION", "FOREIGN DATA WRAPPER");
+            break;

 /* CREATE STATISTICS <name> */
-    else if (Matches("CREATE", "STATISTICS", MatchAny))
+        case Matches("CREATE", "STATISTICS", MatchAny):
         COMPLETE_WITH("(", "ON");
-    else if (Matches("CREATE", "STATISTICS", MatchAny, "("))
+            break;
+        case Matches("CREATE", "STATISTICS", MatchAny, "("):
         COMPLETE_WITH("ndistinct", "dependencies", "mcv");
-    else if (Matches("CREATE", "STATISTICS", MatchAny, "(*)"))
+            break;
+        case Matches("CREATE", "STATISTICS", MatchAny, "(*)"):
         COMPLETE_WITH("ON");
-    else if (Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"))
+            break;
+        case Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;

 /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
     /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
-    else if (TailMatches("CREATE", "TEMP|TEMPORARY"))
+        case TailMatches("CREATE", "TEMP|TEMPORARY"):
         COMPLETE_WITH("SEQUENCE", "TABLE", "VIEW");
+            break;
     /* Complete "CREATE UNLOGGED" with TABLE, SEQUENCE or MATVIEW */
-    else if (TailMatches("CREATE", "UNLOGGED"))
-    {
+        case TailMatches("CREATE", "UNLOGGED"):
         /* but not MATVIEW in CREATE SCHEMA */
         if (HeadMatches("CREATE", "SCHEMA"))
             COMPLETE_WITH("TABLE", "SEQUENCE");
         else
             COMPLETE_WITH("TABLE", "SEQUENCE", "MATERIALIZED VIEW");
-    }
+            break;
     /* Complete PARTITION BY with RANGE ( or LIST ( or ... */
-    else if (TailMatches("PARTITION", "BY"))
+        case TailMatches("PARTITION", "BY"):
         COMPLETE_WITH("RANGE (", "LIST (", "HASH (");
+            break;
     /* If we have xxx PARTITION OF, provide a list of partitioned tables */
-    else if (TailMatches("PARTITION", "OF"))
+        case TailMatches("PARTITION", "OF"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
+            break;
     /* Limited completion support for partition bound specification */
-    else if (TailMatches("PARTITION", "OF", MatchAny))
+        case TailMatches("PARTITION", "OF", MatchAny):
         COMPLETE_WITH("FOR VALUES", "DEFAULT");
+            break;
     /* Complete CREATE TABLE <name> with '(', AS, OF or PARTITION OF */
-    else if (TailMatches("CREATE", "TABLE", MatchAny) ||
-             TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny))
+        case TailMatches("CREATE", "TABLE", MatchAny):
+        case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny):
         COMPLETE_WITH("(", "AS", "OF", "PARTITION OF");
+            break;
     /* Complete CREATE TABLE <name> OF with list of composite types */
-    else if (TailMatches("CREATE", "TABLE", MatchAny, "OF") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "OF"))
+        case TailMatches("CREATE", "TABLE", MatchAny, "OF"):
+        case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "OF"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
+            break;
     /* Complete CREATE TABLE <name> [ (...) ] AS with list of keywords */
-    else if (TailMatches("CREATE", "TABLE", MatchAny, "AS") ||
-             TailMatches("CREATE", "TABLE", MatchAny, "(*)", "AS") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "AS") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "AS"))
+        case TailMatches("CREATE", "TABLE", MatchAny, "AS"):
+        case TailMatches("CREATE", "TABLE", MatchAny, "(*)", "AS"):
+        case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "AS"):
+        case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "AS"):
         COMPLETE_WITH("EXECUTE", "SELECT", "TABLE", "VALUES", "WITH");
+            break;
     /* Complete CREATE TABLE name (...) with supported options */
-    else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)") ||
-             TailMatches("CREATE", "UNLOGGED", "TABLE", MatchAny, "(*)"))
+        case TailMatches("CREATE", "TABLE", MatchAny, "(*)"):
+        case TailMatches("CREATE", "UNLOGGED", "TABLE", MatchAny, "(*)"):
         COMPLETE_WITH("AS", "INHERITS (", "PARTITION BY", "USING", "TABLESPACE", "WITH (");
-    else if (TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)"))
+            break;
+        case TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)"):
         COMPLETE_WITH("AS", "INHERITS (", "ON COMMIT", "PARTITION BY",
                       "TABLESPACE", "WITH (");
+            break;
     /* Complete CREATE TABLE (...) USING with table access methods */
-    else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)", "USING") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "USING"))
+        case TailMatches("CREATE", "TABLE", MatchAny, "(*)", "USING"):
+        case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "USING"):
         COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
+            break;
     /* Complete CREATE TABLE (...) WITH with storage parameters */
-    else if (TailMatches("CREATE", "TABLE", MatchAny, "(*)", "WITH", "(") ||
-             TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "WITH", "("))
+        case TailMatches("CREATE", "TABLE", MatchAny, "(*)", "WITH", "("):
+        case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "WITH", "("):
         COMPLETE_WITH_LIST(table_storage_parameters);
+            break;
     /* Complete CREATE TABLE ON COMMIT with actions */
-    else if (TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)", "ON", "COMMIT"))
+        case TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)", "ON", "COMMIT"):
         COMPLETE_WITH("DELETE ROWS", "DROP", "PRESERVE ROWS");
+            break;

 /* CREATE TABLESPACE */
-    else if (Matches("CREATE", "TABLESPACE", MatchAny))
+        case Matches("CREATE", "TABLESPACE", MatchAny):
         COMPLETE_WITH("OWNER", "LOCATION");
+            break;
     /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
-    else if (Matches("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny))
+        case Matches("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny):
         COMPLETE_WITH("LOCATION");
+            break;

 /* CREATE TEXT SEARCH */
-    else if (Matches("CREATE", "TEXT", "SEARCH"))
+        case Matches("CREATE", "TEXT", "SEARCH"):
         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-    else if (Matches("CREATE", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
+            break;
+        case Matches("CREATE", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny):
         COMPLETE_WITH("(");
+            break;

 /* CREATE TRANSFORM */
-    else if (Matches("CREATE", "TRANSFORM") ||
-             Matches("CREATE", "OR", "REPLACE", "TRANSFORM"))
+        case Matches("CREATE", "TRANSFORM"):
+        case Matches("CREATE", "OR", "REPLACE", "TRANSFORM"):
         COMPLETE_WITH("FOR");
-    else if (Matches("CREATE", "TRANSFORM", "FOR") ||
-             Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR"))
+            break;
+        case Matches("CREATE", "TRANSFORM", "FOR"):
+        case Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny))
+            break;
+        case Matches("CREATE", "TRANSFORM", "FOR", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny):
         COMPLETE_WITH("LANGUAGE");
-    else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE") ||
-             Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
-    {
+            break;
+        case Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
+        case Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-    }
+            break;

 /* CREATE SUBSCRIPTION */
-    else if (Matches("CREATE", "SUBSCRIPTION", MatchAny))
+        case Matches("CREATE", "SUBSCRIPTION", MatchAny):
         COMPLETE_WITH("CONNECTION");
-    else if (Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny))
+            break;
+        case Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny):
         COMPLETE_WITH("PUBLICATION");
-    else if (Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION",
-                     MatchAny, "PUBLICATION"))
-    {
+            break;
+        case Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny, "PUBLICATION"):
         /* complete with nothing here as this refers to remote publications */
-    }
-    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny))
+            break;
+        case Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny):
         COMPLETE_WITH("WITH (");
+            break;
     /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
-    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "("))
+        case Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "("):
         COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
                       "disable_on_error", "enabled", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
                       "streaming", "synchronous_commit", "two_phase");
+            break;

 /* CREATE TRIGGER --- is allowed inside CREATE SCHEMA, so use TailMatches */

@@ -3547,192 +3928,155 @@ match_previous_words(const char *text, int start, int end,
      * Complete CREATE [ OR REPLACE ] TRIGGER <name> with BEFORE|AFTER|INSTEAD
      * OF.
      */
-    else if (TailMatches("CREATE", "TRIGGER", MatchAny) ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny))
+        case TailMatches("CREATE", "TRIGGER", MatchAny):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny):
         COMPLETE_WITH("BEFORE", "AFTER", "INSTEAD OF");
+            break;

     /*
      * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER with an
      * event.
      */
-    else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER") ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER"))
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER"):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER"):
         COMPLETE_WITH("INSERT", "DELETE", "UPDATE", "TRUNCATE");
+            break;
     /* Complete CREATE [ OR REPLACE ] TRIGGER <name> INSTEAD OF with an event */
-    else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF") ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF"))
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF"):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF"):
         COMPLETE_WITH("INSERT", "DELETE", "UPDATE");
+            break;

     /*
      * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER sth with
      * OR|ON.
      */
-    else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny) ||
-             TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny) ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny))
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny):
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny):
         COMPLETE_WITH("ON", "OR");
+            break;

     /*
      * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER event ON
      * with a list of tables.  EXECUTE FUNCTION is the recommended grammar
      * instead of EXECUTE PROCEDURE in version 11 and upwards.
      */
-    else if (TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON") ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"))
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;

     /*
      * Complete CREATE [ OR REPLACE ] TRIGGER ... INSTEAD OF event ON with a
      * list of views.
      */
-    else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON") ||
-             TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"):
+        case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "ON", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "ON", MatchAny))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "ON", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "ON", MatchAny):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
                           "REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
                           "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "DEFERRABLE") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "DEFERRABLE") ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "INITIALLY", "IMMEDIATE|DEFERRED") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "INITIALLY", "IMMEDIATE|DEFERRED"))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "DEFERRABLE"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "DEFERRABLE"):
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "INITIALLY", "IMMEDIATE|DEFERRED"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "INITIALLY", "IMMEDIATE|DEFERRED"):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING"))
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING"):
         COMPLETE_WITH("OLD TABLE", "NEW TABLE");
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "OLD|NEW", "TABLE") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "OLD|NEW", "TABLE"))
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "OLD|NEW", "TABLE"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "OLD|NEW", "TABLE"):
         COMPLETE_WITH("AS");
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD", "TABLE", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD", "TABLE", MatchAny))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", "AS", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", "AS", MatchAny):
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", MatchAny):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "NEW", "TABLE", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "NEW", "TABLE", MatchAny))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", "AS", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", "AS", MatchAny):
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", MatchAny):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW",
"TABLE","AS", MatchAny): 
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS",
MatchAny,"OLD|NEW", "TABLE", "AS", MatchAny): 
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE",
"AS",MatchAny): 
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", MatchAny,
"OLD|NEW","TABLE", "AS", MatchAny): 
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW",
"TABLE",MatchAny): 
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS",
MatchAny,"OLD|NEW", "TABLE", MatchAny): 
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE",
MatchAny):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", MatchAny,
"OLD|NEW","TABLE", MatchAny): 
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "FOR") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "FOR"))
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR"):
         COMPLETE_WITH("EACH", "ROW", "STATEMENT");
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "FOR", "EACH") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "FOR", "EACH"))
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR", "EACH"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR", "EACH"):
         COMPLETE_WITH("ROW", "STATEMENT");
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "FOR", "EACH", "ROW|STATEMENT") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "FOR", "EACH", "ROW|STATEMENT") ||
-             Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "FOR", "ROW|STATEMENT") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "FOR", "ROW|STATEMENT"))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR", "EACH", "ROW|STATEMENT"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR", "EACH", "ROW|STATEMENT"):
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR", "ROW|STATEMENT"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR", "ROW|STATEMENT"):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "WHEN", "(*)") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "WHEN", "(*)"))
-    {
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "WHEN", "(*)"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "WHEN", "(*)"):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
         else
             COMPLETE_WITH("EXECUTE PROCEDURE");
-    }
+            break;

     /*
      * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
      * PROCEDURE|FUNCTION.
      */
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "EXECUTE") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "EXECUTE"))
-    {
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "EXECUTE"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "EXECUTE"):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FUNCTION");
         else
             COMPLETE_WITH("PROCEDURE");
-    }
-    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
-                     "EXECUTE", "FUNCTION|PROCEDURE") ||
-             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
-                     "EXECUTE", "FUNCTION|PROCEDURE"))
+            break;
+        case Matches("CREATE", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"):
+        case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"):
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
+            break;

 /* CREATE ROLE,USER,GROUP <name> */
-    else if (Matches("CREATE", "ROLE|GROUP|USER", MatchAny) &&
-             !TailMatches("USER", "MAPPING"))
+        case Matches("CREATE", "ROLE|GROUP|USER", MatchAny):
+        if (!TailMatches("USER", "MAPPING"))
         COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
                       "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
                       "LOGIN", "NOBYPASSRLS",
@@ -3740,9 +4084,10 @@ match_previous_words(const char *text, int start, int end,
                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
                       "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
                       "VALID UNTIL", "WITH");
+            break;

 /* CREATE ROLE,USER,GROUP <name> WITH */
-    else if (Matches("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"))
+        case Matches("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"):
         /* Similar to the above, but don't complete "WITH" again. */
         COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
                       "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
@@ -3751,27 +4096,30 @@ match_previous_words(const char *text, int start, int end,
                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
                       "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
                       "VALID UNTIL");
+            break;

     /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
-    else if (Matches("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"))
+        case Matches("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"):
         COMPLETE_WITH("GROUP", "ROLE");
+            break;

 /* CREATE TYPE */
-    else if (Matches("CREATE", "TYPE", MatchAny))
+        case Matches("CREATE", "TYPE", MatchAny):
         COMPLETE_WITH("(", "AS");
-    else if (Matches("CREATE", "TYPE", MatchAny, "AS"))
+            break;
+        case Matches("CREATE", "TYPE", MatchAny, "AS"):
         COMPLETE_WITH("ENUM", "RANGE", "(");
-    else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "("))
-    {
+            break;
+        case HeadMatches("CREATE", "TYPE", MatchAny, "AS", "("):
         if (TailMatches("(|*,", MatchAny))
             COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
         else if (TailMatches("(|*,", MatchAny, MatchAnyExcept("*)")))
             COMPLETE_WITH("COLLATE", ",", ")");
-    }
-    else if (Matches("CREATE", "TYPE", MatchAny, "AS", "ENUM|RANGE"))
+            break;
+        case Matches("CREATE", "TYPE", MatchAny, "AS", "ENUM|RANGE"):
         COMPLETE_WITH("(");
-    else if (HeadMatches("CREATE", "TYPE", MatchAny, "("))
-    {
+            break;
+        case HeadMatches("CREATE", "TYPE", MatchAny, "("):
         if (TailMatches("(|*,"))
             COMPLETE_WITH("INPUT", "OUTPUT", "RECEIVE", "SEND",
                           "TYPMOD_IN", "TYPMOD_OUT", "ANALYZE", "SUBSCRIPT",
@@ -3783,9 +4131,8 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH("=");
         else if (TailMatches("=", MatchAnyExcept("*)")))
             COMPLETE_WITH(",", ")");
-    }
-    else if (HeadMatches("CREATE", "TYPE", MatchAny, "AS", "RANGE", "("))
-    {
+            break;
+        case HeadMatches("CREATE", "TYPE", MatchAny, "AS", "RANGE", "("):
         if (TailMatches("(|*,"))
             COMPLETE_WITH("SUBTYPE", "SUBTYPE_OPCLASS", "COLLATION",
                           "CANONICAL", "SUBTYPE_DIFF",
@@ -3794,86 +4141,100 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH("=");
         else if (TailMatches("=", MatchAnyExcept("*)")))
             COMPLETE_WITH(",", ")");
-    }
+            break;

 /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
     /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS or WITH */
-    else if (TailMatches("CREATE", "VIEW", MatchAny) ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny))
+        case TailMatches("CREATE", "VIEW", MatchAny):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny):
         COMPLETE_WITH("AS", "WITH");
+            break;
     /* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "AS") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS"))
+        case TailMatches("CREATE", "VIEW", MatchAny, "AS"):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS"):
         COMPLETE_WITH("SELECT");
+            break;
     /* CREATE [ OR REPLACE ] VIEW <name> WITH ( yyy [= zzz] ) */
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH"))
+        case TailMatches("CREATE", "VIEW", MatchAny, "WITH"):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH"):
         COMPLETE_WITH("(");
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "("))
+            break;
+        case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "("):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "("):
         COMPLETE_WITH_LIST(view_optional_parameters);
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(", "check_option") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(", "check_option"))
+            break;
+        case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(", "check_option"):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(", "check_option"):
         COMPLETE_WITH("=");
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(", "check_option", "=") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(", "check_option", "="))
+            break;
+        case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(", "check_option", "="):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(", "check_option", "="):
         COMPLETE_WITH("local", "cascaded");
+            break;
     /* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS */
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)"))
+        case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)"):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)"):
         COMPLETE_WITH("AS");
+            break;
     /* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS SELECT */
-    else if (TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)", "AS") ||
-             TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)", "AS"))
+        case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)", "AS"):
+        case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)", "AS"):
         COMPLETE_WITH("SELECT");
+            break;

 /* CREATE MATERIALIZED VIEW */
-    else if (Matches("CREATE", "MATERIALIZED"))
+        case Matches("CREATE", "MATERIALIZED"):
         COMPLETE_WITH("VIEW");
+            break;
     /* Complete CREATE MATERIALIZED VIEW <name> with AS */
-    else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny))
+        case Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny):
         COMPLETE_WITH("AS");
+            break;
     /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
-    else if (Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"))
+        case Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"):
         COMPLETE_WITH("SELECT");
+            break;

 /* CREATE EVENT TRIGGER */
-    else if (Matches("CREATE", "EVENT"))
+        case Matches("CREATE", "EVENT"):
         COMPLETE_WITH("TRIGGER");
+            break;
     /* Complete CREATE EVENT TRIGGER <name> with ON */
-    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny))
+        case Matches("CREATE", "EVENT", "TRIGGER", MatchAny):
         COMPLETE_WITH("ON");
+            break;
     /* Complete CREATE EVENT TRIGGER <name> ON with event_type */
-    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"))
+        case Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"):
         COMPLETE_WITH("ddl_command_start", "ddl_command_end", "login",
                       "sql_drop", "table_rewrite");
+            break;

     /*
      * Complete CREATE EVENT TRIGGER <name> ON <event_type>.  EXECUTE FUNCTION
      * is the recommended grammar instead of EXECUTE PROCEDURE in version 11
      * and upwards.
      */
-    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON", MatchAny))
-    {
+        case Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON", MatchAny):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "WHEN|AND", MatchAny, "IN", "(*)"))
-    {
+            break;
+        case Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "WHEN|AND", MatchAny, "IN", "(*)"):
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
         else
             COMPLETE_WITH("EXECUTE PROCEDURE");
-    }
-    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"))
+            break;
+        case Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"):
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
+            break;

 /* DEALLOCATE */
-    else if (Matches("DEALLOCATE"))
+        case Matches("DEALLOCATE"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_prepared_statements,
                                  "ALL");
+            break;

 /* DECLARE */

@@ -3881,9 +4242,10 @@ match_previous_words(const char *text, int start, int end,
      * Complete DECLARE <name> with one of BINARY, ASENSITIVE, INSENSITIVE,
      * SCROLL, NO SCROLL, and CURSOR.
      */
-    else if (Matches("DECLARE", MatchAny))
+        case Matches("DECLARE", MatchAny):
         COMPLETE_WITH("BINARY", "ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL",
                       "CURSOR");
+            break;

     /*
      * Complete DECLARE ... <option> with other options. The PostgreSQL parser
@@ -3892,183 +4254,226 @@ match_previous_words(const char *text, int start, int end,
      * provides, like the syntax of DECLARE command in the documentation
      * indicates.
      */
-    else if (Matches("DECLARE", MatchAnyN, "BINARY"))
+        case Matches("DECLARE", MatchAnyN, "BINARY"):
         COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
-    else if (Matches("DECLARE", MatchAnyN, "ASENSITIVE|INSENSITIVE"))
+            break;
+        case Matches("DECLARE", MatchAnyN, "ASENSITIVE|INSENSITIVE"):
         COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
-    else if (Matches("DECLARE", MatchAnyN, "SCROLL"))
+            break;
+        case Matches("DECLARE", MatchAnyN, "SCROLL"):
         COMPLETE_WITH("CURSOR");
+            break;
     /* Complete DECLARE ... [options] NO with SCROLL */
-    else if (Matches("DECLARE", MatchAnyN, "NO"))
+        case Matches("DECLARE", MatchAnyN, "NO"):
         COMPLETE_WITH("SCROLL");
+            break;

     /*
      * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and
      * FOR
      */
-    else if (Matches("DECLARE", MatchAnyN, "CURSOR"))
+        case Matches("DECLARE", MatchAnyN, "CURSOR"):
         COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
+            break;
     /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
-    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT"))
+        case Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT"):
         COMPLETE_WITH("HOLD");
+            break;
     /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
-    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT", "HOLD"))
+        case Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT", "HOLD"):
         COMPLETE_WITH("FOR");
+            break;

 /* DELETE --- can be inside EXPLAIN, RULE, etc */
     /* Complete DELETE with "FROM" */
-    else if (Matches("DELETE"))
+        case Matches("DELETE"):
         COMPLETE_WITH("FROM");
+            break;
     /* Complete DELETE FROM with a list of tables */
-    else if (TailMatches("DELETE", "FROM"))
+        case TailMatches("DELETE", "FROM"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
+            break;
     /* Complete DELETE FROM <table> */
-    else if (TailMatches("DELETE", "FROM", MatchAny))
+        case TailMatches("DELETE", "FROM", MatchAny):
         COMPLETE_WITH("USING", "WHERE");
+            break;
     /* XXX: implement tab completion for DELETE ... USING */

 /* DISCARD */
-    else if (Matches("DISCARD"))
+        case Matches("DISCARD"):
         COMPLETE_WITH("ALL", "PLANS", "SEQUENCES", "TEMP");
+            break;

 /* DO */
-    else if (Matches("DO"))
+        case Matches("DO"):
         COMPLETE_WITH("LANGUAGE");
+            break;

 /* DROP */
     /* Complete DROP object with CASCADE / RESTRICT */
-    else if (Matches("DROP",
-
"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
-                     MatchAny) ||
-             Matches("DROP", "ACCESS", "METHOD", MatchAny) ||
-             (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
-              ends_with(prev_wd, ')')) ||
-             Matches("DROP", "EVENT", "TRIGGER", MatchAny) ||
-             Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
-             Matches("DROP", "FOREIGN", "TABLE", MatchAny) ||
-             Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
+        case Matches("DROP",
"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
MatchAny):
+        case Matches("DROP", "ACCESS", "METHOD", MatchAny):
+        case Matches("DROP", "EVENT", "TRIGGER", MatchAny):
+        case Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny):
+        case Matches("DROP", "FOREIGN", "TABLE", MatchAny):
+        case Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny):
+        COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;
+        case Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny):
+        if (ends_with(prev_wd, ')'))
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* help completing some of the variants */
-    else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
+        case Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny):
         COMPLETE_WITH("(");
-    else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, "("))
+            break;
+        case Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, "("):
         COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
-    else if (Matches("DROP", "FOREIGN"))
+            break;
+        case Matches("DROP", "FOREIGN"):
         COMPLETE_WITH("DATA WRAPPER", "TABLE");
-    else if (Matches("DROP", "DATABASE", MatchAny))
+            break;
+        case Matches("DROP", "DATABASE", MatchAny):
         COMPLETE_WITH("WITH (");
-    else if (HeadMatches("DROP", "DATABASE") && (ends_with(prev_wd, '(')))
+            break;
+        case HeadMatches("DROP", "DATABASE"):
+        if ((ends_with(prev_wd, '(')))
         COMPLETE_WITH("FORCE");
+            break;

     /* DROP INDEX */
-    else if (Matches("DROP", "INDEX"))
+        case Matches("DROP", "INDEX"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
                                         "CONCURRENTLY");
-    else if (Matches("DROP", "INDEX", "CONCURRENTLY"))
+            break;
+        case Matches("DROP", "INDEX", "CONCURRENTLY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
-    else if (Matches("DROP", "INDEX", MatchAny))
+            break;
+        case Matches("DROP", "INDEX", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
-    else if (Matches("DROP", "INDEX", "CONCURRENTLY", MatchAny))
+            break;
+        case Matches("DROP", "INDEX", "CONCURRENTLY", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* DROP MATERIALIZED VIEW */
-    else if (Matches("DROP", "MATERIALIZED"))
+        case Matches("DROP", "MATERIALIZED"):
         COMPLETE_WITH("VIEW");
-    else if (Matches("DROP", "MATERIALIZED", "VIEW"))
+            break;
+        case Matches("DROP", "MATERIALIZED", "VIEW"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
-    else if (Matches("DROP", "MATERIALIZED", "VIEW", MatchAny))
+            break;
+        case Matches("DROP", "MATERIALIZED", "VIEW", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* DROP OWNED BY */
-    else if (Matches("DROP", "OWNED"))
+        case Matches("DROP", "OWNED"):
         COMPLETE_WITH("BY");
-    else if (Matches("DROP", "OWNED", "BY"))
+            break;
+        case Matches("DROP", "OWNED", "BY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-    else if (Matches("DROP", "OWNED", "BY", MatchAny))
+            break;
+        case Matches("DROP", "OWNED", "BY", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* DROP TEXT SEARCH */
-    else if (Matches("DROP", "TEXT", "SEARCH"))
+        case Matches("DROP", "TEXT", "SEARCH"):
         COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            break;

     /* DROP TRIGGER */
-    else if (Matches("DROP", "TRIGGER", MatchAny))
+        case Matches("DROP", "TRIGGER", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("DROP", "TRIGGER", MatchAny, "ON"))
-    {
+            break;
+        case Matches("DROP", "TRIGGER", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
-    }
-    else if (Matches("DROP", "TRIGGER", MatchAny, "ON", MatchAny))
+            break;
+        case Matches("DROP", "TRIGGER", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* DROP ACCESS METHOD */
-    else if (Matches("DROP", "ACCESS"))
+        case Matches("DROP", "ACCESS"):
         COMPLETE_WITH("METHOD");
-    else if (Matches("DROP", "ACCESS", "METHOD"))
+            break;
+        case Matches("DROP", "ACCESS", "METHOD"):
         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+            break;

     /* DROP EVENT TRIGGER */
-    else if (Matches("DROP", "EVENT"))
+        case Matches("DROP", "EVENT"):
         COMPLETE_WITH("TRIGGER");
-    else if (Matches("DROP", "EVENT", "TRIGGER"))
+            break;
+        case Matches("DROP", "EVENT", "TRIGGER"):
         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            break;

     /* DROP POLICY <name>  */
-    else if (Matches("DROP", "POLICY"))
+        case Matches("DROP", "POLICY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+            break;
     /* DROP POLICY <name> ON */
-    else if (Matches("DROP", "POLICY", MatchAny))
+        case Matches("DROP", "POLICY", MatchAny):
         COMPLETE_WITH("ON");
+            break;
     /* DROP POLICY <name> ON <table> */
-    else if (Matches("DROP", "POLICY", MatchAny, "ON"))
-    {
+        case Matches("DROP", "POLICY", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
-    }
-    else if (Matches("DROP", "POLICY", MatchAny, "ON", MatchAny))
+            break;
+        case Matches("DROP", "POLICY", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* DROP RULE */
-    else if (Matches("DROP", "RULE", MatchAny))
+        case Matches("DROP", "RULE", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("DROP", "RULE", MatchAny, "ON"))
-    {
+            break;
+        case Matches("DROP", "RULE", MatchAny, "ON"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
-    }
-    else if (Matches("DROP", "RULE", MatchAny, "ON", MatchAny))
+            break;
+        case Matches("DROP", "RULE", MatchAny, "ON", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

     /* DROP TRANSFORM */
-    else if (Matches("DROP", "TRANSFORM"))
+        case Matches("DROP", "TRANSFORM"):
         COMPLETE_WITH("FOR");
-    else if (Matches("DROP", "TRANSFORM", "FOR"))
+            break;
+        case Matches("DROP", "TRANSFORM", "FOR"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny))
+            break;
+        case Matches("DROP", "TRANSFORM", "FOR", MatchAny):
         COMPLETE_WITH("LANGUAGE");
-    else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
-    {
+            break;
+        case Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
         set_completion_reference(prev2_wd);
         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-    }
-    else if (Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE", MatchAny))
+            break;
+        case Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE", MatchAny):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

 /* EXECUTE */
-    else if (Matches("EXECUTE"))
+        case Matches("EXECUTE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
+            break;

 /*
  * EXPLAIN [ ( option [, ...] ) ] statement
  * EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
  */
-    else if (Matches("EXPLAIN"))
+        case Matches("EXPLAIN"):
         COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
                       "MERGE INTO", "EXECUTE", "ANALYZE", "VERBOSE");
-    else if (HeadMatches("EXPLAIN", "(*") &&
-             !HeadMatches("EXPLAIN", "(*)"))
+            break;
+        case HeadMatches("EXPLAIN", "(*"):
+        if (!HeadMatches("EXPLAIN", "(*)"))
     {
         /*
          * This fires if we're in an unfinished parenthesized option list.
@@ -4086,14 +4491,17 @@ match_previous_words(const char *text, int start, int end,
         else if (TailMatches("FORMAT"))
             COMPLETE_WITH("TEXT", "XML", "JSON", "YAML");
     }
-    else if (Matches("EXPLAIN", "ANALYZE"))
+            break;
+        case Matches("EXPLAIN", "ANALYZE"):
         COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
                       "MERGE INTO", "EXECUTE", "VERBOSE");
-    else if (Matches("EXPLAIN", "(*)") ||
-             Matches("EXPLAIN", "VERBOSE") ||
-             Matches("EXPLAIN", "ANALYZE", "VERBOSE"))
+            break;
+        case Matches("EXPLAIN", "(*)"):
+        case Matches("EXPLAIN", "VERBOSE"):
+        case Matches("EXPLAIN", "ANALYZE", "VERBOSE"):
         COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
                       "MERGE INTO", "EXECUTE");
+            break;

 /* FETCH && MOVE */

@@ -4101,7 +4509,7 @@ match_previous_words(const char *text, int start, int end,
      * Complete FETCH with one of ABSOLUTE, BACKWARD, FORWARD, RELATIVE, ALL,
      * NEXT, PRIOR, FIRST, LAST, FROM, IN, and a list of cursors
      */
-    else if (Matches("FETCH|MOVE"))
+        case Matches("FETCH|MOVE"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
                                  "ABSOLUTE",
                                  "BACKWARD",
@@ -4114,58 +4522,64 @@ match_previous_words(const char *text, int start, int end,
                                  "LAST",
                                  "FROM",
                                  "IN");
+            break;

     /*
      * Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN, and a
      * list of cursors
      */
-    else if (Matches("FETCH|MOVE", "BACKWARD|FORWARD"))
+        case Matches("FETCH|MOVE", "BACKWARD|FORWARD"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
                                  "ALL",
                                  "FROM",
                                  "IN");
+            break;

     /*
      * Complete FETCH <direction> with "FROM" or "IN". These are equivalent,
      * but we may as well tab-complete both: perhaps some users prefer one
      * variant or the other.
      */
-    else if (Matches("FETCH|MOVE", "ABSOLUTE|BACKWARD|FORWARD|RELATIVE",
-                     MatchAnyExcept("FROM|IN")) ||
-             Matches("FETCH|MOVE", "ALL|NEXT|PRIOR|FIRST|LAST"))
+        case Matches("FETCH|MOVE", "ABSOLUTE|BACKWARD|FORWARD|RELATIVE", MatchAnyExcept("FROM|IN")):
+        case Matches("FETCH|MOVE", "ALL|NEXT|PRIOR|FIRST|LAST"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
                                  "FROM",
                                  "IN");
+            break;
     /* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
-    else if (Matches("FETCH|MOVE", MatchAnyN, "FROM|IN"))
+        case Matches("FETCH|MOVE", MatchAnyN, "FROM|IN"):
         COMPLETE_WITH_QUERY(Query_for_list_of_cursors);
+            break;

 /* FOREIGN DATA WRAPPER */
     /* applies in ALTER/DROP FDW and in CREATE SERVER */
-    else if (TailMatches("FOREIGN", "DATA", "WRAPPER") &&
-             !TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
+        case TailMatches("FOREIGN", "DATA", "WRAPPER"):
+        if (!TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
+            break;
     /* applies in CREATE SERVER */
-    else if (Matches("CREATE", "SERVER", MatchAnyN, "FOREIGN", "DATA", "WRAPPER", MatchAny))
+        case Matches("CREATE", "SERVER", MatchAnyN, "FOREIGN", "DATA", "WRAPPER", MatchAny):
         COMPLETE_WITH("OPTIONS");
+            break;

 /* FOREIGN TABLE */
-    else if (TailMatches("FOREIGN", "TABLE") &&
-             !TailMatches("CREATE", MatchAny, MatchAny))
+        case TailMatches("FOREIGN", "TABLE"):
+        if (!TailMatches("CREATE", MatchAny, MatchAny))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
+            break;

 /* FOREIGN SERVER */
-    else if (TailMatches("FOREIGN", "SERVER"))
+        case TailMatches("FOREIGN", "SERVER"):
         COMPLETE_WITH_QUERY(Query_for_list_of_servers);
+            break;

 /*
  * GRANT and REVOKE are allowed inside CREATE SCHEMA and
  * ALTER DEFAULT PRIVILEGES, so use TailMatches
  */
     /* Complete GRANT/REVOKE with a list of roles and privileges */
-    else if (TailMatches("GRANT|REVOKE") ||
-             TailMatches("REVOKE", "ADMIN|GRANT|INHERIT|SET", "OPTION", "FOR"))
-    {
+        case TailMatches("GRANT|REVOKE"):
+        case TailMatches("REVOKE", "ADMIN|GRANT|INHERIT|SET", "OPTION", "FOR"):
         /*
          * With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
          * privileges (can't grant roles)
@@ -4197,50 +4611,55 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH(Privilege_options_of_grant_and_revoke);
         else if (TailMatches("REVOKE", "ADMIN|INHERIT|SET", "OPTION", "FOR"))
             COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-    }
+            break;

-    else if (TailMatches("GRANT|REVOKE", "ALTER") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER"))
+        case TailMatches("GRANT|REVOKE", "ALTER"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER"):
         COMPLETE_WITH("SYSTEM");
+            break;

-    else if (TailMatches("REVOKE", "SET"))
+        case TailMatches("REVOKE", "SET"):
         COMPLETE_WITH("ON PARAMETER", "OPTION FOR");
-    else if (TailMatches("GRANT", "SET") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET") ||
-             TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM"))
+            break;
+        case TailMatches("GRANT", "SET"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET"):
+        case TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM"):
         COMPLETE_WITH("ON PARAMETER");
+            break;

-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "PARAMETER") ||
-             TailMatches("GRANT|REVOKE", MatchAny, MatchAny, "ON", "PARAMETER") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER"))
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", "PARAMETER"):
+        case TailMatches("GRANT|REVOKE", MatchAny, MatchAny, "ON", "PARAMETER"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER"):
         COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars);
+            break;

-    else if (TailMatches("GRANT", MatchAny, "ON", "PARAMETER", MatchAny) ||
-             TailMatches("GRANT", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny))
+        case TailMatches("GRANT", MatchAny, "ON", "PARAMETER", MatchAny):
+        case TailMatches("GRANT", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny):
         COMPLETE_WITH("TO");
+            break;

-    else if (TailMatches("REVOKE", MatchAny, "ON", "PARAMETER", MatchAny) ||
-             TailMatches("REVOKE", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny))
+        case TailMatches("REVOKE", MatchAny, "ON", "PARAMETER", MatchAny):
+        case TailMatches("REVOKE", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny):
         COMPLETE_WITH("FROM");
+            break;

     /*
      * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
      * TO/FROM
      */
-    else if (TailMatches("GRANT|REVOKE", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny))
-    {
+        case TailMatches("GRANT|REVOKE", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny):
         if
(TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|MAINTAIN|ALL"))
             COMPLETE_WITH("ON");
         else if (TailMatches("GRANT", MatchAny))
             COMPLETE_WITH("TO");
         else
             COMPLETE_WITH("FROM");
-    }
+            break;

     /*
      * Complete GRANT/REVOKE <sth> ON with a list of appropriate relations.
@@ -4249,9 +4668,8 @@ match_previous_words(const char *text, int start, int end,
      * here will only work if the privilege list contains exactly one
      * privilege.
      */
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON"))
-    {
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON"):
         /*
          * With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds of
          * objects supported.
@@ -4280,17 +4698,19 @@ match_previous_words(const char *text, int start, int end,
                                             "TABLE",
                                             "TABLESPACE",
                                             "TYPE");
-    }
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL"))
+            break;
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL"):
         COMPLETE_WITH("FUNCTIONS IN SCHEMA",
                       "PROCEDURES IN SCHEMA",
                       "ROUTINES IN SCHEMA",
                       "SEQUENCES IN SCHEMA",
                       "TABLES IN SCHEMA");
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN") ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN"))
+            break;
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN"):
         COMPLETE_WITH("DATA WRAPPER", "SERVER");
+            break;

     /*
      * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of
@@ -4298,9 +4718,8 @@ match_previous_words(const char *text, int start, int end,
      *
      * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
      */
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", MatchAny))
-    {
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", MatchAny):
         if (TailMatches("DATABASE"))
             COMPLETE_WITH_QUERY(Query_for_list_of_databases);
         else if (TailMatches("DOMAIN"))
@@ -4327,305 +4746,362 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH("TO");
         else
             COMPLETE_WITH("FROM");
-    }
+            break;

     /*
      * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
      * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
      */
-    else if (Matches("GRANT", MatchAnyN, "TO") ||
-             Matches("REVOKE", MatchAnyN, "FROM"))
+        case Matches("GRANT", MatchAnyN, "TO"):
+        case Matches("REVOKE", MatchAnyN, "FROM"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
+            break;

     /*
      * Offer grant options after that.
      */
-    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny))
+        case Matches("GRANT", MatchAnyN, "TO", MatchAny):
         COMPLETE_WITH("WITH ADMIN",
                       "WITH INHERIT",
                       "WITH SET",
                       "WITH GRANT OPTION",
                       "GRANTED BY");
-    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH"))
+            break;
+        case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH"):
         COMPLETE_WITH("ADMIN",
                       "INHERIT",
                       "SET",
                       "GRANT OPTION");
-    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", "ADMIN|INHERIT|SET"))
+            break;
+        case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", "ADMIN|INHERIT|SET"):
         COMPLETE_WITH("OPTION", "TRUE", "FALSE");
-    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION"))
+            break;
+        case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION"):
         COMPLETE_WITH("GRANTED BY");
-    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
+            break;
+        case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
+            break;
     /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO|FROM"))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO|FROM"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
+            break;
     /* Offer WITH GRANT OPTION after that */
-    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny))
+        case Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny):
         COMPLETE_WITH("WITH GRANT OPTION");
+            break;
     /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
-    else if (Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny))
+        case Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny):
         COMPLETE_WITH("TO");
-    else if (Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny))
+            break;
+        case Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny):
         COMPLETE_WITH("FROM");
+            break;

     /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA",
MatchAny))
-    {
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA",
MatchAny):
         if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
             COMPLETE_WITH("TO");
         else
             COMPLETE_WITH("FROM");
-    }
+            break;

     /* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny))
-    {
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny):
         if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
             COMPLETE_WITH("TO");
         else
             COMPLETE_WITH("FROM");
-    }
+            break;

     /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
-    else if (TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny) ||
-             TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny))
-    {
+        case TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny):
+        case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny):
         if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
             COMPLETE_WITH("TO");
         else
             COMPLETE_WITH("FROM");
-    }
+            break;

 /* GROUP BY */
-    else if (TailMatches("FROM", MatchAny, "GROUP"))
+        case TailMatches("FROM", MatchAny, "GROUP"):
         COMPLETE_WITH("BY");
+            break;

 /* IMPORT FOREIGN SCHEMA */
-    else if (Matches("IMPORT"))
+        case Matches("IMPORT"):
         COMPLETE_WITH("FOREIGN SCHEMA");
-    else if (Matches("IMPORT", "FOREIGN"))
+            break;
+        case Matches("IMPORT", "FOREIGN"):
         COMPLETE_WITH("SCHEMA");
-    else if (Matches("IMPORT", "FOREIGN", "SCHEMA", MatchAny))
+            break;
+        case Matches("IMPORT", "FOREIGN", "SCHEMA", MatchAny):
         COMPLETE_WITH("EXCEPT (", "FROM SERVER", "LIMIT TO (");
-    else if (TailMatches("LIMIT", "TO", "(*)") ||
-             TailMatches("EXCEPT", "(*)"))
+            break;
+        case TailMatches("LIMIT", "TO", "(*)"):
+        case TailMatches("EXCEPT", "(*)"):
         COMPLETE_WITH("FROM SERVER");
-    else if (TailMatches("FROM", "SERVER", MatchAny))
+            break;
+        case TailMatches("FROM", "SERVER", MatchAny):
         COMPLETE_WITH("INTO");
-    else if (TailMatches("FROM", "SERVER", MatchAny, "INTO"))
+            break;
+        case TailMatches("FROM", "SERVER", MatchAny, "INTO"):
         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
-    else if (TailMatches("FROM", "SERVER", MatchAny, "INTO", MatchAny))
+            break;
+        case TailMatches("FROM", "SERVER", MatchAny, "INTO", MatchAny):
         COMPLETE_WITH("OPTIONS (");
+            break;

 /* INSERT --- can be inside EXPLAIN, RULE, etc */
     /* Complete NOT MATCHED THEN INSERT */
-    else if (TailMatches("NOT", "MATCHED", "THEN", "INSERT"))
+        case TailMatches("NOT", "MATCHED", "THEN", "INSERT"):
         COMPLETE_WITH("VALUES", "(");
+            break;
     /* Complete INSERT with "INTO" */
-    else if (TailMatches("INSERT"))
+        case TailMatches("INSERT"):
         COMPLETE_WITH("INTO");
+            break;
     /* Complete INSERT INTO with table names */
-    else if (TailMatches("INSERT", "INTO"))
+        case TailMatches("INSERT", "INTO"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
+            break;
     /* Complete "INSERT INTO <table> (" with attribute names */
-    else if (TailMatches("INSERT", "INTO", MatchAny, "("))
+        case TailMatches("INSERT", "INTO", MatchAny, "("):
         COMPLETE_WITH_ATTR(prev2_wd);
+            break;

     /*
      * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
      * "TABLE" or "DEFAULT VALUES" or "OVERRIDING"
      */
-    else if (TailMatches("INSERT", "INTO", MatchAny))
+        case TailMatches("INSERT", "INTO", MatchAny):
         COMPLETE_WITH("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", "OVERRIDING");
+            break;

     /*
      * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
      * "TABLE" or "OVERRIDING"
      */
-    else if (TailMatches("INSERT", "INTO", MatchAny, MatchAny) &&
-             ends_with(prev_wd, ')'))
+        case TailMatches("INSERT", "INTO", MatchAny, MatchAny):
+        if (ends_with(prev_wd, ')'))
         COMPLETE_WITH("SELECT", "TABLE", "VALUES", "OVERRIDING");
+            break;

     /* Complete OVERRIDING */
-    else if (TailMatches("OVERRIDING"))
+        case TailMatches("OVERRIDING"):
         COMPLETE_WITH("SYSTEM VALUE", "USER VALUE");
+            break;

     /* Complete after OVERRIDING clause */
-    else if (TailMatches("OVERRIDING", MatchAny, "VALUE"))
+        case TailMatches("OVERRIDING", MatchAny, "VALUE"):
         COMPLETE_WITH("SELECT", "TABLE", "VALUES");
+            break;

     /* Insert an open parenthesis after "VALUES" */
-    else if (TailMatches("VALUES") && !TailMatches("DEFAULT", "VALUES"))
+        case TailMatches("VALUES"):
+        if (!TailMatches("DEFAULT", "VALUES"))
         COMPLETE_WITH("(");
+            break;

 /* LOCK */
     /* Complete LOCK [TABLE] [ONLY] with a list of tables */
-    else if (Matches("LOCK"))
+        case Matches("LOCK"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
                                         "TABLE", "ONLY");
-    else if (Matches("LOCK", "TABLE"))
+            break;
+        case Matches("LOCK", "TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
                                         "ONLY");
-    else if (Matches("LOCK", "TABLE", "ONLY") || Matches("LOCK", "ONLY"))
+            break;
+        case Matches("LOCK", "TABLE", "ONLY"):
+        case Matches("LOCK", "ONLY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            break;
     /* For the following, handle the case of a single table only for now */

     /* Complete LOCK [TABLE] [ONLY] <table> with IN or NOWAIT */
-    else if (Matches("LOCK", MatchAnyExcept("TABLE|ONLY")) ||
-             Matches("LOCK", "TABLE", MatchAnyExcept("ONLY")) ||
-             Matches("LOCK", "ONLY", MatchAny) ||
-             Matches("LOCK", "TABLE", "ONLY", MatchAny))
+        case Matches("LOCK", MatchAnyExcept("TABLE|ONLY")):
+        case Matches("LOCK", "TABLE", MatchAnyExcept("ONLY")):
+        case Matches("LOCK", "ONLY", MatchAny):
+        case Matches("LOCK", "TABLE", "ONLY", MatchAny):
         COMPLETE_WITH("IN", "NOWAIT");
+            break;

     /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
-    else if (Matches("LOCK", MatchAnyN, "IN"))
+        case Matches("LOCK", MatchAnyN, "IN"):
         COMPLETE_WITH("ACCESS SHARE MODE",
                       "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
                       "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
                       "SHARE ROW EXCLUSIVE MODE",
                       "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
+            break;

     /*
      * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of lock
      * mode
      */
-    else if (Matches("LOCK", MatchAnyN, "IN", "ACCESS|ROW"))
+        case Matches("LOCK", MatchAnyN, "IN", "ACCESS|ROW"):
         COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");
+            break;

     /* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
-    else if (Matches("LOCK", MatchAnyN, "IN", "SHARE"))
+        case Matches("LOCK", MatchAnyN, "IN", "SHARE"):
         COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
                       "UPDATE EXCLUSIVE MODE");
+            break;

     /* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
-    else if (Matches("LOCK", MatchAnyN, "MODE"))
+        case Matches("LOCK", MatchAnyN, "MODE"):
         COMPLETE_WITH("NOWAIT");
+            break;

 /* MERGE --- can be inside EXPLAIN */
-    else if (TailMatches("MERGE"))
+        case TailMatches("MERGE"):
         COMPLETE_WITH("INTO");
-    else if (TailMatches("MERGE", "INTO"))
+            break;
+        case TailMatches("MERGE", "INTO"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_mergetargets);
+            break;

     /* Complete MERGE INTO <table> [[AS] <alias>] with USING */
-    else if (TailMatches("MERGE", "INTO", MatchAny))
+        case TailMatches("MERGE", "INTO", MatchAny):
         COMPLETE_WITH("USING", "AS");
-    else if (TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny) ||
-             TailMatches("MERGE", "INTO", MatchAny, MatchAnyExcept("USING|AS")))
+            break;
+        case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny):
+        case TailMatches("MERGE", "INTO", MatchAny, MatchAnyExcept("USING|AS")):
         COMPLETE_WITH("USING");
+            break;

     /*
      * Complete MERGE INTO ... USING with a list of relations supporting
      * SELECT
      */
-    else if (TailMatches("MERGE", "INTO", MatchAny, "USING") ||
-             TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING") ||
-             TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING"))
+        case TailMatches("MERGE", "INTO", MatchAny, "USING"):
+        case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING"):
+        case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            break;

     /*
      * Complete MERGE INTO <table> [[AS] <alias>] USING <relations> [[AS]
      * alias] with ON
      */
-    else if (TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny) ||
-             TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny) ||
-             TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny))
+        case TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny):
+        case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny):
+        case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny):
         COMPLETE_WITH("AS", "ON");
-    else if (TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, "AS", MatchAny) ||
-             TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny) ||
-             TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, "AS", MatchAny) ||
-             TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")) ||
-             TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")) ||
-             TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")))
+            break;
+        case TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, "AS", MatchAny):
+        case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny):
+        case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, "AS", MatchAny):
+        case TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")):
+        case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")):
+        case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")):
         COMPLETE_WITH("ON");
+            break;

     /* Complete MERGE INTO ... ON with target table attributes */
-    else if (TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"))
+        case TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"):
         COMPLETE_WITH_ATTR(prev4_wd);
-    else if (TailMatches("INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny, "ON"))
+            break;
+        case TailMatches("INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny, "ON"):
         COMPLETE_WITH_ATTR(prev8_wd);
-    else if (TailMatches("INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAny, "ON"))
+            break;
+        case TailMatches("INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAny, "ON"):
         COMPLETE_WITH_ATTR(prev6_wd);
+            break;

     /*
      * Complete ... USING <relation> [[AS] alias] ON join condition
      * (consisting of one or three words typically used) with WHEN [NOT]
      * MATCHED
      */
-    else if (TailMatches("USING", MatchAny, "ON", MatchAny) ||
-             TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny) ||
-             TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny) ||
-             TailMatches("USING", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")) ||
-             TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"),
MatchAnyExcept("WHEN"))|| 
-             TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")))
+        case TailMatches("USING", MatchAny, "ON", MatchAny):
+        case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny):
+        case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny):
+        case TailMatches("USING", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")):
+        case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"),
MatchAnyExcept("WHEN")):
+        case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")):
         COMPLETE_WITH("WHEN MATCHED", "WHEN NOT MATCHED");
-    else if (TailMatches("USING", MatchAny, "ON", MatchAny, "WHEN") ||
-             TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, "WHEN") ||
-             TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, "WHEN") ||
-             TailMatches("USING", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN") ||
-             TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN") ||
-             TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"))
+            break;
+        case TailMatches("USING", MatchAny, "ON", MatchAny, "WHEN"):
+        case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, "WHEN"):
+        case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, "WHEN"):
+        case TailMatches("USING", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"):
+        case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"):
+        case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"):
         COMPLETE_WITH("MATCHED", "NOT MATCHED");
+            break;

     /*
      * Complete ... WHEN MATCHED and WHEN NOT MATCHED BY SOURCE|TARGET with
      * THEN/AND
      */
-    else if (TailMatches("WHEN", "MATCHED") ||
-             TailMatches("WHEN", "NOT", "MATCHED", "BY", "SOURCE|TARGET"))
+        case TailMatches("WHEN", "MATCHED"):
+        case TailMatches("WHEN", "NOT", "MATCHED", "BY", "SOURCE|TARGET"):
         COMPLETE_WITH("THEN", "AND");
+            break;

     /* Complete ... WHEN NOT MATCHED with BY/THEN/AND */
-    else if (TailMatches("WHEN", "NOT", "MATCHED"))
+        case TailMatches("WHEN", "NOT", "MATCHED"):
         COMPLETE_WITH("BY", "THEN", "AND");
+            break;

     /* Complete ... WHEN NOT MATCHED BY with SOURCE/TARGET */
-    else if (TailMatches("WHEN", "NOT", "MATCHED", "BY"))
+        case TailMatches("WHEN", "NOT", "MATCHED", "BY"):
         COMPLETE_WITH("SOURCE", "TARGET");
+            break;

     /*
      * Complete ... WHEN MATCHED THEN and WHEN NOT MATCHED BY SOURCE THEN with
      * UPDATE SET/DELETE/DO NOTHING
      */
-    else if (TailMatches("WHEN", "MATCHED", "THEN") ||
-             TailMatches("WHEN", "NOT", "MATCHED", "BY", "SOURCE", "THEN"))
+        case TailMatches("WHEN", "MATCHED", "THEN"):
+        case TailMatches("WHEN", "NOT", "MATCHED", "BY", "SOURCE", "THEN"):
         COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
+            break;

     /*
      * Complete ... WHEN NOT MATCHED [BY TARGET] THEN with INSERT/DO NOTHING
      */
-    else if (TailMatches("WHEN", "NOT", "MATCHED", "THEN") ||
-             TailMatches("WHEN", "NOT", "MATCHED", "BY", "TARGET", "THEN"))
+        case TailMatches("WHEN", "NOT", "MATCHED", "THEN"):
+        case TailMatches("WHEN", "NOT", "MATCHED", "BY", "TARGET", "THEN"):
         COMPLETE_WITH("INSERT", "DO NOTHING");
+            break;

 /* NOTIFY --- can be inside EXPLAIN, RULE, etc */
-    else if (TailMatches("NOTIFY"))
+        case TailMatches("NOTIFY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_channels);
+            break;

 /* OPTIONS */
-    else if (TailMatches("OPTIONS"))
+        case TailMatches("OPTIONS"):
         COMPLETE_WITH("(");
+            break;

 /* OWNER TO  - complete with available roles */
-    else if (TailMatches("OWNER", "TO"))
+        case TailMatches("OWNER", "TO"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_owner_roles);
+            break;

 /* ORDER BY */
-    else if (TailMatches("FROM", MatchAny, "ORDER"))
+        case TailMatches("FROM", MatchAny, "ORDER"):
         COMPLETE_WITH("BY");
-    else if (TailMatches("FROM", MatchAny, "ORDER", "BY"))
+            break;
+        case TailMatches("FROM", MatchAny, "ORDER", "BY"):
         COMPLETE_WITH_ATTR(prev3_wd);
+            break;

 /* PREPARE xx AS */
-    else if (Matches("PREPARE", MatchAny, "AS"))
+        case Matches("PREPARE", MatchAny, "AS"):
         COMPLETE_WITH("SELECT", "UPDATE", "INSERT INTO", "DELETE FROM");
+            break;

 /*
  * PREPARE TRANSACTION is missing on purpose. It's intended for transaction
@@ -4633,74 +5109,98 @@ match_previous_words(const char *text, int start, int end,
  */

 /* REASSIGN OWNED BY xxx TO yyy */
-    else if (Matches("REASSIGN"))
+        case Matches("REASSIGN"):
         COMPLETE_WITH("OWNED BY");
-    else if (Matches("REASSIGN", "OWNED"))
+            break;
+        case Matches("REASSIGN", "OWNED"):
         COMPLETE_WITH("BY");
-    else if (Matches("REASSIGN", "OWNED", "BY"))
+            break;
+        case Matches("REASSIGN", "OWNED", "BY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-    else if (Matches("REASSIGN", "OWNED", "BY", MatchAny))
+            break;
+        case Matches("REASSIGN", "OWNED", "BY", MatchAny):
         COMPLETE_WITH("TO");
-    else if (Matches("REASSIGN", "OWNED", "BY", MatchAny, "TO"))
+            break;
+        case Matches("REASSIGN", "OWNED", "BY", MatchAny, "TO"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            break;

 /* REFRESH MATERIALIZED VIEW */
-    else if (Matches("REFRESH"))
+        case Matches("REFRESH"):
         COMPLETE_WITH("MATERIALIZED VIEW");
-    else if (Matches("REFRESH", "MATERIALIZED"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED"):
         COMPLETE_WITH("VIEW");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
                                         "CONCURRENTLY");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny):
         COMPLETE_WITH("WITH");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny):
         COMPLETE_WITH("WITH");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"):
         COMPLETE_WITH("NO DATA", "DATA");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"):
         COMPLETE_WITH("NO DATA", "DATA");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"):
         COMPLETE_WITH("DATA");
-    else if (Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO"))
+            break;
+        case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO"):
         COMPLETE_WITH("DATA");
+            break;

 /* REINDEX */
-    else if (Matches("REINDEX") ||
-             Matches("REINDEX", "(*)"))
+        case Matches("REINDEX"):
+        case Matches("REINDEX", "(*)"):
         COMPLETE_WITH("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
-    else if (Matches("REINDEX", "TABLE") ||
-             Matches("REINDEX", "(*)", "TABLE"))
+            break;
+        case Matches("REINDEX", "TABLE"):
+        case Matches("REINDEX", "(*)", "TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexables,
                                         "CONCURRENTLY");
-    else if (Matches("REINDEX", "INDEX") ||
-             Matches("REINDEX", "(*)", "INDEX"))
+            break;
+        case Matches("REINDEX", "INDEX"):
+        case Matches("REINDEX", "(*)", "INDEX"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
                                         "CONCURRENTLY");
-    else if (Matches("REINDEX", "SCHEMA") ||
-             Matches("REINDEX", "(*)", "SCHEMA"))
+            break;
+        case Matches("REINDEX", "SCHEMA"):
+        case Matches("REINDEX", "(*)", "SCHEMA"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
                                  "CONCURRENTLY");
-    else if (Matches("REINDEX", "SYSTEM|DATABASE") ||
-             Matches("REINDEX", "(*)", "SYSTEM|DATABASE"))
+            break;
+        case Matches("REINDEX", "SYSTEM|DATABASE"):
+        case Matches("REINDEX", "(*)", "SYSTEM|DATABASE"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_databases,
                                  "CONCURRENTLY");
-    else if (Matches("REINDEX", "TABLE", "CONCURRENTLY") ||
-             Matches("REINDEX", "(*)", "TABLE", "CONCURRENTLY"))
+            break;
+        case Matches("REINDEX", "TABLE", "CONCURRENTLY"):
+        case Matches("REINDEX", "(*)", "TABLE", "CONCURRENTLY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
-    else if (Matches("REINDEX", "INDEX", "CONCURRENTLY") ||
-             Matches("REINDEX", "(*)", "INDEX", "CONCURRENTLY"))
+            break;
+        case Matches("REINDEX", "INDEX", "CONCURRENTLY"):
+        case Matches("REINDEX", "(*)", "INDEX", "CONCURRENTLY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
-    else if (Matches("REINDEX", "SCHEMA", "CONCURRENTLY") ||
-             Matches("REINDEX", "(*)", "SCHEMA", "CONCURRENTLY"))
+            break;
+        case Matches("REINDEX", "SCHEMA", "CONCURRENTLY"):
+        case Matches("REINDEX", "(*)", "SCHEMA", "CONCURRENTLY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
-    else if (Matches("REINDEX", "SYSTEM|DATABASE", "CONCURRENTLY") ||
-             Matches("REINDEX", "(*)", "SYSTEM|DATABASE", "CONCURRENTLY"))
+            break;
+        case Matches("REINDEX", "SYSTEM|DATABASE", "CONCURRENTLY"):
+        case Matches("REINDEX", "(*)", "SYSTEM|DATABASE", "CONCURRENTLY"):
         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
-    else if (HeadMatches("REINDEX", "(*") &&
-             !HeadMatches("REINDEX", "(*)"))
+            break;
+        case HeadMatches("REINDEX", "(*"):
+        if (!HeadMatches("REINDEX", "(*)"))
     {
         /*
          * This fires if we're in an unfinished parenthesized option list.
@@ -4712,30 +5212,37 @@ match_previous_words(const char *text, int start, int end,
         else if (TailMatches("TABLESPACE"))
             COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
     }
+            break;

 /* SECURITY LABEL */
-    else if (Matches("SECURITY"))
+        case Matches("SECURITY"):
         COMPLETE_WITH("LABEL");
-    else if (Matches("SECURITY", "LABEL"))
+            break;
+        case Matches("SECURITY", "LABEL"):
         COMPLETE_WITH("ON", "FOR");
-    else if (Matches("SECURITY", "LABEL", "FOR", MatchAny))
+            break;
+        case Matches("SECURITY", "LABEL", "FOR", MatchAny):
         COMPLETE_WITH("ON");
-    else if (Matches("SECURITY", "LABEL", "ON") ||
-             Matches("SECURITY", "LABEL", "FOR", MatchAny, "ON"))
+            break;
+        case Matches("SECURITY", "LABEL", "ON"):
+        case Matches("SECURITY", "LABEL", "FOR", MatchAny, "ON"):
         COMPLETE_WITH("TABLE", "COLUMN", "AGGREGATE", "DATABASE", "DOMAIN",
                       "EVENT TRIGGER", "FOREIGN TABLE", "FUNCTION",
                       "LARGE OBJECT", "MATERIALIZED VIEW", "LANGUAGE",
                       "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA",
                       "SEQUENCE", "SUBSCRIPTION", "TABLESPACE", "TYPE", "VIEW");
-    else if (Matches("SECURITY", "LABEL", "ON", MatchAny, MatchAny))
+            break;
+        case Matches("SECURITY", "LABEL", "ON", MatchAny, MatchAny):
         COMPLETE_WITH("IS");
+            break;

 /* SELECT */
     /* naah . . . */

 /* SET, RESET, SHOW */
     /* Complete with a variable name */
-    else if (TailMatches("SET|RESET") && !TailMatches("UPDATE", MatchAny, "SET"))
+        case TailMatches("SET|RESET"):
+        if (!TailMatches("UPDATE", MatchAny, "SET"))
         COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
                                           "CONSTRAINTS",
                                           "TRANSACTION",
@@ -4743,81 +5250,100 @@ match_previous_words(const char *text, int start, int end,
                                           "ROLE",
                                           "TABLESPACE",
                                           "ALL");
-    else if (Matches("SHOW"))
+            break;
+        case Matches("SHOW"):
         COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_show_vars,
                                           "SESSION AUTHORIZATION",
                                           "ALL");
-    else if (Matches("SHOW", "SESSION"))
+            break;
+        case Matches("SHOW", "SESSION"):
         COMPLETE_WITH("AUTHORIZATION");
+            break;
     /* Complete "SET TRANSACTION" */
-    else if (Matches("SET", "TRANSACTION"))
+        case Matches("SET", "TRANSACTION"):
         COMPLETE_WITH("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
-    else if (Matches("BEGIN|START", "TRANSACTION") ||
-             Matches("BEGIN", "WORK") ||
-             Matches("BEGIN") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION"))
+            break;
+        case Matches("BEGIN|START", "TRANSACTION"):
+        case Matches("BEGIN", "WORK"):
+        case Matches("BEGIN"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION"):
         COMPLETE_WITH("ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
-    else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "NOT") ||
-             Matches("BEGIN", "NOT") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "NOT"))
+            break;
+        case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "NOT"):
+        case Matches("BEGIN", "NOT"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "NOT"):
         COMPLETE_WITH("DEFERRABLE");
-    else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION") ||
-             Matches("BEGIN", "ISOLATION") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION"))
+            break;
+        case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION"):
+        case Matches("BEGIN", "ISOLATION"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION"):
         COMPLETE_WITH("LEVEL");
-    else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL") ||
-             Matches("BEGIN", "ISOLATION", "LEVEL") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL"))
+            break;
+        case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL"):
+        case Matches("BEGIN", "ISOLATION", "LEVEL"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL"):
         COMPLETE_WITH("READ", "REPEATABLE READ", "SERIALIZABLE");
-    else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ") ||
-             Matches("BEGIN", "ISOLATION", "LEVEL", "READ") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "READ"))
+            break;
+        case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ"):
+        case Matches("BEGIN", "ISOLATION", "LEVEL", "READ"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "READ"):
         COMPLETE_WITH("UNCOMMITTED", "COMMITTED");
-    else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE") ||
-             Matches("BEGIN", "ISOLATION", "LEVEL", "REPEATABLE") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "REPEATABLE"))
+            break;
+        case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE"):
+        case Matches("BEGIN", "ISOLATION", "LEVEL", "REPEATABLE"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "REPEATABLE"):
         COMPLETE_WITH("READ");
-    else if (Matches("SET|BEGIN|START", "TRANSACTION|WORK", "READ") ||
-             Matches("BEGIN", "READ") ||
-             Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "READ"))
+            break;
+        case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "READ"):
+        case Matches("BEGIN", "READ"):
+        case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "READ"):
         COMPLETE_WITH("ONLY", "WRITE");
+            break;
     /* SET CONSTRAINTS */
-    else if (Matches("SET", "CONSTRAINTS"))
+        case Matches("SET", "CONSTRAINTS"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_constraints_with_schema,
                                         "ALL");
+            break;
     /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
-    else if (Matches("SET", "CONSTRAINTS", MatchAny))
+        case Matches("SET", "CONSTRAINTS", MatchAny):
         COMPLETE_WITH("DEFERRED", "IMMEDIATE");
+            break;
     /* Complete SET ROLE */
-    else if (Matches("SET", "ROLE"))
+        case Matches("SET", "ROLE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            break;
     /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
-    else if (Matches("SET", "SESSION"))
+        case Matches("SET", "SESSION"):
         COMPLETE_WITH("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
+            break;
     /* Complete SET SESSION AUTHORIZATION with username */
-    else if (Matches("SET", "SESSION", "AUTHORIZATION"))
+        case Matches("SET", "SESSION", "AUTHORIZATION"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  "DEFAULT");
+            break;
     /* Complete RESET SESSION with AUTHORIZATION */
-    else if (Matches("RESET", "SESSION"))
+        case Matches("RESET", "SESSION"):
         COMPLETE_WITH("AUTHORIZATION");
+            break;
     /* Complete SET <var> with "TO" */
-    else if (Matches("SET", MatchAny))
+        case Matches("SET", MatchAny):
         COMPLETE_WITH("TO");
+            break;

     /*
      * Complete ALTER DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER ... SET
      * <name>
      */
-    else if (Matches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER", MatchAnyN, "SET",
MatchAnyExcept("SCHEMA")))
+        case Matches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER", MatchAnyN, "SET",
MatchAnyExcept("SCHEMA")):
         COMPLETE_WITH("FROM CURRENT", "TO");
+            break;

     /*
      * Suggest possible variable values in SET variable TO|=, along with the
      * preceding ALTER syntaxes.
      */
-    else if (TailMatches("SET", MatchAny, "TO|=") &&
-             !TailMatches("UPDATE", MatchAny, "SET", MatchAny, "TO|="))
+        case TailMatches("SET", MatchAny, "TO|="):
+        if (!TailMatches("UPDATE", MatchAny, "SET", MatchAny, "TO|="))
     {
         /* special cased code for individual GUCs */
         if (TailMatches("DateStyle", "TO|="))
@@ -4863,98 +5389,122 @@ match_previous_words(const char *text, int start, int end,
             }
         }
     }
+            break;

 /* START TRANSACTION */
-    else if (Matches("START"))
+        case Matches("START"):
         COMPLETE_WITH("TRANSACTION");
+            break;

 /* TABLE, but not TABLE embedded in other commands */
-    else if (Matches("TABLE"))
+        case Matches("TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            break;

 /* TABLESAMPLE */
-    else if (TailMatches("TABLESAMPLE"))
+        case TailMatches("TABLESAMPLE"):
         COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods);
-    else if (TailMatches("TABLESAMPLE", MatchAny))
+            break;
+        case TailMatches("TABLESAMPLE", MatchAny):
         COMPLETE_WITH("(");
+            break;

 /* TRUNCATE */
-    else if (Matches("TRUNCATE"))
+        case Matches("TRUNCATE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
                                         "TABLE", "ONLY");
-    else if (Matches("TRUNCATE", "TABLE"))
+            break;
+        case Matches("TRUNCATE", "TABLE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
                                         "ONLY");
-    else if (Matches("TRUNCATE", MatchAnyN, "ONLY"))
+            break;
+        case Matches("TRUNCATE", MatchAnyN, "ONLY"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
-    else if (Matches("TRUNCATE", MatchAny) ||
-             Matches("TRUNCATE", "TABLE|ONLY", MatchAny) ||
-             Matches("TRUNCATE", "TABLE", "ONLY", MatchAny))
+            break;
+        case Matches("TRUNCATE", MatchAny):
+        case Matches("TRUNCATE", "TABLE|ONLY", MatchAny):
+        case Matches("TRUNCATE", "TABLE", "ONLY", MatchAny):
         COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
-    else if (Matches("TRUNCATE", MatchAnyN, "IDENTITY"))
+            break;
+        case Matches("TRUNCATE", MatchAnyN, "IDENTITY"):
         COMPLETE_WITH("CASCADE", "RESTRICT");
+            break;

 /* UNLISTEN */
-    else if (Matches("UNLISTEN"))
+        case Matches("UNLISTEN"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_channels, "*");
+            break;

 /* UPDATE --- can be inside EXPLAIN, RULE, etc */
     /* If prev. word is UPDATE suggest a list of tables */
-    else if (TailMatches("UPDATE"))
+        case TailMatches("UPDATE"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
+            break;
     /* Complete UPDATE <table> with "SET" */
-    else if (TailMatches("UPDATE", MatchAny))
+        case TailMatches("UPDATE", MatchAny):
         COMPLETE_WITH("SET");
+            break;
     /* Complete UPDATE <table> SET with list of attributes */
-    else if (TailMatches("UPDATE", MatchAny, "SET"))
+        case TailMatches("UPDATE", MatchAny, "SET"):
         COMPLETE_WITH_ATTR(prev2_wd);
+            break;
     /* UPDATE <table> SET <attr> = */
-    else if (TailMatches("UPDATE", MatchAny, "SET", MatchAnyExcept("*=")))
+        case TailMatches("UPDATE", MatchAny, "SET", MatchAnyExcept("*=")):
         COMPLETE_WITH("=");
+            break;

 /* USER MAPPING */
-    else if (Matches("ALTER|CREATE|DROP", "USER", "MAPPING"))
+        case Matches("ALTER|CREATE|DROP", "USER", "MAPPING"):
         COMPLETE_WITH("FOR");
-    else if (Matches("CREATE", "USER", "MAPPING", "FOR"))
+            break;
+        case Matches("CREATE", "USER", "MAPPING", "FOR"):
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  "CURRENT_ROLE",
                                  "CURRENT_USER",
                                  "PUBLIC",
                                  "USER");
-    else if (Matches("ALTER|DROP", "USER", "MAPPING", "FOR"))
+            break;
+        case Matches("ALTER|DROP", "USER", "MAPPING", "FOR"):
         COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
-    else if (Matches("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny))
+            break;
+        case Matches("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny):
         COMPLETE_WITH("SERVER");
-    else if (Matches("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny))
+            break;
+        case Matches("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny):
         COMPLETE_WITH("OPTIONS");
+            break;

 /*
  * VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ]
  * VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]
  */
-    else if (Matches("VACUUM"))
+        case Matches("VACUUM"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
                                         "FULL",
                                         "FREEZE",
                                         "ANALYZE",
                                         "VERBOSE");
-    else if (Matches("VACUUM", "FULL"))
+            break;
+        case Matches("VACUUM", "FULL"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
                                         "FREEZE",
                                         "ANALYZE",
                                         "VERBOSE");
-    else if (Matches("VACUUM", "FREEZE") ||
-             Matches("VACUUM", "FULL", "FREEZE"))
+            break;
+        case Matches("VACUUM", "FREEZE"):
+        case Matches("VACUUM", "FULL", "FREEZE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
                                         "VERBOSE",
                                         "ANALYZE");
-    else if (Matches("VACUUM", "VERBOSE") ||
-             Matches("VACUUM", "FULL|FREEZE", "VERBOSE") ||
-             Matches("VACUUM", "FULL", "FREEZE", "VERBOSE"))
+            break;
+        case Matches("VACUUM", "VERBOSE"):
+        case Matches("VACUUM", "FULL|FREEZE", "VERBOSE"):
+        case Matches("VACUUM", "FULL", "FREEZE", "VERBOSE"):
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
                                         "ANALYZE");
-    else if (HeadMatches("VACUUM", "(*") &&
-             !HeadMatches("VACUUM", "(*)"))
+            break;
+        case HeadMatches("VACUUM", "(*"):
+        if (!HeadMatches("VACUUM", "(*)"))
     {
         /*
          * This fires if we're in an unfinished parenthesized option list.
@@ -4972,11 +5522,14 @@ match_previous_words(const char *text, int start, int end,
         else if (TailMatches("INDEX_CLEANUP"))
             COMPLETE_WITH("AUTO", "ON", "OFF");
     }
-    else if (Matches("VACUUM", MatchAnyN, "("))
+            break;
+        case Matches("VACUUM", MatchAnyN, "("):
         /* "VACUUM (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
-    else if (HeadMatches("VACUUM"))
+            break;
+        case HeadMatches("VACUUM"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables);
+            break;

 /* WITH [RECURSIVE] */

@@ -4984,139 +5537,186 @@ match_previous_words(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 (Matches("WITH"))
+        case Matches("WITH"):
         COMPLETE_WITH("RECURSIVE");
+            break;

 /* WHERE */
     /* Simple case of the word before the where being the table name */
-    else if (TailMatches(MatchAny, "WHERE"))
+        case TailMatches(MatchAny, "WHERE"):
         COMPLETE_WITH_ATTR(prev2_wd);
+            break;

 /* ... FROM ... */
 /* TODO: also include SRF ? */
-    else if (TailMatches("FROM") && !Matches("COPY|\\copy", MatchAny, "FROM"))
+        case TailMatches("FROM"):
+        if (!Matches("COPY|\\copy", MatchAny, "FROM"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            break;

 /* ... JOIN ... */
-    else if (TailMatches("JOIN"))
+        case TailMatches("JOIN"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            break;

 /* ... AT [ LOCAL | TIME ZONE ] ... */
-    else if (TailMatches("AT"))
+        case TailMatches("AT"):
         COMPLETE_WITH("LOCAL", "TIME ZONE");
-    else if (TailMatches("AT", "TIME", "ZONE"))
+            break;
+        case TailMatches("AT", "TIME", "ZONE"):
         COMPLETE_WITH_TIMEZONE_NAME();
+            break;

 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
-    else if (TailMatchesCS("\\?"))
+        case TailMatchesCS("\\?"):
         COMPLETE_WITH_CS("commands", "options", "variables");
-    else if (TailMatchesCS("\\connect|\\c"))
-    {
+            break;
+        case TailMatchesCS("\\connect|\\c"):
         if (!recognized_connection_string(text))
             COMPLETE_WITH_QUERY(Query_for_list_of_databases);
-    }
-    else if (TailMatchesCS("\\connect|\\c", MatchAny))
-    {
+            break;
+        case TailMatchesCS("\\connect|\\c", MatchAny):
         if (!recognized_connection_string(prev_wd))
             COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-    }
-    else if (TailMatchesCS("\\da*"))
+            break;
+        case TailMatchesCS("\\da*"):
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates);
-    else if (TailMatchesCS("\\dAc*", MatchAny) ||
-             TailMatchesCS("\\dAf*", MatchAny))
+            break;
+        case TailMatchesCS("\\dAc*", MatchAny):
+        case TailMatchesCS("\\dAf*", MatchAny):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (TailMatchesCS("\\dAo*", MatchAny) ||
-             TailMatchesCS("\\dAp*", MatchAny))
+            break;
+        case TailMatchesCS("\\dAo*", MatchAny):
+        case TailMatchesCS("\\dAp*", MatchAny):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families);
-    else if (TailMatchesCS("\\dA*"))
+            break;
+        case TailMatchesCS("\\dA*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
-    else if (TailMatchesCS("\\db*"))
+            break;
+        case TailMatchesCS("\\db*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
-    else if (TailMatchesCS("\\dconfig*"))
+            break;
+        case TailMatchesCS("\\dconfig*"):
         COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_show_vars);
-    else if (TailMatchesCS("\\dD*"))
+            break;
+        case TailMatchesCS("\\dD*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
-    else if (TailMatchesCS("\\des*"))
+            break;
+        case TailMatchesCS("\\des*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_servers);
-    else if (TailMatchesCS("\\deu*"))
+            break;
+        case TailMatchesCS("\\deu*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
-    else if (TailMatchesCS("\\dew*"))
+            break;
+        case TailMatchesCS("\\dew*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
-    else if (TailMatchesCS("\\df*"))
+            break;
+        case TailMatchesCS("\\df*"):
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
-    else if (HeadMatchesCS("\\df*"))
+            break;
+        case HeadMatchesCS("\\df*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            break;

-    else if (TailMatchesCS("\\dFd*"))
+        case TailMatchesCS("\\dFd*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
-    else if (TailMatchesCS("\\dFp*"))
+            break;
+        case TailMatchesCS("\\dFp*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
-    else if (TailMatchesCS("\\dFt*"))
+            break;
+        case TailMatchesCS("\\dFt*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
+            break;
     /* must be at end of \dF alternatives: */
-    else if (TailMatchesCS("\\dF*"))
+        case TailMatchesCS("\\dF*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
+            break;

-    else if (TailMatchesCS("\\di*"))
+        case TailMatchesCS("\\di*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
-    else if (TailMatchesCS("\\dL*"))
+            break;
+        case TailMatchesCS("\\dL*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-    else if (TailMatchesCS("\\dn*"))
+            break;
+        case TailMatchesCS("\\dn*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
+            break;
     /* no support for completing operators, but we can complete types: */
-    else if (HeadMatchesCS("\\do*", MatchAny))
+        case HeadMatchesCS("\\do*", MatchAny):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (TailMatchesCS("\\dp") || TailMatchesCS("\\z"))
+            break;
+        case TailMatchesCS("\\dp"):
+        case TailMatchesCS("\\z"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
-    else if (TailMatchesCS("\\dPi*"))
+            break;
+        case TailMatchesCS("\\dPi*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes);
-    else if (TailMatchesCS("\\dPt*"))
+            break;
+        case TailMatchesCS("\\dPt*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
-    else if (TailMatchesCS("\\dP*"))
+            break;
+        case TailMatchesCS("\\dP*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations);
-    else if (TailMatchesCS("\\dRp*"))
+            break;
+        case TailMatchesCS("\\dRp*"):
         COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_publications);
-    else if (TailMatchesCS("\\dRs*"))
+            break;
+        case TailMatchesCS("\\dRs*"):
         COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_subscriptions);
-    else if (TailMatchesCS("\\ds*"))
+            break;
+        case TailMatchesCS("\\ds*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
-    else if (TailMatchesCS("\\dt*"))
+            break;
+        case TailMatchesCS("\\dt*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
-    else if (TailMatchesCS("\\dT*"))
+            break;
+        case TailMatchesCS("\\dT*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-    else if (TailMatchesCS("\\du*") ||
-             TailMatchesCS("\\dg*") ||
-             TailMatchesCS("\\drg*"))
+            break;
+        case TailMatchesCS("\\du*"):
+        case TailMatchesCS("\\dg*"):
+        case TailMatchesCS("\\drg*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-    else if (TailMatchesCS("\\dv*"))
+            break;
+        case TailMatchesCS("\\dv*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
-    else if (TailMatchesCS("\\dx*"))
+            break;
+        case TailMatchesCS("\\dx*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
-    else if (TailMatchesCS("\\dX*"))
+            break;
+        case TailMatchesCS("\\dX*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics);
-    else if (TailMatchesCS("\\dm*"))
+            break;
+        case TailMatchesCS("\\dm*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
-    else if (TailMatchesCS("\\dE*"))
+            break;
+        case TailMatchesCS("\\dE*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
-    else if (TailMatchesCS("\\dy*"))
+            break;
+        case TailMatchesCS("\\dy*"):
         COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            break;

     /* must be at end of \d alternatives: */
-    else if (TailMatchesCS("\\d*"))
+        case TailMatchesCS("\\d*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
+            break;

-    else if (TailMatchesCS("\\ef"))
+        case TailMatchesCS("\\ef"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
-    else if (TailMatchesCS("\\ev"))
+            break;
+        case TailMatchesCS("\\ev"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
+            break;

-    else if (TailMatchesCS("\\encoding"))
+        case TailMatchesCS("\\encoding"):
         COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_encodings);
-    else if (TailMatchesCS("\\h|\\help"))
+            break;
+        case TailMatchesCS("\\h|\\help"):
         COMPLETE_WITH_LIST(sql_commands);
-    else if (TailMatchesCS("\\h|\\help", MatchAny))
-    {
+            break;
+        case TailMatchesCS("\\h|\\help", MatchAny):
         if (TailMatches("DROP"))
             matches = rl_completion_matches(text, drop_command_generator);
         else if (TailMatches("ALTER"))
@@ -5126,9 +5726,8 @@ match_previous_words(const char *text, int start, int end,
          * CREATE is recognized by tail match elsewhere, so doesn't need to be
          * repeated here
          */
-    }
-    else if (TailMatchesCS("\\h|\\help", MatchAny, MatchAny))
-    {
+            break;
+        case TailMatchesCS("\\h|\\help", MatchAny, MatchAny):
         if (TailMatches("CREATE|DROP", "ACCESS"))
             COMPLETE_WITH("METHOD");
         else if (TailMatches("ALTER", "DEFAULT"))
@@ -5145,21 +5744,23 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH("SEARCH");
         else if (TailMatches("CREATE|ALTER|DROP", "USER"))
             COMPLETE_WITH("MAPPING FOR");
-    }
-    else if (TailMatchesCS("\\h|\\help", MatchAny, MatchAny, MatchAny))
-    {
+            break;
+        case TailMatchesCS("\\h|\\help", MatchAny, MatchAny, MatchAny):
         if (TailMatches("CREATE|ALTER|DROP", "FOREIGN", "DATA"))
             COMPLETE_WITH("WRAPPER");
         else if (TailMatches("CREATE|ALTER|DROP", "TEXT", "SEARCH"))
             COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
         else if (TailMatches("CREATE|ALTER|DROP", "USER", "MAPPING"))
             COMPLETE_WITH("FOR");
-    }
-    else if (TailMatchesCS("\\l*") && !TailMatchesCS("\\lo*"))
+            break;
+        case TailMatchesCS("\\l*"):
+        if (!TailMatchesCS("\\lo*"))
         COMPLETE_WITH_QUERY(Query_for_list_of_databases);
-    else if (TailMatchesCS("\\password"))
+            break;
+        case TailMatchesCS("\\password"):
         COMPLETE_WITH_QUERY(Query_for_list_of_roles);
-    else if (TailMatchesCS("\\pset"))
+            break;
+        case TailMatchesCS("\\pset"):
         COMPLETE_WITH_CS("border", "columns", "csv_fieldsep", "expanded",
                          "fieldsep", "fieldsep_zero", "footer", "format",
                          "linestyle", "null", "numericlocale",
@@ -5170,8 +5771,8 @@ match_previous_words(const char *text, int start, int end,
                          "unicode_column_linestyle",
                          "unicode_header_linestyle",
                          "xheader_width");
-    else if (TailMatchesCS("\\pset", MatchAny))
-    {
+            break;
+        case TailMatchesCS("\\pset", MatchAny):
         if (TailMatchesCS("format"))
             COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
                              "latex-longtable", "troff-ms", "unaligned",
@@ -5186,13 +5787,14 @@ match_previous_words(const char *text, int start, int end,
                                "unicode_column_linestyle|"
                                "unicode_header_linestyle"))
             COMPLETE_WITH_CS("single", "double");
-    }
-    else if (TailMatchesCS("\\unset"))
+            break;
+        case TailMatchesCS("\\unset"):
         matches = complete_from_variables(text, "", "", true);
-    else if (TailMatchesCS("\\set"))
+            break;
+        case TailMatchesCS("\\set"):
         matches = complete_from_variables(text, "", "", false);
-    else if (TailMatchesCS("\\set", MatchAny))
-    {
+            break;
+        case TailMatchesCS("\\set", MatchAny):
         if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|SHOW_ALL_RESULTS|"
                           "SINGLELINE|SINGLESTEP"))
             COMPLETE_WITH_CS("on", "off");
@@ -5212,18 +5814,18 @@ match_previous_words(const char *text, int start, int end,
             COMPLETE_WITH_CS("never", "errors", "always");
         else if (TailMatchesCS("VERBOSITY"))
             COMPLETE_WITH_CS("default", "verbose", "terse", "sqlstate");
-    }
-    else if (TailMatchesCS("\\sf*"))
+            break;
+        case TailMatchesCS("\\sf*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
-    else if (TailMatchesCS("\\sv*"))
+            break;
+        case TailMatchesCS("\\sv*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
-    else if (TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|"
-                           "\\ir|\\include_relative|\\o|\\out|"
-                           "\\s|\\w|\\write|\\lo_import"))
-    {
+            break;
+        case
TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|\\ir|\\include_relative|\\o|\\out|\\s|\\w|\\write|\\lo_import"):
         completion_charp = "\\";
         completion_force_quote = false;
         matches = rl_completion_matches(text, complete_from_files);
+            break;
     }

     return matches;
--
2.43.5

From a6e65c54c9b620f90b3c810bb74953df15167e6e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 13 Jul 2024 12:50:43 -0400
Subject: [PATCH v1 5/5] Run pgindent on tab-complete.in.c.

For now, this is mostly to keep the cfbot off my back.
But I'd envision committing this as a separate step that we
can add to .git-blame-ignore-revs, since it reindents a whole
lotta existing code.
---
 src/bin/psql/tab-complete.in.c   | 3779 +++++++++++++++---------------
 src/tools/pgindent/typedefs.list |    1 +
 2 files changed, 1942 insertions(+), 1838 deletions(-)

diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index a3bd6b7970..ec0bd8ea67 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -2073,1057 +2073,1093 @@ match_previous_words(int pattern_id,
     switch (pattern_id)
     {
 /* CREATE */
-    /* complete with something you can create */
+            /* complete with something you can create */
         case TailMatches("CREATE"):
-        /* only some object types can be created as part of CREATE SCHEMA */
-        if (HeadMatches("CREATE", "SCHEMA"))
-            COMPLETE_WITH("TABLE", "VIEW", "INDEX", "SEQUENCE", "TRIGGER",
-            /* for INDEX and TABLE/SEQUENCE, respectively */
-                          "UNIQUE", "UNLOGGED");
-        else
-            matches = rl_completion_matches(text, create_command_generator);
+            /* only some object types can be created as part of CREATE SCHEMA */
+            if (HeadMatches("CREATE", "SCHEMA"))
+                COMPLETE_WITH("TABLE", "VIEW", "INDEX", "SEQUENCE", "TRIGGER",
+                /* for INDEX and TABLE/SEQUENCE, respectively */
+                              "UNIQUE", "UNLOGGED");
+            else
+                matches = rl_completion_matches(text, create_command_generator);
             break;
-    /* complete with something you can create or replace */
+            /* complete with something you can create or replace */
         case TailMatches("CREATE", "OR", "REPLACE"):
-        COMPLETE_WITH("FUNCTION", "PROCEDURE", "LANGUAGE", "RULE", "VIEW",
-                      "AGGREGATE", "TRANSFORM", "TRIGGER");
+            COMPLETE_WITH("FUNCTION", "PROCEDURE", "LANGUAGE", "RULE", "VIEW",
+                          "AGGREGATE", "TRANSFORM", "TRIGGER");
             break;

 /* DROP, but not DROP embedded in other commands */
-    /* complete with something you can drop */
+            /* complete with something you can drop */
         case Matches("DROP"):
-        matches = rl_completion_matches(text, drop_command_generator);
+            matches = rl_completion_matches(text, drop_command_generator);
             break;

 /* ALTER */

-    /* ALTER TABLE */
+            /* ALTER TABLE */
         case Matches("ALTER", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
-                                        "ALL IN TABLESPACE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
+                                            "ALL IN TABLESPACE");
             break;

-    /* ALTER something */
+            /* ALTER something */
         case Matches("ALTER"):
-        matches = rl_completion_matches(text, alter_command_generator);
+            matches = rl_completion_matches(text, alter_command_generator);
             break;
-    /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
+            /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx */
         case TailMatches("ALL", "IN", "TABLESPACE", MatchAny):
-        COMPLETE_WITH("SET TABLESPACE", "OWNED BY");
+            COMPLETE_WITH("SET TABLESPACE", "OWNED BY");
             break;
-    /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY */
+
+            /*
+             * ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED
+             * BY
+             */
         case TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
-    /* ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED BY xxx */
+
+            /*
+             * ALTER TABLE,INDEX,MATERIALIZED VIEW ALL IN TABLESPACE xxx OWNED
+             * BY xxx
+             */
         case TailMatches("ALL", "IN", "TABLESPACE", MatchAny, "OWNED", "BY", MatchAny):
-        COMPLETE_WITH("SET TABLESPACE");
+            COMPLETE_WITH("SET TABLESPACE");
             break;
-    /* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> */
+            /* ALTER AGGREGATE,FUNCTION,PROCEDURE,ROUTINE <name> */
         case Matches("ALTER", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* ALTER AGGREGATE <name> (...) */
+            /* ALTER AGGREGATE <name> (...) */
         case Matches("ALTER", "AGGREGATE", MatchAny, MatchAny):
-        if (ends_with(prev_wd, ')'))
-            COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
-        else
-            COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
+            if (ends_with(prev_wd, ')'))
+                COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
+            else
+                COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
             break;
-    /* ALTER FUNCTION <name> (...) */
+            /* ALTER FUNCTION <name> (...) */
         case Matches("ALTER", "FUNCTION", MatchAny, MatchAny):
-        if (ends_with(prev_wd, ')'))
-            COMPLETE_WITH(Alter_function_options);
-        else
-            COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
+            if (ends_with(prev_wd, ')'))
+                COMPLETE_WITH(Alter_function_options);
+            else
+                COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
             break;
-    /* ALTER PROCEDURE <name> (...) */
+            /* ALTER PROCEDURE <name> (...) */
         case Matches("ALTER", "PROCEDURE", MatchAny, MatchAny):
-        if (ends_with(prev_wd, ')'))
-            COMPLETE_WITH(Alter_procedure_options);
-        else
-            COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
+            if (ends_with(prev_wd, ')'))
+                COMPLETE_WITH(Alter_procedure_options);
+            else
+                COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
             break;
-    /* ALTER ROUTINE <name> (...) */
+            /* ALTER ROUTINE <name> (...) */
         case Matches("ALTER", "ROUTINE", MatchAny, MatchAny):
-        if (ends_with(prev_wd, ')'))
-            COMPLETE_WITH(Alter_routine_options);
-        else
-            COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
+            if (ends_with(prev_wd, ')'))
+                COMPLETE_WITH(Alter_routine_options);
+            else
+                COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
             break;
-    /* ALTER FUNCTION|ROUTINE <name> (...) PARALLEL */
+            /* ALTER FUNCTION|ROUTINE <name> (...) PARALLEL */
         case Matches("ALTER", "FUNCTION|ROUTINE", MatchAny, MatchAny, "PARALLEL"):
-        COMPLETE_WITH("RESTRICTED", "SAFE", "UNSAFE");
+            COMPLETE_WITH("RESTRICTED", "SAFE", "UNSAFE");
             break;
-    /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) [EXTERNAL] SECURITY */
+
+            /*
+             * ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) [EXTERNAL]
+             * SECURITY
+             */
         case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "SECURITY"):
         case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "EXTERNAL", "SECURITY"):
-        COMPLETE_WITH("DEFINER", "INVOKER");
+            COMPLETE_WITH("DEFINER", "INVOKER");
             break;
-    /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) RESET */
+            /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) RESET */
         case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "RESET"):
-        COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
-                                          "ALL");
+            COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
+                                              "ALL");
             break;
-    /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) SET */
+            /* ALTER FUNCTION|PROCEDURE|ROUTINE <name> (...) SET */
         case Matches("ALTER", "FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny, "SET"):
-        COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
-                                          "SCHEMA");
+            COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
+                                              "SCHEMA");
             break;

-    /* ALTER PUBLICATION <name> */
+            /* ALTER PUBLICATION <name> */
         case Matches("ALTER", "PUBLICATION", MatchAny):
-        COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
+            COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME TO", "SET");
             break;
-    /* ALTER PUBLICATION <name> ADD */
+            /* ALTER PUBLICATION <name> ADD */
         case Matches("ALTER", "PUBLICATION", MatchAny, "ADD"):
-        COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
+            COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
             break;
         case Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
         case HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
-        if (ends_with(prev_wd, ','))
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            if (ends_with(prev_wd, ','))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;

-    /*
-     * "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete with
-     * table attributes
-     *
-     * "ALTER PUBLICATION <name> ADD TABLE <name> WHERE (" - complete with
-     * table attributes
-     */
+            /*
+             * "ALTER PUBLICATION <name> SET TABLE <name> WHERE (" - complete
+             * with table attributes
+             *
+             * "ALTER PUBLICATION <name> ADD TABLE <name> WHERE (" - complete
+             * with table attributes
+             */
         case Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
         case Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;
         case HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
-        if (!TailMatches("WHERE", "(*)"))
-        COMPLETE_WITH(",", "WHERE (");
+            if (!TailMatches("WHERE", "(*)"))
+                COMPLETE_WITH(",", "WHERE (");
             break;
         case HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"):
-        COMPLETE_WITH(",");
+            COMPLETE_WITH(",");
             break;
-    /* ALTER PUBLICATION <name> DROP */
+            /* ALTER PUBLICATION <name> DROP */
         case Matches("ALTER", "PUBLICATION", MatchAny, "DROP"):
-        COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
+            COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
             break;
-    /* ALTER PUBLICATION <name> SET */
+            /* ALTER PUBLICATION <name> SET */
         case Matches("ALTER", "PUBLICATION", MatchAny, "SET"):
-        COMPLETE_WITH("(", "TABLES IN SCHEMA", "TABLE");
+            COMPLETE_WITH("(", "TABLES IN SCHEMA", "TABLE");
             break;
         case Matches("ALTER", "PUBLICATION", MatchAny, "ADD|DROP|SET", "TABLES", "IN", "SCHEMA"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
-                                 " AND nspname NOT LIKE E'pg\\\\_%%'",
-                                 "CURRENT_SCHEMA");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
+                                     " AND nspname NOT LIKE E'pg\\\\_%%'",
+                                     "CURRENT_SCHEMA");
             break;
-    /* ALTER PUBLICATION <name> SET ( */
+            /* ALTER PUBLICATION <name> SET ( */
         case Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "("):
-        COMPLETE_WITH("publish", "publish_via_partition_root");
+            COMPLETE_WITH("publish", "publish_via_partition_root");
             break;
-    /* ALTER SUBSCRIPTION <name> */
+            /* ALTER SUBSCRIPTION <name> */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny):
-        COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO",
-                      "RENAME TO", "REFRESH PUBLICATION", "SET", "SKIP (",
-                      "ADD PUBLICATION", "DROP PUBLICATION");
+            COMPLETE_WITH("CONNECTION", "ENABLE", "DISABLE", "OWNER TO",
+                          "RENAME TO", "REFRESH PUBLICATION", "SET", "SKIP (",
+                          "ADD PUBLICATION", "DROP PUBLICATION");
             break;
-    /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
+            /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"):
-        COMPLETE_WITH("WITH (");
+            COMPLETE_WITH("WITH (");
             break;
-    /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
+            /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("):
-        COMPLETE_WITH("copy_data");
+            COMPLETE_WITH("copy_data");
             break;
-    /* ALTER SUBSCRIPTION <name> SET */
+            /* ALTER SUBSCRIPTION <name> SET */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"):
-        COMPLETE_WITH("(", "PUBLICATION");
+            COMPLETE_WITH("(", "PUBLICATION");
             break;
-    /* ALTER SUBSCRIPTION <name> SET ( */
+            /* ALTER SUBSCRIPTION <name> SET ( */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "("):
-        COMPLETE_WITH("binary", "disable_on_error", "failover", "origin",
-                      "password_required", "run_as_owner", "slot_name",
-                      "streaming", "synchronous_commit");
+            COMPLETE_WITH("binary", "disable_on_error", "failover", "origin",
+                          "password_required", "run_as_owner", "slot_name",
+                          "streaming", "synchronous_commit");
             break;
-    /* ALTER SUBSCRIPTION <name> SKIP ( */
+            /* ALTER SUBSCRIPTION <name> SKIP ( */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "("):
-        COMPLETE_WITH("lsn");
+            COMPLETE_WITH("lsn");
             break;
-    /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
+            /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "PUBLICATION"):
-        /* complete with nothing here as this refers to remote publications */
+
+            /*
+             * complete with nothing here as this refers to remote
+             * publications
+             */
             break;
-    /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
+            /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "ADD|DROP|SET", "PUBLICATION", MatchAny):
-        COMPLETE_WITH("WITH (");
+            COMPLETE_WITH("WITH (");
             break;
-    /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
+
+            /*
+             * ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH
+             * (
+             */
         case Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH",
"("):
-        COMPLETE_WITH("copy_data", "refresh");
+            COMPLETE_WITH("copy_data", "refresh");
             break;

-    /* ALTER SCHEMA <name> */
+            /* ALTER SCHEMA <name> */
         case Matches("ALTER", "SCHEMA", MatchAny):
-        COMPLETE_WITH("OWNER TO", "RENAME TO");
+            COMPLETE_WITH("OWNER TO", "RENAME TO");
             break;

-    /* ALTER COLLATION <name> */
+            /* ALTER COLLATION <name> */
         case Matches("ALTER", "COLLATION", MatchAny):
-        COMPLETE_WITH("OWNER TO", "REFRESH VERSION", "RENAME TO", "SET SCHEMA");
+            COMPLETE_WITH("OWNER TO", "REFRESH VERSION", "RENAME TO", "SET SCHEMA");
             break;

-    /* ALTER CONVERSION <name> */
+            /* ALTER CONVERSION <name> */
         case Matches("ALTER", "CONVERSION", MatchAny):
-        COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
+            COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
             break;

-    /* ALTER DATABASE <name> */
+            /* ALTER DATABASE <name> */
         case Matches("ALTER", "DATABASE", MatchAny):
-        COMPLETE_WITH("RESET", "SET", "OWNER TO", "REFRESH COLLATION VERSION", "RENAME TO",
-                      "IS_TEMPLATE", "ALLOW_CONNECTIONS",
-                      "CONNECTION LIMIT");
+            COMPLETE_WITH("RESET", "SET", "OWNER TO", "REFRESH COLLATION VERSION", "RENAME TO",
+                          "IS_TEMPLATE", "ALLOW_CONNECTIONS",
+                          "CONNECTION LIMIT");
             break;

-    /* ALTER DATABASE <name> SET TABLESPACE */
+            /* ALTER DATABASE <name> SET TABLESPACE */
         case Matches("ALTER", "DATABASE", MatchAny, "SET", "TABLESPACE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
             break;

-    /* ALTER EVENT TRIGGER */
+            /* ALTER EVENT TRIGGER */
         case Matches("ALTER", "EVENT", "TRIGGER"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
             break;

-    /* ALTER EVENT TRIGGER <name> */
+            /* ALTER EVENT TRIGGER <name> */
         case Matches("ALTER", "EVENT", "TRIGGER", MatchAny):
-        COMPLETE_WITH("DISABLE", "ENABLE", "OWNER TO", "RENAME TO");
+            COMPLETE_WITH("DISABLE", "ENABLE", "OWNER TO", "RENAME TO");
             break;

-    /* ALTER EVENT TRIGGER <name> ENABLE */
+            /* ALTER EVENT TRIGGER <name> ENABLE */
         case Matches("ALTER", "EVENT", "TRIGGER", MatchAny, "ENABLE"):
-        COMPLETE_WITH("REPLICA", "ALWAYS");
+            COMPLETE_WITH("REPLICA", "ALWAYS");
             break;

-    /* ALTER EXTENSION <name> */
+            /* ALTER EXTENSION <name> */
         case Matches("ALTER", "EXTENSION", MatchAny):
-        COMPLETE_WITH("ADD", "DROP", "UPDATE", "SET SCHEMA");
+            COMPLETE_WITH("ADD", "DROP", "UPDATE", "SET SCHEMA");
             break;

-    /* ALTER EXTENSION <name> ADD|DROP */
+            /* ALTER EXTENSION <name> ADD|DROP */
         case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP"):
-        COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
-                      "CONVERSION", "DOMAIN", "EVENT TRIGGER", "FOREIGN",
-                      "FUNCTION", "MATERIALIZED VIEW", "OPERATOR",
-                      "LANGUAGE", "PROCEDURE", "ROUTINE", "SCHEMA",
-                      "SEQUENCE", "SERVER", "TABLE", "TEXT SEARCH",
-                      "TRANSFORM FOR", "TYPE", "VIEW");
+            COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
+                          "CONVERSION", "DOMAIN", "EVENT TRIGGER", "FOREIGN",
+                          "FUNCTION", "MATERIALIZED VIEW", "OPERATOR",
+                          "LANGUAGE", "PROCEDURE", "ROUTINE", "SCHEMA",
+                          "SEQUENCE", "SERVER", "TABLE", "TEXT SEARCH",
+                          "TRANSFORM FOR", "TYPE", "VIEW");
             break;

-    /* ALTER EXTENSION <name> ADD|DROP FOREIGN */
+            /* ALTER EXTENSION <name> ADD|DROP FOREIGN */
         case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "FOREIGN"):
-        COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            COMPLETE_WITH("DATA WRAPPER", "TABLE");
             break;

-    /* ALTER EXTENSION <name> ADD|DROP OPERATOR */
+            /* ALTER EXTENSION <name> ADD|DROP OPERATOR */
         case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "OPERATOR"):
-        COMPLETE_WITH("CLASS", "FAMILY");
+            COMPLETE_WITH("CLASS", "FAMILY");
             break;

-    /* ALTER EXTENSION <name> ADD|DROP TEXT SEARCH */
+            /* ALTER EXTENSION <name> ADD|DROP TEXT SEARCH */
         case Matches("ALTER", "EXTENSION", MatchAny, "ADD|DROP", "TEXT", "SEARCH"):
-        COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
             break;

-    /* ALTER EXTENSION <name> UPDATE */
+            /* ALTER EXTENSION <name> UPDATE */
         case Matches("ALTER", "EXTENSION", MatchAny, "UPDATE"):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

-    /* ALTER EXTENSION <name> UPDATE TO */
+            /* ALTER EXTENSION <name> UPDATE TO */
         case Matches("ALTER", "EXTENSION", MatchAny, "UPDATE", "TO"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
             break;

-    /* ALTER FOREIGN */
+            /* ALTER FOREIGN */
         case Matches("ALTER", "FOREIGN"):
-        COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            COMPLETE_WITH("DATA WRAPPER", "TABLE");
             break;

-    /* ALTER FOREIGN DATA WRAPPER <name> */
+            /* ALTER FOREIGN DATA WRAPPER <name> */
         case Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny):
-        COMPLETE_WITH("HANDLER", "VALIDATOR", "NO",
-                      "OPTIONS", "OWNER TO", "RENAME TO");
+            COMPLETE_WITH("HANDLER", "VALIDATOR", "NO",
+                          "OPTIONS", "OWNER TO", "RENAME TO");
             break;
         case Matches("ALTER", "FOREIGN", "DATA", "WRAPPER", MatchAny, "NO"):
-        COMPLETE_WITH("HANDLER", "VALIDATOR");
+            COMPLETE_WITH("HANDLER", "VALIDATOR");
             break;

-    /* ALTER FOREIGN TABLE <name> */
+            /* ALTER FOREIGN TABLE <name> */
         case Matches("ALTER", "FOREIGN", "TABLE", MatchAny):
-        COMPLETE_WITH("ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE",
-                      "INHERIT", "NO INHERIT", "OPTIONS", "OWNER TO",
-                      "RENAME", "SET", "VALIDATE CONSTRAINT");
+            COMPLETE_WITH("ADD", "ALTER", "DISABLE TRIGGER", "DROP", "ENABLE",
+                          "INHERIT", "NO INHERIT", "OPTIONS", "OWNER TO",
+                          "RENAME", "SET", "VALIDATE CONSTRAINT");
             break;

-    /* ALTER INDEX */
+            /* ALTER INDEX */
         case Matches("ALTER", "INDEX"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
-                                        "ALL IN TABLESPACE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
+                                            "ALL IN TABLESPACE");
             break;
-    /* ALTER INDEX <name> */
+            /* ALTER INDEX <name> */
         case Matches("ALTER", "INDEX", MatchAny):
-        COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET",
-                      "RESET", "ATTACH PARTITION",
-                      "DEPENDS ON EXTENSION", "NO DEPENDS ON EXTENSION");
+            COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME TO", "SET",
+                          "RESET", "ATTACH PARTITION",
+                          "DEPENDS ON EXTENSION", "NO DEPENDS ON EXTENSION");
             break;
         case Matches("ALTER", "INDEX", MatchAny, "ATTACH"):
-        COMPLETE_WITH("PARTITION");
+            COMPLETE_WITH("PARTITION");
             break;
         case Matches("ALTER", "INDEX", MatchAny, "ATTACH", "PARTITION"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
             break;
-    /* ALTER INDEX <name> ALTER */
+            /* ALTER INDEX <name> ALTER */
         case Matches("ALTER", "INDEX", MatchAny, "ALTER"):
-        COMPLETE_WITH("COLUMN");
+            COMPLETE_WITH("COLUMN");
             break;
-    /* ALTER INDEX <name> ALTER COLUMN */
+            /* ALTER INDEX <name> ALTER COLUMN */
         case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(Query_for_list_of_attribute_numbers);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY_VERBATIM(Query_for_list_of_attribute_numbers);
             break;
-    /* ALTER INDEX <name> ALTER COLUMN <colnum> */
+            /* ALTER INDEX <name> ALTER COLUMN <colnum> */
         case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny):
-        COMPLETE_WITH("SET STATISTICS");
+            COMPLETE_WITH("SET STATISTICS");
             break;
-    /* ALTER INDEX <name> ALTER COLUMN <colnum> SET */
+            /* ALTER INDEX <name> ALTER COLUMN <colnum> SET */
         case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET"):
-        COMPLETE_WITH("STATISTICS");
+            COMPLETE_WITH("STATISTICS");
             break;
-    /* ALTER INDEX <name> ALTER COLUMN <colnum> SET STATISTICS */
+            /* ALTER INDEX <name> ALTER COLUMN <colnum> SET STATISTICS */
         case Matches("ALTER", "INDEX", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS"):
-        /* Enforce no completion here, as an integer has to be specified */
+            /* Enforce no completion here, as an integer has to be specified */
             break;
-    /* ALTER INDEX <name> SET */
+            /* ALTER INDEX <name> SET */
         case Matches("ALTER", "INDEX", MatchAny, "SET"):
-        COMPLETE_WITH("(", "TABLESPACE");
+            COMPLETE_WITH("(", "TABLESPACE");
             break;
-    /* ALTER INDEX <name> RESET */
+            /* ALTER INDEX <name> RESET */
         case Matches("ALTER", "INDEX", MatchAny, "RESET"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* ALTER INDEX <foo> SET|RESET ( */
+            /* ALTER INDEX <foo> SET|RESET ( */
         case Matches("ALTER", "INDEX", MatchAny, "RESET", "("):
-        COMPLETE_WITH("fillfactor",
-                      "deduplicate_items",    /* BTREE */
-                      "fastupdate", "gin_pending_list_limit",    /* GIN */
-                      "buffering",    /* GiST */
-                      "pages_per_range", "autosummarize"    /* BRIN */
-            );
+            COMPLETE_WITH("fillfactor",
+                          "deduplicate_items",    /* BTREE */
+                          "fastupdate", "gin_pending_list_limit",    /* GIN */
+                          "buffering",    /* GiST */
+                          "pages_per_range", "autosummarize"    /* BRIN */
+                );
             break;
         case Matches("ALTER", "INDEX", MatchAny, "SET", "("):
-        COMPLETE_WITH("fillfactor =",
-                      "deduplicate_items =",    /* BTREE */
-                      "fastupdate =", "gin_pending_list_limit =",    /* GIN */
-                      "buffering =",    /* GiST */
-                      "pages_per_range =", "autosummarize ="    /* BRIN */
-            );
+            COMPLETE_WITH("fillfactor =",
+                          "deduplicate_items =",    /* BTREE */
+                          "fastupdate =", "gin_pending_list_limit =",    /* GIN */
+                          "buffering =",    /* GiST */
+                          "pages_per_range =", "autosummarize ="    /* BRIN */
+                );
             break;
         case Matches("ALTER", "INDEX", MatchAny, "NO", "DEPENDS"):
-        COMPLETE_WITH("ON EXTENSION");
+            COMPLETE_WITH("ON EXTENSION");
             break;
         case Matches("ALTER", "INDEX", MatchAny, "DEPENDS"):
-        COMPLETE_WITH("ON EXTENSION");
+            COMPLETE_WITH("ON EXTENSION");
             break;

-    /* ALTER LANGUAGE <name> */
+            /* ALTER LANGUAGE <name> */
         case Matches("ALTER", "LANGUAGE", MatchAny):
-        COMPLETE_WITH("OWNER TO", "RENAME TO");
+            COMPLETE_WITH("OWNER TO", "RENAME TO");
             break;

-    /* ALTER LARGE OBJECT <oid> */
+            /* ALTER LARGE OBJECT <oid> */
         case Matches("ALTER", "LARGE", "OBJECT", MatchAny):
-        COMPLETE_WITH("OWNER TO");
+            COMPLETE_WITH("OWNER TO");
             break;

-    /* ALTER MATERIALIZED VIEW */
+            /* ALTER MATERIALIZED VIEW */
         case Matches("ALTER", "MATERIALIZED", "VIEW"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
-                                        "ALL IN TABLESPACE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
+                                            "ALL IN TABLESPACE");
             break;

-    /* ALTER USER,ROLE <name> */
+            /* ALTER USER,ROLE <name> */
         case Matches("ALTER", "USER|ROLE", MatchAny):
-        if (!TailMatches("USER", "MAPPING"))
-        COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
-                      "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
-                      "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
-                      "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
-                      "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
-                      "VALID UNTIL", "WITH");
+            if (!TailMatches("USER", "MAPPING"))
+                COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
+                              "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
+                              "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
+                              "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
+                              "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
+                              "VALID UNTIL", "WITH");
             break;

-    /* ALTER USER,ROLE <name> WITH */
+            /* ALTER USER,ROLE <name> WITH */
         case Matches("ALTER", "USER|ROLE", MatchAny, "WITH"):
-        /* Similar to the above, but don't complete "WITH" again. */
-        COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
-                      "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
-                      "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
-                      "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
-                      "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
-                      "VALID UNTIL");
+            /* Similar to the above, but don't complete "WITH" again. */
+            COMPLETE_WITH("BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
+                          "ENCRYPTED PASSWORD", "INHERIT", "LOGIN", "NOBYPASSRLS",
+                          "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
+                          "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
+                          "RENAME TO", "REPLICATION", "RESET", "SET", "SUPERUSER",
+                          "VALID UNTIL");
             break;

-    /* ALTER DEFAULT PRIVILEGES */
+            /* ALTER DEFAULT PRIVILEGES */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES"):
-        COMPLETE_WITH("FOR", "GRANT", "IN SCHEMA", "REVOKE");
+            COMPLETE_WITH("FOR", "GRANT", "IN SCHEMA", "REVOKE");
             break;
-    /* ALTER DEFAULT PRIVILEGES FOR */
+            /* ALTER DEFAULT PRIVILEGES FOR */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR"):
-        COMPLETE_WITH("ROLE");
+            COMPLETE_WITH("ROLE");
             break;
-    /* ALTER DEFAULT PRIVILEGES IN */
+            /* ALTER DEFAULT PRIVILEGES IN */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN"):
-        COMPLETE_WITH("SCHEMA");
+            COMPLETE_WITH("SCHEMA");
             break;
-    /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... */
+            /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny):
-        COMPLETE_WITH("GRANT", "REVOKE", "IN SCHEMA");
+            COMPLETE_WITH("GRANT", "REVOKE", "IN SCHEMA");
             break;
-    /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... */
+            /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny):
-        COMPLETE_WITH("GRANT", "REVOKE", "FOR ROLE");
+            COMPLETE_WITH("GRANT", "REVOKE", "FOR ROLE");
             break;
-    /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR */
+            /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny, "FOR"):
-        COMPLETE_WITH("ROLE");
+            COMPLETE_WITH("ROLE");
             break;
-    /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... IN SCHEMA ... */
-    /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR ROLE|USER ... */
+            /* ALTER DEFAULT PRIVILEGES FOR ROLE|USER ... IN SCHEMA ... */
+            /* ALTER DEFAULT PRIVILEGES IN SCHEMA ... FOR ROLE|USER ... */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "FOR", "ROLE|USER", MatchAny, "IN", "SCHEMA", MatchAny):
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", "IN", "SCHEMA", MatchAny, "FOR", "ROLE|USER", MatchAny):
-        COMPLETE_WITH("GRANT", "REVOKE");
+            COMPLETE_WITH("GRANT", "REVOKE");
             break;
-    /* ALTER DOMAIN <name> */
+            /* ALTER DOMAIN <name> */
         case Matches("ALTER", "DOMAIN", MatchAny):
-        COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME", "SET",
-                      "VALIDATE CONSTRAINT");
+            COMPLETE_WITH("ADD", "DROP", "OWNER TO", "RENAME", "SET",
+                          "VALIDATE CONSTRAINT");
             break;
-    /* ALTER DOMAIN <sth> DROP */
+            /* ALTER DOMAIN <sth> DROP */
         case Matches("ALTER", "DOMAIN", MatchAny, "DROP"):
-        COMPLETE_WITH("CONSTRAINT", "DEFAULT", "NOT NULL");
+            COMPLETE_WITH("CONSTRAINT", "DEFAULT", "NOT NULL");
             break;
-    /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
+            /* ALTER DOMAIN <sth> DROP|RENAME|VALIDATE CONSTRAINT */
         case Matches("ALTER", "DOMAIN", MatchAny, "DROP|RENAME|VALIDATE", "CONSTRAINT"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_type);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_type);
             break;
-    /* ALTER DOMAIN <sth> RENAME */
+            /* ALTER DOMAIN <sth> RENAME */
         case Matches("ALTER", "DOMAIN", MatchAny, "RENAME"):
-        COMPLETE_WITH("CONSTRAINT", "TO");
+            COMPLETE_WITH("CONSTRAINT", "TO");
             break;
-    /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
+            /* ALTER DOMAIN <sth> RENAME CONSTRAINT <sth> */
         case Matches("ALTER", "DOMAIN", MatchAny, "RENAME", "CONSTRAINT", MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

-    /* ALTER DOMAIN <sth> SET */
+            /* ALTER DOMAIN <sth> SET */
         case Matches("ALTER", "DOMAIN", MatchAny, "SET"):
-        COMPLETE_WITH("DEFAULT", "NOT NULL", "SCHEMA");
+            COMPLETE_WITH("DEFAULT", "NOT NULL", "SCHEMA");
             break;
-    /* ALTER SEQUENCE <name> */
+            /* ALTER SEQUENCE <name> */
         case Matches("ALTER", "SEQUENCE", MatchAny):
-        COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART",
-                      "START", "NO", "CACHE", "CYCLE", "SET", "OWNED BY",
-                      "OWNER TO", "RENAME TO");
+            COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART",
+                          "START", "NO", "CACHE", "CYCLE", "SET", "OWNED BY",
+                          "OWNER TO", "RENAME TO");
             break;
-    /* ALTER SEQUENCE <name> AS */
+            /* ALTER SEQUENCE <name> AS */
         case TailMatches("ALTER", "SEQUENCE", MatchAny, "AS"):
-        COMPLETE_WITH_CS("smallint", "integer", "bigint");
+            COMPLETE_WITH_CS("smallint", "integer", "bigint");
             break;
-    /* ALTER SEQUENCE <name> NO */
+            /* ALTER SEQUENCE <name> NO */
         case Matches("ALTER", "SEQUENCE", MatchAny, "NO"):
-        COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+            COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
             break;
-    /* ALTER SEQUENCE <name> SET */
+            /* ALTER SEQUENCE <name> SET */
         case Matches("ALTER", "SEQUENCE", MatchAny, "SET"):
-        COMPLETE_WITH("SCHEMA", "LOGGED", "UNLOGGED");
+            COMPLETE_WITH("SCHEMA", "LOGGED", "UNLOGGED");
             break;
-    /* ALTER SERVER <name> */
+            /* ALTER SERVER <name> */
         case Matches("ALTER", "SERVER", MatchAny):
-        COMPLETE_WITH("VERSION", "OPTIONS", "OWNER TO", "RENAME TO");
+            COMPLETE_WITH("VERSION", "OPTIONS", "OWNER TO", "RENAME TO");
             break;
-    /* ALTER SERVER <name> VERSION <version> */
+            /* ALTER SERVER <name> VERSION <version> */
         case Matches("ALTER", "SERVER", MatchAny, "VERSION", MatchAny):
-        COMPLETE_WITH("OPTIONS");
+            COMPLETE_WITH("OPTIONS");
             break;
-    /* ALTER SYSTEM SET, RESET, RESET ALL */
+            /* ALTER SYSTEM SET, RESET, RESET ALL */
         case Matches("ALTER", "SYSTEM"):
-        COMPLETE_WITH("SET", "RESET");
+            COMPLETE_WITH("SET", "RESET");
             break;
         case Matches("ALTER", "SYSTEM", "SET|RESET"):
-        COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_set_vars,
-                                          "ALL");
+            COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_alter_system_set_vars,
+                                              "ALL");
             break;
         case Matches("ALTER", "SYSTEM", "SET", MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
-    /* ALTER VIEW <name> */
+            /* ALTER VIEW <name> */
         case Matches("ALTER", "VIEW", MatchAny):
-        COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", "RESET", "SET");
+            COMPLETE_WITH("ALTER COLUMN", "OWNER TO", "RENAME", "RESET", "SET");
             break;
-    /* ALTER VIEW xxx RENAME */
+            /* ALTER VIEW xxx RENAME */
         case Matches("ALTER", "VIEW", MatchAny, "RENAME"):
-        COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
+            COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
             break;
         case Matches("ALTER", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;
-    /* ALTER VIEW xxx ALTER [ COLUMN ] yyy */
+            /* ALTER VIEW xxx ALTER [ COLUMN ] yyy */
         case Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny):
         case Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny):
-        COMPLETE_WITH("SET DEFAULT", "DROP DEFAULT");
+            COMPLETE_WITH("SET DEFAULT", "DROP DEFAULT");
             break;
-    /* ALTER VIEW xxx RENAME yyy */
+            /* ALTER VIEW xxx RENAME yyy */
         case Matches("ALTER", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
-    /* ALTER VIEW xxx RENAME COLUMN yyy */
+            /* ALTER VIEW xxx RENAME COLUMN yyy */
         case Matches("ALTER", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
-    /* ALTER VIEW xxx RESET ( */
+            /* ALTER VIEW xxx RESET ( */
         case Matches("ALTER", "VIEW", MatchAny, "RESET"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* Complete ALTER VIEW xxx SET with "(" or "SCHEMA" */
+            /* Complete ALTER VIEW xxx SET with "(" or "SCHEMA" */
         case Matches("ALTER", "VIEW", MatchAny, "SET"):
-        COMPLETE_WITH("(", "SCHEMA");
+            COMPLETE_WITH("(", "SCHEMA");
             break;
-    /* ALTER VIEW xxx SET|RESET ( yyy [= zzz] ) */
+            /* ALTER VIEW xxx SET|RESET ( yyy [= zzz] ) */
         case Matches("ALTER", "VIEW", MatchAny, "SET|RESET", "("):
-        COMPLETE_WITH_LIST(view_optional_parameters);
+            COMPLETE_WITH_LIST(view_optional_parameters);
             break;
         case Matches("ALTER", "VIEW", MatchAny, "SET", "(", MatchAny):
-        COMPLETE_WITH("=");
+            COMPLETE_WITH("=");
             break;
         case Matches("ALTER", "VIEW", MatchAny, "SET", "(", "check_option", "="):
-        COMPLETE_WITH("local", "cascaded");
+            COMPLETE_WITH("local", "cascaded");
             break;
         case Matches("ALTER", "VIEW", MatchAny, "SET", "(", "security_barrier|security_invoker", "="):
-        COMPLETE_WITH("true", "false");
+            COMPLETE_WITH("true", "false");
             break;

-    /* ALTER MATERIALIZED VIEW <name> */
+            /* ALTER MATERIALIZED VIEW <name> */
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny):
-        COMPLETE_WITH("ALTER COLUMN", "CLUSTER ON", "DEPENDS ON EXTENSION",
-                      "NO DEPENDS ON EXTENSION", "OWNER TO", "RENAME",
-                      "RESET (", "SET");
+            COMPLETE_WITH("ALTER COLUMN", "CLUSTER ON", "DEPENDS ON EXTENSION",
+                          "NO DEPENDS ON EXTENSION", "OWNER TO", "RENAME",
+                          "RESET (", "SET");
             break;
-    /* ALTER MATERIALIZED VIEW xxx RENAME */
+            /* ALTER MATERIALIZED VIEW xxx RENAME */
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME"):
-        COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
+            COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "TO");
             break;
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "ALTER|RENAME", "COLUMN"):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;
-    /* ALTER MATERIALIZED VIEW xxx RENAME yyy */
+            /* ALTER MATERIALIZED VIEW xxx RENAME yyy */
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", MatchAnyExcept("TO")):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
-    /* ALTER MATERIALIZED VIEW xxx RENAME COLUMN yyy */
+            /* ALTER MATERIALIZED VIEW xxx RENAME COLUMN yyy */
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "RENAME", "COLUMN", MatchAnyExcept("TO")):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
-    /* ALTER MATERIALIZED VIEW xxx SET */
+            /* ALTER MATERIALIZED VIEW xxx SET */
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET"):
-        COMPLETE_WITH("(", "ACCESS METHOD", "SCHEMA", "TABLESPACE", "WITHOUT CLUSTER");
+            COMPLETE_WITH("(", "ACCESS METHOD", "SCHEMA", "TABLESPACE", "WITHOUT CLUSTER");
             break;
-    /* ALTER MATERIALIZED VIEW xxx SET ACCESS METHOD */
+            /* ALTER MATERIALIZED VIEW xxx SET ACCESS METHOD */
         case Matches("ALTER", "MATERIALIZED", "VIEW", MatchAny, "SET", "ACCESS", "METHOD"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
             break;

-    /* ALTER POLICY <name> */
+            /* ALTER POLICY <name> */
         case Matches("ALTER", "POLICY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+            COMPLETE_WITH_QUERY(Query_for_list_of_policies);
             break;
-    /* ALTER POLICY <name> ON */
+            /* ALTER POLICY <name> ON */
         case Matches("ALTER", "POLICY", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
-    /* ALTER POLICY <name> ON <table> */
+            /* ALTER POLICY <name> ON <table> */
         case Matches("ALTER", "POLICY", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
             break;
-    /* ALTER POLICY <name> ON <table> - show options */
+            /* ALTER POLICY <name> ON <table> - show options */
         case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("RENAME TO", "TO", "USING (", "WITH CHECK (");
+            COMPLETE_WITH("RENAME TO", "TO", "USING (", "WITH CHECK (");
             break;
-    /* ALTER POLICY <name> ON <table> TO <role> */
+            /* ALTER POLICY <name> ON <table> TO <role> */
         case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "TO"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_grant_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_grant_roles);
             break;
-    /* ALTER POLICY <name> ON <table> USING ( */
+            /* ALTER POLICY <name> ON <table> USING ( */
         case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "USING"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* ALTER POLICY <name> ON <table> WITH CHECK ( */
+            /* ALTER POLICY <name> ON <table> WITH CHECK ( */
         case Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;

-    /* ALTER RULE <name>, add ON */
+            /* ALTER RULE <name>, add ON */
         case Matches("ALTER", "RULE", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;

-    /* If we have ALTER RULE <name> ON, then add the correct tablename */
+            /* If we have ALTER RULE <name> ON, then add the correct tablename */
         case Matches("ALTER", "RULE", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
             break;

-    /* ALTER RULE <name> ON <name> */
+            /* ALTER RULE <name> ON <name> */
         case Matches("ALTER", "RULE", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("RENAME TO");
+            COMPLETE_WITH("RENAME TO");
             break;

-    /* ALTER STATISTICS <name> */
+            /* ALTER STATISTICS <name> */
         case Matches("ALTER", "STATISTICS", MatchAny):
-        COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA", "SET STATISTICS");
+            COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA", "SET STATISTICS");
             break;
-    /* ALTER STATISTICS <name> SET */
+            /* ALTER STATISTICS <name> SET */
         case Matches("ALTER", "STATISTICS", MatchAny, "SET"):
-        COMPLETE_WITH("SCHEMA", "STATISTICS");
+            COMPLETE_WITH("SCHEMA", "STATISTICS");
             break;

-    /* ALTER TRIGGER <name>, add ON */
+            /* ALTER TRIGGER <name>, add ON */
         case Matches("ALTER", "TRIGGER", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;

         case Matches("ALTER", "TRIGGER", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
             break;

-    /* ALTER TRIGGER <name> ON <name> */
+            /* ALTER TRIGGER <name> ON <name> */
         case Matches("ALTER", "TRIGGER", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("RENAME TO", "DEPENDS ON EXTENSION",
-                      "NO DEPENDS ON EXTENSION");
+            COMPLETE_WITH("RENAME TO", "DEPENDS ON EXTENSION",
+                          "NO DEPENDS ON EXTENSION");
             break;

-    /*
-     * If we detect ALTER TABLE <name>, suggest sub commands
-     */
+            /*
+             * If we detect ALTER TABLE <name>, suggest sub commands
+             */
         case Matches("ALTER", "TABLE", MatchAny):
-        COMPLETE_WITH("ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP",
-                      "ENABLE", "INHERIT", "NO", "RENAME", "RESET",
-                      "OWNER TO", "SET", "VALIDATE CONSTRAINT",
-                      "REPLICA IDENTITY", "ATTACH PARTITION",
-                      "DETACH PARTITION", "FORCE ROW LEVEL SECURITY",
-                      "SPLIT PARTITION", "MERGE PARTITIONS (",
-                      "OF", "NOT OF");
-            break;
-    /* ALTER TABLE xxx ADD */
+            COMPLETE_WITH("ADD", "ALTER", "CLUSTER ON", "DISABLE", "DROP",
+                          "ENABLE", "INHERIT", "NO", "RENAME", "RESET",
+                          "OWNER TO", "SET", "VALIDATE CONSTRAINT",
+                          "REPLICA IDENTITY", "ATTACH PARTITION",
+                          "DETACH PARTITION", "FORCE ROW LEVEL SECURITY",
+                          "SPLIT PARTITION", "MERGE PARTITIONS (",
+                          "OF", "NOT OF");
+            break;
+            /* ALTER TABLE xxx ADD */
         case Matches("ALTER", "TABLE", MatchAny, "ADD"):
-        /* make sure to keep this list and the !Matches() below in sync */
-        COMPLETE_WITH("COLUMN", "CONSTRAINT", "CHECK", "UNIQUE", "PRIMARY KEY",
-                      "EXCLUDE", "FOREIGN KEY");
+            /* make sure to keep this list and the !Matches() below in sync */
+            COMPLETE_WITH("COLUMN", "CONSTRAINT", "CHECK", "UNIQUE", "PRIMARY KEY",
+                          "EXCLUDE", "FOREIGN KEY");
             break;
-    /* ALTER TABLE xxx ADD [COLUMN] yyy */
+            /* ALTER TABLE xxx ADD [COLUMN] yyy */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny):
         case Matches("ALTER", "TABLE", MatchAny, "ADD",
MatchAnyExcept("COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
-    /* ALTER TABLE xxx ADD CONSTRAINT yyy */
+            /* ALTER TABLE xxx ADD CONSTRAINT yyy */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny):
-        COMPLETE_WITH("CHECK", "UNIQUE", "PRIMARY KEY", "EXCLUDE", "FOREIGN KEY");
+            COMPLETE_WITH("CHECK", "UNIQUE", "PRIMARY KEY", "EXCLUDE", "FOREIGN KEY");
             break;
-    /* ALTER TABLE xxx ADD [CONSTRAINT yyy] (PRIMARY KEY|UNIQUE) */
+            /* ALTER TABLE xxx ADD [CONSTRAINT yyy] (PRIMARY KEY|UNIQUE) */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY"):
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE"):
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "PRIMARY", "KEY"):
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "UNIQUE"):
-        COMPLETE_WITH("(", "USING INDEX");
+            COMPLETE_WITH("(", "USING INDEX");
             break;
-    /* ALTER TABLE xxx ADD PRIMARY KEY USING INDEX */
+            /* ALTER TABLE xxx ADD PRIMARY KEY USING INDEX */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "PRIMARY", "KEY", "USING", "INDEX"):
-        set_completion_reference(prev6_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
+            set_completion_reference(prev6_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
             break;
-    /* ALTER TABLE xxx ADD UNIQUE USING INDEX */
+            /* ALTER TABLE xxx ADD UNIQUE USING INDEX */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "UNIQUE", "USING", "INDEX"):
-        set_completion_reference(prev5_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
+            set_completion_reference(prev5_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
             break;
-    /* ALTER TABLE xxx ADD CONSTRAINT yyy PRIMARY KEY USING INDEX */
+            /* ALTER TABLE xxx ADD CONSTRAINT yyy PRIMARY KEY USING INDEX */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "PRIMARY", "KEY", "USING", "INDEX"):
-        set_completion_reference(prev8_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
+            set_completion_reference(prev8_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
             break;
-    /* ALTER TABLE xxx ADD CONSTRAINT yyy UNIQUE USING INDEX */
+            /* ALTER TABLE xxx ADD CONSTRAINT yyy UNIQUE USING INDEX */
         case Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny, "UNIQUE", "USING", "INDEX"):
-        set_completion_reference(prev7_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
+            set_completion_reference(prev7_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_unique_index_of_table);
             break;
-    /* ALTER TABLE xxx ENABLE */
+            /* ALTER TABLE xxx ENABLE */
         case Matches("ALTER", "TABLE", MatchAny, "ENABLE"):
-        COMPLETE_WITH("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE",
-                      "TRIGGER");
+            COMPLETE_WITH("ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE",
+                          "TRIGGER");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "ENABLE", "REPLICA|ALWAYS"):
-        COMPLETE_WITH("RULE", "TRIGGER");
+            COMPLETE_WITH("RULE", "TRIGGER");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "ENABLE", "RULE"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "RULE"):
-        set_completion_reference(prev4_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
+            set_completion_reference(prev4_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "ENABLE", "TRIGGER"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "ENABLE", MatchAny, "TRIGGER"):
-        set_completion_reference(prev4_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
+            set_completion_reference(prev4_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
             break;
-    /* ALTER TABLE xxx INHERIT */
+            /* ALTER TABLE xxx INHERIT */
         case Matches("ALTER", "TABLE", MatchAny, "INHERIT"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
-    /* ALTER TABLE xxx NO */
+            /* ALTER TABLE xxx NO */
         case Matches("ALTER", "TABLE", MatchAny, "NO"):
-        COMPLETE_WITH("FORCE ROW LEVEL SECURITY", "INHERIT");
+            COMPLETE_WITH("FORCE ROW LEVEL SECURITY", "INHERIT");
             break;
-    /* ALTER TABLE xxx NO INHERIT */
+            /* ALTER TABLE xxx NO INHERIT */
         case Matches("ALTER", "TABLE", MatchAny, "NO", "INHERIT"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
-    /* ALTER TABLE xxx DISABLE */
+            /* ALTER TABLE xxx DISABLE */
         case Matches("ALTER", "TABLE", MatchAny, "DISABLE"):
-        COMPLETE_WITH("ROW LEVEL SECURITY", "RULE", "TRIGGER");
+            COMPLETE_WITH("ROW LEVEL SECURITY", "RULE", "TRIGGER");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "DISABLE", "RULE"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_rule_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "DISABLE", "TRIGGER"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_trigger_of_table);
             break;

-    /* ALTER TABLE xxx ALTER */
+            /* ALTER TABLE xxx ALTER */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER"):
-        COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT");
+            COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT");
             break;

-    /* ALTER TABLE xxx RENAME */
+            /* ALTER TABLE xxx RENAME */
         case Matches("ALTER", "TABLE", MatchAny, "RENAME"):
-        COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT", "TO");
+            COMPLETE_WITH_ATTR_PLUS(prev2_wd, "COLUMN", "CONSTRAINT", "TO");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "ALTER|RENAME", "COLUMN"):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;

-    /* ALTER TABLE xxx RENAME yyy */
+            /* ALTER TABLE xxx RENAME yyy */
         case Matches("ALTER", "TABLE", MatchAny, "RENAME", MatchAnyExcept("CONSTRAINT|TO")):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

-    /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
+            /* ALTER TABLE xxx RENAME COLUMN/CONSTRAINT yyy */
         case Matches("ALTER", "TABLE", MatchAny, "RENAME", "COLUMN|CONSTRAINT", MatchAnyExcept("TO")):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

-    /* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
+            /* If we have ALTER TABLE <sth> DROP, provide COLUMN or CONSTRAINT */
         case Matches("ALTER", "TABLE", MatchAny, "DROP"):
-        COMPLETE_WITH("COLUMN", "CONSTRAINT");
+            COMPLETE_WITH("COLUMN", "CONSTRAINT");
             break;
-    /* If we have ALTER TABLE <sth> DROP COLUMN, provide list of columns */
+
+            /*
+             * If we have ALTER TABLE <sth> DROP COLUMN, provide list of
+             * columns
+             */
         case Matches("ALTER", "TABLE", MatchAny, "DROP", "COLUMN"):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;
-    /* ALTER TABLE <sth> ALTER|DROP|RENAME CONSTRAINT <constraint> */
+            /* ALTER TABLE <sth> ALTER|DROP|RENAME CONSTRAINT <constraint> */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER|DROP|RENAME", "CONSTRAINT"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table);
             break;
-    /* ALTER TABLE <sth> VALIDATE CONSTRAINT <non-validated constraint> */
+
+            /*
+             * ALTER TABLE <sth> VALIDATE CONSTRAINT <non-validated
+             * constraint>
+             */
         case Matches("ALTER", "TABLE", MatchAny, "VALIDATE", "CONSTRAINT"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table_not_validated);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_constraint_of_table_not_validated);
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> */
+            /* ALTER TABLE ALTER [COLUMN] <foo> */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny):
-        COMPLETE_WITH("TYPE", "SET", "RESET", "RESTART", "ADD", "DROP");
+            COMPLETE_WITH("TYPE", "SET", "RESET", "RESTART", "ADD", "DROP");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> ADD */
+            /* ALTER TABLE ALTER [COLUMN] <foo> ADD */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD"):
-        COMPLETE_WITH("GENERATED");
+            COMPLETE_WITH("GENERATED");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
+            /* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED"):
-        COMPLETE_WITH("ALWAYS", "BY DEFAULT");
+            COMPLETE_WITH("ALWAYS", "BY DEFAULT");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
+            /* ALTER TABLE ALTER [COLUMN] <foo> ADD GENERATED */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED", "ALWAYS"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED", "ALWAYS"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "ADD", "GENERATED", "BY", "DEFAULT"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "ADD", "GENERATED", "BY", "DEFAULT"):
-        COMPLETE_WITH("AS IDENTITY");
+            COMPLETE_WITH("AS IDENTITY");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET"):
-        COMPLETE_WITH("(", "COMPRESSION", "DATA TYPE", "DEFAULT", "EXPRESSION", "GENERATED", "NOT NULL",
-                      "STATISTICS", "STORAGE",
-        /* a subset of ALTER SEQUENCE options */
-                      "INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE");
+            COMPLETE_WITH("(", "COMPRESSION", "DATA TYPE", "DEFAULT", "EXPRESSION", "GENERATED", "NOT NULL",
+                          "STATISTICS", "STORAGE",
+            /* a subset of ALTER SEQUENCE options */
+                          "INCREMENT", "MINVALUE", "MAXVALUE", "START", "NO", "CACHE", "CYCLE");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET ( */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "("):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "("):
-        COMPLETE_WITH("n_distinct", "n_distinct_inherited");
+            COMPLETE_WITH("n_distinct", "n_distinct_inherited");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET COMPRESSION */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET COMPRESSION */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "COMPRESSION"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "COMPRESSION"):
-        COMPLETE_WITH("DEFAULT", "PGLZ", "LZ4");
+            COMPLETE_WITH("DEFAULT", "PGLZ", "LZ4");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION"):
-        COMPLETE_WITH("AS");
+            COMPLETE_WITH("AS");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION AS */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET EXPRESSION AS */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "EXPRESSION", "AS"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "EXPRESSION", "AS"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET GENERATED */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET GENERATED */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "GENERATED"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "GENERATED"):
-        COMPLETE_WITH("ALWAYS", "BY DEFAULT");
+            COMPLETE_WITH("ALWAYS", "BY DEFAULT");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET NO */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET NO */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "NO"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "NO"):
-        COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+            COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET STORAGE */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STORAGE"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STORAGE"):
-        COMPLETE_WITH("DEFAULT", "PLAIN", "EXTERNAL", "EXTENDED", "MAIN");
+            COMPLETE_WITH("DEFAULT", "PLAIN", "EXTERNAL", "EXTENDED", "MAIN");
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> SET STATISTICS */
+            /* ALTER TABLE ALTER [COLUMN] <foo> SET STATISTICS */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "SET", "STATISTICS"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "SET", "STATISTICS"):
-        /* Enforce no completion here, as an integer has to be specified */
+            /* Enforce no completion here, as an integer has to be specified */
             break;
-    /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
+            /* ALTER TABLE ALTER [COLUMN] <foo> DROP */
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", "COLUMN", MatchAny, "DROP"):
         case Matches("ALTER", "TABLE", MatchAny, "ALTER", MatchAny, "DROP"):
-        COMPLETE_WITH("DEFAULT", "EXPRESSION", "IDENTITY", "NOT NULL");
+            COMPLETE_WITH("DEFAULT", "EXPRESSION", "IDENTITY", "NOT NULL");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "CLUSTER"):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "CLUSTER", "ON"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
             break;
-    /* If we have ALTER TABLE <sth> SET, provide list of attributes and '(' */
+
+            /*
+             * If we have ALTER TABLE <sth> SET, provide list of attributes
+             * and '('
+             */
         case Matches("ALTER", "TABLE", MatchAny, "SET"):
-        COMPLETE_WITH("(", "ACCESS METHOD", "LOGGED", "SCHEMA",
-                      "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT");
+            COMPLETE_WITH("(", "ACCESS METHOD", "LOGGED", "SCHEMA",
+                          "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT");
             break;

-    /*
-     * If we have ALTER TABLE <sth> SET ACCESS METHOD provide a list of table
-     * AMs.
-     */
+            /*
+             * If we have ALTER TABLE <sth> SET ACCESS METHOD provide a list
+             * of table AMs.
+             */
         case Matches("ALTER", "TABLE", MatchAny, "SET", "ACCESS", "METHOD"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_table_access_methods,
-                                 "DEFAULT");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_table_access_methods,
+                                     "DEFAULT");
             break;

-    /*
-     * If we have ALTER TABLE <sth> SET TABLESPACE provide a list of
-     * tablespaces
-     */
+            /*
+             * If we have ALTER TABLE <sth> SET TABLESPACE provide a list of
+             * tablespaces
+             */
         case Matches("ALTER", "TABLE", MatchAny, "SET", "TABLESPACE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
             break;
-    /* If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
+
+            /*
+             * If we have ALTER TABLE <sth> SET WITHOUT provide CLUSTER or
+             * OIDS
+             */
         case Matches("ALTER", "TABLE", MatchAny, "SET", "WITHOUT"):
-        COMPLETE_WITH("CLUSTER", "OIDS");
+            COMPLETE_WITH("CLUSTER", "OIDS");
             break;
-    /* ALTER TABLE <foo> RESET */
+            /* ALTER TABLE <foo> RESET */
         case Matches("ALTER", "TABLE", MatchAny, "RESET"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* ALTER TABLE <foo> SET|RESET ( */
+            /* ALTER TABLE <foo> SET|RESET ( */
         case Matches("ALTER", "TABLE", MatchAny, "SET|RESET", "("):
-        COMPLETE_WITH_LIST(table_storage_parameters);
+            COMPLETE_WITH_LIST(table_storage_parameters);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING", "INDEX"):
-        set_completion_reference(prev5_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
+            set_completion_reference(prev5_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY", "USING"):
-        COMPLETE_WITH("INDEX");
+            COMPLETE_WITH("INDEX");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "REPLICA", "IDENTITY"):
-        COMPLETE_WITH("FULL", "NOTHING", "DEFAULT", "USING");
+            COMPLETE_WITH("FULL", "NOTHING", "DEFAULT", "USING");
             break;
         case Matches("ALTER", "TABLE", MatchAny, "REPLICA"):
-        COMPLETE_WITH("IDENTITY");
+            COMPLETE_WITH("IDENTITY");
             break;

-    /*
-     * If we have ALTER TABLE <foo> ATTACH PARTITION, provide a list of
-     * tables.
-     */
+            /*
+             * If we have ALTER TABLE <foo> ATTACH PARTITION, provide a list
+             * of tables.
+             */
         case Matches("ALTER", "TABLE", MatchAny, "ATTACH", "PARTITION"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
-    /* Limited completion support for partition bound specification */
+            /* Limited completion support for partition bound specification */
         case TailMatches("ATTACH", "PARTITION", MatchAny):
-        COMPLETE_WITH("FOR VALUES", "DEFAULT");
+            COMPLETE_WITH("FOR VALUES", "DEFAULT");
             break;
         case TailMatches("FOR", "VALUES"):
-        COMPLETE_WITH("FROM (", "IN (", "WITH (");
+            COMPLETE_WITH("FROM (", "IN (", "WITH (");
             break;

-    /*
-     * If we have ALTER TABLE <foo> DETACH|SPLIT PARTITION, provide a list of
-     * partitions of <foo>.
-     */
+            /*
+             * If we have ALTER TABLE <foo> DETACH|SPLIT PARTITION, provide a
+             * list of partitions of <foo>.
+             */
         case Matches("ALTER", "TABLE", MatchAny, "DETACH|SPLIT", "PARTITION"):
-        set_completion_reference(prev3_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
+            set_completion_reference(prev3_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "DETACH", "PARTITION", MatchAny):
-        COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
+            COMPLETE_WITH("CONCURRENTLY", "FINALIZE");
             break;

-    /* ALTER TABLE <name> SPLIT PARTITION <name> */
+            /* ALTER TABLE <name> SPLIT PARTITION <name> */
         case Matches("ALTER", "TABLE", MatchAny, "SPLIT", "PARTITION", MatchAny):
-        COMPLETE_WITH("INTO ( PARTITION");
+            COMPLETE_WITH("INTO ( PARTITION");
             break;

-    /* ALTER TABLE <name> MERGE PARTITIONS ( */
+            /* ALTER TABLE <name> MERGE PARTITIONS ( */
         case Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "("):
-        set_completion_reference(prev4_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
+            set_completion_reference(prev4_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_partition_of_table);
             break;
         case Matches("ALTER", "TABLE", MatchAny, "MERGE", "PARTITIONS", "(*)"):
-        COMPLETE_WITH("INTO");
+            COMPLETE_WITH("INTO");
             break;

-    /* ALTER TABLE <name> OF */
+            /* ALTER TABLE <name> OF */
         case Matches("ALTER", "TABLE", MatchAny, "OF"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
             break;

-    /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
+            /* ALTER TABLESPACE <foo> with RENAME TO, OWNER TO, SET, RESET */
         case Matches("ALTER", "TABLESPACE", MatchAny):
-        COMPLETE_WITH("RENAME TO", "OWNER TO", "SET", "RESET");
+            COMPLETE_WITH("RENAME TO", "OWNER TO", "SET", "RESET");
             break;
-    /* ALTER TABLESPACE <foo> SET|RESET */
+            /* ALTER TABLESPACE <foo> SET|RESET */
         case Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
-    /* ALTER TABLESPACE <foo> SET|RESET ( */
+            /* ALTER TABLESPACE <foo> SET|RESET ( */
         case Matches("ALTER", "TABLESPACE", MatchAny, "SET|RESET", "("):
-        COMPLETE_WITH("seq_page_cost", "random_page_cost",
-                      "effective_io_concurrency", "maintenance_io_concurrency");
+            COMPLETE_WITH("seq_page_cost", "random_page_cost",
+                          "effective_io_concurrency", "maintenance_io_concurrency");
             break;

-    /* ALTER TEXT SEARCH */
+            /* ALTER TEXT SEARCH */
         case Matches("ALTER", "TEXT", "SEARCH"):
-        COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
             break;
         case Matches("ALTER", "TEXT", "SEARCH", "TEMPLATE|PARSER", MatchAny):
-        COMPLETE_WITH("RENAME TO", "SET SCHEMA");
+            COMPLETE_WITH("RENAME TO", "SET SCHEMA");
             break;
         case Matches("ALTER", "TEXT", "SEARCH", "DICTIONARY", MatchAny):
-        COMPLETE_WITH("(", "OWNER TO", "RENAME TO", "SET SCHEMA");
+            COMPLETE_WITH("(", "OWNER TO", "RENAME TO", "SET SCHEMA");
             break;
         case Matches("ALTER", "TEXT", "SEARCH", "CONFIGURATION", MatchAny):
-        COMPLETE_WITH("ADD MAPPING FOR", "ALTER MAPPING",
-                      "DROP MAPPING FOR",
-                      "OWNER TO", "RENAME TO", "SET SCHEMA");
+            COMPLETE_WITH("ADD MAPPING FOR", "ALTER MAPPING",
+                          "DROP MAPPING FOR",
+                          "OWNER TO", "RENAME TO", "SET SCHEMA");
             break;

-    /* complete ALTER TYPE <foo> with actions */
+            /* complete ALTER TYPE <foo> with actions */
         case Matches("ALTER", "TYPE", MatchAny):
-        COMPLETE_WITH("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE",
-                      "DROP ATTRIBUTE",
-                      "OWNER TO", "RENAME", "SET SCHEMA", "SET (");
+            COMPLETE_WITH("ADD ATTRIBUTE", "ADD VALUE", "ALTER ATTRIBUTE",
+                          "DROP ATTRIBUTE",
+                          "OWNER TO", "RENAME", "SET SCHEMA", "SET (");
             break;
-    /* complete ALTER TYPE <foo> ADD with actions */
+            /* complete ALTER TYPE <foo> ADD with actions */
         case Matches("ALTER", "TYPE", MatchAny, "ADD"):
-        COMPLETE_WITH("ATTRIBUTE", "VALUE");
+            COMPLETE_WITH("ATTRIBUTE", "VALUE");
             break;
-    /* ALTER TYPE <foo> RENAME    */
+            /* ALTER TYPE <foo> RENAME    */
         case Matches("ALTER", "TYPE", MatchAny, "RENAME"):
-        COMPLETE_WITH("ATTRIBUTE", "TO", "VALUE");
+            COMPLETE_WITH("ATTRIBUTE", "TO", "VALUE");
             break;
-    /* ALTER TYPE xxx RENAME (ATTRIBUTE|VALUE) yyy */
+            /* ALTER TYPE xxx RENAME (ATTRIBUTE|VALUE) yyy */
         case Matches("ALTER", "TYPE", MatchAny, "RENAME", "ATTRIBUTE|VALUE", MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

-    /*
-     * If we have ALTER TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE, provide list
-     * of attributes
-     */
+            /*
+             * If we have ALTER TYPE <sth> ALTER/DROP/RENAME ATTRIBUTE,
+             * provide list of attributes
+             */
         case Matches("ALTER", "TYPE", MatchAny, "ALTER|DROP|RENAME", "ATTRIBUTE"):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;
-    /* ALTER TYPE ALTER ATTRIBUTE <foo> */
+            /* ALTER TYPE ALTER ATTRIBUTE <foo> */
         case Matches("ALTER", "TYPE", MatchAny, "ALTER", "ATTRIBUTE", MatchAny):
-        COMPLETE_WITH("TYPE");
+            COMPLETE_WITH("TYPE");
             break;
-    /* complete ALTER TYPE <sth> RENAME VALUE with list of enum values */
+            /* complete ALTER TYPE <sth> RENAME VALUE with list of enum values */
         case Matches("ALTER", "TYPE", MatchAny, "RENAME", "VALUE"):
-        COMPLETE_WITH_ENUM_VALUE(prev3_wd);
+            COMPLETE_WITH_ENUM_VALUE(prev3_wd);
             break;
-    /* ALTER TYPE <foo> SET */
+            /* ALTER TYPE <foo> SET */
         case Matches("ALTER", "TYPE", MatchAny, "SET"):
-        COMPLETE_WITH("(", "SCHEMA");
+            COMPLETE_WITH("(", "SCHEMA");
             break;
-    /* complete ALTER TYPE <foo> SET ( with settable properties */
+            /* complete ALTER TYPE <foo> SET ( with settable properties */
         case Matches("ALTER", "TYPE", MatchAny, "SET", "("):
-        COMPLETE_WITH("ANALYZE", "RECEIVE", "SEND", "STORAGE", "SUBSCRIPT",
-                      "TYPMOD_IN", "TYPMOD_OUT");
+            COMPLETE_WITH("ANALYZE", "RECEIVE", "SEND", "STORAGE", "SUBSCRIPT",
+                          "TYPMOD_IN", "TYPMOD_OUT");
             break;

-    /* complete ALTER GROUP <foo> */
+            /* complete ALTER GROUP <foo> */
         case Matches("ALTER", "GROUP", MatchAny):
-        COMPLETE_WITH("ADD USER", "DROP USER", "RENAME TO");
+            COMPLETE_WITH("ADD USER", "DROP USER", "RENAME TO");
             break;
-    /* complete ALTER GROUP <foo> ADD|DROP with USER */
+            /* complete ALTER GROUP <foo> ADD|DROP with USER */
         case Matches("ALTER", "GROUP", MatchAny, "ADD|DROP"):
-        COMPLETE_WITH("USER");
+            COMPLETE_WITH("USER");
             break;
-    /* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
+            /* complete ALTER GROUP <foo> ADD|DROP USER with a user name */
         case Matches("ALTER", "GROUP", MatchAny, "ADD|DROP", "USER"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;

 /*
@@ -3131,894 +3167,935 @@ match_previous_words(int pattern_id,
  * ANALYZE [ VERBOSE ] [ table_and_columns [, ...] ]
  */
         case Matches("ANALYZE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,
-                                        "VERBOSE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_analyzables,
+                                            "VERBOSE");
             break;
         case HeadMatches("ANALYZE", "(*"):
-        if (!HeadMatches("ANALYZE", "(*)"))
-    {
-        /*
-         * This fires if we're in an unfinished parenthesized option list.
-         * get_previous_words treats a completed parenthesized option list as
-         * one word, so the above test is correct.
-         */
-        if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-            COMPLETE_WITH("VERBOSE", "SKIP_LOCKED", "BUFFER_USAGE_LIMIT");
-        else if (TailMatches("VERBOSE|SKIP_LOCKED"))
-            COMPLETE_WITH("ON", "OFF");
-    }
+            if (!HeadMatches("ANALYZE", "(*)"))
+            {
+                /*
+                 * This fires if we're in an unfinished parenthesized option
+                 * list. get_previous_words treats a completed parenthesized
+                 * option list as one word, so the above test is correct.
+                 */
+                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+                    COMPLETE_WITH("VERBOSE", "SKIP_LOCKED", "BUFFER_USAGE_LIMIT");
+                else if (TailMatches("VERBOSE|SKIP_LOCKED"))
+                    COMPLETE_WITH("ON", "OFF");
+            }
             break;
         case Matches("ANALYZE", MatchAnyN, "("):
-        /* "ANALYZE (" should be caught above, so assume we want columns */
-        COMPLETE_WITH_ATTR(prev2_wd);
+            /* "ANALYZE (" should be caught above, so assume we want columns */
+            COMPLETE_WITH_ATTR(prev2_wd);
             break;
         case HeadMatches("ANALYZE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_analyzables);
             break;

 /* BEGIN */
         case Matches("BEGIN"):
-        COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
+            COMPLETE_WITH("WORK", "TRANSACTION", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
             break;
 /* END, ABORT */
         case Matches("END|ABORT"):
-        COMPLETE_WITH("AND", "WORK", "TRANSACTION");
+            COMPLETE_WITH("AND", "WORK", "TRANSACTION");
             break;
 /* COMMIT */
         case Matches("COMMIT"):
-        COMPLETE_WITH("AND", "WORK", "TRANSACTION", "PREPARED");
+            COMPLETE_WITH("AND", "WORK", "TRANSACTION", "PREPARED");
             break;
 /* RELEASE SAVEPOINT */
         case Matches("RELEASE"):
-        COMPLETE_WITH("SAVEPOINT");
+            COMPLETE_WITH("SAVEPOINT");
             break;
 /* ROLLBACK */
         case Matches("ROLLBACK"):
-        COMPLETE_WITH("AND", "WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
+            COMPLETE_WITH("AND", "WORK", "TRANSACTION", "TO SAVEPOINT", "PREPARED");
             break;
         case Matches("ABORT|END|COMMIT|ROLLBACK", "AND"):
-        COMPLETE_WITH("CHAIN");
+            COMPLETE_WITH("CHAIN");
             break;
 /* CALL */
         case Matches("CALL"):
-        COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
+            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
             break;
         case Matches("CALL", MatchAny):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
 /* CLOSE */
         case Matches("CLOSE"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
-                                 "ALL");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
+                                     "ALL");
             break;
 /* CLUSTER */
         case Matches("CLUSTER"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,
-                                        "VERBOSE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_clusterables,
+                                            "VERBOSE");
             break;
         case Matches("CLUSTER", "VERBOSE"):
         case Matches("CLUSTER", "(*)"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables);
             break;
-    /* If we have CLUSTER <sth>, then add "USING" */
+            /* If we have CLUSTER <sth>, then add "USING" */
         case Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON|(|(*)")):
-        COMPLETE_WITH("USING");
+            COMPLETE_WITH("USING");
             break;
-    /* If we have CLUSTER VERBOSE <sth>, then add "USING" */
+            /* If we have CLUSTER VERBOSE <sth>, then add "USING" */
         case Matches("CLUSTER", "VERBOSE|(*)", MatchAny):
-        COMPLETE_WITH("USING");
+            COMPLETE_WITH("USING");
             break;
-    /* If we have CLUSTER <sth> USING, then add the index as well */
+            /* If we have CLUSTER <sth> USING, then add the index as well */
         case Matches("CLUSTER", MatchAny, "USING"):
         case Matches("CLUSTER", "VERBOSE|(*)", MatchAny, "USING"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_index_of_table);
             break;
         case HeadMatches("CLUSTER", "(*"):
-        if (!HeadMatches("CLUSTER", "(*)"))
-    {
-        /*
-         * This fires if we're in an unfinished parenthesized option list.
-         * get_previous_words treats a completed parenthesized option list as
-         * one word, so the above test is correct.
-         */
-        if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-            COMPLETE_WITH("VERBOSE");
-    }
+            if (!HeadMatches("CLUSTER", "(*)"))
+            {
+                /*
+                 * This fires if we're in an unfinished parenthesized option
+                 * list. get_previous_words treats a completed parenthesized
+                 * option list as one word, so the above test is correct.
+                 */
+                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+                    COMPLETE_WITH("VERBOSE");
+            }
             break;

 /* COMMENT */
         case Matches("COMMENT"):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("COMMENT", "ON"):
-        COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
-                      "COLUMN", "CONSTRAINT", "CONVERSION", "DATABASE",
-                      "DOMAIN", "EXTENSION", "EVENT TRIGGER",
-                      "FOREIGN DATA WRAPPER", "FOREIGN TABLE",
-                      "FUNCTION", "INDEX", "LANGUAGE", "LARGE OBJECT",
-                      "MATERIALIZED VIEW", "OPERATOR", "POLICY",
-                      "PROCEDURE", "PROCEDURAL LANGUAGE", "PUBLICATION", "ROLE",
-                      "ROUTINE", "RULE", "SCHEMA", "SEQUENCE", "SERVER",
-                      "STATISTICS", "SUBSCRIPTION", "TABLE",
-                      "TABLESPACE", "TEXT SEARCH", "TRANSFORM FOR",
-                      "TRIGGER", "TYPE", "VIEW");
+            COMPLETE_WITH("ACCESS METHOD", "AGGREGATE", "CAST", "COLLATION",
+                          "COLUMN", "CONSTRAINT", "CONVERSION", "DATABASE",
+                          "DOMAIN", "EXTENSION", "EVENT TRIGGER",
+                          "FOREIGN DATA WRAPPER", "FOREIGN TABLE",
+                          "FUNCTION", "INDEX", "LANGUAGE", "LARGE OBJECT",
+                          "MATERIALIZED VIEW", "OPERATOR", "POLICY",
+                          "PROCEDURE", "PROCEDURAL LANGUAGE", "PUBLICATION", "ROLE",
+                          "ROUTINE", "RULE", "SCHEMA", "SEQUENCE", "SERVER",
+                          "STATISTICS", "SUBSCRIPTION", "TABLE",
+                          "TABLESPACE", "TEXT SEARCH", "TRANSFORM FOR",
+                          "TRIGGER", "TYPE", "VIEW");
             break;
         case Matches("COMMENT", "ON", "ACCESS", "METHOD"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
             break;
         case Matches("COMMENT", "ON", "CONSTRAINT"):
-        COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
+            COMPLETE_WITH_QUERY(Query_for_all_table_constraints);
             break;
         case Matches("COMMENT", "ON", "CONSTRAINT", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables_for_constraint,
-                                        "DOMAIN");
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables_for_constraint,
+                                            "DOMAIN");
             break;
         case Matches("COMMENT", "ON", "CONSTRAINT", MatchAny, "ON", "DOMAIN"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
             break;
         case Matches("COMMENT", "ON", "EVENT", "TRIGGER"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
             break;
         case Matches("COMMENT", "ON", "FOREIGN"):
-        COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            COMPLETE_WITH("DATA WRAPPER", "TABLE");
             break;
         case Matches("COMMENT", "ON", "FOREIGN", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
             break;
         case Matches("COMMENT", "ON", "MATERIALIZED", "VIEW"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
             break;
         case Matches("COMMENT", "ON", "POLICY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+            COMPLETE_WITH_QUERY(Query_for_list_of_policies);
             break;
         case Matches("COMMENT", "ON", "POLICY", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("COMMENT", "ON", "POLICY", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
             break;
         case Matches("COMMENT", "ON", "PROCEDURAL", "LANGUAGE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+            COMPLETE_WITH_QUERY(Query_for_list_of_languages);
             break;
         case Matches("COMMENT", "ON", "RULE", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("COMMENT", "ON", "RULE", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
             break;
         case Matches("COMMENT", "ON", "TEXT", "SEARCH"):
-        COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
             break;
         case Matches("COMMENT", "ON", "TEXT", "SEARCH", "CONFIGURATION"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
             break;
         case Matches("COMMENT", "ON", "TEXT", "SEARCH", "DICTIONARY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
             break;
         case Matches("COMMENT", "ON", "TEXT", "SEARCH", "PARSER"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
             break;
         case Matches("COMMENT", "ON", "TEXT", "SEARCH", "TEMPLATE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
             break;
         case Matches("COMMENT", "ON", "TRANSFORM", "FOR"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny):
-        COMPLETE_WITH("LANGUAGE");
+            COMPLETE_WITH("LANGUAGE");
             break;
         case Matches("COMMENT", "ON", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_QUERY(Query_for_list_of_languages);
             break;
         case Matches("COMMENT", "ON", "TRIGGER", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("COMMENT", "ON", "TRIGGER", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
             break;
         case Matches("COMMENT", "ON", MatchAny, MatchAnyExcept("IS")):
         case Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAnyExcept("IS")):
         case Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")):
         case Matches("COMMENT", "ON", MatchAny, MatchAny, MatchAny, MatchAny, MatchAnyExcept("IS")):
-        COMPLETE_WITH("IS");
+            COMPLETE_WITH("IS");
             break;

 /* COPY */

-    /*
-     * If we have COPY, offer list of tables or "(" (Also cover the analogous
-     * backslash command).
-     */
+            /*
+             * If we have COPY, offer list of tables or "(" (Also cover the
+             * analogous backslash command).
+             */
         case Matches("COPY|\\copy"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables, "(");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables, "(");
             break;
-    /* Complete COPY ( with legal query commands */
+            /* Complete COPY ( with legal query commands */
         case Matches("COPY|\\copy", "("):
-        COMPLETE_WITH("SELECT", "TABLE", "VALUES", "INSERT INTO", "UPDATE", "DELETE FROM", "WITH");
+            COMPLETE_WITH("SELECT", "TABLE", "VALUES", "INSERT INTO", "UPDATE", "DELETE FROM", "WITH");
             break;
-    /* Complete COPY <sth> */
+            /* Complete COPY <sth> */
         case Matches("COPY|\\copy", MatchAny):
-        COMPLETE_WITH("FROM", "TO");
+            COMPLETE_WITH("FROM", "TO");
             break;
-    /* Complete COPY <sth> FROM|TO with filename */
+            /* Complete COPY <sth> FROM|TO with filename */
         case Matches("COPY", MatchAny, "FROM|TO"):
-        completion_charp = "";
-        completion_force_quote = true;    /* COPY requires quoted filename */
-        matches = rl_completion_matches(text, complete_from_files);
+            completion_charp = "";
+            completion_force_quote = true;    /* COPY requires quoted filename */
+            matches = rl_completion_matches(text, complete_from_files);
             break;
         case Matches("\\copy", MatchAny, "FROM|TO"):
-        completion_charp = "";
-        completion_force_quote = false;
-        matches = rl_completion_matches(text, complete_from_files);
+            completion_charp = "";
+            completion_force_quote = false;
+            matches = rl_completion_matches(text, complete_from_files);
             break;

-    /* Complete COPY <sth> TO <sth> */
+            /* Complete COPY <sth> TO <sth> */
         case Matches("COPY|\\copy", MatchAny, "TO", MatchAny):
-        COMPLETE_WITH("WITH (");
+            COMPLETE_WITH("WITH (");
             break;

-    /* Complete COPY <sth> FROM <sth> */
+            /* Complete COPY <sth> FROM <sth> */
         case Matches("COPY|\\copy", MatchAny, "FROM", MatchAny):
-        COMPLETE_WITH("WITH (", "WHERE");
+            COMPLETE_WITH("WITH (", "WHERE");
             break;

-    /* Complete COPY <sth> FROM|TO filename WITH ( */
+            /* Complete COPY <sth> FROM|TO filename WITH ( */
         case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "("):
-        COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
-                      "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
-                      "FORCE_NOT_NULL", "FORCE_NULL", "ENCODING", "DEFAULT",
-                      "ON_ERROR", "LOG_VERBOSITY");
+            COMPLETE_WITH("FORMAT", "FREEZE", "DELIMITER", "NULL",
+                          "HEADER", "QUOTE", "ESCAPE", "FORCE_QUOTE",
+                          "FORCE_NOT_NULL", "FORCE_NULL", "ENCODING", "DEFAULT",
+                          "ON_ERROR", "LOG_VERBOSITY");
             break;

-    /* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
+            /* Complete COPY <sth> FROM|TO filename WITH (FORMAT */
         case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "FORMAT"):
-        COMPLETE_WITH("binary", "csv", "text");
+            COMPLETE_WITH("binary", "csv", "text");
             break;

-    /* Complete COPY <sth> FROM filename WITH (ON_ERROR */
+            /* Complete COPY <sth> FROM filename WITH (ON_ERROR */
         case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "ON_ERROR"):
-        COMPLETE_WITH("stop", "ignore");
+            COMPLETE_WITH("stop", "ignore");
             break;

-    /* Complete COPY <sth> FROM filename WITH (LOG_VERBOSITY */
+            /* Complete COPY <sth> FROM filename WITH (LOG_VERBOSITY */
         case Matches("COPY|\\copy", MatchAny, "FROM|TO", MatchAny, "WITH", "(", "LOG_VERBOSITY"):
-        COMPLETE_WITH("default", "verbose");
+            COMPLETE_WITH("default", "verbose");
             break;

-    /* Complete COPY <sth> FROM <sth> WITH (<options>) */
+            /* Complete COPY <sth> FROM <sth> WITH (<options>) */
         case Matches("COPY|\\copy", MatchAny, "FROM", MatchAny, "WITH", MatchAny):
-        COMPLETE_WITH("WHERE");
+            COMPLETE_WITH("WHERE");
             break;

-    /* CREATE ACCESS METHOD */
-    /* Complete "CREATE ACCESS METHOD <name>" */
+            /* CREATE ACCESS METHOD */
+            /* Complete "CREATE ACCESS METHOD <name>" */
         case Matches("CREATE", "ACCESS", "METHOD", MatchAny):
-        COMPLETE_WITH("TYPE");
+            COMPLETE_WITH("TYPE");
             break;
-    /* Complete "CREATE ACCESS METHOD <name> TYPE" */
+            /* Complete "CREATE ACCESS METHOD <name> TYPE" */
         case Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE"):
-        COMPLETE_WITH("INDEX", "TABLE");
+            COMPLETE_WITH("INDEX", "TABLE");
             break;
-    /* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
+            /* Complete "CREATE ACCESS METHOD <name> TYPE <type>" */
         case Matches("CREATE", "ACCESS", "METHOD", MatchAny, "TYPE", MatchAny):
-        COMPLETE_WITH("HANDLER");
+            COMPLETE_WITH("HANDLER");
             break;

-    /* CREATE COLLATION */
+            /* CREATE COLLATION */
         case Matches("CREATE", "COLLATION", MatchAny):
-        COMPLETE_WITH("(", "FROM");
+            COMPLETE_WITH("(", "FROM");
             break;
         case Matches("CREATE", "COLLATION", MatchAny, "FROM"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
             break;
         case HeadMatches("CREATE", "COLLATION", MatchAny, "(*"):
-        if (TailMatches("(|*,"))
-            COMPLETE_WITH("LOCALE =", "LC_COLLATE =", "LC_CTYPE =",
-                          "PROVIDER =", "DETERMINISTIC =");
-        else if (TailMatches("PROVIDER", "="))
-            COMPLETE_WITH("libc", "icu");
-        else if (TailMatches("DETERMINISTIC", "="))
-            COMPLETE_WITH("true", "false");
+            if (TailMatches("(|*,"))
+                COMPLETE_WITH("LOCALE =", "LC_COLLATE =", "LC_CTYPE =",
+                              "PROVIDER =", "DETERMINISTIC =");
+            else if (TailMatches("PROVIDER", "="))
+                COMPLETE_WITH("libc", "icu");
+            else if (TailMatches("DETERMINISTIC", "="))
+                COMPLETE_WITH("true", "false");
             break;

-    /* CREATE DATABASE */
+            /* CREATE DATABASE */
         case Matches("CREATE", "DATABASE", MatchAny):
-        COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
-                      "IS_TEMPLATE", "STRATEGY",
-                      "ALLOW_CONNECTIONS", "CONNECTION LIMIT",
-                      "LC_COLLATE", "LC_CTYPE", "LOCALE", "OID",
-                      "LOCALE_PROVIDER", "ICU_LOCALE");
+            COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
+                          "IS_TEMPLATE", "STRATEGY",
+                          "ALLOW_CONNECTIONS", "CONNECTION LIMIT",
+                          "LC_COLLATE", "LC_CTYPE", "LOCALE", "OID",
+                          "LOCALE_PROVIDER", "ICU_LOCALE");
             break;

         case Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
+            COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
             break;
         case Matches("CREATE", "DATABASE", MatchAny, "STRATEGY"):
-        COMPLETE_WITH("WAL_LOG", "FILE_COPY");
+            COMPLETE_WITH("WAL_LOG", "FILE_COPY");
             break;

-    /* CREATE DOMAIN */
+            /* CREATE DOMAIN */
         case Matches("CREATE", "DOMAIN", MatchAny):
-        COMPLETE_WITH("AS");
+            COMPLETE_WITH("AS");
             break;
         case Matches("CREATE", "DOMAIN", MatchAny, "AS"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny):
-        COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
-                      "NOT NULL", "NULL", "CHECK (");
+            COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
+                          "NOT NULL", "NULL", "CHECK (");
             break;
         case Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations);
             break;

-    /* CREATE EXTENSION */
-    /* Complete with available extensions rather than installed ones. */
+            /* CREATE EXTENSION */
+            /* Complete with available extensions rather than installed ones. */
         case Matches("CREATE", "EXTENSION"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
+            COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions);
             break;
-    /* CREATE EXTENSION <name> */
+            /* CREATE EXTENSION <name> */
         case Matches("CREATE", "EXTENSION", MatchAny):
-        COMPLETE_WITH("WITH SCHEMA", "CASCADE", "VERSION");
+            COMPLETE_WITH("WITH SCHEMA", "CASCADE", "VERSION");
             break;
-    /* CREATE EXTENSION <name> VERSION */
+            /* CREATE EXTENSION <name> VERSION */
         case Matches("CREATE", "EXTENSION", MatchAny, "VERSION"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_QUERY(Query_for_list_of_available_extension_versions);
             break;

-    /* CREATE FOREIGN */
+            /* CREATE FOREIGN */
         case Matches("CREATE", "FOREIGN"):
-        COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            COMPLETE_WITH("DATA WRAPPER", "TABLE");
             break;

-    /* CREATE FOREIGN DATA WRAPPER */
+            /* CREATE FOREIGN DATA WRAPPER */
         case Matches("CREATE", "FOREIGN", "DATA", "WRAPPER", MatchAny):
-        COMPLETE_WITH("HANDLER", "VALIDATOR", "OPTIONS");
+            COMPLETE_WITH("HANDLER", "VALIDATOR", "OPTIONS");
             break;

-    /* CREATE FOREIGN TABLE */
+            /* CREATE FOREIGN TABLE */
         case Matches("CREATE", "FOREIGN", "TABLE", MatchAny):
-        COMPLETE_WITH("(", "PARTITION OF");
+            COMPLETE_WITH("(", "PARTITION OF");
             break;

-    /* CREATE INDEX --- is allowed inside CREATE SCHEMA, so use TailMatches */
-    /* First off we complete CREATE UNIQUE with "INDEX" */
+            /*
+             * CREATE INDEX --- is allowed inside CREATE SCHEMA, so use
+             * TailMatches
+             */
+            /* First off we complete CREATE UNIQUE with "INDEX" */
         case TailMatches("CREATE", "UNIQUE"):
-        COMPLETE_WITH("INDEX");
+            COMPLETE_WITH("INDEX");
             break;

-    /*
-     * If we have CREATE|UNIQUE INDEX, then add "ON", "CONCURRENTLY", and
-     * existing indexes
-     */
+            /*
+             * If we have CREATE|UNIQUE INDEX, then add "ON", "CONCURRENTLY",
+             * and existing indexes
+             */
         case TailMatches("CREATE|UNIQUE", "INDEX"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
-                                        "ON", "CONCURRENTLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
+                                            "ON", "CONCURRENTLY");
             break;

-    /*
-     * Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of relations
-     * that indexes can be created on
-     */
+            /*
+             * Complete ... INDEX|CONCURRENTLY [<name>] ON with a list of
+             * relations that indexes can be created on
+             */
         case TailMatches("INDEX|CONCURRENTLY", MatchAny, "ON"):
         case TailMatches("INDEX|CONCURRENTLY", "ON"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
             break;

-    /*
-     * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and existing
-     * indexes
-     */
+            /*
+             * Complete CREATE|UNIQUE INDEX CONCURRENTLY with "ON" and
+             * existing indexes
+             */
         case TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
-                                        "ON");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
+                                            "ON");
             break;
-    /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
+            /* Complete CREATE|UNIQUE INDEX [CONCURRENTLY] <sth> with "ON" */
         case TailMatches("CREATE|UNIQUE", "INDEX", MatchAny):
         case TailMatches("CREATE|UNIQUE", "INDEX", "CONCURRENTLY", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;

-    /*
-     * Complete INDEX <name> ON <table> with a list of table columns (which
-     * should really be in parens)
-     */
+            /*
+             * Complete INDEX <name> ON <table> with a list of table columns
+             * (which should really be in parens)
+             */
         case TailMatches("INDEX", MatchAny, "ON", MatchAny):
         case TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny):
-        COMPLETE_WITH("(", "USING");
+            COMPLETE_WITH("(", "USING");
             break;
         case TailMatches("INDEX", MatchAny, "ON", MatchAny, "("):
         case TailMatches("INDEX|CONCURRENTLY", "ON", MatchAny, "("):
-        COMPLETE_WITH_ATTR(prev2_wd);
+            COMPLETE_WITH_ATTR(prev2_wd);
             break;
-    /* same if you put in USING */
+            /* same if you put in USING */
         case TailMatches("ON", MatchAny, "USING", MatchAny, "("):
-        COMPLETE_WITH_ATTR(prev4_wd);
+            COMPLETE_WITH_ATTR(prev4_wd);
             break;
-    /* Complete USING with an index method */
+            /* Complete USING with an index method */
         case TailMatches("INDEX", MatchAny, MatchAny, "ON", MatchAny, "USING"):
         case TailMatches("INDEX", MatchAny, "ON", MatchAny, "USING"):
         case TailMatches("INDEX", "ON", MatchAny, "USING"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_index_access_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_index_access_methods);
             break;
         case TailMatches("ON", MatchAny, "USING", MatchAny):
-        if (!TailMatches("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) && !TailMatches("FOR", MatchAny,
MatchAny,MatchAny)) 
-        COMPLETE_WITH("(");
+            if (!TailMatches("POLICY", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny) && !TailMatches("FOR",
MatchAny,MatchAny, MatchAny)) 
+                COMPLETE_WITH("(");
             break;

-    /* CREATE OR REPLACE */
+            /* CREATE OR REPLACE */
         case Matches("CREATE", "OR"):
-        COMPLETE_WITH("REPLACE");
+            COMPLETE_WITH("REPLACE");
             break;

-    /* CREATE POLICY */
-    /* Complete "CREATE POLICY <name> ON" */
+            /* CREATE POLICY */
+            /* Complete "CREATE POLICY <name> ON" */
         case Matches("CREATE", "POLICY", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
-    /* Complete "CREATE POLICY <name> ON <table>" */
+            /* Complete "CREATE POLICY <name> ON <table>" */
         case Matches("CREATE", "POLICY", MatchAny, "ON"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
-    /* Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH CHECK" */
+
+            /*
+             * Complete "CREATE POLICY <name> ON <table> AS|FOR|TO|USING|WITH
+             * CHECK"
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("AS", "FOR", "TO", "USING (", "WITH CHECK (");
+            COMPLETE_WITH("AS", "FOR", "TO", "USING (", "WITH CHECK (");
             break;
-    /* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */
+            /* CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS"):
-        COMPLETE_WITH("PERMISSIVE", "RESTRICTIVE");
+            COMPLETE_WITH("PERMISSIVE", "RESTRICTIVE");
             break;

-    /*
-     * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
-     * FOR|TO|USING|WITH CHECK
-     */
+            /*
+             * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
+             * FOR|TO|USING|WITH CHECK
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny):
-        COMPLETE_WITH("FOR", "TO", "USING", "WITH CHECK");
+            COMPLETE_WITH("FOR", "TO", "USING", "WITH CHECK");
             break;
-    /* CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE */
+
+            /*
+             * CREATE POLICY <name> ON <table> FOR
+             * ALL|SELECT|INSERT|UPDATE|DELETE
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR"):
-        COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
+            COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
             break;
-    /* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */
+
+            /*
+             * Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH
+             * CHECK"
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "INSERT"):
-        COMPLETE_WITH("TO", "WITH CHECK (");
+            COMPLETE_WITH("TO", "WITH CHECK (");
             break;
-    /* Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE TO|USING" */
+
+            /*
+             * Complete "CREATE POLICY <name> ON <table> FOR SELECT|DELETE
+             * TO|USING"
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "SELECT|DELETE"):
-        COMPLETE_WITH("TO", "USING (");
+            COMPLETE_WITH("TO", "USING (");
             break;
-    /* CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH CHECK */
+
+            /*
+             * CREATE POLICY <name> ON <table> FOR ALL|UPDATE TO|USING|WITH
+             * CHECK
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "FOR", "ALL|UPDATE"):
-        COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
+            COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
             break;
-    /* Complete "CREATE POLICY <name> ON <table> TO <role>" */
+            /* Complete "CREATE POLICY <name> ON <table> TO <role>" */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "TO"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_grant_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_grant_roles);
             break;
-    /* Complete "CREATE POLICY <name> ON <table> USING (" */
+            /* Complete "CREATE POLICY <name> ON <table> USING (" */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "USING"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;

-    /*
-     * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
-     * ALL|SELECT|INSERT|UPDATE|DELETE
-     */
+            /*
+             * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
+             * ALL|SELECT|INSERT|UPDATE|DELETE
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR"):
-        COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
+            COMPLETE_WITH("ALL", "SELECT", "INSERT", "UPDATE", "DELETE");
             break;

-    /*
-     * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
-     * INSERT TO|WITH CHECK"
-     */
+            /*
+             * Complete "CREATE POLICY <name> ON <table> AS
+             * PERMISSIVE|RESTRICTIVE FOR INSERT TO|WITH CHECK"
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "INSERT"):
-        COMPLETE_WITH("TO", "WITH CHECK (");
+            COMPLETE_WITH("TO", "WITH CHECK (");
             break;

-    /*
-     * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
-     * SELECT|DELETE TO|USING"
-     */
+            /*
+             * Complete "CREATE POLICY <name> ON <table> AS
+             * PERMISSIVE|RESTRICTIVE FOR SELECT|DELETE TO|USING"
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "SELECT|DELETE"):
-        COMPLETE_WITH("TO", "USING (");
+            COMPLETE_WITH("TO", "USING (");
             break;

-    /*
-     * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
-     * ALL|UPDATE TO|USING|WITH CHECK
-     */
+            /*
+             * CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE FOR
+             * ALL|UPDATE TO|USING|WITH CHECK
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "FOR", "ALL|UPDATE"):
-        COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
+            COMPLETE_WITH("TO", "USING (", "WITH CHECK (");
             break;

-    /*
-     * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE TO
-     * <role>"
-     */
+            /*
+             * Complete "CREATE POLICY <name> ON <table> AS
+             * PERMISSIVE|RESTRICTIVE TO <role>"
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "TO"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_grant_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_grant_roles);
             break;

-    /*
-     * Complete "CREATE POLICY <name> ON <table> AS PERMISSIVE|RESTRICTIVE
-     * USING ("
-     */
+            /*
+             * Complete "CREATE POLICY <name> ON <table> AS
+             * PERMISSIVE|RESTRICTIVE USING ("
+             */
         case Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "USING"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;


 /* CREATE PUBLICATION */
         case Matches("CREATE", "PUBLICATION", MatchAny):
-        COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR TABLES IN SCHEMA", "WITH (");
+            COMPLETE_WITH("FOR TABLE", "FOR ALL TABLES", "FOR TABLES IN SCHEMA", "WITH (");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR"):
-        COMPLETE_WITH("TABLE", "ALL TABLES", "TABLES IN SCHEMA");
+            COMPLETE_WITH("TABLE", "ALL TABLES", "TABLES IN SCHEMA");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL"):
-        COMPLETE_WITH("TABLES");
+            COMPLETE_WITH("TABLES");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "ALL", "TABLES"):
-        COMPLETE_WITH("WITH (");
+            COMPLETE_WITH("WITH (");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES"):
-        COMPLETE_WITH("IN SCHEMA");
+            COMPLETE_WITH("IN SCHEMA");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny):
-        if (!ends_with(prev_wd, ','))
-        COMPLETE_WITH("WHERE (", "WITH (");
+            if (!ends_with(prev_wd, ','))
+                COMPLETE_WITH("WHERE (", "WITH (");
             break;
-    /* Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>, ..." */
+
+            /*
+             * Complete "CREATE PUBLICATION <name> FOR TABLE" with "<table>,
+             * ..."
+             */
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;

-    /*
-     * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
-     * table attributes
-     */
+            /*
+             * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete
+             * with table attributes
+             */
         case Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "(*)"):
-        COMPLETE_WITH(" WITH (");
+            COMPLETE_WITH(" WITH (");
             break;

-    /*
-     * Complete "CREATE PUBLICATION <name> FOR TABLES IN SCHEMA <schema>, ..."
-     */
+            /*
+             * Complete "CREATE PUBLICATION <name> FOR TABLES IN SCHEMA
+             * <schema>, ..."
+             */
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
-                                 " AND nspname NOT LIKE E'pg\\\\_%%'",
-                                 "CURRENT_SCHEMA");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
+                                     " AND nspname NOT LIKE E'pg\\\\_%%'",
+                                     "CURRENT_SCHEMA");
             break;
         case Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny):
-        if ((!ends_with(prev_wd, ',')))
-        COMPLETE_WITH("WITH (");
+            if ((!ends_with(prev_wd, ',')))
+                COMPLETE_WITH("WITH (");
             break;
-    /* Complete "CREATE PUBLICATION <name> [...] WITH" */
+            /* Complete "CREATE PUBLICATION <name> [...] WITH" */
         case Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "("):
-        COMPLETE_WITH("publish", "publish_via_partition_root");
+            COMPLETE_WITH("publish", "publish_via_partition_root");
             break;

 /* CREATE RULE */
-    /* Complete "CREATE [ OR REPLACE ] RULE <sth>" with "AS ON" */
+            /* Complete "CREATE [ OR REPLACE ] RULE <sth>" with "AS ON" */
         case Matches("CREATE", "RULE", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny):
-        COMPLETE_WITH("AS ON");
+            COMPLETE_WITH("AS ON");
             break;
-    /* Complete "CREATE [ OR REPLACE ] RULE <sth> AS" with "ON" */
+            /* Complete "CREATE [ OR REPLACE ] RULE <sth> AS" with "ON" */
         case Matches("CREATE", "RULE", MatchAny, "AS"):
         case Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS"):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;

-    /*
-     * Complete "CREATE [ OR REPLACE ] RULE <sth> AS ON" with
-     * SELECT|UPDATE|INSERT|DELETE
-     */
+            /*
+             * Complete "CREATE [ OR REPLACE ] RULE <sth> AS ON" with
+             * SELECT|UPDATE|INSERT|DELETE
+             */
         case Matches("CREATE", "RULE", MatchAny, "AS", "ON"):
         case Matches("CREATE", "OR", "REPLACE", "RULE", MatchAny, "AS", "ON"):
-        COMPLETE_WITH("SELECT", "UPDATE", "INSERT", "DELETE");
+            COMPLETE_WITH("SELECT", "UPDATE", "INSERT", "DELETE");
             break;
-    /* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */
+            /* Complete "AS ON SELECT|UPDATE|INSERT|DELETE" with a "TO" */
         case TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE"):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
-    /* Complete "AS ON <sth> TO" with a table name */
+            /* Complete "AS ON <sth> TO" with a table name */
         case TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;

 /* CREATE SCHEMA [ <name> ] [ AUTHORIZATION ] */
         case Matches("CREATE", "SCHEMA"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
-                                 "AUTHORIZATION");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
+                                     "AUTHORIZATION");
             break;
         case Matches("CREATE", "SCHEMA", "AUTHORIZATION"):
         case Matches("CREATE", "SCHEMA", MatchAny, "AUTHORIZATION"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_owner_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_owner_roles);
             break;
         case Matches("CREATE", "SCHEMA", "AUTHORIZATION", MatchAny):
         case Matches("CREATE", "SCHEMA", MatchAny, "AUTHORIZATION", MatchAny):
-        COMPLETE_WITH("CREATE", "GRANT");
+            COMPLETE_WITH("CREATE", "GRANT");
             break;
         case Matches("CREATE", "SCHEMA", MatchAny):
-        COMPLETE_WITH("AUTHORIZATION", "CREATE", "GRANT");
+            COMPLETE_WITH("AUTHORIZATION", "CREATE", "GRANT");
             break;

 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
         case TailMatches("CREATE", "SEQUENCE", MatchAny):
         case TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny):
-        COMPLETE_WITH("AS", "INCREMENT BY", "MINVALUE", "MAXVALUE", "NO",
-                      "CACHE", "CYCLE", "OWNED BY", "START WITH");
+            COMPLETE_WITH("AS", "INCREMENT BY", "MINVALUE", "MAXVALUE", "NO",
+                          "CACHE", "CYCLE", "OWNED BY", "START WITH");
             break;
         case TailMatches("CREATE", "SEQUENCE", MatchAny, "AS"):
         case TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"):
-        COMPLETE_WITH_CS("smallint", "integer", "bigint");
+            COMPLETE_WITH_CS("smallint", "integer", "bigint");
             break;
         case TailMatches("CREATE", "SEQUENCE", MatchAny, "NO"):
         case TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"):
-        COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
+            COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
             break;

 /* CREATE SERVER <name> */
         case Matches("CREATE", "SERVER", MatchAny):
-        COMPLETE_WITH("TYPE", "VERSION", "FOREIGN DATA WRAPPER");
+            COMPLETE_WITH("TYPE", "VERSION", "FOREIGN DATA WRAPPER");
             break;

 /* CREATE STATISTICS <name> */
         case Matches("CREATE", "STATISTICS", MatchAny):
-        COMPLETE_WITH("(", "ON");
+            COMPLETE_WITH("(", "ON");
             break;
         case Matches("CREATE", "STATISTICS", MatchAny, "("):
-        COMPLETE_WITH("ndistinct", "dependencies", "mcv");
+            COMPLETE_WITH("ndistinct", "dependencies", "mcv");
             break;
         case Matches("CREATE", "STATISTICS", MatchAny, "(*)"):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;

 /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
-    /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
+            /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */
         case TailMatches("CREATE", "TEMP|TEMPORARY"):
-        COMPLETE_WITH("SEQUENCE", "TABLE", "VIEW");
+            COMPLETE_WITH("SEQUENCE", "TABLE", "VIEW");
             break;
-    /* Complete "CREATE UNLOGGED" with TABLE, SEQUENCE or MATVIEW */
+            /* Complete "CREATE UNLOGGED" with TABLE, SEQUENCE or MATVIEW */
         case TailMatches("CREATE", "UNLOGGED"):
-        /* but not MATVIEW in CREATE SCHEMA */
-        if (HeadMatches("CREATE", "SCHEMA"))
-            COMPLETE_WITH("TABLE", "SEQUENCE");
-        else
-            COMPLETE_WITH("TABLE", "SEQUENCE", "MATERIALIZED VIEW");
+            /* but not MATVIEW in CREATE SCHEMA */
+            if (HeadMatches("CREATE", "SCHEMA"))
+                COMPLETE_WITH("TABLE", "SEQUENCE");
+            else
+                COMPLETE_WITH("TABLE", "SEQUENCE", "MATERIALIZED VIEW");
             break;
-    /* Complete PARTITION BY with RANGE ( or LIST ( or ... */
+            /* Complete PARTITION BY with RANGE ( or LIST ( or ... */
         case TailMatches("PARTITION", "BY"):
-        COMPLETE_WITH("RANGE (", "LIST (", "HASH (");
+            COMPLETE_WITH("RANGE (", "LIST (", "HASH (");
             break;
-    /* If we have xxx PARTITION OF, provide a list of partitioned tables */
+
+            /*
+             * If we have xxx PARTITION OF, provide a list of partitioned
+             * tables
+             */
         case TailMatches("PARTITION", "OF"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
             break;
-    /* Limited completion support for partition bound specification */
+            /* Limited completion support for partition bound specification */
         case TailMatches("PARTITION", "OF", MatchAny):
-        COMPLETE_WITH("FOR VALUES", "DEFAULT");
+            COMPLETE_WITH("FOR VALUES", "DEFAULT");
             break;
-    /* Complete CREATE TABLE <name> with '(', AS, OF or PARTITION OF */
+            /* Complete CREATE TABLE <name> with '(', AS, OF or PARTITION OF */
         case TailMatches("CREATE", "TABLE", MatchAny):
         case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny):
-        COMPLETE_WITH("(", "AS", "OF", "PARTITION OF");
+            COMPLETE_WITH("(", "AS", "OF", "PARTITION OF");
             break;
-    /* Complete CREATE TABLE <name> OF with list of composite types */
+            /* Complete CREATE TABLE <name> OF with list of composite types */
         case TailMatches("CREATE", "TABLE", MatchAny, "OF"):
         case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "OF"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_composite_datatypes);
             break;
-    /* Complete CREATE TABLE <name> [ (...) ] AS with list of keywords */
+            /* Complete CREATE TABLE <name> [ (...) ] AS with list of keywords */
         case TailMatches("CREATE", "TABLE", MatchAny, "AS"):
         case TailMatches("CREATE", "TABLE", MatchAny, "(*)", "AS"):
         case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "AS"):
         case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "AS"):
-        COMPLETE_WITH("EXECUTE", "SELECT", "TABLE", "VALUES", "WITH");
+            COMPLETE_WITH("EXECUTE", "SELECT", "TABLE", "VALUES", "WITH");
             break;
-    /* Complete CREATE TABLE name (...) with supported options */
+            /* Complete CREATE TABLE name (...) with supported options */
         case TailMatches("CREATE", "TABLE", MatchAny, "(*)"):
         case TailMatches("CREATE", "UNLOGGED", "TABLE", MatchAny, "(*)"):
-        COMPLETE_WITH("AS", "INHERITS (", "PARTITION BY", "USING", "TABLESPACE", "WITH (");
+            COMPLETE_WITH("AS", "INHERITS (", "PARTITION BY", "USING", "TABLESPACE", "WITH (");
             break;
         case TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)"):
-        COMPLETE_WITH("AS", "INHERITS (", "ON COMMIT", "PARTITION BY",
-                      "TABLESPACE", "WITH (");
+            COMPLETE_WITH("AS", "INHERITS (", "ON COMMIT", "PARTITION BY",
+                          "TABLESPACE", "WITH (");
             break;
-    /* Complete CREATE TABLE (...) USING with table access methods */
+            /* Complete CREATE TABLE (...) USING with table access methods */
         case TailMatches("CREATE", "TABLE", MatchAny, "(*)", "USING"):
         case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "USING"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_table_access_methods);
             break;
-    /* Complete CREATE TABLE (...) WITH with storage parameters */
+            /* Complete CREATE TABLE (...) WITH with storage parameters */
         case TailMatches("CREATE", "TABLE", MatchAny, "(*)", "WITH", "("):
         case TailMatches("CREATE", "TEMP|TEMPORARY|UNLOGGED", "TABLE", MatchAny, "(*)", "WITH", "("):
-        COMPLETE_WITH_LIST(table_storage_parameters);
+            COMPLETE_WITH_LIST(table_storage_parameters);
             break;
-    /* Complete CREATE TABLE ON COMMIT with actions */
+            /* Complete CREATE TABLE ON COMMIT with actions */
         case TailMatches("CREATE", "TEMP|TEMPORARY", "TABLE", MatchAny, "(*)", "ON", "COMMIT"):
-        COMPLETE_WITH("DELETE ROWS", "DROP", "PRESERVE ROWS");
+            COMPLETE_WITH("DELETE ROWS", "DROP", "PRESERVE ROWS");
             break;

 /* CREATE TABLESPACE */
         case Matches("CREATE", "TABLESPACE", MatchAny):
-        COMPLETE_WITH("OWNER", "LOCATION");
+            COMPLETE_WITH("OWNER", "LOCATION");
             break;
-    /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
+            /* Complete CREATE TABLESPACE name OWNER name with "LOCATION" */
         case Matches("CREATE", "TABLESPACE", MatchAny, "OWNER", MatchAny):
-        COMPLETE_WITH("LOCATION");
+            COMPLETE_WITH("LOCATION");
             break;

 /* CREATE TEXT SEARCH */
         case Matches("CREATE", "TEXT", "SEARCH"):
-        COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
             break;
         case Matches("CREATE", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;

 /* CREATE TRANSFORM */
         case Matches("CREATE", "TRANSFORM"):
         case Matches("CREATE", "OR", "REPLACE", "TRANSFORM"):
-        COMPLETE_WITH("FOR");
+            COMPLETE_WITH("FOR");
             break;
         case Matches("CREATE", "TRANSFORM", "FOR"):
         case Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case Matches("CREATE", "TRANSFORM", "FOR", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny):
-        COMPLETE_WITH("LANGUAGE");
+            COMPLETE_WITH("LANGUAGE");
             break;
         case Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
         case Matches("CREATE", "OR", "REPLACE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_QUERY(Query_for_list_of_languages);
             break;

 /* CREATE SUBSCRIPTION */
         case Matches("CREATE", "SUBSCRIPTION", MatchAny):
-        COMPLETE_WITH("CONNECTION");
+            COMPLETE_WITH("CONNECTION");
             break;
         case Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny):
-        COMPLETE_WITH("PUBLICATION");
+            COMPLETE_WITH("PUBLICATION");
             break;
         case Matches("CREATE", "SUBSCRIPTION", MatchAny, "CONNECTION", MatchAny, "PUBLICATION"):
-        /* complete with nothing here as this refers to remote publications */
+
+            /*
+             * complete with nothing here as this refers to remote
+             * publications
+             */
             break;
         case Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny):
-        COMPLETE_WITH("WITH (");
+            COMPLETE_WITH("WITH (");
             break;
-    /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
+            /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
         case Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "("):
-        COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
-                      "disable_on_error", "enabled", "failover", "origin",
-                      "password_required", "run_as_owner", "slot_name",
-                      "streaming", "synchronous_commit", "two_phase");
+            COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
+                          "disable_on_error", "enabled", "failover", "origin",
+                          "password_required", "run_as_owner", "slot_name",
+                          "streaming", "synchronous_commit", "two_phase");
             break;

 /* CREATE TRIGGER --- is allowed inside CREATE SCHEMA, so use TailMatches */

-    /*
-     * Complete CREATE [ OR REPLACE ] TRIGGER <name> with BEFORE|AFTER|INSTEAD
-     * OF.
-     */
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER <name> with
+             * BEFORE|AFTER|INSTEAD OF.
+             */
         case TailMatches("CREATE", "TRIGGER", MatchAny):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny):
-        COMPLETE_WITH("BEFORE", "AFTER", "INSTEAD OF");
+            COMPLETE_WITH("BEFORE", "AFTER", "INSTEAD OF");
             break;

-    /*
-     * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER with an
-     * event.
-     */
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER with
+             * an event.
+             */
         case TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER"):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER"):
-        COMPLETE_WITH("INSERT", "DELETE", "UPDATE", "TRUNCATE");
+            COMPLETE_WITH("INSERT", "DELETE", "UPDATE", "TRUNCATE");
             break;
-    /* Complete CREATE [ OR REPLACE ] TRIGGER <name> INSTEAD OF with an event */
+
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER <name> INSTEAD OF with
+             * an event
+             */
         case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF"):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF"):
-        COMPLETE_WITH("INSERT", "DELETE", "UPDATE");
+            COMPLETE_WITH("INSERT", "DELETE", "UPDATE");
             break;

-    /*
-     * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER sth with
-     * OR|ON.
-     */
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER sth
+             * with OR|ON.
+             */
         case TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny):
         case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny):
-        COMPLETE_WITH("ON", "OR");
+            COMPLETE_WITH("ON", "OR");
             break;

-    /*
-     * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER event ON
-     * with a list of tables.  EXECUTE FUNCTION is the recommended grammar
-     * instead of EXECUTE PROCEDURE in version 11 and upwards.
-     */
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER <name> BEFORE,AFTER
+             * event ON with a list of tables.  EXECUTE FUNCTION is the
+             * recommended grammar instead of EXECUTE PROCEDURE in version 11
+             * and upwards.
+             */
         case TailMatches("CREATE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "BEFORE|AFTER", MatchAny, "ON"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;

-    /*
-     * Complete CREATE [ OR REPLACE ] TRIGGER ... INSTEAD OF event ON with a
-     * list of views.
-     */
-        case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"):
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER ... INSTEAD OF event ON
+             * with a list of views.
+             */
+        case TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"):
         case TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "ON", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "ON", MatchAny):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
-                          "REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
-                          "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
+                              "REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
+                              "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "DEFERRABLE"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "DEFERRABLE"):
         case Matches("CREATE", "TRIGGER", MatchAnyN, "INITIALLY", "IMMEDIATE|DEFERRED"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "INITIALLY", "IMMEDIATE|DEFERRED"):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING"):
-        COMPLETE_WITH("OLD TABLE", "NEW TABLE");
+            COMPLETE_WITH("OLD TABLE", "NEW TABLE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "OLD|NEW", "TABLE"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "OLD|NEW", "TABLE"):
-        COMPLETE_WITH("AS");
+            COMPLETE_WITH("AS");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", "AS", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", "AS", MatchAny):
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD", "TABLE", MatchAny):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", "AS", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", "AS", MatchAny):
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "NEW", "TABLE", MatchAny):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW",
"TABLE","AS", MatchAny): 
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS",
MatchAny,"OLD|NEW", "TABLE", "AS", MatchAny): 
@@ -4028,440 +4105,440 @@ match_previous_words(int pattern_id,
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", "AS",
MatchAny,"OLD|NEW", "TABLE", MatchAny): 
         case Matches("CREATE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE",
MatchAny):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "REFERENCING", "OLD|NEW", "TABLE", MatchAny,
"OLD|NEW","TABLE", MatchAny): 
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR"):
-        COMPLETE_WITH("EACH", "ROW", "STATEMENT");
+            COMPLETE_WITH("EACH", "ROW", "STATEMENT");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR", "EACH"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR", "EACH"):
-        COMPLETE_WITH("ROW", "STATEMENT");
+            COMPLETE_WITH("ROW", "STATEMENT");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR", "EACH", "ROW|STATEMENT"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR", "EACH", "ROW|STATEMENT"):
         case Matches("CREATE", "TRIGGER", MatchAnyN, "FOR", "ROW|STATEMENT"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "FOR", "ROW|STATEMENT"):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "WHEN", "(*)"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "WHEN", "(*)"):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("EXECUTE PROCEDURE");
             break;

-    /*
-     * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
-     * PROCEDURE|FUNCTION.
-     */
+            /*
+             * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
+             * PROCEDURE|FUNCTION.
+             */
         case Matches("CREATE", "TRIGGER", MatchAnyN, "EXECUTE"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "EXECUTE"):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("FUNCTION");
-        else
-            COMPLETE_WITH("PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("FUNCTION");
+            else
+                COMPLETE_WITH("PROCEDURE");
             break;
         case Matches("CREATE", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"):
         case Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"):
-        COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
+            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
             break;

 /* CREATE ROLE,USER,GROUP <name> */
         case Matches("CREATE", "ROLE|GROUP|USER", MatchAny):
-        if (!TailMatches("USER", "MAPPING"))
-        COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
-                      "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
-                      "LOGIN", "NOBYPASSRLS",
-                      "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
-                      "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
-                      "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
-                      "VALID UNTIL", "WITH");
+            if (!TailMatches("USER", "MAPPING"))
+                COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
+                              "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
+                              "LOGIN", "NOBYPASSRLS",
+                              "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
+                              "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
+                              "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
+                              "VALID UNTIL", "WITH");
             break;

 /* CREATE ROLE,USER,GROUP <name> WITH */
         case Matches("CREATE", "ROLE|GROUP|USER", MatchAny, "WITH"):
-        /* Similar to the above, but don't complete "WITH" again. */
-        COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
-                      "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
-                      "LOGIN", "NOBYPASSRLS",
-                      "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
-                      "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
-                      "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
-                      "VALID UNTIL");
+            /* Similar to the above, but don't complete "WITH" again. */
+            COMPLETE_WITH("ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB",
+                          "CREATEROLE", "ENCRYPTED PASSWORD", "IN", "INHERIT",
+                          "LOGIN", "NOBYPASSRLS",
+                          "NOCREATEDB", "NOCREATEROLE", "NOINHERIT",
+                          "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "PASSWORD",
+                          "REPLICATION", "ROLE", "SUPERUSER", "SYSID",
+                          "VALID UNTIL");
             break;

-    /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
+            /* complete CREATE ROLE,USER,GROUP <name> IN with ROLE,GROUP */
         case Matches("CREATE", "ROLE|USER|GROUP", MatchAny, "IN"):
-        COMPLETE_WITH("GROUP", "ROLE");
+            COMPLETE_WITH("GROUP", "ROLE");
             break;

 /* CREATE TYPE */
         case Matches("CREATE", "TYPE", MatchAny):
-        COMPLETE_WITH("(", "AS");
+            COMPLETE_WITH("(", "AS");
             break;
         case Matches("CREATE", "TYPE", MatchAny, "AS"):
-        COMPLETE_WITH("ENUM", "RANGE", "(");
+            COMPLETE_WITH("ENUM", "RANGE", "(");
             break;
         case HeadMatches("CREATE", "TYPE", MatchAny, "AS", "("):
-        if (TailMatches("(|*,", MatchAny))
-            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-        else if (TailMatches("(|*,", MatchAny, MatchAnyExcept("*)")))
-            COMPLETE_WITH("COLLATE", ",", ")");
+            if (TailMatches("(|*,", MatchAny))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            else if (TailMatches("(|*,", MatchAny, MatchAnyExcept("*)")))
+                COMPLETE_WITH("COLLATE", ",", ")");
             break;
         case Matches("CREATE", "TYPE", MatchAny, "AS", "ENUM|RANGE"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
         case HeadMatches("CREATE", "TYPE", MatchAny, "("):
-        if (TailMatches("(|*,"))
-            COMPLETE_WITH("INPUT", "OUTPUT", "RECEIVE", "SEND",
-                          "TYPMOD_IN", "TYPMOD_OUT", "ANALYZE", "SUBSCRIPT",
-                          "INTERNALLENGTH", "PASSEDBYVALUE", "ALIGNMENT",
-                          "STORAGE", "LIKE", "CATEGORY", "PREFERRED",
-                          "DEFAULT", "ELEMENT", "DELIMITER",
-                          "COLLATABLE");
-        else if (TailMatches("(*|*,", MatchAnyExcept("*=")))
-            COMPLETE_WITH("=");
-        else if (TailMatches("=", MatchAnyExcept("*)")))
-            COMPLETE_WITH(",", ")");
+            if (TailMatches("(|*,"))
+                COMPLETE_WITH("INPUT", "OUTPUT", "RECEIVE", "SEND",
+                              "TYPMOD_IN", "TYPMOD_OUT", "ANALYZE", "SUBSCRIPT",
+                              "INTERNALLENGTH", "PASSEDBYVALUE", "ALIGNMENT",
+                              "STORAGE", "LIKE", "CATEGORY", "PREFERRED",
+                              "DEFAULT", "ELEMENT", "DELIMITER",
+                              "COLLATABLE");
+            else if (TailMatches("(*|*,", MatchAnyExcept("*=")))
+                COMPLETE_WITH("=");
+            else if (TailMatches("=", MatchAnyExcept("*)")))
+                COMPLETE_WITH(",", ")");
             break;
         case HeadMatches("CREATE", "TYPE", MatchAny, "AS", "RANGE", "("):
-        if (TailMatches("(|*,"))
-            COMPLETE_WITH("SUBTYPE", "SUBTYPE_OPCLASS", "COLLATION",
-                          "CANONICAL", "SUBTYPE_DIFF",
-                          "MULTIRANGE_TYPE_NAME");
-        else if (TailMatches("(*|*,", MatchAnyExcept("*=")))
-            COMPLETE_WITH("=");
-        else if (TailMatches("=", MatchAnyExcept("*)")))
-            COMPLETE_WITH(",", ")");
+            if (TailMatches("(|*,"))
+                COMPLETE_WITH("SUBTYPE", "SUBTYPE_OPCLASS", "COLLATION",
+                              "CANONICAL", "SUBTYPE_DIFF",
+                              "MULTIRANGE_TYPE_NAME");
+            else if (TailMatches("(*|*,", MatchAnyExcept("*=")))
+                COMPLETE_WITH("=");
+            else if (TailMatches("=", MatchAnyExcept("*)")))
+                COMPLETE_WITH(",", ")");
             break;

 /* CREATE VIEW --- is allowed inside CREATE SCHEMA, so use TailMatches */
-    /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS or WITH */
+            /* Complete CREATE [ OR REPLACE ] VIEW <name> with AS or WITH */
         case TailMatches("CREATE", "VIEW", MatchAny):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny):
-        COMPLETE_WITH("AS", "WITH");
+            COMPLETE_WITH("AS", "WITH");
             break;
-    /* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */
+            /* Complete "CREATE [ OR REPLACE ] VIEW <sth> AS with "SELECT" */
         case TailMatches("CREATE", "VIEW", MatchAny, "AS"):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "AS"):
-        COMPLETE_WITH("SELECT");
+            COMPLETE_WITH("SELECT");
             break;
-    /* CREATE [ OR REPLACE ] VIEW <name> WITH ( yyy [= zzz] ) */
+            /* CREATE [ OR REPLACE ] VIEW <name> WITH ( yyy [= zzz] ) */
         case TailMatches("CREATE", "VIEW", MatchAny, "WITH"):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
         case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "("):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "("):
-        COMPLETE_WITH_LIST(view_optional_parameters);
+            COMPLETE_WITH_LIST(view_optional_parameters);
             break;
         case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(", "check_option"):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(", "check_option"):
-        COMPLETE_WITH("=");
+            COMPLETE_WITH("=");
             break;
         case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(", "check_option", "="):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(", "check_option", "="):
-        COMPLETE_WITH("local", "cascaded");
+            COMPLETE_WITH("local", "cascaded");
             break;
-    /* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS */
+            /* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS */
         case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)"):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)"):
-        COMPLETE_WITH("AS");
+            COMPLETE_WITH("AS");
             break;
-    /* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS SELECT */
+            /* CREATE [ OR REPLACE ] VIEW <name> WITH ( ... ) AS SELECT */
         case TailMatches("CREATE", "VIEW", MatchAny, "WITH", "(*)", "AS"):
         case TailMatches("CREATE", "OR", "REPLACE", "VIEW", MatchAny, "WITH", "(*)", "AS"):
-        COMPLETE_WITH("SELECT");
+            COMPLETE_WITH("SELECT");
             break;

 /* CREATE MATERIALIZED VIEW */
         case Matches("CREATE", "MATERIALIZED"):
-        COMPLETE_WITH("VIEW");
+            COMPLETE_WITH("VIEW");
             break;
-    /* Complete CREATE MATERIALIZED VIEW <name> with AS */
+            /* Complete CREATE MATERIALIZED VIEW <name> with AS */
         case Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny):
-        COMPLETE_WITH("AS");
+            COMPLETE_WITH("AS");
             break;
-    /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
+            /* Complete "CREATE MATERIALIZED VIEW <sth> AS with "SELECT" */
         case Matches("CREATE", "MATERIALIZED", "VIEW", MatchAny, "AS"):
-        COMPLETE_WITH("SELECT");
+            COMPLETE_WITH("SELECT");
             break;

 /* CREATE EVENT TRIGGER */
         case Matches("CREATE", "EVENT"):
-        COMPLETE_WITH("TRIGGER");
+            COMPLETE_WITH("TRIGGER");
             break;
-    /* Complete CREATE EVENT TRIGGER <name> with ON */
+            /* Complete CREATE EVENT TRIGGER <name> with ON */
         case Matches("CREATE", "EVENT", "TRIGGER", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
-    /* Complete CREATE EVENT TRIGGER <name> ON with event_type */
+            /* Complete CREATE EVENT TRIGGER <name> ON with event_type */
         case Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON"):
-        COMPLETE_WITH("ddl_command_start", "ddl_command_end", "login",
-                      "sql_drop", "table_rewrite");
+            COMPLETE_WITH("ddl_command_start", "ddl_command_end", "login",
+                          "sql_drop", "table_rewrite");
             break;

-    /*
-     * Complete CREATE EVENT TRIGGER <name> ON <event_type>.  EXECUTE FUNCTION
-     * is the recommended grammar instead of EXECUTE PROCEDURE in version 11
-     * and upwards.
-     */
+            /*
+             * Complete CREATE EVENT TRIGGER <name> ON <event_type>.  EXECUTE
+             * FUNCTION is the recommended grammar instead of EXECUTE
+             * PROCEDURE in version 11 and upwards.
+             */
         case Matches("CREATE", "EVENT", "TRIGGER", MatchAny, "ON", MatchAny):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("WHEN TAG IN (", "EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("WHEN TAG IN (", "EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "WHEN|AND", MatchAny, "IN", "(*)"):
-        if (pset.sversion >= 110000)
-            COMPLETE_WITH("EXECUTE FUNCTION");
-        else
-            COMPLETE_WITH("EXECUTE PROCEDURE");
+            if (pset.sversion >= 110000)
+                COMPLETE_WITH("EXECUTE FUNCTION");
+            else
+                COMPLETE_WITH("EXECUTE PROCEDURE");
             break;
         case Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"):
-        COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
+            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
             break;

 /* DEALLOCATE */
         case Matches("DEALLOCATE"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_prepared_statements,
-                                 "ALL");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_prepared_statements,
+                                     "ALL");
             break;

 /* DECLARE */

-    /*
-     * Complete DECLARE <name> with one of BINARY, ASENSITIVE, INSENSITIVE,
-     * SCROLL, NO SCROLL, and CURSOR.
-     */
+            /*
+             * Complete DECLARE <name> with one of BINARY, ASENSITIVE,
+             * INSENSITIVE, SCROLL, NO SCROLL, and CURSOR.
+             */
         case Matches("DECLARE", MatchAny):
-        COMPLETE_WITH("BINARY", "ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL",
-                      "CURSOR");
+            COMPLETE_WITH("BINARY", "ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL",
+                          "CURSOR");
             break;

-    /*
-     * Complete DECLARE ... <option> with other options. The PostgreSQL parser
-     * allows DECLARE options to be specified in any order. But the
-     * tab-completion follows the ordering of them that the SQL standard
-     * provides, like the syntax of DECLARE command in the documentation
-     * indicates.
-     */
+            /*
+             * Complete DECLARE ... <option> with other options. The
+             * PostgreSQL parser allows DECLARE options to be specified in any
+             * order. But the tab-completion follows the ordering of them that
+             * the SQL standard provides, like the syntax of DECLARE command
+             * in the documentation indicates.
+             */
         case Matches("DECLARE", MatchAnyN, "BINARY"):
-        COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
+            COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
             break;
         case Matches("DECLARE", MatchAnyN, "ASENSITIVE|INSENSITIVE"):
-        COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
+            COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
             break;
         case Matches("DECLARE", MatchAnyN, "SCROLL"):
-        COMPLETE_WITH("CURSOR");
+            COMPLETE_WITH("CURSOR");
             break;
-    /* Complete DECLARE ... [options] NO with SCROLL */
+            /* Complete DECLARE ... [options] NO with SCROLL */
         case Matches("DECLARE", MatchAnyN, "NO"):
-        COMPLETE_WITH("SCROLL");
+            COMPLETE_WITH("SCROLL");
             break;

-    /*
-     * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and
-     * FOR
-     */
+            /*
+             * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT
+             * HOLD, and FOR
+             */
         case Matches("DECLARE", MatchAnyN, "CURSOR"):
-        COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
+            COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
             break;
-    /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
+            /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
         case Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT"):
-        COMPLETE_WITH("HOLD");
+            COMPLETE_WITH("HOLD");
             break;
-    /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
+            /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
         case Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT", "HOLD"):
-        COMPLETE_WITH("FOR");
+            COMPLETE_WITH("FOR");
             break;

 /* DELETE --- can be inside EXPLAIN, RULE, etc */
-    /* Complete DELETE with "FROM" */
+            /* Complete DELETE with "FROM" */
         case Matches("DELETE"):
-        COMPLETE_WITH("FROM");
+            COMPLETE_WITH("FROM");
             break;
-    /* Complete DELETE FROM with a list of tables */
+            /* Complete DELETE FROM with a list of tables */
         case TailMatches("DELETE", "FROM"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
             break;
-    /* Complete DELETE FROM <table> */
+            /* Complete DELETE FROM <table> */
         case TailMatches("DELETE", "FROM", MatchAny):
-        COMPLETE_WITH("USING", "WHERE");
+            COMPLETE_WITH("USING", "WHERE");
             break;
-    /* XXX: implement tab completion for DELETE ... USING */
+            /* XXX: implement tab completion for DELETE ... USING */

 /* DISCARD */
         case Matches("DISCARD"):
-        COMPLETE_WITH("ALL", "PLANS", "SEQUENCES", "TEMP");
+            COMPLETE_WITH("ALL", "PLANS", "SEQUENCES", "TEMP");
             break;

 /* DO */
         case Matches("DO"):
-        COMPLETE_WITH("LANGUAGE");
+            COMPLETE_WITH("LANGUAGE");
             break;

 /* DROP */
-    /* Complete DROP object with CASCADE / RESTRICT */
+            /* Complete DROP object with CASCADE / RESTRICT */
         case Matches("DROP",
"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
MatchAny):
         case Matches("DROP", "ACCESS", "METHOD", MatchAny):
         case Matches("DROP", "EVENT", "TRIGGER", MatchAny):
         case Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny):
         case Matches("DROP", "FOREIGN", "TABLE", MatchAny):
         case Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;
         case Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny):
-        if (ends_with(prev_wd, ')'))
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            if (ends_with(prev_wd, ')'))
+                COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* help completing some of the variants */
+            /* help completing some of the variants */
         case Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;
         case Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, "("):
-        COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
+            COMPLETE_WITH_FUNCTION_ARG(prev2_wd);
             break;
         case Matches("DROP", "FOREIGN"):
-        COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            COMPLETE_WITH("DATA WRAPPER", "TABLE");
             break;
         case Matches("DROP", "DATABASE", MatchAny):
-        COMPLETE_WITH("WITH (");
+            COMPLETE_WITH("WITH (");
             break;
         case HeadMatches("DROP", "DATABASE"):
-        if ((ends_with(prev_wd, '(')))
-        COMPLETE_WITH("FORCE");
+            if ((ends_with(prev_wd, '(')))
+                COMPLETE_WITH("FORCE");
             break;

-    /* DROP INDEX */
+            /* DROP INDEX */
         case Matches("DROP", "INDEX"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
-                                        "CONCURRENTLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
+                                            "CONCURRENTLY");
             break;
         case Matches("DROP", "INDEX", "CONCURRENTLY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
             break;
         case Matches("DROP", "INDEX", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;
         case Matches("DROP", "INDEX", "CONCURRENTLY", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* DROP MATERIALIZED VIEW */
+            /* DROP MATERIALIZED VIEW */
         case Matches("DROP", "MATERIALIZED"):
-        COMPLETE_WITH("VIEW");
+            COMPLETE_WITH("VIEW");
             break;
         case Matches("DROP", "MATERIALIZED", "VIEW"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
             break;
         case Matches("DROP", "MATERIALIZED", "VIEW", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* DROP OWNED BY */
+            /* DROP OWNED BY */
         case Matches("DROP", "OWNED"):
-        COMPLETE_WITH("BY");
+            COMPLETE_WITH("BY");
             break;
         case Matches("DROP", "OWNED", "BY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
         case Matches("DROP", "OWNED", "BY", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* DROP TEXT SEARCH */
+            /* DROP TEXT SEARCH */
         case Matches("DROP", "TEXT", "SEARCH"):
-        COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
             break;

-    /* DROP TRIGGER */
+            /* DROP TRIGGER */
         case Matches("DROP", "TRIGGER", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("DROP", "TRIGGER", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_trigger);
             break;
         case Matches("DROP", "TRIGGER", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* DROP ACCESS METHOD */
+            /* DROP ACCESS METHOD */
         case Matches("DROP", "ACCESS"):
-        COMPLETE_WITH("METHOD");
+            COMPLETE_WITH("METHOD");
             break;
         case Matches("DROP", "ACCESS", "METHOD"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
             break;

-    /* DROP EVENT TRIGGER */
+            /* DROP EVENT TRIGGER */
         case Matches("DROP", "EVENT"):
-        COMPLETE_WITH("TRIGGER");
+            COMPLETE_WITH("TRIGGER");
             break;
         case Matches("DROP", "EVENT", "TRIGGER"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
             break;

-    /* DROP POLICY <name>  */
+            /* DROP POLICY <name>  */
         case Matches("DROP", "POLICY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_policies);
+            COMPLETE_WITH_QUERY(Query_for_list_of_policies);
             break;
-    /* DROP POLICY <name> ON */
+            /* DROP POLICY <name> ON */
         case Matches("DROP", "POLICY", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
-    /* DROP POLICY <name> ON <table> */
+            /* DROP POLICY <name> ON <table> */
         case Matches("DROP", "POLICY", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_policy);
             break;
         case Matches("DROP", "POLICY", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* DROP RULE */
+            /* DROP RULE */
         case Matches("DROP", "RULE", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("DROP", "RULE", MatchAny, "ON"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables_for_rule);
             break;
         case Matches("DROP", "RULE", MatchAny, "ON", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

-    /* DROP TRANSFORM */
+            /* DROP TRANSFORM */
         case Matches("DROP", "TRANSFORM"):
-        COMPLETE_WITH("FOR");
+            COMPLETE_WITH("FOR");
             break;
         case Matches("DROP", "TRANSFORM", "FOR"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case Matches("DROP", "TRANSFORM", "FOR", MatchAny):
-        COMPLETE_WITH("LANGUAGE");
+            COMPLETE_WITH("LANGUAGE");
             break;
         case Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"):
-        set_completion_reference(prev2_wd);
-        COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+            set_completion_reference(prev2_wd);
+            COMPLETE_WITH_QUERY(Query_for_list_of_languages);
             break;
         case Matches("DROP", "TRANSFORM", "FOR", MatchAny, "LANGUAGE", MatchAny):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

 /* EXECUTE */
         case Matches("EXECUTE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
+            COMPLETE_WITH_QUERY(Query_for_list_of_prepared_statements);
             break;

 /*
@@ -4469,532 +4546,553 @@ match_previous_words(int pattern_id,
  * EXPLAIN [ ANALYZE ] [ VERBOSE ] statement
  */
         case Matches("EXPLAIN"):
-        COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
-                      "MERGE INTO", "EXECUTE", "ANALYZE", "VERBOSE");
+            COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
+                          "MERGE INTO", "EXECUTE", "ANALYZE", "VERBOSE");
             break;
         case HeadMatches("EXPLAIN", "(*"):
-        if (!HeadMatches("EXPLAIN", "(*)"))
-    {
-        /*
-         * This fires if we're in an unfinished parenthesized option list.
-         * get_previous_words treats a completed parenthesized option list as
-         * one word, so the above test is correct.
-         */
-        if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-            COMPLETE_WITH("ANALYZE", "VERBOSE", "COSTS", "SETTINGS", "GENERIC_PLAN",
-                          "BUFFERS", "SERIALIZE", "WAL", "TIMING", "SUMMARY",
-                          "MEMORY", "FORMAT");
-        else if (TailMatches("ANALYZE|VERBOSE|COSTS|SETTINGS|GENERIC_PLAN|BUFFERS|WAL|TIMING|SUMMARY|MEMORY"))
-            COMPLETE_WITH("ON", "OFF");
-        else if (TailMatches("SERIALIZE"))
-            COMPLETE_WITH("TEXT", "NONE", "BINARY");
-        else if (TailMatches("FORMAT"))
-            COMPLETE_WITH("TEXT", "XML", "JSON", "YAML");
-    }
+            if (!HeadMatches("EXPLAIN", "(*)"))
+            {
+                /*
+                 * This fires if we're in an unfinished parenthesized option
+                 * list. get_previous_words treats a completed parenthesized
+                 * option list as one word, so the above test is correct.
+                 */
+                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+                    COMPLETE_WITH("ANALYZE", "VERBOSE", "COSTS", "SETTINGS", "GENERIC_PLAN",
+                                  "BUFFERS", "SERIALIZE", "WAL", "TIMING", "SUMMARY",
+                                  "MEMORY", "FORMAT");
+                else if (TailMatches("ANALYZE|VERBOSE|COSTS|SETTINGS|GENERIC_PLAN|BUFFERS|WAL|TIMING|SUMMARY|MEMORY"))
+                    COMPLETE_WITH("ON", "OFF");
+                else if (TailMatches("SERIALIZE"))
+                    COMPLETE_WITH("TEXT", "NONE", "BINARY");
+                else if (TailMatches("FORMAT"))
+                    COMPLETE_WITH("TEXT", "XML", "JSON", "YAML");
+            }
             break;
         case Matches("EXPLAIN", "ANALYZE"):
-        COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
-                      "MERGE INTO", "EXECUTE", "VERBOSE");
+            COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
+                          "MERGE INTO", "EXECUTE", "VERBOSE");
             break;
         case Matches("EXPLAIN", "(*)"):
         case Matches("EXPLAIN", "VERBOSE"):
         case Matches("EXPLAIN", "ANALYZE", "VERBOSE"):
-        COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
-                      "MERGE INTO", "EXECUTE");
+            COMPLETE_WITH("SELECT", "INSERT INTO", "DELETE FROM", "UPDATE", "DECLARE",
+                          "MERGE INTO", "EXECUTE");
             break;

 /* FETCH && MOVE */

-    /*
-     * Complete FETCH with one of ABSOLUTE, BACKWARD, FORWARD, RELATIVE, ALL,
-     * NEXT, PRIOR, FIRST, LAST, FROM, IN, and a list of cursors
-     */
+            /*
+             * Complete FETCH with one of ABSOLUTE, BACKWARD, FORWARD,
+             * RELATIVE, ALL, NEXT, PRIOR, FIRST, LAST, FROM, IN, and a list
+             * of cursors
+             */
         case Matches("FETCH|MOVE"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
-                                 "ABSOLUTE",
-                                 "BACKWARD",
-                                 "FORWARD",
-                                 "RELATIVE",
-                                 "ALL",
-                                 "NEXT",
-                                 "PRIOR",
-                                 "FIRST",
-                                 "LAST",
-                                 "FROM",
-                                 "IN");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
+                                     "ABSOLUTE",
+                                     "BACKWARD",
+                                     "FORWARD",
+                                     "RELATIVE",
+                                     "ALL",
+                                     "NEXT",
+                                     "PRIOR",
+                                     "FIRST",
+                                     "LAST",
+                                     "FROM",
+                                     "IN");
             break;

-    /*
-     * Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN, and a
-     * list of cursors
-     */
+            /*
+             * Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN,
+             * and a list of cursors
+             */
         case Matches("FETCH|MOVE", "BACKWARD|FORWARD"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
-                                 "ALL",
-                                 "FROM",
-                                 "IN");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
+                                     "ALL",
+                                     "FROM",
+                                     "IN");
             break;

-    /*
-     * Complete FETCH <direction> with "FROM" or "IN". These are equivalent,
-     * but we may as well tab-complete both: perhaps some users prefer one
-     * variant or the other.
-     */
+            /*
+             * Complete FETCH <direction> with "FROM" or "IN". These are
+             * equivalent, but we may as well tab-complete both: perhaps some
+             * users prefer one variant or the other.
+             */
         case Matches("FETCH|MOVE", "ABSOLUTE|BACKWARD|FORWARD|RELATIVE", MatchAnyExcept("FROM|IN")):
         case Matches("FETCH|MOVE", "ALL|NEXT|PRIOR|FIRST|LAST"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
-                                 "FROM",
-                                 "IN");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
+                                     "FROM",
+                                     "IN");
             break;
-    /* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
+
+            /*
+             * Complete FETCH <direction> "FROM" or "IN" with a list of
+             * cursors
+             */
         case Matches("FETCH|MOVE", MatchAnyN, "FROM|IN"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_cursors);
+            COMPLETE_WITH_QUERY(Query_for_list_of_cursors);
             break;

 /* FOREIGN DATA WRAPPER */
-    /* applies in ALTER/DROP FDW and in CREATE SERVER */
+            /* applies in ALTER/DROP FDW and in CREATE SERVER */
         case TailMatches("FOREIGN", "DATA", "WRAPPER"):
-        if (!TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
-        COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
+            if (!TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
+                COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
             break;
-    /* applies in CREATE SERVER */
+            /* applies in CREATE SERVER */
         case Matches("CREATE", "SERVER", MatchAnyN, "FOREIGN", "DATA", "WRAPPER", MatchAny):
-        COMPLETE_WITH("OPTIONS");
+            COMPLETE_WITH("OPTIONS");
             break;

 /* FOREIGN TABLE */
         case TailMatches("FOREIGN", "TABLE"):
-        if (!TailMatches("CREATE", MatchAny, MatchAny))
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
+            if (!TailMatches("CREATE", MatchAny, MatchAny))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
             break;

 /* FOREIGN SERVER */
         case TailMatches("FOREIGN", "SERVER"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_servers);
+            COMPLETE_WITH_QUERY(Query_for_list_of_servers);
             break;

 /*
  * GRANT and REVOKE are allowed inside CREATE SCHEMA and
  * ALTER DEFAULT PRIVILEGES, so use TailMatches
  */
-    /* Complete GRANT/REVOKE with a list of roles and privileges */
+            /* Complete GRANT/REVOKE with a list of roles and privileges */
         case TailMatches("GRANT|REVOKE"):
         case TailMatches("REVOKE", "ADMIN|GRANT|INHERIT|SET", "OPTION", "FOR"):
-        /*
-         * With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
-         * privileges (can't grant roles)
-         */
-        if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
-        {
-            if (TailMatches("GRANT") ||
-                TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
-                COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
-                              "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
-                              "CREATE", "EXECUTE", "USAGE", "MAINTAIN", "ALL");
+
+            /*
+             * With ALTER DEFAULT PRIVILEGES, restrict completion to grantable
+             * privileges (can't grant roles)
+             */
+            if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
+            {
+                if (TailMatches("GRANT") ||
+                    TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
+                    COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
+                                  "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
+                                  "CREATE", "EXECUTE", "USAGE", "MAINTAIN", "ALL");
+                else if (TailMatches("REVOKE"))
+                    COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
+                                  "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
+                                  "CREATE", "EXECUTE", "USAGE", "MAINTAIN", "ALL",
+                                  "GRANT OPTION FOR");
+            }
+            else if (TailMatches("GRANT"))
+                COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                         Privilege_options_of_grant_and_revoke);
             else if (TailMatches("REVOKE"))
-                COMPLETE_WITH("SELECT", "INSERT", "UPDATE",
-                              "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER",
-                              "CREATE", "EXECUTE", "USAGE", "MAINTAIN", "ALL",
-                              "GRANT OPTION FOR");
-        }
-        else if (TailMatches("GRANT"))
-            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                     Privilege_options_of_grant_and_revoke);
-        else if (TailMatches("REVOKE"))
-            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                     Privilege_options_of_grant_and_revoke,
-                                     "GRANT OPTION FOR",
-                                     "ADMIN OPTION FOR",
-                                     "INHERIT OPTION FOR",
-                                     "SET OPTION FOR");
-        else if (TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
-            COMPLETE_WITH(Privilege_options_of_grant_and_revoke);
-        else if (TailMatches("REVOKE", "ADMIN|INHERIT|SET", "OPTION", "FOR"))
-            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+                COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                         Privilege_options_of_grant_and_revoke,
+                                         "GRANT OPTION FOR",
+                                         "ADMIN OPTION FOR",
+                                         "INHERIT OPTION FOR",
+                                         "SET OPTION FOR");
+            else if (TailMatches("REVOKE", "GRANT", "OPTION", "FOR"))
+                COMPLETE_WITH(Privilege_options_of_grant_and_revoke);
+            else if (TailMatches("REVOKE", "ADMIN|INHERIT|SET", "OPTION", "FOR"))
+                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;

         case TailMatches("GRANT|REVOKE", "ALTER"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER"):
-        COMPLETE_WITH("SYSTEM");
+            COMPLETE_WITH("SYSTEM");
             break;

         case TailMatches("REVOKE", "SET"):
-        COMPLETE_WITH("ON PARAMETER", "OPTION FOR");
+            COMPLETE_WITH("ON PARAMETER", "OPTION FOR");
             break;
         case TailMatches("GRANT", "SET"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "SET"):
         case TailMatches("GRANT|REVOKE", "ALTER", "SYSTEM"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", "ALTER", "SYSTEM"):
-        COMPLETE_WITH("ON PARAMETER");
+            COMPLETE_WITH("ON PARAMETER");
             break;

         case TailMatches("GRANT|REVOKE", MatchAny, "ON", "PARAMETER"):
         case TailMatches("GRANT|REVOKE", MatchAny, MatchAny, "ON", "PARAMETER"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER"):
-        COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars);
+            COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_alter_system_set_vars);
             break;

         case TailMatches("GRANT", MatchAny, "ON", "PARAMETER", MatchAny):
         case TailMatches("GRANT", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

         case TailMatches("REVOKE", MatchAny, "ON", "PARAMETER", MatchAny):
         case TailMatches("REVOKE", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "PARAMETER", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, MatchAny, "ON", "PARAMETER", MatchAny):
-        COMPLETE_WITH("FROM");
+            COMPLETE_WITH("FROM");
             break;

-    /*
-     * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE <role> with
-     * TO/FROM
-     */
+            /*
+             * Complete GRANT/REVOKE <privilege> with "ON", GRANT/REVOKE
+             * <role> with TO/FROM
+             */
         case TailMatches("GRANT|REVOKE", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny):
-        if
(TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|MAINTAIN|ALL"))
-            COMPLETE_WITH("ON");
-        else if (TailMatches("GRANT", MatchAny))
-            COMPLETE_WITH("TO");
-        else
-            COMPLETE_WITH("FROM");
+            if
(TailMatches("SELECT|INSERT|UPDATE|DELETE|TRUNCATE|REFERENCES|TRIGGER|CREATE|CONNECT|TEMPORARY|TEMP|EXECUTE|USAGE|MAINTAIN|ALL"))
+                COMPLETE_WITH("ON");
+            else if (TailMatches("GRANT", MatchAny))
+                COMPLETE_WITH("TO");
+            else
+                COMPLETE_WITH("FROM");
             break;

-    /*
-     * Complete GRANT/REVOKE <sth> ON with a list of appropriate relations.
-     *
-     * Note: GRANT/REVOKE can get quite complex; tab-completion as implemented
-     * here will only work if the privilege list contains exactly one
-     * privilege.
-     */
+            /*
+             * Complete GRANT/REVOKE <sth> ON with a list of appropriate
+             * relations.
+             *
+             * Note: GRANT/REVOKE can get quite complex; tab-completion as
+             * implemented here will only work if the privilege list contains
+             * exactly one privilege.
+             */
         case TailMatches("GRANT|REVOKE", MatchAny, "ON"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON"):
-        /*
-         * With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds of
-         * objects supported.
-         */
-        if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
-            COMPLETE_WITH("TABLES", "SEQUENCES", "FUNCTIONS", "PROCEDURES", "ROUTINES", "TYPES", "SCHEMAS");
-        else
-            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_grantables,
-                                            "ALL FUNCTIONS IN SCHEMA",
-                                            "ALL PROCEDURES IN SCHEMA",
-                                            "ALL ROUTINES IN SCHEMA",
-                                            "ALL SEQUENCES IN SCHEMA",
-                                            "ALL TABLES IN SCHEMA",
-                                            "DATABASE",
-                                            "DOMAIN",
-                                            "FOREIGN DATA WRAPPER",
-                                            "FOREIGN SERVER",
-                                            "FUNCTION",
-                                            "LANGUAGE",
-                                            "LARGE OBJECT",
-                                            "PARAMETER",
-                                            "PROCEDURE",
-                                            "ROUTINE",
-                                            "SCHEMA",
-                                            "SEQUENCE",
-                                            "TABLE",
-                                            "TABLESPACE",
-                                            "TYPE");
+
+            /*
+             * With ALTER DEFAULT PRIVILEGES, restrict completion to the kinds
+             * of objects supported.
+             */
+            if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES"))
+                COMPLETE_WITH("TABLES", "SEQUENCES", "FUNCTIONS", "PROCEDURES", "ROUTINES", "TYPES", "SCHEMAS");
+            else
+                COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_grantables,
+                                                "ALL FUNCTIONS IN SCHEMA",
+                                                "ALL PROCEDURES IN SCHEMA",
+                                                "ALL ROUTINES IN SCHEMA",
+                                                "ALL SEQUENCES IN SCHEMA",
+                                                "ALL TABLES IN SCHEMA",
+                                                "DATABASE",
+                                                "DOMAIN",
+                                                "FOREIGN DATA WRAPPER",
+                                                "FOREIGN SERVER",
+                                                "FUNCTION",
+                                                "LANGUAGE",
+                                                "LARGE OBJECT",
+                                                "PARAMETER",
+                                                "PROCEDURE",
+                                                "ROUTINE",
+                                                "SCHEMA",
+                                                "SEQUENCE",
+                                                "TABLE",
+                                                "TABLESPACE",
+                                                "TYPE");
             break;
         case TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL"):
-        COMPLETE_WITH("FUNCTIONS IN SCHEMA",
-                      "PROCEDURES IN SCHEMA",
-                      "ROUTINES IN SCHEMA",
-                      "SEQUENCES IN SCHEMA",
-                      "TABLES IN SCHEMA");
+            COMPLETE_WITH("FUNCTIONS IN SCHEMA",
+                          "PROCEDURES IN SCHEMA",
+                          "ROUTINES IN SCHEMA",
+                          "SEQUENCES IN SCHEMA",
+                          "TABLES IN SCHEMA");
             break;
         case TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN"):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN"):
-        COMPLETE_WITH("DATA WRAPPER", "SERVER");
+            COMPLETE_WITH("DATA WRAPPER", "SERVER");
             break;

-    /*
-     * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of
-     * appropriate objects.
-     *
-     * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
-     */
+            /*
+             * Complete "GRANT/REVOKE * ON DATABASE/DOMAIN/..." with a list of
+             * appropriate objects.
+             *
+             * Complete "GRANT/REVOKE * ON *" with "TO/FROM".
+             */
         case TailMatches("GRANT|REVOKE", MatchAny, "ON", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", MatchAny):
-        if (TailMatches("DATABASE"))
-            COMPLETE_WITH_QUERY(Query_for_list_of_databases);
-        else if (TailMatches("DOMAIN"))
-            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
-        else if (TailMatches("FUNCTION"))
-            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
-        else if (TailMatches("LANGUAGE"))
-            COMPLETE_WITH_QUERY(Query_for_list_of_languages);
-        else if (TailMatches("PROCEDURE"))
-            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
-        else if (TailMatches("ROUTINE"))
-            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
-        else if (TailMatches("SCHEMA"))
-            COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
-        else if (TailMatches("SEQUENCE"))
-            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
-        else if (TailMatches("TABLE"))
-            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
-        else if (TailMatches("TABLESPACE"))
-            COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
-        else if (TailMatches("TYPE"))
-            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
-        else if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny))
-            COMPLETE_WITH("TO");
-        else
-            COMPLETE_WITH("FROM");
+            if (TailMatches("DATABASE"))
+                COMPLETE_WITH_QUERY(Query_for_list_of_databases);
+            else if (TailMatches("DOMAIN"))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
+            else if (TailMatches("FUNCTION"))
+                COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
+            else if (TailMatches("LANGUAGE"))
+                COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+            else if (TailMatches("PROCEDURE"))
+                COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
+            else if (TailMatches("ROUTINE"))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
+            else if (TailMatches("SCHEMA"))
+                COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
+            else if (TailMatches("SEQUENCE"))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
+            else if (TailMatches("TABLE"))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
+            else if (TailMatches("TABLESPACE"))
+                COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            else if (TailMatches("TYPE"))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            else if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny))
+                COMPLETE_WITH("TO");
+            else
+                COMPLETE_WITH("FROM");
             break;

-    /*
-     * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
-     * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
-     */
+            /*
+             * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
+             * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
+             */
         case Matches("GRANT", MatchAnyN, "TO"):
         case Matches("REVOKE", MatchAnyN, "FROM"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_grant_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_grant_roles);
             break;

-    /*
-     * Offer grant options after that.
-     */
+            /*
+             * Offer grant options after that.
+             */
         case Matches("GRANT", MatchAnyN, "TO", MatchAny):
-        COMPLETE_WITH("WITH ADMIN",
-                      "WITH INHERIT",
-                      "WITH SET",
-                      "WITH GRANT OPTION",
-                      "GRANTED BY");
+            COMPLETE_WITH("WITH ADMIN",
+                          "WITH INHERIT",
+                          "WITH SET",
+                          "WITH GRANT OPTION",
+                          "GRANTED BY");
             break;
         case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH"):
-        COMPLETE_WITH("ADMIN",
-                      "INHERIT",
-                      "SET",
-                      "GRANT OPTION");
+            COMPLETE_WITH("ADMIN",
+                          "INHERIT",
+                          "SET",
+                          "GRANT OPTION");
             break;
         case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", "ADMIN|INHERIT|SET"):
-        COMPLETE_WITH("OPTION", "TRUE", "FALSE");
+            COMPLETE_WITH("OPTION", "TRUE", "FALSE");
             break;
         case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION"):
-        COMPLETE_WITH("GRANTED BY");
+            COMPLETE_WITH("GRANTED BY");
             break;
         case Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_grant_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_grant_roles);
             break;
-    /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
+            /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO|FROM"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_grant_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_grant_roles);
             break;
-    /* Offer WITH GRANT OPTION after that */
+            /* Offer WITH GRANT OPTION after that */
         case Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny):
-        COMPLETE_WITH("WITH GRANT OPTION");
+            COMPLETE_WITH("WITH GRANT OPTION");
             break;
-    /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
+            /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
         case Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
         case Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny):
-        COMPLETE_WITH("FROM");
+            COMPLETE_WITH("FROM");
             break;

-    /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
+            /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
         case TailMatches("GRANT|REVOKE", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "ALL", MatchAny, "IN", "SCHEMA",
MatchAny):
-        if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
-            COMPLETE_WITH("TO");
-        else
-            COMPLETE_WITH("FROM");
+            if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+                COMPLETE_WITH("TO");
+            else
+                COMPLETE_WITH("FROM");
             break;

-    /* Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with TO/FROM */
+            /*
+             * Complete "GRANT/REVOKE * ON FOREIGN DATA WRAPPER *" with
+             * TO/FROM
+             */
         case TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "DATA", "WRAPPER", MatchAny):
-        if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
-            COMPLETE_WITH("TO");
-        else
-            COMPLETE_WITH("FROM");
+            if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+                COMPLETE_WITH("TO");
+            else
+                COMPLETE_WITH("FROM");
             break;

-    /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
+            /* Complete "GRANT/REVOKE * ON FOREIGN SERVER *" with TO/FROM */
         case TailMatches("GRANT|REVOKE", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny):
         case TailMatches("REVOKE", "GRANT", "OPTION", "FOR", MatchAny, "ON", "FOREIGN", "SERVER", MatchAny):
-        if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
-            COMPLETE_WITH("TO");
-        else
-            COMPLETE_WITH("FROM");
+            if (TailMatches("GRANT", MatchAny, MatchAny, MatchAny, MatchAny, MatchAny))
+                COMPLETE_WITH("TO");
+            else
+                COMPLETE_WITH("FROM");
             break;

 /* GROUP BY */
         case TailMatches("FROM", MatchAny, "GROUP"):
-        COMPLETE_WITH("BY");
+            COMPLETE_WITH("BY");
             break;

 /* IMPORT FOREIGN SCHEMA */
         case Matches("IMPORT"):
-        COMPLETE_WITH("FOREIGN SCHEMA");
+            COMPLETE_WITH("FOREIGN SCHEMA");
             break;
         case Matches("IMPORT", "FOREIGN"):
-        COMPLETE_WITH("SCHEMA");
+            COMPLETE_WITH("SCHEMA");
             break;
         case Matches("IMPORT", "FOREIGN", "SCHEMA", MatchAny):
-        COMPLETE_WITH("EXCEPT (", "FROM SERVER", "LIMIT TO (");
+            COMPLETE_WITH("EXCEPT (", "FROM SERVER", "LIMIT TO (");
             break;
         case TailMatches("LIMIT", "TO", "(*)"):
         case TailMatches("EXCEPT", "(*)"):
-        COMPLETE_WITH("FROM SERVER");
+            COMPLETE_WITH("FROM SERVER");
             break;
         case TailMatches("FROM", "SERVER", MatchAny):
-        COMPLETE_WITH("INTO");
+            COMPLETE_WITH("INTO");
             break;
         case TailMatches("FROM", "SERVER", MatchAny, "INTO"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
+            COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
             break;
         case TailMatches("FROM", "SERVER", MatchAny, "INTO", MatchAny):
-        COMPLETE_WITH("OPTIONS (");
+            COMPLETE_WITH("OPTIONS (");
             break;

 /* INSERT --- can be inside EXPLAIN, RULE, etc */
-    /* Complete NOT MATCHED THEN INSERT */
+            /* Complete NOT MATCHED THEN INSERT */
         case TailMatches("NOT", "MATCHED", "THEN", "INSERT"):
-        COMPLETE_WITH("VALUES", "(");
+            COMPLETE_WITH("VALUES", "(");
             break;
-    /* Complete INSERT with "INTO" */
+            /* Complete INSERT with "INTO" */
         case TailMatches("INSERT"):
-        COMPLETE_WITH("INTO");
+            COMPLETE_WITH("INTO");
             break;
-    /* Complete INSERT INTO with table names */
+            /* Complete INSERT INTO with table names */
         case TailMatches("INSERT", "INTO"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
             break;
-    /* Complete "INSERT INTO <table> (" with attribute names */
+            /* Complete "INSERT INTO <table> (" with attribute names */
         case TailMatches("INSERT", "INTO", MatchAny, "("):
-        COMPLETE_WITH_ATTR(prev2_wd);
+            COMPLETE_WITH_ATTR(prev2_wd);
             break;

-    /*
-     * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT" or
-     * "TABLE" or "DEFAULT VALUES" or "OVERRIDING"
-     */
+            /*
+             * Complete INSERT INTO <table> with "(" or "VALUES" or "SELECT"
+             * or "TABLE" or "DEFAULT VALUES" or "OVERRIDING"
+             */
         case TailMatches("INSERT", "INTO", MatchAny):
-        COMPLETE_WITH("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", "OVERRIDING");
+            COMPLETE_WITH("(", "DEFAULT VALUES", "SELECT", "TABLE", "VALUES", "OVERRIDING");
             break;

-    /*
-     * Complete INSERT INTO <table> (attribs) with "VALUES" or "SELECT" or
-     * "TABLE" or "OVERRIDING"
-     */
+            /*
+             * Complete INSERT INTO <table> (attribs) with "VALUES" or
+             * "SELECT" or "TABLE" or "OVERRIDING"
+             */
         case TailMatches("INSERT", "INTO", MatchAny, MatchAny):
-        if (ends_with(prev_wd, ')'))
-        COMPLETE_WITH("SELECT", "TABLE", "VALUES", "OVERRIDING");
+            if (ends_with(prev_wd, ')'))
+                COMPLETE_WITH("SELECT", "TABLE", "VALUES", "OVERRIDING");
             break;

-    /* Complete OVERRIDING */
+            /* Complete OVERRIDING */
         case TailMatches("OVERRIDING"):
-        COMPLETE_WITH("SYSTEM VALUE", "USER VALUE");
+            COMPLETE_WITH("SYSTEM VALUE", "USER VALUE");
             break;

-    /* Complete after OVERRIDING clause */
+            /* Complete after OVERRIDING clause */
         case TailMatches("OVERRIDING", MatchAny, "VALUE"):
-        COMPLETE_WITH("SELECT", "TABLE", "VALUES");
+            COMPLETE_WITH("SELECT", "TABLE", "VALUES");
             break;

-    /* Insert an open parenthesis after "VALUES" */
+            /* Insert an open parenthesis after "VALUES" */
         case TailMatches("VALUES"):
-        if (!TailMatches("DEFAULT", "VALUES"))
-        COMPLETE_WITH("(");
+            if (!TailMatches("DEFAULT", "VALUES"))
+                COMPLETE_WITH("(");
             break;

 /* LOCK */
-    /* Complete LOCK [TABLE] [ONLY] with a list of tables */
+            /* Complete LOCK [TABLE] [ONLY] with a list of tables */
         case Matches("LOCK"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
-                                        "TABLE", "ONLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
+                                            "TABLE", "ONLY");
             break;
         case Matches("LOCK", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
-                                        "ONLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_tables,
+                                            "ONLY");
             break;
         case Matches("LOCK", "TABLE", "ONLY"):
         case Matches("LOCK", "ONLY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
-    /* For the following, handle the case of a single table only for now */

-    /* Complete LOCK [TABLE] [ONLY] <table> with IN or NOWAIT */
+            /*
+             * For the following, handle the case of a single table only for
+             * now
+             */
+
+            /* Complete LOCK [TABLE] [ONLY] <table> with IN or NOWAIT */
         case Matches("LOCK", MatchAnyExcept("TABLE|ONLY")):
         case Matches("LOCK", "TABLE", MatchAnyExcept("ONLY")):
         case Matches("LOCK", "ONLY", MatchAny):
         case Matches("LOCK", "TABLE", "ONLY", MatchAny):
-        COMPLETE_WITH("IN", "NOWAIT");
+            COMPLETE_WITH("IN", "NOWAIT");
             break;

-    /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
+            /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
         case Matches("LOCK", MatchAnyN, "IN"):
-        COMPLETE_WITH("ACCESS SHARE MODE",
-                      "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
-                      "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
-                      "SHARE ROW EXCLUSIVE MODE",
-                      "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
+            COMPLETE_WITH("ACCESS SHARE MODE",
+                          "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
+                          "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
+                          "SHARE ROW EXCLUSIVE MODE",
+                          "EXCLUSIVE MODE", "ACCESS EXCLUSIVE MODE");
             break;

-    /*
-     * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of lock
-     * mode
-     */
+            /*
+             * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of
+             * lock mode
+             */
         case Matches("LOCK", MatchAnyN, "IN", "ACCESS|ROW"):
-        COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");
+            COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");
             break;

-    /* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
+            /*
+             * Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock
+             * mode
+             */
         case Matches("LOCK", MatchAnyN, "IN", "SHARE"):
-        COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
-                      "UPDATE EXCLUSIVE MODE");
+            COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
+                          "UPDATE EXCLUSIVE MODE");
             break;

-    /* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
+            /*
+             * Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with
+             * "NOWAIT"
+             */
         case Matches("LOCK", MatchAnyN, "MODE"):
-        COMPLETE_WITH("NOWAIT");
+            COMPLETE_WITH("NOWAIT");
             break;

 /* MERGE --- can be inside EXPLAIN */
         case TailMatches("MERGE"):
-        COMPLETE_WITH("INTO");
+            COMPLETE_WITH("INTO");
             break;
         case TailMatches("MERGE", "INTO"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_mergetargets);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_mergetargets);
             break;

-    /* Complete MERGE INTO <table> [[AS] <alias>] with USING */
+            /* Complete MERGE INTO <table> [[AS] <alias>] with USING */
         case TailMatches("MERGE", "INTO", MatchAny):
-        COMPLETE_WITH("USING", "AS");
+            COMPLETE_WITH("USING", "AS");
             break;
         case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny):
         case TailMatches("MERGE", "INTO", MatchAny, MatchAnyExcept("USING|AS")):
-        COMPLETE_WITH("USING");
+            COMPLETE_WITH("USING");
             break;

-    /*
-     * Complete MERGE INTO ... USING with a list of relations supporting
-     * SELECT
-     */
+            /*
+             * Complete MERGE INTO ... USING with a list of relations
+             * supporting SELECT
+             */
         case TailMatches("MERGE", "INTO", MatchAny, "USING"):
         case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING"):
         case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
             break;

-    /*
-     * Complete MERGE INTO <table> [[AS] <alias>] USING <relations> [[AS]
-     * alias] with ON
-     */
+            /*
+             * Complete MERGE INTO <table> [[AS] <alias>] USING <relations>
+             * [[AS] alias] with ON
+             */
         case TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny):
         case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny):
         case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny):
-        COMPLETE_WITH("AS", "ON");
+            COMPLETE_WITH("AS", "ON");
             break;
         case TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, "AS", MatchAny):
         case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny):
@@ -5002,32 +5100,32 @@ match_previous_words(int pattern_id,
         case TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")):
         case TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")):
         case TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;

-    /* Complete MERGE INTO ... ON with target table attributes */
+            /* Complete MERGE INTO ... ON with target table attributes */
         case TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"):
-        COMPLETE_WITH_ATTR(prev4_wd);
+            COMPLETE_WITH_ATTR(prev4_wd);
             break;
         case TailMatches("INTO", MatchAny, "AS", MatchAny, "USING", MatchAny, "AS", MatchAny, "ON"):
-        COMPLETE_WITH_ATTR(prev8_wd);
+            COMPLETE_WITH_ATTR(prev8_wd);
             break;
         case TailMatches("INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAny, "ON"):
-        COMPLETE_WITH_ATTR(prev6_wd);
+            COMPLETE_WITH_ATTR(prev6_wd);
             break;

-    /*
-     * Complete ... USING <relation> [[AS] alias] ON join condition
-     * (consisting of one or three words typically used) with WHEN [NOT]
-     * MATCHED
-     */
+            /*
+             * Complete ... USING <relation> [[AS] alias] ON join condition
+             * (consisting of one or three words typically used) with WHEN
+             * [NOT] MATCHED
+             */
         case TailMatches("USING", MatchAny, "ON", MatchAny):
         case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny):
         case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny):
         case TailMatches("USING", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")):
         case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"),
MatchAnyExcept("WHEN")):
         case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAnyExcept("WHEN"), MatchAnyExcept("WHEN")):
-        COMPLETE_WITH("WHEN MATCHED", "WHEN NOT MATCHED");
+            COMPLETE_WITH("WHEN MATCHED", "WHEN NOT MATCHED");
             break;
         case TailMatches("USING", MatchAny, "ON", MatchAny, "WHEN"):
         case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, "WHEN"):
@@ -5035,72 +5133,73 @@ match_previous_words(int pattern_id,
         case TailMatches("USING", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"):
         case TailMatches("USING", MatchAny, "AS", MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"):
         case TailMatches("USING", MatchAny, MatchAny, "ON", MatchAny, MatchAny, MatchAny, "WHEN"):
-        COMPLETE_WITH("MATCHED", "NOT MATCHED");
+            COMPLETE_WITH("MATCHED", "NOT MATCHED");
             break;

-    /*
-     * Complete ... WHEN MATCHED and WHEN NOT MATCHED BY SOURCE|TARGET with
-     * THEN/AND
-     */
+            /*
+             * Complete ... WHEN MATCHED and WHEN NOT MATCHED BY SOURCE|TARGET
+             * with THEN/AND
+             */
         case TailMatches("WHEN", "MATCHED"):
         case TailMatches("WHEN", "NOT", "MATCHED", "BY", "SOURCE|TARGET"):
-        COMPLETE_WITH("THEN", "AND");
+            COMPLETE_WITH("THEN", "AND");
             break;

-    /* Complete ... WHEN NOT MATCHED with BY/THEN/AND */
+            /* Complete ... WHEN NOT MATCHED with BY/THEN/AND */
         case TailMatches("WHEN", "NOT", "MATCHED"):
-        COMPLETE_WITH("BY", "THEN", "AND");
+            COMPLETE_WITH("BY", "THEN", "AND");
             break;

-    /* Complete ... WHEN NOT MATCHED BY with SOURCE/TARGET */
+            /* Complete ... WHEN NOT MATCHED BY with SOURCE/TARGET */
         case TailMatches("WHEN", "NOT", "MATCHED", "BY"):
-        COMPLETE_WITH("SOURCE", "TARGET");
+            COMPLETE_WITH("SOURCE", "TARGET");
             break;

-    /*
-     * Complete ... WHEN MATCHED THEN and WHEN NOT MATCHED BY SOURCE THEN with
-     * UPDATE SET/DELETE/DO NOTHING
-     */
+            /*
+             * Complete ... WHEN MATCHED THEN and WHEN NOT MATCHED BY SOURCE
+             * THEN with UPDATE SET/DELETE/DO NOTHING
+             */
         case TailMatches("WHEN", "MATCHED", "THEN"):
         case TailMatches("WHEN", "NOT", "MATCHED", "BY", "SOURCE", "THEN"):
-        COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
+            COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
             break;

-    /*
-     * Complete ... WHEN NOT MATCHED [BY TARGET] THEN with INSERT/DO NOTHING
-     */
+            /*
+             * Complete ... WHEN NOT MATCHED [BY TARGET] THEN with INSERT/DO
+             * NOTHING
+             */
         case TailMatches("WHEN", "NOT", "MATCHED", "THEN"):
         case TailMatches("WHEN", "NOT", "MATCHED", "BY", "TARGET", "THEN"):
-        COMPLETE_WITH("INSERT", "DO NOTHING");
+            COMPLETE_WITH("INSERT", "DO NOTHING");
             break;

 /* NOTIFY --- can be inside EXPLAIN, RULE, etc */
         case TailMatches("NOTIFY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_channels);
+            COMPLETE_WITH_QUERY(Query_for_list_of_channels);
             break;

 /* OPTIONS */
         case TailMatches("OPTIONS"):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;

 /* OWNER TO  - complete with available roles */
         case TailMatches("OWNER", "TO"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 Keywords_for_list_of_owner_roles);
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     Keywords_for_list_of_owner_roles);
             break;

 /* ORDER BY */
         case TailMatches("FROM", MatchAny, "ORDER"):
-        COMPLETE_WITH("BY");
+            COMPLETE_WITH("BY");
             break;
         case TailMatches("FROM", MatchAny, "ORDER", "BY"):
-        COMPLETE_WITH_ATTR(prev3_wd);
+            COMPLETE_WITH_ATTR(prev3_wd);
             break;

 /* PREPARE xx AS */
         case Matches("PREPARE", MatchAny, "AS"):
-        COMPLETE_WITH("SELECT", "UPDATE", "INSERT INTO", "DELETE FROM");
+            COMPLETE_WITH("SELECT", "UPDATE", "INSERT INTO", "DELETE FROM");
             break;

 /*
@@ -5110,368 +5209,372 @@ match_previous_words(int pattern_id,

 /* REASSIGN OWNED BY xxx TO yyy */
         case Matches("REASSIGN"):
-        COMPLETE_WITH("OWNED BY");
+            COMPLETE_WITH("OWNED BY");
             break;
         case Matches("REASSIGN", "OWNED"):
-        COMPLETE_WITH("BY");
+            COMPLETE_WITH("BY");
             break;
         case Matches("REASSIGN", "OWNED", "BY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
         case Matches("REASSIGN", "OWNED", "BY", MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;
         case Matches("REASSIGN", "OWNED", "BY", MatchAny, "TO"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;

 /* REFRESH MATERIALIZED VIEW */
         case Matches("REFRESH"):
-        COMPLETE_WITH("MATERIALIZED VIEW");
+            COMPLETE_WITH("MATERIALIZED VIEW");
             break;
         case Matches("REFRESH", "MATERIALIZED"):
-        COMPLETE_WITH("VIEW");
+            COMPLETE_WITH("VIEW");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
-                                        "CONCURRENTLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_matviews,
+                                            "CONCURRENTLY");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny):
-        COMPLETE_WITH("WITH");
+            COMPLETE_WITH("WITH");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny):
-        COMPLETE_WITH("WITH");
+            COMPLETE_WITH("WITH");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH"):
-        COMPLETE_WITH("NO DATA", "DATA");
+            COMPLETE_WITH("NO DATA", "DATA");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH"):
-        COMPLETE_WITH("NO DATA", "DATA");
+            COMPLETE_WITH("NO DATA", "DATA");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", MatchAny, "WITH", "NO"):
-        COMPLETE_WITH("DATA");
+            COMPLETE_WITH("DATA");
             break;
         case Matches("REFRESH", "MATERIALIZED", "VIEW", "CONCURRENTLY", MatchAny, "WITH", "NO"):
-        COMPLETE_WITH("DATA");
+            COMPLETE_WITH("DATA");
             break;

 /* REINDEX */
         case Matches("REINDEX"):
         case Matches("REINDEX", "(*)"):
-        COMPLETE_WITH("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
+            COMPLETE_WITH("TABLE", "INDEX", "SYSTEM", "SCHEMA", "DATABASE");
             break;
         case Matches("REINDEX", "TABLE"):
         case Matches("REINDEX", "(*)", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexables,
-                                        "CONCURRENTLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexables,
+                                            "CONCURRENTLY");
             break;
         case Matches("REINDEX", "INDEX"):
         case Matches("REINDEX", "(*)", "INDEX"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
-                                        "CONCURRENTLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_indexes,
+                                            "CONCURRENTLY");
             break;
         case Matches("REINDEX", "SCHEMA"):
         case Matches("REINDEX", "(*)", "SCHEMA"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
-                                 "CONCURRENTLY");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas,
+                                     "CONCURRENTLY");
             break;
         case Matches("REINDEX", "SYSTEM|DATABASE"):
         case Matches("REINDEX", "(*)", "SYSTEM|DATABASE"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_databases,
-                                 "CONCURRENTLY");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_databases,
+                                     "CONCURRENTLY");
             break;
         case Matches("REINDEX", "TABLE", "CONCURRENTLY"):
         case Matches("REINDEX", "(*)", "TABLE", "CONCURRENTLY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexables);
             break;
         case Matches("REINDEX", "INDEX", "CONCURRENTLY"):
         case Matches("REINDEX", "(*)", "INDEX", "CONCURRENTLY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
             break;
         case Matches("REINDEX", "SCHEMA", "CONCURRENTLY"):
         case Matches("REINDEX", "(*)", "SCHEMA", "CONCURRENTLY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
+            COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
             break;
         case Matches("REINDEX", "SYSTEM|DATABASE", "CONCURRENTLY"):
         case Matches("REINDEX", "(*)", "SYSTEM|DATABASE", "CONCURRENTLY"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_databases);
+            COMPLETE_WITH_QUERY(Query_for_list_of_databases);
             break;
         case HeadMatches("REINDEX", "(*"):
-        if (!HeadMatches("REINDEX", "(*)"))
-    {
-        /*
-         * This fires if we're in an unfinished parenthesized option list.
-         * get_previous_words treats a completed parenthesized option list as
-         * one word, so the above test is correct.
-         */
-        if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-            COMPLETE_WITH("CONCURRENTLY", "TABLESPACE", "VERBOSE");
-        else if (TailMatches("TABLESPACE"))
-            COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
-    }
+            if (!HeadMatches("REINDEX", "(*)"))
+            {
+                /*
+                 * This fires if we're in an unfinished parenthesized option
+                 * list. get_previous_words treats a completed parenthesized
+                 * option list as one word, so the above test is correct.
+                 */
+                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+                    COMPLETE_WITH("CONCURRENTLY", "TABLESPACE", "VERBOSE");
+                else if (TailMatches("TABLESPACE"))
+                    COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            }
             break;

 /* SECURITY LABEL */
         case Matches("SECURITY"):
-        COMPLETE_WITH("LABEL");
+            COMPLETE_WITH("LABEL");
             break;
         case Matches("SECURITY", "LABEL"):
-        COMPLETE_WITH("ON", "FOR");
+            COMPLETE_WITH("ON", "FOR");
             break;
         case Matches("SECURITY", "LABEL", "FOR", MatchAny):
-        COMPLETE_WITH("ON");
+            COMPLETE_WITH("ON");
             break;
         case Matches("SECURITY", "LABEL", "ON"):
         case Matches("SECURITY", "LABEL", "FOR", MatchAny, "ON"):
-        COMPLETE_WITH("TABLE", "COLUMN", "AGGREGATE", "DATABASE", "DOMAIN",
-                      "EVENT TRIGGER", "FOREIGN TABLE", "FUNCTION",
-                      "LARGE OBJECT", "MATERIALIZED VIEW", "LANGUAGE",
-                      "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA",
-                      "SEQUENCE", "SUBSCRIPTION", "TABLESPACE", "TYPE", "VIEW");
+            COMPLETE_WITH("TABLE", "COLUMN", "AGGREGATE", "DATABASE", "DOMAIN",
+                          "EVENT TRIGGER", "FOREIGN TABLE", "FUNCTION",
+                          "LARGE OBJECT", "MATERIALIZED VIEW", "LANGUAGE",
+                          "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA",
+                          "SEQUENCE", "SUBSCRIPTION", "TABLESPACE", "TYPE", "VIEW");
             break;
         case Matches("SECURITY", "LABEL", "ON", MatchAny, MatchAny):
-        COMPLETE_WITH("IS");
+            COMPLETE_WITH("IS");
             break;

 /* SELECT */
-    /* naah . . . */
+            /* naah . . . */

 /* SET, RESET, SHOW */
-    /* Complete with a variable name */
+            /* Complete with a variable name */
         case TailMatches("SET|RESET"):
-        if (!TailMatches("UPDATE", MatchAny, "SET"))
-        COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
-                                          "CONSTRAINTS",
-                                          "TRANSACTION",
-                                          "SESSION",
-                                          "ROLE",
-                                          "TABLESPACE",
-                                          "ALL");
+            if (!TailMatches("UPDATE", MatchAny, "SET"))
+                COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_set_vars,
+                                                  "CONSTRAINTS",
+                                                  "TRANSACTION",
+                                                  "SESSION",
+                                                  "ROLE",
+                                                  "TABLESPACE",
+                                                  "ALL");
             break;
         case Matches("SHOW"):
-        COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_show_vars,
-                                          "SESSION AUTHORIZATION",
-                                          "ALL");
+            COMPLETE_WITH_QUERY_VERBATIM_PLUS(Query_for_list_of_show_vars,
+                                              "SESSION AUTHORIZATION",
+                                              "ALL");
             break;
         case Matches("SHOW", "SESSION"):
-        COMPLETE_WITH("AUTHORIZATION");
+            COMPLETE_WITH("AUTHORIZATION");
             break;
-    /* Complete "SET TRANSACTION" */
+            /* Complete "SET TRANSACTION" */
         case Matches("SET", "TRANSACTION"):
-        COMPLETE_WITH("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
+            COMPLETE_WITH("SNAPSHOT", "ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
             break;
         case Matches("BEGIN|START", "TRANSACTION"):
         case Matches("BEGIN", "WORK"):
         case Matches("BEGIN"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION"):
-        COMPLETE_WITH("ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
+            COMPLETE_WITH("ISOLATION LEVEL", "READ", "DEFERRABLE", "NOT DEFERRABLE");
             break;
         case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "NOT"):
         case Matches("BEGIN", "NOT"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "NOT"):
-        COMPLETE_WITH("DEFERRABLE");
+            COMPLETE_WITH("DEFERRABLE");
             break;
         case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION"):
         case Matches("BEGIN", "ISOLATION"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION"):
-        COMPLETE_WITH("LEVEL");
+            COMPLETE_WITH("LEVEL");
             break;
         case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL"):
         case Matches("BEGIN", "ISOLATION", "LEVEL"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL"):
-        COMPLETE_WITH("READ", "REPEATABLE READ", "SERIALIZABLE");
+            COMPLETE_WITH("READ", "REPEATABLE READ", "SERIALIZABLE");
             break;
         case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "READ"):
         case Matches("BEGIN", "ISOLATION", "LEVEL", "READ"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "READ"):
-        COMPLETE_WITH("UNCOMMITTED", "COMMITTED");
+            COMPLETE_WITH("UNCOMMITTED", "COMMITTED");
             break;
         case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "ISOLATION", "LEVEL", "REPEATABLE"):
         case Matches("BEGIN", "ISOLATION", "LEVEL", "REPEATABLE"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "ISOLATION", "LEVEL", "REPEATABLE"):
-        COMPLETE_WITH("READ");
+            COMPLETE_WITH("READ");
             break;
         case Matches("SET|BEGIN|START", "TRANSACTION|WORK", "READ"):
         case Matches("BEGIN", "READ"):
         case Matches("SET", "SESSION", "CHARACTERISTICS", "AS", "TRANSACTION", "READ"):
-        COMPLETE_WITH("ONLY", "WRITE");
+            COMPLETE_WITH("ONLY", "WRITE");
             break;
-    /* SET CONSTRAINTS */
+            /* SET CONSTRAINTS */
         case Matches("SET", "CONSTRAINTS"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_constraints_with_schema,
-                                        "ALL");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_constraints_with_schema,
+                                            "ALL");
             break;
-    /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
+            /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
         case Matches("SET", "CONSTRAINTS", MatchAny):
-        COMPLETE_WITH("DEFERRED", "IMMEDIATE");
+            COMPLETE_WITH("DEFERRED", "IMMEDIATE");
             break;
-    /* Complete SET ROLE */
+            /* Complete SET ROLE */
         case Matches("SET", "ROLE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
-    /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
+            /* Complete SET SESSION with AUTHORIZATION or CHARACTERISTICS... */
         case Matches("SET", "SESSION"):
-        COMPLETE_WITH("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
+            COMPLETE_WITH("AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION");
             break;
-    /* Complete SET SESSION AUTHORIZATION with username */
+            /* Complete SET SESSION AUTHORIZATION with username */
         case Matches("SET", "SESSION", "AUTHORIZATION"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 "DEFAULT");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     "DEFAULT");
             break;
-    /* Complete RESET SESSION with AUTHORIZATION */
+            /* Complete RESET SESSION with AUTHORIZATION */
         case Matches("RESET", "SESSION"):
-        COMPLETE_WITH("AUTHORIZATION");
+            COMPLETE_WITH("AUTHORIZATION");
             break;
-    /* Complete SET <var> with "TO" */
+            /* Complete SET <var> with "TO" */
         case Matches("SET", MatchAny):
-        COMPLETE_WITH("TO");
+            COMPLETE_WITH("TO");
             break;

-    /*
-     * Complete ALTER DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER ... SET
-     * <name>
-     */
+            /*
+             * Complete ALTER DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER
+             * ... SET <name>
+             */
         case Matches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER", MatchAnyN, "SET",
MatchAnyExcept("SCHEMA")):
-        COMPLETE_WITH("FROM CURRENT", "TO");
+            COMPLETE_WITH("FROM CURRENT", "TO");
             break;

-    /*
-     * Suggest possible variable values in SET variable TO|=, along with the
-     * preceding ALTER syntaxes.
-     */
-        case TailMatches("SET", MatchAny, "TO|="):
-        if (!TailMatches("UPDATE", MatchAny, "SET", MatchAny, "TO|="))
-    {
-        /* special cased code for individual GUCs */
-        if (TailMatches("DateStyle", "TO|="))
-            COMPLETE_WITH("ISO", "SQL", "Postgres", "German",
-                          "YMD", "DMY", "MDY",
-                          "US", "European", "NonEuropean",
-                          "DEFAULT");
-        else if (TailMatches("search_path", "TO|="))
-        {
-            /* Here, we want to allow pg_catalog, so use narrower exclusion */
-            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
-                                     " AND nspname NOT LIKE E'pg\\\\_toast%%'"
-                                     " AND nspname NOT LIKE E'pg\\\\_temp%%'",
-                                     "DEFAULT");
-        }
-        else if (TailMatches("TimeZone", "TO|="))
-            COMPLETE_WITH_TIMEZONE_NAME();
-        else
-        {
-            /* generic, type based, GUC support */
-            char       *guctype = get_guctype(prev2_wd);
-
             /*
-             * Note: if we don't recognize the GUC name, it's important to not
-             * offer any completions, as most likely we've misinterpreted the
-             * context and this isn't a GUC-setting command at all.
+             * Suggest possible variable values in SET variable TO|=, along
+             * with the preceding ALTER syntaxes.
              */
-            if (guctype)
+        case TailMatches("SET", MatchAny, "TO|="):
+            if (!TailMatches("UPDATE", MatchAny, "SET", MatchAny, "TO|="))
             {
-                if (strcmp(guctype, "enum") == 0)
+                /* special cased code for individual GUCs */
+                if (TailMatches("DateStyle", "TO|="))
+                    COMPLETE_WITH("ISO", "SQL", "Postgres", "German",
+                                  "YMD", "DMY", "MDY",
+                                  "US", "European", "NonEuropean",
+                                  "DEFAULT");
+                else if (TailMatches("search_path", "TO|="))
                 {
-                    set_completion_reference_verbatim(prev2_wd);
-                    COMPLETE_WITH_QUERY_PLUS(Query_for_values_of_enum_GUC,
+                    /*
+                     * Here, we want to allow pg_catalog, so use narrower
+                     * exclusion
+                     */
+                    COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_schemas
+                                             " AND nspname NOT LIKE E'pg\\\\_toast%%'"
+                                             " AND nspname NOT LIKE E'pg\\\\_temp%%'",
                                              "DEFAULT");
                 }
-                else if (strcmp(guctype, "bool") == 0)
-                    COMPLETE_WITH("on", "off", "true", "false", "yes", "no",
-                                  "1", "0", "DEFAULT");
+                else if (TailMatches("TimeZone", "TO|="))
+                    COMPLETE_WITH_TIMEZONE_NAME();
                 else
-                    COMPLETE_WITH("DEFAULT");
+                {
+                    /* generic, type based, GUC support */
+                    char       *guctype = get_guctype(prev2_wd);

-                free(guctype);
+                    /*
+                     * Note: if we don't recognize the GUC name, it's
+                     * important to not offer any completions, as most likely
+                     * we've misinterpreted the context and this isn't a
+                     * GUC-setting command at all.
+                     */
+                    if (guctype)
+                    {
+                        if (strcmp(guctype, "enum") == 0)
+                        {
+                            set_completion_reference_verbatim(prev2_wd);
+                            COMPLETE_WITH_QUERY_PLUS(Query_for_values_of_enum_GUC,
+                                                     "DEFAULT");
+                        }
+                        else if (strcmp(guctype, "bool") == 0)
+                            COMPLETE_WITH("on", "off", "true", "false", "yes", "no",
+                                          "1", "0", "DEFAULT");
+                        else
+                            COMPLETE_WITH("DEFAULT");
+
+                        free(guctype);
+                    }
+                }
             }
-        }
-    }
             break;

 /* START TRANSACTION */
         case Matches("START"):
-        COMPLETE_WITH("TRANSACTION");
+            COMPLETE_WITH("TRANSACTION");
             break;

 /* TABLE, but not TABLE embedded in other commands */
         case Matches("TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
             break;

 /* TABLESAMPLE */
         case TailMatches("TABLESAMPLE"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_tablesample_methods);
             break;
         case TailMatches("TABLESAMPLE", MatchAny):
-        COMPLETE_WITH("(");
+            COMPLETE_WITH("(");
             break;

 /* TRUNCATE */
         case Matches("TRUNCATE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
-                                        "TABLE", "ONLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
+                                            "TABLE", "ONLY");
             break;
         case Matches("TRUNCATE", "TABLE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
-                                        "ONLY");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
+                                            "ONLY");
             break;
         case Matches("TRUNCATE", MatchAnyN, "ONLY"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
             break;
         case Matches("TRUNCATE", MatchAny):
         case Matches("TRUNCATE", "TABLE|ONLY", MatchAny):
         case Matches("TRUNCATE", "TABLE", "ONLY", MatchAny):
-        COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
+            COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
             break;
         case Matches("TRUNCATE", MatchAnyN, "IDENTITY"):
-        COMPLETE_WITH("CASCADE", "RESTRICT");
+            COMPLETE_WITH("CASCADE", "RESTRICT");
             break;

 /* UNLISTEN */
         case Matches("UNLISTEN"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_channels, "*");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_channels, "*");
             break;

 /* UPDATE --- can be inside EXPLAIN, RULE, etc */
-    /* If prev. word is UPDATE suggest a list of tables */
+            /* If prev. word is UPDATE suggest a list of tables */
         case TailMatches("UPDATE"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_updatables);
             break;
-    /* Complete UPDATE <table> with "SET" */
+            /* Complete UPDATE <table> with "SET" */
         case TailMatches("UPDATE", MatchAny):
-        COMPLETE_WITH("SET");
+            COMPLETE_WITH("SET");
             break;
-    /* Complete UPDATE <table> SET with list of attributes */
+            /* Complete UPDATE <table> SET with list of attributes */
         case TailMatches("UPDATE", MatchAny, "SET"):
-        COMPLETE_WITH_ATTR(prev2_wd);
+            COMPLETE_WITH_ATTR(prev2_wd);
             break;
-    /* UPDATE <table> SET <attr> = */
+            /* UPDATE <table> SET <attr> = */
         case TailMatches("UPDATE", MatchAny, "SET", MatchAnyExcept("*=")):
-        COMPLETE_WITH("=");
+            COMPLETE_WITH("=");
             break;

 /* USER MAPPING */
         case Matches("ALTER|CREATE|DROP", "USER", "MAPPING"):
-        COMPLETE_WITH("FOR");
+            COMPLETE_WITH("FOR");
             break;
         case Matches("CREATE", "USER", "MAPPING", "FOR"):
-        COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
-                                 "CURRENT_ROLE",
-                                 "CURRENT_USER",
-                                 "PUBLIC",
-                                 "USER");
+            COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
+                                     "CURRENT_ROLE",
+                                     "CURRENT_USER",
+                                     "PUBLIC",
+                                     "USER");
             break;
         case Matches("ALTER|DROP", "USER", "MAPPING", "FOR"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
+            COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
             break;
         case Matches("CREATE|ALTER|DROP", "USER", "MAPPING", "FOR", MatchAny):
-        COMPLETE_WITH("SERVER");
+            COMPLETE_WITH("SERVER");
             break;
         case Matches("CREATE|ALTER", "USER", "MAPPING", "FOR", MatchAny, "SERVER", MatchAny):
-        COMPLETE_WITH("OPTIONS");
+            COMPLETE_WITH("OPTIONS");
             break;

 /*
@@ -5479,352 +5582,352 @@ match_previous_words(int pattern_id,
  * VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns [, ...] ]
  */
         case Matches("VACUUM"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
-                                        "FULL",
-                                        "FREEZE",
-                                        "ANALYZE",
-                                        "VERBOSE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
+                                            "FULL",
+                                            "FREEZE",
+                                            "ANALYZE",
+                                            "VERBOSE");
             break;
         case Matches("VACUUM", "FULL"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
-                                        "FREEZE",
-                                        "ANALYZE",
-                                        "VERBOSE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
+                                            "FREEZE",
+                                            "ANALYZE",
+                                            "VERBOSE");
             break;
         case Matches("VACUUM", "FREEZE"):
         case Matches("VACUUM", "FULL", "FREEZE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
-                                        "VERBOSE",
-                                        "ANALYZE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
+                                            "VERBOSE",
+                                            "ANALYZE");
             break;
         case Matches("VACUUM", "VERBOSE"):
         case Matches("VACUUM", "FULL|FREEZE", "VERBOSE"):
         case Matches("VACUUM", "FULL", "FREEZE", "VERBOSE"):
-        COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
-                                        "ANALYZE");
+            COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_vacuumables,
+                                            "ANALYZE");
             break;
         case HeadMatches("VACUUM", "(*"):
-        if (!HeadMatches("VACUUM", "(*)"))
-    {
-        /*
-         * This fires if we're in an unfinished parenthesized option list.
-         * get_previous_words treats a completed parenthesized option list as
-         * one word, so the above test is correct.
-         */
-        if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-            COMPLETE_WITH("FULL", "FREEZE", "ANALYZE", "VERBOSE",
-                          "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
-                          "INDEX_CLEANUP", "PROCESS_MAIN", "PROCESS_TOAST",
-                          "TRUNCATE", "PARALLEL", "SKIP_DATABASE_STATS",
-                          "ONLY_DATABASE_STATS", "BUFFER_USAGE_LIMIT");
-        else if
(TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_MAIN|PROCESS_TOAST|TRUNCATE|SKIP_DATABASE_STATS|ONLY_DATABASE_STATS"))
-            COMPLETE_WITH("ON", "OFF");
-        else if (TailMatches("INDEX_CLEANUP"))
-            COMPLETE_WITH("AUTO", "ON", "OFF");
-    }
+            if (!HeadMatches("VACUUM", "(*)"))
+            {
+                /*
+                 * This fires if we're in an unfinished parenthesized option
+                 * list. get_previous_words treats a completed parenthesized
+                 * option list as one word, so the above test is correct.
+                 */
+                if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+                    COMPLETE_WITH("FULL", "FREEZE", "ANALYZE", "VERBOSE",
+                                  "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
+                                  "INDEX_CLEANUP", "PROCESS_MAIN", "PROCESS_TOAST",
+                                  "TRUNCATE", "PARALLEL", "SKIP_DATABASE_STATS",
+                                  "ONLY_DATABASE_STATS", "BUFFER_USAGE_LIMIT");
+                else if
(TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_MAIN|PROCESS_TOAST|TRUNCATE|SKIP_DATABASE_STATS|ONLY_DATABASE_STATS"))
+                    COMPLETE_WITH("ON", "OFF");
+                else if (TailMatches("INDEX_CLEANUP"))
+                    COMPLETE_WITH("AUTO", "ON", "OFF");
+            }
             break;
         case Matches("VACUUM", MatchAnyN, "("):
-        /* "VACUUM (" should be caught above, so assume we want columns */
-        COMPLETE_WITH_ATTR(prev2_wd);
+            /* "VACUUM (" should be caught above, so assume we want columns */
+            COMPLETE_WITH_ATTR(prev2_wd);
             break;
         case HeadMatches("VACUUM"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_vacuumables);
             break;

 /* WITH [RECURSIVE] */

-    /*
-     * Only match when WITH is the first word, as WITH may appear in many
-     * other contexts.
-     */
+            /*
+             * Only match when WITH is the first word, as WITH may appear in
+             * many other contexts.
+             */
         case Matches("WITH"):
-        COMPLETE_WITH("RECURSIVE");
+            COMPLETE_WITH("RECURSIVE");
             break;

 /* WHERE */
-    /* Simple case of the word before the where being the table name */
+            /* Simple case of the word before the where being the table name */
         case TailMatches(MatchAny, "WHERE"):
-        COMPLETE_WITH_ATTR(prev2_wd);
+            COMPLETE_WITH_ATTR(prev2_wd);
             break;

 /* ... FROM ... */
 /* TODO: also include SRF ? */
         case TailMatches("FROM"):
-        if (!Matches("COPY|\\copy", MatchAny, "FROM"))
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            if (!Matches("COPY|\\copy", MatchAny, "FROM"))
+                COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
             break;

 /* ... JOIN ... */
         case TailMatches("JOIN"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_selectables);
             break;

 /* ... AT [ LOCAL | TIME ZONE ] ... */
         case TailMatches("AT"):
-        COMPLETE_WITH("LOCAL", "TIME ZONE");
+            COMPLETE_WITH("LOCAL", "TIME ZONE");
             break;
         case TailMatches("AT", "TIME", "ZONE"):
-        COMPLETE_WITH_TIMEZONE_NAME();
+            COMPLETE_WITH_TIMEZONE_NAME();
             break;

 /* Backslash commands */
 /* TODO:  \dc \dd \dl */
         case TailMatchesCS("\\?"):
-        COMPLETE_WITH_CS("commands", "options", "variables");
+            COMPLETE_WITH_CS("commands", "options", "variables");
             break;
         case TailMatchesCS("\\connect|\\c"):
-        if (!recognized_connection_string(text))
-            COMPLETE_WITH_QUERY(Query_for_list_of_databases);
+            if (!recognized_connection_string(text))
+                COMPLETE_WITH_QUERY(Query_for_list_of_databases);
             break;
         case TailMatchesCS("\\connect|\\c", MatchAny):
-        if (!recognized_connection_string(prev_wd))
-            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            if (!recognized_connection_string(prev_wd))
+                COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
         case TailMatchesCS("\\da*"):
-        COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates);
+            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_aggregates);
             break;
         case TailMatchesCS("\\dAc*", MatchAny):
         case TailMatchesCS("\\dAf*", MatchAny):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case TailMatchesCS("\\dAo*", MatchAny):
         case TailMatchesCS("\\dAp*", MatchAny):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_operator_families);
             break;
         case TailMatchesCS("\\dA*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
+            COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
             break;
         case TailMatchesCS("\\db*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+            COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
             break;
         case TailMatchesCS("\\dconfig*"):
-        COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_show_vars);
+            COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_show_vars);
             break;
         case TailMatchesCS("\\dD*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_domains);
             break;
         case TailMatchesCS("\\des*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_servers);
+            COMPLETE_WITH_QUERY(Query_for_list_of_servers);
             break;
         case TailMatchesCS("\\deu*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
+            COMPLETE_WITH_QUERY(Query_for_list_of_user_mappings);
             break;
         case TailMatchesCS("\\dew*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
+            COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
             break;
         case TailMatchesCS("\\df*"):
-        COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
+            COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);
             break;
         case HeadMatchesCS("\\df*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;

         case TailMatchesCS("\\dFd*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_dictionaries);
             break;
         case TailMatchesCS("\\dFp*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_parsers);
             break;
         case TailMatchesCS("\\dFt*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_templates);
             break;
-    /* must be at end of \dF alternatives: */
+            /* must be at end of \dF alternatives: */
         case TailMatchesCS("\\dF*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_ts_configurations);
             break;

         case TailMatchesCS("\\di*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_indexes);
             break;
         case TailMatchesCS("\\dL*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+            COMPLETE_WITH_QUERY(Query_for_list_of_languages);
             break;
         case TailMatchesCS("\\dn*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
+            COMPLETE_WITH_QUERY(Query_for_list_of_schemas);
             break;
-    /* no support for completing operators, but we can complete types: */
+            /* no support for completing operators, but we can complete types: */
         case HeadMatchesCS("\\do*", MatchAny):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case TailMatchesCS("\\dp"):
         case TailMatchesCS("\\z"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_grantables);
             break;
         case TailMatchesCS("\\dPi*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_indexes);
             break;
         case TailMatchesCS("\\dPt*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_tables);
             break;
         case TailMatchesCS("\\dP*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_partitioned_relations);
             break;
         case TailMatchesCS("\\dRp*"):
-        COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_publications);
+            COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_publications);
             break;
         case TailMatchesCS("\\dRs*"):
-        COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_subscriptions);
+            COMPLETE_WITH_VERSIONED_QUERY(Query_for_list_of_subscriptions);
             break;
         case TailMatchesCS("\\ds*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_sequences);
             break;
         case TailMatchesCS("\\dt*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
             break;
         case TailMatchesCS("\\dT*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
             break;
         case TailMatchesCS("\\du*"):
         case TailMatchesCS("\\dg*"):
         case TailMatchesCS("\\drg*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
         case TailMatchesCS("\\dv*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
             break;
         case TailMatchesCS("\\dx*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
+            COMPLETE_WITH_QUERY(Query_for_list_of_extensions);
             break;
         case TailMatchesCS("\\dX*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_statistics);
             break;
         case TailMatchesCS("\\dm*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_matviews);
             break;
         case TailMatchesCS("\\dE*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_foreign_tables);
             break;
         case TailMatchesCS("\\dy*"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
+            COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
             break;

-    /* must be at end of \d alternatives: */
+            /* must be at end of \d alternatives: */
         case TailMatchesCS("\\d*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_relations);
             break;

         case TailMatchesCS("\\ef"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
             break;
         case TailMatchesCS("\\ev"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
             break;

         case TailMatchesCS("\\encoding"):
-        COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_encodings);
+            COMPLETE_WITH_QUERY_VERBATIM(Query_for_list_of_encodings);
             break;
         case TailMatchesCS("\\h|\\help"):
-        COMPLETE_WITH_LIST(sql_commands);
+            COMPLETE_WITH_LIST(sql_commands);
             break;
         case TailMatchesCS("\\h|\\help", MatchAny):
-        if (TailMatches("DROP"))
-            matches = rl_completion_matches(text, drop_command_generator);
-        else if (TailMatches("ALTER"))
-            matches = rl_completion_matches(text, alter_command_generator);
+            if (TailMatches("DROP"))
+                matches = rl_completion_matches(text, drop_command_generator);
+            else if (TailMatches("ALTER"))
+                matches = rl_completion_matches(text, alter_command_generator);

-        /*
-         * CREATE is recognized by tail match elsewhere, so doesn't need to be
-         * repeated here
-         */
+            /*
+             * CREATE is recognized by tail match elsewhere, so doesn't need
+             * to be repeated here
+             */
             break;
         case TailMatchesCS("\\h|\\help", MatchAny, MatchAny):
-        if (TailMatches("CREATE|DROP", "ACCESS"))
-            COMPLETE_WITH("METHOD");
-        else if (TailMatches("ALTER", "DEFAULT"))
-            COMPLETE_WITH("PRIVILEGES");
-        else if (TailMatches("CREATE|ALTER|DROP", "EVENT"))
-            COMPLETE_WITH("TRIGGER");
-        else if (TailMatches("CREATE|ALTER|DROP", "FOREIGN"))
-            COMPLETE_WITH("DATA WRAPPER", "TABLE");
-        else if (TailMatches("ALTER", "LARGE"))
-            COMPLETE_WITH("OBJECT");
-        else if (TailMatches("CREATE|ALTER|DROP", "MATERIALIZED"))
-            COMPLETE_WITH("VIEW");
-        else if (TailMatches("CREATE|ALTER|DROP", "TEXT"))
-            COMPLETE_WITH("SEARCH");
-        else if (TailMatches("CREATE|ALTER|DROP", "USER"))
-            COMPLETE_WITH("MAPPING FOR");
+            if (TailMatches("CREATE|DROP", "ACCESS"))
+                COMPLETE_WITH("METHOD");
+            else if (TailMatches("ALTER", "DEFAULT"))
+                COMPLETE_WITH("PRIVILEGES");
+            else if (TailMatches("CREATE|ALTER|DROP", "EVENT"))
+                COMPLETE_WITH("TRIGGER");
+            else if (TailMatches("CREATE|ALTER|DROP", "FOREIGN"))
+                COMPLETE_WITH("DATA WRAPPER", "TABLE");
+            else if (TailMatches("ALTER", "LARGE"))
+                COMPLETE_WITH("OBJECT");
+            else if (TailMatches("CREATE|ALTER|DROP", "MATERIALIZED"))
+                COMPLETE_WITH("VIEW");
+            else if (TailMatches("CREATE|ALTER|DROP", "TEXT"))
+                COMPLETE_WITH("SEARCH");
+            else if (TailMatches("CREATE|ALTER|DROP", "USER"))
+                COMPLETE_WITH("MAPPING FOR");
             break;
         case TailMatchesCS("\\h|\\help", MatchAny, MatchAny, MatchAny):
-        if (TailMatches("CREATE|ALTER|DROP", "FOREIGN", "DATA"))
-            COMPLETE_WITH("WRAPPER");
-        else if (TailMatches("CREATE|ALTER|DROP", "TEXT", "SEARCH"))
-            COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
-        else if (TailMatches("CREATE|ALTER|DROP", "USER", "MAPPING"))
-            COMPLETE_WITH("FOR");
+            if (TailMatches("CREATE|ALTER|DROP", "FOREIGN", "DATA"))
+                COMPLETE_WITH("WRAPPER");
+            else if (TailMatches("CREATE|ALTER|DROP", "TEXT", "SEARCH"))
+                COMPLETE_WITH("CONFIGURATION", "DICTIONARY", "PARSER", "TEMPLATE");
+            else if (TailMatches("CREATE|ALTER|DROP", "USER", "MAPPING"))
+                COMPLETE_WITH("FOR");
             break;
         case TailMatchesCS("\\l*"):
-        if (!TailMatchesCS("\\lo*"))
-        COMPLETE_WITH_QUERY(Query_for_list_of_databases);
+            if (!TailMatchesCS("\\lo*"))
+                COMPLETE_WITH_QUERY(Query_for_list_of_databases);
             break;
         case TailMatchesCS("\\password"):
-        COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+            COMPLETE_WITH_QUERY(Query_for_list_of_roles);
             break;
         case TailMatchesCS("\\pset"):
-        COMPLETE_WITH_CS("border", "columns", "csv_fieldsep", "expanded",
-                         "fieldsep", "fieldsep_zero", "footer", "format",
-                         "linestyle", "null", "numericlocale",
-                         "pager", "pager_min_lines",
-                         "recordsep", "recordsep_zero",
-                         "tableattr", "title", "tuples_only",
-                         "unicode_border_linestyle",
-                         "unicode_column_linestyle",
-                         "unicode_header_linestyle",
-                         "xheader_width");
+            COMPLETE_WITH_CS("border", "columns", "csv_fieldsep", "expanded",
+                             "fieldsep", "fieldsep_zero", "footer", "format",
+                             "linestyle", "null", "numericlocale",
+                             "pager", "pager_min_lines",
+                             "recordsep", "recordsep_zero",
+                             "tableattr", "title", "tuples_only",
+                             "unicode_border_linestyle",
+                             "unicode_column_linestyle",
+                             "unicode_header_linestyle",
+                             "xheader_width");
             break;
         case TailMatchesCS("\\pset", MatchAny):
-        if (TailMatchesCS("format"))
-            COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
-                             "latex-longtable", "troff-ms", "unaligned",
-                             "wrapped");
-        else if (TailMatchesCS("xheader_width"))
-            COMPLETE_WITH_CS("full", "column", "page");
-        else if (TailMatchesCS("linestyle"))
-            COMPLETE_WITH_CS("ascii", "old-ascii", "unicode");
-        else if (TailMatchesCS("pager"))
-            COMPLETE_WITH_CS("on", "off", "always");
-        else if (TailMatchesCS("unicode_border_linestyle|"
-                               "unicode_column_linestyle|"
-                               "unicode_header_linestyle"))
-            COMPLETE_WITH_CS("single", "double");
+            if (TailMatchesCS("format"))
+                COMPLETE_WITH_CS("aligned", "asciidoc", "csv", "html", "latex",
+                                 "latex-longtable", "troff-ms", "unaligned",
+                                 "wrapped");
+            else if (TailMatchesCS("xheader_width"))
+                COMPLETE_WITH_CS("full", "column", "page");
+            else if (TailMatchesCS("linestyle"))
+                COMPLETE_WITH_CS("ascii", "old-ascii", "unicode");
+            else if (TailMatchesCS("pager"))
+                COMPLETE_WITH_CS("on", "off", "always");
+            else if (TailMatchesCS("unicode_border_linestyle|"
+                                   "unicode_column_linestyle|"
+                                   "unicode_header_linestyle"))
+                COMPLETE_WITH_CS("single", "double");
             break;
         case TailMatchesCS("\\unset"):
-        matches = complete_from_variables(text, "", "", true);
+            matches = complete_from_variables(text, "", "", true);
             break;
         case TailMatchesCS("\\set"):
-        matches = complete_from_variables(text, "", "", false);
+            matches = complete_from_variables(text, "", "", false);
             break;
         case TailMatchesCS("\\set", MatchAny):
-        if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|SHOW_ALL_RESULTS|"
-                          "SINGLELINE|SINGLESTEP"))
-            COMPLETE_WITH_CS("on", "off");
-        else if (TailMatchesCS("COMP_KEYWORD_CASE"))
-            COMPLETE_WITH_CS("lower", "upper",
-                             "preserve-lower", "preserve-upper");
-        else if (TailMatchesCS("ECHO"))
-            COMPLETE_WITH_CS("errors", "queries", "all", "none");
-        else if (TailMatchesCS("ECHO_HIDDEN"))
-            COMPLETE_WITH_CS("noexec", "off", "on");
-        else if (TailMatchesCS("HISTCONTROL"))
-            COMPLETE_WITH_CS("ignorespace", "ignoredups",
-                             "ignoreboth", "none");
-        else if (TailMatchesCS("ON_ERROR_ROLLBACK"))
-            COMPLETE_WITH_CS("on", "off", "interactive");
-        else if (TailMatchesCS("SHOW_CONTEXT"))
-            COMPLETE_WITH_CS("never", "errors", "always");
-        else if (TailMatchesCS("VERBOSITY"))
-            COMPLETE_WITH_CS("default", "verbose", "terse", "sqlstate");
+            if (TailMatchesCS("AUTOCOMMIT|ON_ERROR_STOP|QUIET|SHOW_ALL_RESULTS|"
+                              "SINGLELINE|SINGLESTEP"))
+                COMPLETE_WITH_CS("on", "off");
+            else if (TailMatchesCS("COMP_KEYWORD_CASE"))
+                COMPLETE_WITH_CS("lower", "upper",
+                                 "preserve-lower", "preserve-upper");
+            else if (TailMatchesCS("ECHO"))
+                COMPLETE_WITH_CS("errors", "queries", "all", "none");
+            else if (TailMatchesCS("ECHO_HIDDEN"))
+                COMPLETE_WITH_CS("noexec", "off", "on");
+            else if (TailMatchesCS("HISTCONTROL"))
+                COMPLETE_WITH_CS("ignorespace", "ignoredups",
+                                 "ignoreboth", "none");
+            else if (TailMatchesCS("ON_ERROR_ROLLBACK"))
+                COMPLETE_WITH_CS("on", "off", "interactive");
+            else if (TailMatchesCS("SHOW_CONTEXT"))
+                COMPLETE_WITH_CS("never", "errors", "always");
+            else if (TailMatchesCS("VERBOSITY"))
+                COMPLETE_WITH_CS("default", "verbose", "terse", "sqlstate");
             break;
         case TailMatchesCS("\\sf*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_routines);
             break;
         case TailMatchesCS("\\sv*"):
-        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
+            COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
             break;
         case
TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|\\ir|\\include_relative|\\o|\\out|\\s|\\w|\\write|\\lo_import"):
-        completion_charp = "\\";
-        completion_force_quote = false;
-        matches = rl_completion_matches(text, complete_from_files);
+            completion_charp = "\\";
+            completion_force_quote = false;
+            matches = rl_completion_matches(text, complete_from_files);
             break;
     }

diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 635e6d6e21..2e4d786d45 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2802,6 +2802,7 @@ TBMSharedIterator
 TBMSharedIteratorState
 TBMStatus
 TBlockState
+TCPattern
 TIDBitmap
 TM_FailureData
 TM_IndexDelete
--
2.43.5

#----------------------------------------------------------------------
#
# convert_tabcomplete.pl
#    Crude hack to help convert tab-complete's else-if chain to a switch.
#
# Lines looking like
#    else if (Matches("A", "B"))
# are converted to
#    case Matches("A", "B"):
# with insertion of a preceding "break;".
# The function name can be any one of Matches, HeadMatches, TailMatches,
# MatchesCS, HeadMatchesCS, or TailMatchesCS.
#
# There is a limited ability to deal with AND conditions
#    else if (Matches("A", "B") && othercondition)
# by converting that to
#    case Matches("A", "B"):
#        if (othercondition)
# Also, simple OR conditions such as
#    else if (Matches("A", "B") || Matches("C", "D"))
# are converted to adjacent case labels.
#
# Manual intervention is needed at the beginning and end of the switch
# construct, and for any else-ifs that the script fails to handle.
# There are a couple of places in the existing tab-complete.c file
# that have to be adjusted to prevent "can't decipher" complaints
# before the output is usable.
#
# If the input file is named something.c, the output file will be
# named something.out.c.
#----------------------------------------------------------------------

use strict;
use warnings FATAL => 'all';
use Getopt::Long;

my $output_path = '';

GetOptions('output:s' => \$output_path) || usage();

my $input_file = shift @ARGV || die "No input file.\n";

# Make sure output_path ends in a slash if needed.
if ($output_path ne '' && substr($output_path, -1) ne '/')
{
    $output_path .= '/';
}

$input_file =~ /([\w.-]+)\.c$/
  || die "Input file must be named something.c.\n";
my $base_filename = $1;
my $output_file = $output_path . $base_filename . '.out.c';

open(my $infd, '<', $input_file) || die "$input_file: $!\n";
open(my $outfd, '>', $output_file) || die "$output_file: $!\n";

# delayed_lines accumulates comments that we might want to move to
# after an inserted "break;".
my $delayed_lines = '';
# True if we're handling a multiline else-if condition
my $in_else_if = 0;
# The accumulated line
my $else_if_line;
my $else_if_lineno;
# True if we're expecting to suppress any braces appearing within case
my $suppress_braces = 0;

while (<$infd>)
{
    chomp;
    if ($in_else_if)
    {
        my $rest = $_;
        # collapse leading whitespace
        $rest =~ s/^\s+//;
        $else_if_line .= ' ' . $rest;
        # Double right paren is currently sufficient to detect completion
        if ($else_if_line =~ m/\)\)$/)
        {
            process_else_if($else_if_line, $else_if_lineno);
            $in_else_if = 0;
        }
    }
    elsif (m/^\telse if \(.*Matches(CS)?\(/)
    {
        $else_if_line = $_;
        $else_if_lineno = $.;
        # Double right paren is currently sufficient to detect completion
        if ($else_if_line =~ m/\)\)$/)
        {
            process_else_if($else_if_line, $else_if_lineno);
        }
        else
        {
            $in_else_if = 1;
        }
    }
    elsif ($suppress_braces == 1 and m/^\t\{$/)
    {
        # don't print
        $suppress_braces = 2;
    }
    elsif ($suppress_braces == 2 and m/^\t\}$/)
    {
        # don't print, but do flush delayed lines
        if ($delayed_lines ne '')
        {
            print $outfd $delayed_lines;
            $delayed_lines = '';
        }
        $suppress_braces = 0;
    }
    elsif (m/^$/
        || m|^\s*/\*|
        || m|^\s*\*/|
        || m|^\s+\*|)
    {
        # delay output of blank and comment lines
        $delayed_lines .= $_ . "\n";
    }
    else
    {
        if ($delayed_lines ne '')
        {
            print $outfd $delayed_lines;
            $delayed_lines = '';
        }
        print $outfd "$_\n";
    }
}

close($infd);
close($outfd);


sub process_else_if
{
    my ($else_if_line, $else_if_lineno) = @_;

    # Strip the initial "else if (", which we know is there
    $else_if_line =~ s/^\telse if \(//;

    # Handle OR'd conditions
    my $isfirst = 1;
    while ($else_if_line =~
        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\s*\|\|\s*//
      )
    {
        my $typ = $1;
        my $cs = $2;
        my $args = $3;
        process_match($typ, $cs, $args, $isfirst);
        $isfirst = 0;
    }

    # Check for AND'd condition
    if ($else_if_line =~
        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\s*&&\s*//
      )
    {
        my $typ = $1;
        my $cs = $2;
        my $args = $3;
        process_match($typ, $cs, $args, $isfirst);
        $isfirst = 0;
        print $outfd "\t\tif ($else_if_line\n";
        $suppress_braces = 0;
    }
    elsif ($else_if_line =~
        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\)$//
      )
    {
        my $typ = $1;
        my $cs = $2;
        my $args = $3;
        process_match($typ, $cs, $args, $isfirst);
        $isfirst = 0;
        $suppress_braces = 1;
    }
    else
    {
        warn
          "could not process if condition at line $else_if_lineno: the rest looks like $else_if_line\n";
        if ($delayed_lines ne '')
        {
            print $outfd $delayed_lines;
            $delayed_lines = '';
        }
        print $outfd "\telse if ($else_if_line\n";
    }
}

sub process_match
{
    my ($typ, $cs, $args, $needbreak) = @_;

    print $outfd "\t\t\tbreak;\n" if $needbreak;
    if ($delayed_lines ne '')
    {
        print $outfd $delayed_lines;
        $delayed_lines = '';
    }
    print $outfd "\t\tcase ${typ}Matches${cs}(${args}):\n";
}

sub usage
{
    die <<EOM;
Usage: convert_tabcomplete.pl [--output/-o <path>] input_file
    --output        Output directory (default '.')

convert_tabcomplete.pl transforms tab-complete.c to tab-complete.out.c.
EOM
}
--- tab-complete.in.c.orig    2024-07-13 12:12:49.659229840 -0400
+++ tab-complete.in.c    2024-07-13 12:28:07.394700992 -0400
@@ -1304,6 +1304,47 @@ static const pgsql_thing_t words_after_c
     {NULL}                        /* end of list */
 };

+/*
+ * The tcpatterns[] table provides the initial pattern-match rule for each
+ * switch case in match_previous_words().  The contents of the table
+ * are constructed by gen_tabcomplete.pl.
+ */
+
+/* Basic match rules appearing in tcpatterns[].kind */
+enum TCPatternKind
+{
+    Match,
+    MatchCS,
+    HeadMatch,
+    HeadMatchCS,
+    TailMatch,
+    TailMatchCS,
+};
+
+/* Things besides string literals that can appear in tcpatterns[].words */
+#define MatchAny  NULL
+#define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""
+
+/* One entry in tcpatterns[] */
+typedef struct
+{
+    int            id;                /* case label used in match_previous_words */
+    enum TCPatternKind kind;    /* match kind, see above */
+    int            nwords;            /* length of words[] array */
+    const char *const *words;    /* array of match words */
+} TCPattern;
+
+/* Macro emitted by gen_tabcomplete.pl to fill a tcpatterns[] entry */
+#define TCPAT(id, kind, ...) \
+    { (id), (kind), VA_ARGS_NARGS(__VA_ARGS__), \
+      (const char * const []) { __VA_ARGS__ } }
+
+static const TCPattern tcpatterns[] =
+{
+    /* Insert tab-completion pattern data here. */
+};
+
 /* Storage parameters for CREATE TABLE and ALTER TABLE */
 static const char *const table_storage_parameters[] = {
     "autovacuum_analyze_scale_factor",
@@ -1469,10 +1510,6 @@ initialize_readline(void)
  * just be written directly in patterns.)  There is also MatchAnyN, but that
  * is supported only in Matches/MatchesCS and is not handled here.
  */
-#define MatchAny  NULL
-#define MatchAnyExcept(pattern)  ("!" pattern)
-#define MatchAnyN ""
-
 static bool
 word_matches(const char *pattern,
              const char *word,
@@ -2071,9 +2108,10 @@ match_previous_words(const char *text, i
     /* ALTER PUBLICATION <name> ADD */
     else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
         COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
-             (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
-              ends_with(prev_wd, ',')))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"))
+        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
+             ends_with(prev_wd, ','))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);

     /*
@@ -2539,8 +2577,7 @@ match_previous_words(const char *text, i
     }
     /* ALTER TABLE xxx ADD [COLUMN] yyy */
     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny) ||
-             (Matches("ALTER", "TABLE", MatchAny, "ADD", MatchAny) &&
-              !Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
+             Matches("ALTER", "TABLE", MatchAny, "ADD",
MatchAnyExcept("COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
     /* ALTER TABLE xxx ADD CONSTRAINT yyy */
     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny))
@@ -3941,13 +3978,14 @@ match_previous_words(const char *text, i

"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
                      MatchAny) ||
              Matches("DROP", "ACCESS", "METHOD", MatchAny) ||
-             (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
-              ends_with(prev_wd, ')')) ||
              Matches("DROP", "EVENT", "TRIGGER", MatchAny) ||
              Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
              Matches("DROP", "FOREIGN", "TABLE", MatchAny) ||
              Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
         COMPLETE_WITH("CASCADE", "RESTRICT");
+    else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
+             ends_with(prev_wd, ')'))
+        COMPLETE_WITH("CASCADE", "RESTRICT");

     /* help completing some of the variants */
     else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
--- tab-complete.in.out.c.orig    2024-07-13 12:29:47.798865506 -0400
+++ tab-complete.in.out.c    2024-07-13 12:33:51.329264489 -0400
@@ -1398,7 +1398,8 @@ static const char *const view_optional_p

 /* Forward declaration of functions */
 static char **psql_completion(const char *text, int start, int end);
-static char **match_previous_words(const char *text, int start, int end,
+static char **match_previous_words(int pattern_id,
+                                   const char *text, int start, int end,
                                    char **previous_words,
                                    int previous_words_count);
 static char *create_command_generator(const char *text, int state);
@@ -1931,17 +1932,78 @@ psql_completion(const char *text, int st
         COMPLETE_WITH_LIST(sql_commands);

     /* Else try completions based on matching patterns of previous words */
-    else if ((matches = match_previous_words(text, start, end,
+    else
+    {
+        /*
+         * For now, we have to try the patterns in the order they are stored
+         * (matching the order of switch cases in match_previous_words),
+         * because some of the logic in match_previous_words assumes that
+         * previous matches have been eliminated.  This is fairly
+         * unprincipled, and it is likely that there are undesirable as well
+         * as desirable interactions hidden in the order of the pattern
+         * checks.  TODO: think about a better way to manage that.
+         */
+        for (int tindx = 0; tindx < lengthof(tcpatterns); tindx++)
+        {
+            const TCPattern *tcpat = tcpatterns + tindx;
+            bool        match = false;
+
+            switch (tcpat->kind)
+            {
+                case Match:
+                    match = MatchesArray(false,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case MatchCS:
+                    match = MatchesArray(true,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatch:
+                    match = HeadMatchesArray(false,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatchCS:
+                    match = HeadMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatch:
+                    match = TailMatchesArray(false,
+                                             previous_words_count,
                                              previous_words,
-                                             previous_words_count)) != NULL)
-         /* skip */ ;
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatchCS:
+                    match = TailMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+            }
+            if (match)
+            {
+                matches = match_previous_words(tcpat->id, text, start, end,
+                                               previous_words,
+                                               previous_words_count);
+                if (matches != NULL)
+                    break;
+            }
+        }
+    }

     /*
      * Finally, we look through the list of "things", such as TABLE, INDEX and
      * check if that was the previous word. If so, execute the query to get a
      * list of them.
      */
-    else
+    if (matches == NULL)
     {
         const pgsql_thing_t *wac;

@@ -2000,17 +2062,16 @@ psql_completion(const char *text, int st
  * Returns a matches list, or NULL if no match.
  */
 static char **
-match_previous_words(const char *text, int start, int end,
+match_previous_words(int pattern_id,
+                     const char *text, int start, int end,
                      char **previous_words, int previous_words_count)
 {
     /* This is the variable we'll return. */
     char      **matches = NULL;

-    /* Dummy, to be replaced by switch */
-    if (0)
-        ;
-            break;
-
+    /* Examine the pattern identified by the caller. */
+    switch (pattern_id)
+    {
 /* CREATE */
     /* complete with something you can create */
         case TailMatches("CREATE"):
@@ -5760,10 +5821,12 @@ match_previous_words(const char *text, i
         case TailMatchesCS("\\sv*"):
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
             break;
-        case TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|" "\\ir|\\include_relative|\\o|\\out|"
"\\s|\\w|\\write|\\lo_import"):
+        case
TailMatchesCS("\\cd|\\e|\\edit|\\g|\\gx|\\i|\\include|\\ir|\\include_relative|\\o|\\out|\\s|\\w|\\write|\\lo_import"):
         completion_charp = "\\";
         completion_force_quote = false;
         matches = rl_completion_matches(text, complete_from_files);
+            break;
+    }

     return matches;
 }

Re: Converting tab-complete.c's else-if chain to a switch

От
Andres Freund
Дата:
Hi,

On 2024-07-13 13:16:14 -0400, Tom Lane wrote:
> I wrote:
> > Per discussion elsewhere [1], I've been looking at $SUBJECT.
>
> Here's a finished patchset to perform this change.

Awesome!

> With gcc 8.5.0 I see the time drop from about 3 seconds to about 0.7.  The
> win is less noticeable with clang version 15.0 on a Mac M1: from about 0.7s
> to 0.45s.

Nice!


With gcc 15 I see:

     before   after
-O0  1.2s     0.6s
-O2  10.5s    2.6s
-O3  10.8s    2.8s

Quite a nice win.


> Of course the main point is the hope that it will work at all with MSVC, but
> I'm not in a position to test that.

It does work with just your patches applied - very nice.

The only obvious thing that doesn't is that ctrl-c without a running query
terminates psql - interrupting a running query works without terminating psql,
which is a bit weird. But that seems independent of this series.


> Subject: [PATCH v1 3/5] Install preprocessor infrastructure for
>  tab-complete.c.

Ah - I was wondering how the above would actually work when I read your intro :)


> +tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
> +    $(PERL) $^ --outfile $@
> +

When I first built this with make, I got this error:
make: *** No rule to make target '/home/andres/src/postgresql/src/bin/psql/tab-complete.c', needed by 'tab-complete.o'.
Stop.
 

but that's just a a consequence of the make dependency handling... Removing
the .deps directory fixed it.



> +# The input is a C file that contains some case labels that are not
> +# constants, but look like function calls, for example:
> +#    case Matches("A", "B"):
> +# The function name can be any one of Matches, HeadMatches, TailMatches,
> +# MatchesCS, HeadMatchesCS, or TailMatchesCS.  The argument(s) must be
> +# string literals or macros that expand to string literals or NULL.

Hm, the fact that we are continuing to use the same macros in the switch makes
it a bit painful to edit the .in.c in an editor with compiler-warnings
integration - I see a lot of reported errors ("Expression is not an integer
constant expression") due to case statements not being something that the
normal C switch can handle.

Perhaps we could use a distinct macro for use in the switch, which generates
valid (but nonsensical) code, so we avoid warnings?


> +# These lines are replaced by "case N:" where N is a unique number
> +# for each such case label.  (For convenience, we use the line number
> +# of the case label, although other values would work just as well.)

Hm, using the line number seems to make it a bit harder to compare the output
of the script after making changes to the input. Not the end of the world, but
...



> +tabcomplete = custom_target('tabcomplete',
> +  input: 'tab-complete.in.c',
> +  output: 'tab-complete.c',
> +  command: [
> +    perl, files('gen_tabcomplete.pl'), files('tab-complete.in.c'),
> +    '--outfile', '@OUTPUT@',
> +    '@INPUT@',
> +  ],
> +  install: true,
> +  install_dir: dir_include_server / 'utils',
> +)

I don't think we want to install tab-complete.c?



Greetings,

Andres Freund



Re: Converting tab-complete.c's else-if chain to a switch

От
Tom Lane
Дата:
Andres Freund <andres@anarazel.de> writes:
> On 2024-07-13 13:16:14 -0400, Tom Lane wrote:
>> Of course the main point is the hope that it will work at all with MSVC, but
>> I'm not in a position to test that.

> It does work with just your patches applied - very nice.

Thanks for testing!

> The only obvious thing that doesn't is that ctrl-c without a running query
> terminates psql - interrupting a running query works without terminating psql,
> which is a bit weird. But that seems independent of this series.

Yeah, whatever's going on there is surely orthogonal to this.

>> +# The input is a C file that contains some case labels that are not
>> +# constants, but look like function calls, for example:
>> +#    case Matches("A", "B"):

> Hm, the fact that we are continuing to use the same macros in the switch makes
> it a bit painful to edit the .in.c in an editor with compiler-warnings
> integration - I see a lot of reported errors ("Expression is not an integer
> constant expression") due to case statements not being something that the
> normal C switch can handle.

Ugh, good point.

> Perhaps we could use a distinct macro for use in the switch, which generates
> valid (but nonsensical) code, so we avoid warnings?

Dunno.  I'd explicitly wanted to not have different notations for
patterns implemented in switch labels and those used in ad-hoc tests.

Thinking a bit further outside the box ... maybe the original source
code could be like it is now (or more likely, like it is after 0002,
with the switch-to-be stuff all in a separate function), and we
could make the preprocessor perform the else-to-switch conversion
every time instead of viewing that as a one-time conversion?
That would make it a bit more fragile, but perhaps not impossibly so.

>> +# These lines are replaced by "case N:" where N is a unique number
>> +# for each such case label.  (For convenience, we use the line number
>> +# of the case label, although other values would work just as well.)

> Hm, using the line number seems to make it a bit harder to compare the output
> of the script after making changes to the input.

OK.  I'd thought this choice might be helpful for debugging, but I'm
not wedded to it.  The obvious alternative is to use sequential
numbers for case labels, but wouldn't that also be a bit problematic
if "making changes" includes adding or removing rules?

> I don't think we want to install tab-complete.c?

Good point --- I borrowed that rule from somewhere else, and
failed to study it closely enough.

            regards, tom lane



Re: Converting tab-complete.c's else-if chain to a switch

От
Tom Lane
Дата:
I wrote:
> Andres Freund <andres@anarazel.de> writes:
>> Hm, the fact that we are continuing to use the same macros in the switch makes
>> it a bit painful to edit the .in.c in an editor with compiler-warnings
>> integration - I see a lot of reported errors ("Expression is not an integer
>> constant expression") due to case statements not being something that the
>> normal C switch can handle.

> Ugh, good point.

> Thinking a bit further outside the box ... maybe the original source
> code could be like it is now (or more likely, like it is after 0002,
> with the switch-to-be stuff all in a separate function), and we
> could make the preprocessor perform the else-to-switch conversion
> every time instead of viewing that as a one-time conversion?
> That would make it a bit more fragile, but perhaps not impossibly so.

I modified the preprocessor to work like that, and I like the results
better than what I had.  This version of the patch is set up so that
both the preprocessor input and output files are legal, functional C:
without preprocessing it runs through the else-if chain, but after
preprocessing it uses a loop around the switch.  Maybe that's
overkill, but to do anything less you have to make assumptions about
how smart the editor's compiler helper is.  That doesn't sound like
an arms race that I want to engage in.

0001 attached is the same as the v1 version, but 0002 does a little
more than before, and then 0003 adds the preprocessor.  (I fixed the
bogus install rule, too.)

            regards, tom lane

From f5e02af68a9c5cc3c2035bf453f5de7c960f4404 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 15 Jul 2024 18:45:10 -0400
Subject: [PATCH v2 1/3] Invent "MatchAnyN" option for tab-complete.c's
 Matches/MatchesCS.

This argument matches any number (including zero) of previous words.
Use it to replace the common coding pattern

    if (HeadMatches("A", "B") && TailMatches("X", "Y"))

with

    if (Matches("A", "B", MatchAnyN, "X", "Y"))

In itself this feature doesn't do much except (arguably) make the
code slightly shorter and more readable.  However, it reduces the
number of complex if-condition patterns that have to be dealt with
in the next commits in this series.

While here, restructure the *Matches implementation functions so
that the actual work is done in functions that take a char **
array of pattern strings, and the versions taking variadic arguments
are thin wrappers around the array ones.  This simplifies the
new Matches logic considerably.  At the end of this patch series,
the array functions will be the only ones that are material to
performance, so having the variadic ones be wrappers makes sense.

Discussion: https://postgr.es/m/2208466.1720729502@sss.pgh.pa.us
---
 src/bin/psql/tab-complete.c | 400 +++++++++++++++++++++++-------------
 1 file changed, 258 insertions(+), 142 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index d453e224d9..2878506c30 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1446,10 +1446,12 @@ initialize_readline(void)
  *
  * For readability, callers should use the macros MatchAny and MatchAnyExcept
  * to invoke those two special cases for 'pattern'.  (But '|' and '*' must
- * just be written directly in patterns.)
+ * just be written directly in patterns.)  There is also MatchAnyN, but that
+ * is supported only in Matches/MatchesCS and is not handled here.
  */
 #define MatchAny  NULL
 #define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""

 static bool
 word_matches(const char *pattern,
@@ -1514,107 +1516,196 @@ word_matches(const char *pattern,
 }

 /*
- * Implementation of TailMatches and TailMatchesCS macros: do the last N words
- * in previous_words match the variadic arguments?
+ * Implementation of TailMatches and TailMatchesCS tests: do the last N words
+ * in previous_words match the pattern arguments?
  *
  * The array indexing might look backwards, but remember that
  * previous_words[0] contains the *last* word on the line, not the first.
  */
 static bool
-TailMatchesImpl(bool case_sensitive,
-                int previous_words_count, char **previous_words,
-                int narg,...)
+TailMatchesArray(bool case_sensitive,
+                 int previous_words_count, char **previous_words,
+                 int narg, const char *const *args)
 {
-    va_list        args;
-
     if (previous_words_count < narg)
         return false;

-    va_start(args, narg);
-
     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

         if (!word_matches(arg, previous_words[narg - argno - 1],
                           case_sensitive))
-        {
-            va_end(args);
             return false;
-        }
     }

-    va_end(args);
-
     return true;
 }

 /*
- * Implementation of Matches and MatchesCS macros: do all of the words
- * in previous_words match the variadic arguments?
+ * As above, but the pattern is passed as a variadic argument list.
  */
 static bool
-MatchesImpl(bool case_sensitive,
-            int previous_words_count, char **previous_words,
-            int narg,...)
+TailMatchesImpl(bool case_sensitive,
+                int previous_words_count, char **previous_words,
+                int narg,...)
 {
+    const char *argarray[64];
     va_list        args;

-    if (previous_words_count != narg)
+    Assert(narg <= lengthof(argarray));
+
+    if (previous_words_count < narg)
         return false;

     va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);
+
+    return TailMatchesArray(case_sensitive,
+                            previous_words_count, previous_words,
+                            narg, argarray);
+}
+
+/*
+ * Implementation of HeadMatches and HeadMatchesCS tests: do the first N
+ * words in previous_words match the pattern arguments?
+ */
+static bool
+HeadMatchesArray(bool case_sensitive,
+                 int previous_words_count, char **previous_words,
+                 int narg, const char *const *args)
+{
+    if (previous_words_count < narg)
+        return false;

     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

-        if (!word_matches(arg, previous_words[narg - argno - 1],
+        if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
                           case_sensitive))
-        {
-            va_end(args);
             return false;
-        }
     }

-    va_end(args);
-
     return true;
 }

 /*
- * Implementation of HeadMatches and HeadMatchesCS macros: do the first N
- * words in previous_words match the variadic arguments?
+ * As above, but the pattern is passed as a variadic argument list.
  */
 static bool
 HeadMatchesImpl(bool case_sensitive,
                 int previous_words_count, char **previous_words,
                 int narg,...)
 {
+    const char *argarray[64];
     va_list        args;

+    Assert(narg <= lengthof(argarray));
+
     if (previous_words_count < narg)
         return false;

     va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);

+    return HeadMatchesArray(case_sensitive,
+                            previous_words_count, previous_words,
+                            narg, argarray);
+}
+
+/*
+ * Implementation of Matches and MatchesCS tests: do all of the words
+ * in previous_words match the pattern arguments?
+ *
+ * This supports an additional kind of wildcard: MatchAnyN (represented as "")
+ * can match any number of words, including zero, in the middle of the list.
+ */
+static bool
+MatchesArray(bool case_sensitive,
+             int previous_words_count, char **previous_words,
+             int narg, const char *const *args)
+{
+    int            match_any_pos = -1;
+
+    /* Even with MatchAnyN, there must be at least N-1 words */
+    if (previous_words_count < narg - 1)
+        return false;
+
+    /* Check for MatchAnyN */
     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

-        if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
-                          case_sensitive))
+        if (arg != NULL && arg[0] == '\0')
         {
-            va_end(args);
-            return false;
+            match_any_pos = argno;
+            break;
         }
     }

-    va_end(args);
+    if (match_any_pos < 0)
+    {
+        /* Standard case without MatchAnyN */
+        if (previous_words_count != narg)
+            return false;
+
+        /* Either Head or Tail match will do for the rest */
+        if (!HeadMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              narg, args))
+            return false;
+    }
+    else
+    {
+        /* Match against head */
+        if (!HeadMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              match_any_pos, args))
+            return false;
+
+        /* Match against tail */
+        if (!TailMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              narg - match_any_pos - 1,
+                              args + match_any_pos + 1))
+            return false;
+    }

     return true;
 }

+/*
+ * As above, but the pattern is passed as a variadic argument list.
+ */
+static bool
+MatchesImpl(bool case_sensitive,
+            int previous_words_count, char **previous_words,
+            int narg,...)
+{
+    const char *argarray[64];
+    va_list        args;
+
+    Assert(narg <= lengthof(argarray));
+
+    /* Even with MatchAnyN, there must be at least N-1 words */
+    if (previous_words_count < narg - 1)
+        return false;
+
+    va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);
+
+    return MatchesArray(case_sensitive,
+                        previous_words_count, previous_words,
+                        narg, argarray);
+}
+
 /*
  * Check if the final character of 's' is 'c'.
  */
@@ -1906,9 +1997,9 @@ psql_completion(const char *text, int start, int end)
      * "ALTER PUBLICATION <name> ADD TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
         COMPLETE_WITH("(");
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("WHERE", "("))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
         COMPLETE_WITH_ATTR(prev3_wd);
     else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
              !TailMatches("WHERE", "(*)"))
@@ -1926,7 +2017,7 @@ psql_completion(const char *text, int start, int end)
                                  " AND nspname NOT LIKE E'pg\\\\_%%'",
                                  "CURRENT_SCHEMA");
     /* ALTER PUBLICATION <name> SET ( */
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("SET", "("))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "("))
         COMPLETE_WITH("publish", "publish_via_partition_root");
     /* ALTER SUBSCRIPTION <name> */
     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny))
@@ -1934,36 +2025,34 @@ psql_completion(const char *text, int start, int end)
                       "RENAME TO", "REFRESH PUBLICATION", "SET", "SKIP (",
                       "ADD PUBLICATION", "DROP PUBLICATION");
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("REFRESH", "PUBLICATION"))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"))
         COMPLETE_WITH("WITH (");
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("REFRESH", "PUBLICATION", "WITH", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("))
         COMPLETE_WITH("copy_data");
     /* ALTER SUBSCRIPTION <name> SET */
     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"))
         COMPLETE_WITH("(", "PUBLICATION");
     /* ALTER SUBSCRIPTION <name> SET ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "("))
         COMPLETE_WITH("binary", "disable_on_error", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
                       "streaming", "synchronous_commit");
     /* ALTER SUBSCRIPTION <name> SKIP ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SKIP", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "("))
         COMPLETE_WITH("lsn");
     /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "PUBLICATION"))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "PUBLICATION"))
     {
         /* complete with nothing here as this refers to remote publications */
     }
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
+                     "ADD|DROP|SET", "PUBLICATION", MatchAny))
         COMPLETE_WITH("WITH (");
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
+                     "ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
         COMPLETE_WITH("copy_data", "refresh");

     /* ALTER SCHEMA <name> */
@@ -2726,7 +2815,7 @@ psql_completion(const char *text, int start, int end)
         else if (TailMatches("VERBOSE|SKIP_LOCKED"))
             COMPLETE_WITH("ON", "OFF");
     }
-    else if (HeadMatches("ANALYZE") && TailMatches("("))
+    else if (Matches("ANALYZE", MatchAnyN, "("))
         /* "ANALYZE (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
     else if (HeadMatches("ANALYZE"))
@@ -3178,11 +3267,11 @@ psql_completion(const char *text, int start, int end)
      * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
         COMPLETE_WITH("(");
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE", "("))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
         COMPLETE_WITH_ATTR(prev3_wd);
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE", "(*)"))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "(*)"))
         COMPLETE_WITH(" WITH (");

     /*
@@ -3195,7 +3284,7 @@ psql_completion(const char *text, int start, int end)
     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny) &&
(!ends_with(prev_wd,','))) 
         COMPLETE_WITH("WITH (");
     /* Complete "CREATE PUBLICATION <name> [...] WITH" */
-    else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))
+    else if (Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "("))
         COMPLETE_WITH("publish", "publish_via_partition_root");

 /* CREATE RULE */
@@ -3259,8 +3348,7 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH("ndistinct", "dependencies", "mcv");
     else if (Matches("CREATE", "STATISTICS", MatchAny, "(*)"))
         COMPLETE_WITH("ON");
-    else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
-             TailMatches("FROM"))
+    else if (Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);

 /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
@@ -3358,10 +3446,10 @@ psql_completion(const char *text, int start, int end)
     {
         /* complete with nothing here as this refers to remote publications */
     }
-    else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("PUBLICATION", MatchAny))
+    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny))
         COMPLETE_WITH("WITH (");
     /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
-    else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("WITH", "("))
+    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "("))
         COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
                       "disable_on_error", "enabled", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
@@ -3415,9 +3503,10 @@ psql_completion(const char *text, int start, int end)
     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON") ||
              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("ON", MatchAny))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "ON", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "ON", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
@@ -3426,76 +3515,108 @@ psql_completion(const char *text, int start, int end)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
                           "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("DEFERRABLE") || TailMatches("INITIALLY", "IMMEDIATE|DEFERRED")))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "DEFERRABLE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "DEFERRABLE") ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "INITIALLY", "IMMEDIATE|DEFERRED") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "INITIALLY", "IMMEDIATE|DEFERRED"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("REFERENCING"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING"))
         COMPLETE_WITH("OLD TABLE", "NEW TABLE");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("OLD|NEW", "TABLE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "OLD|NEW", "TABLE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "OLD|NEW", "TABLE"))
         COMPLETE_WITH("AS");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "NEW", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("FOR"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR"))
         COMPLETE_WITH("EACH", "ROW", "STATEMENT");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("FOR", "EACH"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH"))
         COMPLETE_WITH("ROW", "STATEMENT");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("FOR", "EACH", "ROW|STATEMENT") ||
-              TailMatches("FOR", "ROW|STATEMENT")))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH", "ROW|STATEMENT") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH", "ROW|STATEMENT") ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "ROW|STATEMENT") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "ROW|STATEMENT"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("WHEN", "(*)"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "WHEN", "(*)") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "WHEN", "(*)"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
@@ -3507,18 +3628,20 @@ psql_completion(const char *text, int start, int end)
      * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
      * PROCEDURE|FUNCTION.
      */
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("EXECUTE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "EXECUTE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "EXECUTE"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FUNCTION");
         else
             COMPLETE_WITH("PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "EXECUTE", "FUNCTION|PROCEDURE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "EXECUTE", "FUNCTION|PROCEDURE"))
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);

 /* CREATE ROLE,USER,GROUP <name> */
@@ -3651,16 +3774,14 @@ psql_completion(const char *text, int start, int end)
         else
             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
     }
-    else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
-             TailMatches("WHEN|AND", MatchAny, "IN", "(*)"))
+    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "WHEN|AND", MatchAny, "IN", "(*)"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
         else
             COMPLETE_WITH("EXECUTE PROCEDURE");
     }
-    else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
-             TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
+    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"))
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);

 /* DEALLOCATE */
@@ -3685,27 +3806,27 @@ psql_completion(const char *text, int start, int end)
      * provides, like the syntax of DECLARE command in the documentation
      * indicates.
      */
-    else if (HeadMatches("DECLARE") && TailMatches("BINARY"))
+    else if (Matches("DECLARE", MatchAnyN, "BINARY"))
         COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
-    else if (HeadMatches("DECLARE") && TailMatches("ASENSITIVE|INSENSITIVE"))
+    else if (Matches("DECLARE", MatchAnyN, "ASENSITIVE|INSENSITIVE"))
         COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
-    else if (HeadMatches("DECLARE") && TailMatches("SCROLL"))
+    else if (Matches("DECLARE", MatchAnyN, "SCROLL"))
         COMPLETE_WITH("CURSOR");
     /* Complete DECLARE ... [options] NO with SCROLL */
-    else if (HeadMatches("DECLARE") && TailMatches("NO"))
+    else if (Matches("DECLARE", MatchAnyN, "NO"))
         COMPLETE_WITH("SCROLL");

     /*
      * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and
      * FOR
      */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR"))
         COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
     /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT"))
         COMPLETE_WITH("HOLD");
     /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT", "HOLD"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT", "HOLD"))
         COMPLETE_WITH("FOR");

 /* DELETE --- can be inside EXPLAIN, RULE, etc */
@@ -3930,8 +4051,7 @@ psql_completion(const char *text, int start, int end)
                                  "FROM",
                                  "IN");
     /* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
-    else if (HeadMatches("FETCH|MOVE") &&
-             TailMatches("FROM|IN"))
+    else if (Matches("FETCH|MOVE", MatchAnyN, "FROM|IN"))
         COMPLETE_WITH_QUERY(Query_for_list_of_cursors);

 /* FOREIGN DATA WRAPPER */
@@ -3940,8 +4060,7 @@ psql_completion(const char *text, int start, int end)
              !TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
     /* applies in CREATE SERVER */
-    else if (TailMatches("FOREIGN", "DATA", "WRAPPER", MatchAny) &&
-             HeadMatches("CREATE", "SERVER"))
+    else if (Matches("CREATE", "SERVER", MatchAnyN, "FOREIGN", "DATA", "WRAPPER", MatchAny))
         COMPLETE_WITH("OPTIONS");

 /* FOREIGN TABLE */
@@ -4128,44 +4247,43 @@ psql_completion(const char *text, int start, int end)
      * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
      * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
      */
-    else if ((HeadMatches("GRANT") && TailMatches("TO")) ||
-             (HeadMatches("REVOKE") && TailMatches("FROM")))
+    else if (Matches("GRANT", MatchAnyN, "TO") ||
+             Matches("REVOKE", MatchAnyN, "FROM"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);

     /*
      * Offer grant options after that.
      */
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny))
         COMPLETE_WITH("WITH ADMIN",
                       "WITH INHERIT",
                       "WITH SET",
                       "WITH GRANT OPTION",
                       "GRANTED BY");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH"))
         COMPLETE_WITH("ADMIN",
                       "INHERIT",
                       "SET",
                       "GRANT OPTION");
-    else if (HeadMatches("GRANT") &&
-             (TailMatches("TO", MatchAny, "WITH", "ADMIN|INHERIT|SET")))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", "ADMIN|INHERIT|SET"))
         COMPLETE_WITH("OPTION", "TRUE", "FALSE");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION"))
         COMPLETE_WITH("GRANTED BY");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
     /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
-    else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
+    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO|FROM"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
     /* Offer WITH GRANT OPTION after that */
-    else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO", MatchAny))
+    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny))
         COMPLETE_WITH("WITH GRANT OPTION");
     /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
-    else if (HeadMatches("GRANT") && TailMatches("ON", MatchAny, MatchAny))
+    else if (Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny))
         COMPLETE_WITH("TO");
-    else if (HeadMatches("REVOKE") && TailMatches("ON", MatchAny, MatchAny))
+    else if (Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny))
         COMPLETE_WITH("FROM");

     /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
@@ -4280,7 +4398,7 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH("IN", "NOWAIT");

     /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
-    else if (HeadMatches("LOCK") && TailMatches("IN"))
+    else if (Matches("LOCK", MatchAnyN, "IN"))
         COMPLETE_WITH("ACCESS SHARE MODE",
                       "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
                       "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
@@ -4291,16 +4409,16 @@ psql_completion(const char *text, int start, int end)
      * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of lock
      * mode
      */
-    else if (HeadMatches("LOCK") && TailMatches("IN", "ACCESS|ROW"))
+    else if (Matches("LOCK", MatchAnyN, "IN", "ACCESS|ROW"))
         COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");

     /* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
-    else if (HeadMatches("LOCK") && TailMatches("IN", "SHARE"))
+    else if (Matches("LOCK", MatchAnyN, "IN", "SHARE"))
         COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
                       "UPDATE EXCLUSIVE MODE");

     /* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
-    else if (HeadMatches("LOCK") && TailMatches("MODE"))
+    else if (Matches("LOCK", MatchAnyN, "MODE"))
         COMPLETE_WITH("NOWAIT");

 /* MERGE --- can be inside EXPLAIN */
@@ -4605,9 +4723,7 @@ psql_completion(const char *text, int start, int end)
      * Complete ALTER DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER ... SET
      * <name>
      */
-    else if (HeadMatches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER") &&
-             TailMatches("SET", MatchAny) &&
-             !TailMatches("SCHEMA"))
+    else if (Matches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER", MatchAnyN, "SET",
MatchAnyExcept("SCHEMA")))
         COMPLETE_WITH("FROM CURRENT", "TO");

     /*
@@ -4683,13 +4799,13 @@ psql_completion(const char *text, int start, int end)
     else if (Matches("TRUNCATE", "TABLE"))
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
                                         "ONLY");
-    else if (HeadMatches("TRUNCATE") && TailMatches("ONLY"))
+    else if (Matches("TRUNCATE", MatchAnyN, "ONLY"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
     else if (Matches("TRUNCATE", MatchAny) ||
              Matches("TRUNCATE", "TABLE|ONLY", MatchAny) ||
              Matches("TRUNCATE", "TABLE", "ONLY", MatchAny))
         COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
-    else if (HeadMatches("TRUNCATE") && TailMatches("IDENTITY"))
+    else if (Matches("TRUNCATE", MatchAnyN, "IDENTITY"))
         COMPLETE_WITH("CASCADE", "RESTRICT");

 /* UNLISTEN */
@@ -4770,7 +4886,7 @@ psql_completion(const char *text, int start, int end)
         else if (TailMatches("INDEX_CLEANUP"))
             COMPLETE_WITH("AUTO", "ON", "OFF");
     }
-    else if (HeadMatches("VACUUM") && TailMatches("("))
+    else if (Matches("VACUUM", MatchAnyN, "("))
         /* "VACUUM (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
     else if (HeadMatches("VACUUM"))
--
2.43.5

From 2daa2d3125795051917254c055cbbb47e83c347a Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 15 Jul 2024 20:16:07 -0400
Subject: [PATCH v2 2/3] Prepare tab-complete.c for preprocessing.

Separate out psql_completion's giant else-if chain of *Matches
tests into a new function.  Add the infrastructure needed for
table-driven checking of the initial match of each completion
rule.  As-is, however, the code continues to operate as it did.
The new behavior applies only if SWITCH_CONVERSION_APPLIED
is #defined, which it is not here.  (The preprocessor added
in the next patch will add a #define for that.)

The first and last couple of bits of psql_completion are not
based on HeadMatches/TailMatches/Matches tests, so they stay
where they are; they won't become part of the switch.

This patch also fixes up a couple of if-conditions that didn't meet
the conditions enumerated in the comment for match_previous_words().
Those restrictions exist to simplify the preprocessor.
---
 src/bin/psql/tab-complete.c | 344 ++++++++++++++++++++++++++++--------
 1 file changed, 268 insertions(+), 76 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2878506c30..3d1ae3d82d 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -4,6 +4,13 @@
  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
  *
  * src/bin/psql/tab-complete.c
+ *
+ * Note: this will compile and work as-is if SWITCH_CONVERSION_APPLIED
+ * is not defined.  However, the expected usage is that it's first run
+ * through gen_tabcomplete.pl, which will #define that symbol, fill in the
+ * tcpatterns[] array, and convert the else-if chain in match_previous_words()
+ * into a switch.  See comments for match_previous_words() and the header
+ * comment in gen_tabcomplete.pl for more detail.
  */

 /*----------------------------------------------------------------------
@@ -1195,6 +1202,20 @@ static const VersionedQuery Query_for_list_of_subscriptions[] = {
     {0, NULL}
 };

+ /* Known command-starting keywords. */
+static const char *const sql_commands[] = {
+    "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
+    "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
+    "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
+    "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
+    "MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
+    "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
+    "RESET", "REVOKE", "ROLLBACK",
+    "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
+    "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
+    NULL
+};
+
 /*
  * This is a list of all "things" in Pgsql, which can show up after CREATE or
  * DROP; and there is also a query to get a list of them.
@@ -1287,6 +1308,51 @@ static const pgsql_thing_t words_after_create[] = {
     {NULL}                        /* end of list */
 };

+/*
+ * The tcpatterns[] table provides the initial pattern-match rule for each
+ * switch case in match_previous_words().  The contents of the table
+ * are constructed by gen_tabcomplete.pl.
+ */
+
+/* Basic match rules appearing in tcpatterns[].kind */
+enum TCPatternKind
+{
+    Match,
+    MatchCS,
+    HeadMatch,
+    HeadMatchCS,
+    TailMatch,
+    TailMatchCS,
+};
+
+/* Things besides string literals that can appear in tcpatterns[].words */
+#define MatchAny  NULL
+#define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""
+
+/* One entry in tcpatterns[] */
+typedef struct
+{
+    int            id;                /* case label used in match_previous_words */
+    enum TCPatternKind kind;    /* match kind, see above */
+    int            nwords;            /* length of words[] array */
+    const char *const *words;    /* array of match words */
+} TCPattern;
+
+/* Macro emitted by gen_tabcomplete.pl to fill a tcpatterns[] entry */
+#define TCPAT(id, kind, ...) \
+    { (id), (kind), VA_ARGS_NARGS(__VA_ARGS__), \
+      (const char * const []) { __VA_ARGS__ } }
+
+#ifdef SWITCH_CONVERSION_APPLIED
+
+static const TCPattern tcpatterns[] =
+{
+    /* Insert tab-completion pattern data here. */
+};
+
+#endif                            /* SWITCH_CONVERSION_APPLIED */
+
 /* Storage parameters for CREATE TABLE and ALTER TABLE */
 static const char *const table_storage_parameters[] = {
     "autovacuum_analyze_scale_factor",
@@ -1340,6 +1406,10 @@ static const char *const view_optional_parameters[] = {

 /* Forward declaration of functions */
 static char **psql_completion(const char *text, int start, int end);
+static char **match_previous_words(int pattern_id,
+                                   const char *text, int start, int end,
+                                   char **previous_words,
+                                   int previous_words_count);
 static char *create_command_generator(const char *text, int state);
 static char *drop_command_generator(const char *text, int state);
 static char *alter_command_generator(const char *text, int state);
@@ -1449,10 +1519,6 @@ initialize_readline(void)
  * just be written directly in patterns.)  There is also MatchAnyN, but that
  * is supported only in Matches/MatchesCS and is not handled here.
  */
-#define MatchAny  NULL
-#define MatchAnyExcept(pattern)  ("!" pattern)
-#define MatchAnyN ""
-
 static bool
 word_matches(const char *pattern,
              const char *word,
@@ -1787,20 +1853,6 @@ psql_completion(const char *text, int start, int end)
     HeadMatchesImpl(true, previous_words_count, previous_words, \
                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)

-    /* Known command-starting keywords. */
-    static const char *const sql_commands[] = {
-        "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
-        "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
-        "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
-        "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
-        "MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
-        "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
-        "RESET", "REVOKE", "ROLLBACK",
-        "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
-        "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
-        NULL
-    };
-
     /* psql's backslash commands. */
     static const char *const backslash_commands[] = {
         "\\a",
@@ -1887,6 +1939,194 @@ psql_completion(const char *text, int start, int end)
     else if (previous_words_count == 0)
         COMPLETE_WITH_LIST(sql_commands);

+    /* Else try completions based on matching patterns of previous words */
+    else
+    {
+#ifdef SWITCH_CONVERSION_APPLIED
+        /*
+         * If we have transformed match_previous_words into a switch, iterate
+         * through tcpatterns[] to see which pattern ids match.
+         *
+         * For now, we have to try the patterns in the order they are stored
+         * (matching the order of switch cases in match_previous_words),
+         * because some of the logic in match_previous_words assumes that
+         * previous matches have been eliminated.  This is fairly
+         * unprincipled, and it is likely that there are undesirable as well
+         * as desirable interactions hidden in the order of the pattern
+         * checks.  TODO: think about a better way to manage that.
+         */
+        for (int tindx = 0; tindx < lengthof(tcpatterns); tindx++)
+        {
+            const TCPattern *tcpat = tcpatterns + tindx;
+            bool        match = false;
+
+            switch (tcpat->kind)
+            {
+                case Match:
+                    match = MatchesArray(false,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case MatchCS:
+                    match = MatchesArray(true,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatch:
+                    match = HeadMatchesArray(false,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatchCS:
+                    match = HeadMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatch:
+                    match = TailMatchesArray(false,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatchCS:
+                    match = TailMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+            }
+            if (match)
+            {
+                matches = match_previous_words(tcpat->id, text, start, end,
+                                               previous_words,
+                                               previous_words_count);
+                if (matches != NULL)
+                    break;
+            }
+        }
+#else                            /* !SWITCH_CONVERSION_APPLIED */
+        /*
+         * If gen_tabcomplete.pl hasn't been applied to this code, just let
+         * match_previous_words scan through all its patterns.
+         */
+        matches = match_previous_words(0, text, start, end,
+                                       previous_words,
+                                       previous_words_count);
+#endif                            /* SWITCH_CONVERSION_APPLIED */
+    }
+
+    /*
+     * Finally, we look through the list of "things", such as TABLE, INDEX and
+     * check if that was the previous word. If so, execute the query to get a
+     * list of them.
+     */
+    if (matches == NULL)
+    {
+        const pgsql_thing_t *wac;
+
+        for (wac = words_after_create; wac->name != NULL; wac++)
+        {
+            if (pg_strcasecmp(prev_wd, wac->name) == 0)
+            {
+                if (wac->query)
+                    COMPLETE_WITH_QUERY_LIST(wac->query,
+                                             wac->keywords);
+                else if (wac->vquery)
+                    COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
+                                                       wac->keywords);
+                else if (wac->squery)
+                    COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
+                                                              wac->keywords);
+                break;
+            }
+        }
+    }
+
+    /*
+     * 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(true, "");
+        /* Also, prevent Readline from appending stuff to the non-match */
+        rl_completion_append_character = '\0';
+#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
+        rl_completion_suppress_quote = 1;
+#endif
+    }
+
+    /* free storage */
+    free(previous_words);
+    free(words_buffer);
+    free(text_copy);
+    free(completion_ref_object);
+    completion_ref_object = NULL;
+    free(completion_ref_schema);
+    completion_ref_schema = NULL;
+
+    /* Return our Grand List O' Matches */
+    return matches;
+}
+
+/*
+ * Subroutine to try matches based on previous_words.
+ *
+ * This can operate in one of two modes.  As presented, the body of the
+ * function is a long if-else-if chain that sequentially tries each known
+ * match rule.  That works, but some C compilers have trouble with such a long
+ * else-if chain, either taking extra time to compile or failing altogether.
+ * Therefore, we prefer to transform the else-if chain into a switch, and then
+ * each call of this function considers just one match rule (under control of
+ * a loop in psql_completion()).  Compilers tend to be more ready to deal
+ * with many-arm switches than many-arm else-if chains.
+ *
+ * Each if-condition in this function must begin with a call of one of the
+ * functions Matches, HeadMatches, TailMatches, MatchesCS, HeadMatchesCS, or
+ * TailMatchesCS.  The preprocessor gen_tabcomplete.pl strips out those
+ * calls and converts them into entries in tcpatterns[], which are evaluated
+ * by the calling loop in psql_completion().  Successful matches result in
+ * calls to this function with the appropriate pattern_id, causing just the
+ * corresponding switch case to be executed.
+ *
+ * If-conditions in this function can be more complex than a single *Matches
+ * function call in one of two ways (but not both!).  They can be OR's
+ * of *Matches calls, such as
+ *  else if (Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny) ||
+ *           Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny))
+ * or they can be a *Matches call AND'ed with some other condition, e.g.
+ *  else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny) &&
+ *           !ends_with(prev_wd, ','))
+ * The former case is transformed into multiple tcpatterns[] entries and
+ * multiple case labels for the same bit of code.  The latter case is
+ * transformed into a case label and a contained if-statement.
+ *
+ * This is split out of psql_completion() primarily to separate code that
+ * gen_tabcomplete.pl should process from code that it should not, although
+ * doing so also helps to avoid extra indentation of this code.
+ *
+ * Returns a matches list, or NULL if no match.
+ */
+static char **
+match_previous_words(int pattern_id,
+                     const char *text, int start, int end,
+                     char **previous_words, int previous_words_count)
+{
+    /* This is the variable we'll return. */
+    char      **matches = NULL;
+
+    /* Dummy statement, allowing all the match rules to look like "else if" */
+    if (0)
+         /* skip */ ;
+
+    /* gen_tabcomplete.pl begins special processing here */
+    /* BEGIN GEN_TABCOMPLETE */
+
 /* CREATE */
     /* complete with something you can create */
     else if (TailMatches("CREATE"))
@@ -1985,9 +2225,10 @@ psql_completion(const char *text, int start, int end)
     /* ALTER PUBLICATION <name> ADD */
     else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
         COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
-             (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
-              ends_with(prev_wd, ',')))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"))
+        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
+             ends_with(prev_wd, ','))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);

     /*
@@ -2453,8 +2694,7 @@ psql_completion(const char *text, int start, int end)
     }
     /* ALTER TABLE xxx ADD [COLUMN] yyy */
     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny) ||
-             (Matches("ALTER", "TABLE", MatchAny, "ADD", MatchAny) &&
-              !Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
+             Matches("ALTER", "TABLE", MatchAny, "ADD",
MatchAnyExcept("COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
     /* ALTER TABLE xxx ADD CONSTRAINT yyy */
     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny))
@@ -3855,13 +4095,14 @@ psql_completion(const char *text, int start, int end)

"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
                      MatchAny) ||
              Matches("DROP", "ACCESS", "METHOD", MatchAny) ||
-             (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
-              ends_with(prev_wd, ')')) ||
              Matches("DROP", "EVENT", "TRIGGER", MatchAny) ||
              Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
              Matches("DROP", "FOREIGN", "TABLE", MatchAny) ||
              Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
         COMPLETE_WITH("CASCADE", "RESTRICT");
+    else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
+             ends_with(prev_wd, ')'))
+        COMPLETE_WITH("CASCADE", "RESTRICT");

     /* help completing some of the variants */
     else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
@@ -5140,58 +5381,9 @@ psql_completion(const char *text, int start, int end)
         matches = rl_completion_matches(text, complete_from_files);
     }

-    /*
-     * Finally, we look through the list of "things", such as TABLE, INDEX and
-     * check if that was the previous word. If so, execute the query to get a
-     * list of them.
-     */
-    else
-    {
-        const pgsql_thing_t *wac;
-
-        for (wac = words_after_create; wac->name != NULL; wac++)
-        {
-            if (pg_strcasecmp(prev_wd, wac->name) == 0)
-            {
-                if (wac->query)
-                    COMPLETE_WITH_QUERY_LIST(wac->query,
-                                             wac->keywords);
-                else if (wac->vquery)
-                    COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
-                                                       wac->keywords);
-                else if (wac->squery)
-                    COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
-                                                              wac->keywords);
-                break;
-            }
-        }
-    }
-
-    /*
-     * 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(true, "");
-        /* Also, prevent Readline from appending stuff to the non-match */
-        rl_completion_append_character = '\0';
-#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
-        rl_completion_suppress_quote = 1;
-#endif
-    }
-
-    /* free storage */
-    free(previous_words);
-    free(words_buffer);
-    free(text_copy);
-    free(completion_ref_object);
-    completion_ref_object = NULL;
-    free(completion_ref_schema);
-    completion_ref_schema = NULL;
+    /* gen_tabcomplete.pl ends special processing here */
+    /* END GEN_TABCOMPLETE */

-    /* Return our Grand List O' Matches */
     return matches;
 }

--
2.43.5

From 5cad180c54bf8717bbf82970e0210d897d4b9ad6 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 16 Jul 2024 15:58:55 -0400
Subject: [PATCH v2 3/3] Convert tab-complete's long else-if chain to a switch
 statement.

Rename tab-complete.c to tab-complete.in.c, create the preprocessor
script gen_tabcomplete.pl, and install Makefile/meson.build rules
to create tab-complete.c from tab-complete.in.c.  The preprocessor
converts match_previous_words' else-if chain into a switch and
populates tcpatterns[] with the data needed by the driver loop.

The initial HeadMatches/TailMatches/Matches test in each else-if arm
is now performed in a table-driven loop.  Where we get a match, the
corresponding switch case is invoked to see if the match succeeds.
(It might not, if there were additional conditions in the original
else-if test.)

The total number of string comparisons done is just about the
same as it was in the previous coding; however, now that we
have table-driven logic underlying the handmade rules, there
is room to improve that.  For now I haven't bothered because
tab completion is still plenty fast enough for human use.
If the number of rules keeps increasing, we might someday
need to do more in that area.

The immediate benefit of all this thrashing is that C compilers
frequently don't deal well with long else-if chains.  On gcc 8.5.0,
this reduces the compile time of tab-complete.c by about a factor of
four, while MSVC is reported to crash outright with the previous
coding.
---
 src/bin/psql/.gitignore                       |   1 +
 src/bin/psql/Makefile                         |   5 +-
 src/bin/psql/gen_tabcomplete.pl               | 306 ++++++++++++++++++
 src/bin/psql/meson.build                      |  12 +-
 .../{tab-complete.c => tab-complete.in.c}     |   2 +-
 src/tools/pgindent/typedefs.list              |   1 +
 6 files changed, 324 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/psql/gen_tabcomplete.pl
 rename src/bin/psql/{tab-complete.c => tab-complete.in.c} (99%)

diff --git a/src/bin/psql/.gitignore b/src/bin/psql/.gitignore
index 10b6dd3a6b..7272f6e35d 100644
--- a/src/bin/psql/.gitignore
+++ b/src/bin/psql/.gitignore
@@ -1,4 +1,5 @@
 /psqlscanslash.c
+/tab-complete.c
 /sql_help.h
 /sql_help.c
 /psql
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index 374c4c3ab8..62636d2663 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -62,6 +62,9 @@ psqlscanslash.c: FLEXFLAGS = -Cfe -p -p
 psqlscanslash.c: FLEX_NO_BACKUP=yes
 psqlscanslash.c: FLEX_FIX_WARNING=yes

+tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
+    $(PERL) $^ --outfile $@
+
 install: all installdirs
     $(INSTALL_PROGRAM) psql$(X) '$(DESTDIR)$(bindir)/psql$(X)'
     $(INSTALL_DATA) $(srcdir)/psqlrc.sample '$(DESTDIR)$(datadir)/psqlrc.sample'
@@ -75,7 +78,7 @@ uninstall:
 clean distclean:
     rm -f psql$(X) $(OBJS) lex.backup
     rm -rf tmp_check
-    rm -f sql_help.h sql_help.c psqlscanslash.c
+    rm -f sql_help.h sql_help.c psqlscanslash.c tab-complete.c

 check:
     $(prove_check)
diff --git a/src/bin/psql/gen_tabcomplete.pl b/src/bin/psql/gen_tabcomplete.pl
new file mode 100644
index 0000000000..f534ec60da
--- /dev/null
+++ b/src/bin/psql/gen_tabcomplete.pl
@@ -0,0 +1,306 @@
+#----------------------------------------------------------------------
+#
+# gen_tabcomplete.pl
+#    Perl script that transforms tab-complete.in.c to tab-complete.c.
+#
+# This script converts a C else-if chain into a switch statement.
+# The else-if statements to be processed must appear at single-tab-stop
+# indentation between lines reading
+#    /* BEGIN GEN_TABCOMPLETE */
+#    /* END GEN_TABCOMPLETE */
+# The first clause in each if-condition must be a call of one of the
+# functions Matches, HeadMatches, TailMatches, MatchesCS, HeadMatchesCS,
+# or TailMatchesCS.  Its argument(s) must be string literals or macros
+# that expand to string literals or NULL.  These clauses are removed from
+# the code and replaced by "break; case N:", where N is a unique number
+# for each such case label.
+# The BEGIN GEN_TABCOMPLETE and END GEN_TABCOMPLETE lines are replaced
+# by "switch (pattern_id) {" and "}" wrapping to make a valid switch.
+# The remainder of the code is copied verbatim.
+#
+# An if-condition can also be an OR ("||") of several *Matches function
+# calls, or it can be an AND ("&&") of a *Matches call with some other
+# condition.  For example,
+#
+#    else if (HeadMatches("DROP", "DATABASE") && ends_with(prev_wd, '('))
+#
+# will be transformed to
+#
+#        break;
+#    case N:
+#        if (ends_with(prev_wd, '('))
+#
+# In addition, there must be one input line that reads
+#    /* Insert tab-completion pattern data here. */
+# This line is replaced in the output file by macro calls, one for each
+# replaced match condition.  The output for the above example would be
+#    TCPAT(N, HeadMatch, "DROP", "DATABASE"),
+# where N is the replacement case label, "HeadMatch" is the original
+# function name shorn of "es", and the rest are the function arguments.
+# The tab-completion data line must appear before BEGIN GEN_TABCOMPLETE.
+#
+#
+# Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/bin/psql/gen_tabcomplete.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings FATAL => 'all';
+use Getopt::Long;
+
+my $outfile = '';
+
+GetOptions('outfile=s' => \$outfile) or die "$0: wrong arguments";
+
+open my $infh, '<', $ARGV[0]
+  or die "$0: could not open input file '$ARGV[0]': $!\n";
+
+my $outfh;
+if ($outfile)
+{
+    open $outfh, '>', $outfile
+      or die "$0: could not open output file '$outfile': $!\n";
+}
+else
+{
+    $outfh = *STDOUT;
+}
+
+# Opening boilerplate for output file.
+printf $outfh <<EOM;
+/*-------------------------------------------------------------------------
+ *
+ * tab-complete.c
+ *    Preprocessed tab-completion code.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *  ******************************
+ *  *** DO NOT EDIT THIS FILE! ***
+ *  ******************************
+ *
+ *  It has been GENERATED by src/bin/psql/gen_tabcomplete.pl
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define SWITCH_CONVERSION_APPLIED
+
+#line 1 "tab-complete.in.c"
+EOM
+
+# Scan input file until we find the data-replacement label line.
+# Dump what we scan directly into the output file.
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* Insert tab-completion pattern data here\. \*/\s*$|;
+    print $outfh "$_\n";
+}
+
+# $table_data collects what we will substitute for the "pattern data" line.
+my $table_data = '';
+# $output_code collects code that we can't emit till after $table_data.
+my $output_code = '';
+# last case label assigned
+my $last_case_label = 0;
+
+# We emit #line directives to keep the output file's line numbering in sync
+# with the line numbering of the original, to simplify compiler error message
+# reading and debugging.
+my $next_line_no = $. + 1;
+$output_code .= "#line ${next_line_no} \"tab-complete.in.c\"\n";
+
+# Scan until we find the BEGIN GEN_TABCOMPLETE line.
+# Add the scanned code to $output_code verbatim.
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* BEGIN GEN_TABCOMPLETE \*/\s*$|;
+    $output_code .= $_ . "\n";
+}
+
+# Emit the switch-starting lines.
+$output_code .= "\tswitch (pattern_id)\n";
+$output_code .= "\t{\n";
+
+# Keep line numbering in sync.
+$next_line_no = $. + 1;
+$output_code .= "#line ${next_line_no} \"tab-complete.in.c\"\n";
+
+# Scan input file, collecting outer-level else-if conditions
+# to pass to process_else_if.
+# Lines that aren't else-if conditions go to $output_code verbatim.
+# True if we're handling a multiline else-if condition
+my $in_else_if = 0;
+# The accumulated line
+my $else_if_line;
+my $else_if_lineno;
+
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* END GEN_TABCOMPLETE \*/\s*$|;
+    if ($in_else_if)
+    {
+        my $rest = $_;
+        # collapse leading whitespace
+        $rest =~ s/^\s+//;
+        $else_if_line .= ' ' . $rest;
+        # Double right paren is currently sufficient to detect completion
+        if ($else_if_line =~ m/\)\)$/)
+        {
+            process_else_if($else_if_line, $else_if_lineno, $.);
+            $in_else_if = 0;
+        }
+    }
+    elsif (m/^\telse if \(/)
+    {
+        $else_if_line = $_;
+        $else_if_lineno = $.;
+        # Double right paren is currently sufficient to detect completion
+        if ($else_if_line =~ m/\)\)$/)
+        {
+            process_else_if($else_if_line, $else_if_lineno, $.);
+        }
+        else
+        {
+            $in_else_if = 1;
+        }
+    }
+    else
+    {
+        $output_code .= $_ . "\n";
+    }
+}
+
+die "unfinished else-if" if $in_else_if;
+
+# Emit the switch-ending lines.
+$output_code .= "\tbreak;\n";
+$output_code .= "\tdefault:\n";
+$output_code .= "\t\tAssert(false);\n";
+$output_code .= "\t\tbreak;\n";
+$output_code .= "\t}\n";
+
+# Keep line numbering in sync.
+$next_line_no = $. + 1;
+$output_code .= "#line ${next_line_no} \"tab-complete.in.c\"\n";
+
+# Scan the rest, adding it to $output_code verbatim.
+while (<$infh>)
+{
+    chomp;
+    $output_code .= $_ . "\n";
+}
+
+# Dump out the table data.
+print $outfh $table_data;
+
+# Dump out the modified code, and we're done!
+print $outfh $output_code;
+
+close($infh);
+close($outfh);
+
+# Disassemble an else-if condition.
+# Add the generated table-contents macro(s) to $table_data,
+# and add the replacement case label(s) to $output_code.
+sub process_else_if
+{
+    my ($else_if_line, $else_if_lineno, $end_lineno) = @_;
+
+    # Strip the initial "else if (", which we know is there
+    $else_if_line =~ s/^\telse if \(//;
+
+    # Handle OR'd conditions
+    my $isfirst = 1;
+    while ($else_if_line =~
+        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\s*\|\|\s*//
+      )
+    {
+        my $typ = $1;
+        my $cs = $2;
+        my $args = $3;
+        process_match($typ, $cs, $args, $else_if_lineno, $isfirst);
+        $isfirst = 0;
+    }
+
+    # Check for AND'd condition
+    if ($else_if_line =~
+        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\s*&&\s*//
+      )
+    {
+        my $typ = $1;
+        my $cs = $2;
+        my $args = $3;
+        warn
+          "could not process OR/ANDed if condition at line $else_if_lineno\n"
+          if !$isfirst;
+        process_match($typ, $cs, $args, $else_if_lineno, $isfirst);
+        $isfirst = 0;
+        # approximate line positioning of AND'd condition
+        $output_code .= "#line ${end_lineno} \"tab-complete.in.c\"\n";
+        $output_code .= "\tif ($else_if_line\n";
+    }
+    elsif ($else_if_line =~
+        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\)$//
+      )
+    {
+        my $typ = $1;
+        my $cs = $2;
+        my $args = $3;
+        process_match($typ, $cs, $args, $else_if_lineno, $isfirst);
+        $isfirst = 0;
+    }
+    else
+    {
+        warn
+          "could not process if condition at line $else_if_lineno: the rest looks like $else_if_line\n";
+        $output_code .= "\telse if ($else_if_line\n";
+    }
+
+    # Keep line numbering in sync.
+    if ($end_lineno != $else_if_lineno)
+    {
+        my $next_lineno = $end_lineno + 1;
+        $output_code .= "#line ${next_lineno} \"tab-complete.in.c\"\n";
+    }
+}
+
+sub process_match
+{
+    my ($typ, $cs, $args, $lineno, $isfirst) = @_;
+
+    # Assign a new case label only for the first pattern in an OR group.
+    if ($isfirst)
+    {
+        $last_case_label++;
+
+        # We intentionally keep the "break;" and the "case" on one line, so
+        # that they have the same line number as the original "else if"'s
+        # first line.  This avoids misleading displays in, e.g., lcov.
+        $output_code .= "\t";
+        $output_code .= "break; " if $last_case_label > 1;
+        $output_code .= "case $last_case_label:\n";
+    }
+
+    $table_data .=
+      "\tTCPAT(${last_case_label}, ${typ}Match${cs}, ${args}),\n";
+}
+
+
+sub usage
+{
+    die <<EOM;
+Usage: gen_tabcomplete.pl [--outfile/-o <path>] input_file
+    --outfile       Output file (default is stdout)
+
+gen_tabcomplete.pl transforms tab-complete.in.c to tab-complete.c.
+EOM
+}
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f3a6392138..b7c026c900 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -13,7 +13,6 @@ psql_sources = files(
   'prompt.c',
   'startup.c',
   'stringutils.c',
-  'tab-complete.c',
   'variables.c',
 )

@@ -24,6 +23,17 @@ psqlscanslash = custom_target('psqlscanslash',
 generated_sources += psqlscanslash
 psql_sources += psqlscanslash

+tabcomplete = custom_target('tabcomplete',
+  input: 'tab-complete.in.c',
+  output: 'tab-complete.c',
+  command: [
+    perl, files('gen_tabcomplete.pl'), files('tab-complete.in.c'),
+    '--outfile', '@OUTPUT@', '@INPUT@',
+  ],
+)
+generated_sources += tabcomplete
+psql_sources += tabcomplete
+
 sql_help = custom_target('psql_help',
   output: ['sql_help.c', 'sql_help.h'],
   depfile: 'sql_help.dep',
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.in.c
similarity index 99%
rename from src/bin/psql/tab-complete.c
rename to src/bin/psql/tab-complete.in.c
index 3d1ae3d82d..8e87bf306d 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
  *
- * src/bin/psql/tab-complete.c
+ * src/bin/psql/tab-complete.in.c
  *
  * Note: this will compile and work as-is if SWITCH_CONVERSION_APPLIED
  * is not defined.  However, the expected usage is that it's first run
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b4d7f9217c..ca5c476cca 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2803,6 +2803,7 @@ TBMSharedIterator
 TBMSharedIteratorState
 TBMStatus
 TBlockState
+TCPattern
 TIDBitmap
 TM_FailureData
 TM_IndexDelete
--
2.43.5


Re: Converting tab-complete.c's else-if chain to a switch

От
Tom Lane
Дата:
I wrote:
> I modified the preprocessor to work like that, and I like the results
> better than what I had.

I thought this version of the patch would be less subject to merge
conflicts than v1, but it didn't take long for somebody to break it.
Rebased v3 attached -- no nontrivial changes from v2.

I'd like to get this merged soon ...

            regards, tom lane

From 273ec2558fad646a2835bfe795fb12b8e821a12f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 26 Jul 2024 15:41:20 -0400
Subject: [PATCH v3 1/3] Invent "MatchAnyN" option for tab-complete.c's
 Matches/MatchesCS.

This argument matches any number (including zero) of previous words.
Use it to replace the common coding pattern

    if (HeadMatches("A", "B") && TailMatches("X", "Y"))

with

    if (Matches("A", "B", MatchAnyN, "X", "Y"))

In itself this feature doesn't do much except (arguably) make the
code slightly shorter and more readable.  However, it reduces the
number of complex if-condition patterns that have to be dealt with
in the next commits in this series.

While here, restructure the *Matches implementation functions so
that the actual work is done in functions that take a char **
array of pattern strings, and the versions taking variadic arguments
are thin wrappers around the array ones.  This simplifies the
new Matches logic considerably.  At the end of this patch series,
the array functions will be the only ones that are material to
performance, so having the variadic ones be wrappers makes sense.

Discussion: https://postgr.es/m/2208466.1720729502@sss.pgh.pa.us
---
 src/bin/psql/tab-complete.c | 400 +++++++++++++++++++++++-------------
 1 file changed, 258 insertions(+), 142 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 891face1b6..8f68bfc54d 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1446,10 +1446,12 @@ initialize_readline(void)
  *
  * For readability, callers should use the macros MatchAny and MatchAnyExcept
  * to invoke those two special cases for 'pattern'.  (But '|' and '*' must
- * just be written directly in patterns.)
+ * just be written directly in patterns.)  There is also MatchAnyN, but that
+ * is supported only in Matches/MatchesCS and is not handled here.
  */
 #define MatchAny  NULL
 #define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""

 static bool
 word_matches(const char *pattern,
@@ -1514,107 +1516,196 @@ word_matches(const char *pattern,
 }

 /*
- * Implementation of TailMatches and TailMatchesCS macros: do the last N words
- * in previous_words match the variadic arguments?
+ * Implementation of TailMatches and TailMatchesCS tests: do the last N words
+ * in previous_words match the pattern arguments?
  *
  * The array indexing might look backwards, but remember that
  * previous_words[0] contains the *last* word on the line, not the first.
  */
 static bool
-TailMatchesImpl(bool case_sensitive,
-                int previous_words_count, char **previous_words,
-                int narg,...)
+TailMatchesArray(bool case_sensitive,
+                 int previous_words_count, char **previous_words,
+                 int narg, const char *const *args)
 {
-    va_list        args;
-
     if (previous_words_count < narg)
         return false;

-    va_start(args, narg);
-
     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

         if (!word_matches(arg, previous_words[narg - argno - 1],
                           case_sensitive))
-        {
-            va_end(args);
             return false;
-        }
     }

-    va_end(args);
-
     return true;
 }

 /*
- * Implementation of Matches and MatchesCS macros: do all of the words
- * in previous_words match the variadic arguments?
+ * As above, but the pattern is passed as a variadic argument list.
  */
 static bool
-MatchesImpl(bool case_sensitive,
-            int previous_words_count, char **previous_words,
-            int narg,...)
+TailMatchesImpl(bool case_sensitive,
+                int previous_words_count, char **previous_words,
+                int narg,...)
 {
+    const char *argarray[64];
     va_list        args;

-    if (previous_words_count != narg)
+    Assert(narg <= lengthof(argarray));
+
+    if (previous_words_count < narg)
         return false;

     va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);
+
+    return TailMatchesArray(case_sensitive,
+                            previous_words_count, previous_words,
+                            narg, argarray);
+}
+
+/*
+ * Implementation of HeadMatches and HeadMatchesCS tests: do the first N
+ * words in previous_words match the pattern arguments?
+ */
+static bool
+HeadMatchesArray(bool case_sensitive,
+                 int previous_words_count, char **previous_words,
+                 int narg, const char *const *args)
+{
+    if (previous_words_count < narg)
+        return false;

     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

-        if (!word_matches(arg, previous_words[narg - argno - 1],
+        if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
                           case_sensitive))
-        {
-            va_end(args);
             return false;
-        }
     }

-    va_end(args);
-
     return true;
 }

 /*
- * Implementation of HeadMatches and HeadMatchesCS macros: do the first N
- * words in previous_words match the variadic arguments?
+ * As above, but the pattern is passed as a variadic argument list.
  */
 static bool
 HeadMatchesImpl(bool case_sensitive,
                 int previous_words_count, char **previous_words,
                 int narg,...)
 {
+    const char *argarray[64];
     va_list        args;

+    Assert(narg <= lengthof(argarray));
+
     if (previous_words_count < narg)
         return false;

     va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);

+    return HeadMatchesArray(case_sensitive,
+                            previous_words_count, previous_words,
+                            narg, argarray);
+}
+
+/*
+ * Implementation of Matches and MatchesCS tests: do all of the words
+ * in previous_words match the pattern arguments?
+ *
+ * This supports an additional kind of wildcard: MatchAnyN (represented as "")
+ * can match any number of words, including zero, in the middle of the list.
+ */
+static bool
+MatchesArray(bool case_sensitive,
+             int previous_words_count, char **previous_words,
+             int narg, const char *const *args)
+{
+    int            match_any_pos = -1;
+
+    /* Even with MatchAnyN, there must be at least N-1 words */
+    if (previous_words_count < narg - 1)
+        return false;
+
+    /* Check for MatchAnyN */
     for (int argno = 0; argno < narg; argno++)
     {
-        const char *arg = va_arg(args, const char *);
+        const char *arg = args[argno];

-        if (!word_matches(arg, previous_words[previous_words_count - argno - 1],
-                          case_sensitive))
+        if (arg != NULL && arg[0] == '\0')
         {
-            va_end(args);
-            return false;
+            match_any_pos = argno;
+            break;
         }
     }

-    va_end(args);
+    if (match_any_pos < 0)
+    {
+        /* Standard case without MatchAnyN */
+        if (previous_words_count != narg)
+            return false;
+
+        /* Either Head or Tail match will do for the rest */
+        if (!HeadMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              narg, args))
+            return false;
+    }
+    else
+    {
+        /* Match against head */
+        if (!HeadMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              match_any_pos, args))
+            return false;
+
+        /* Match against tail */
+        if (!TailMatchesArray(case_sensitive,
+                              previous_words_count, previous_words,
+                              narg - match_any_pos - 1,
+                              args + match_any_pos + 1))
+            return false;
+    }

     return true;
 }

+/*
+ * As above, but the pattern is passed as a variadic argument list.
+ */
+static bool
+MatchesImpl(bool case_sensitive,
+            int previous_words_count, char **previous_words,
+            int narg,...)
+{
+    const char *argarray[64];
+    va_list        args;
+
+    Assert(narg <= lengthof(argarray));
+
+    /* Even with MatchAnyN, there must be at least N-1 words */
+    if (previous_words_count < narg - 1)
+        return false;
+
+    va_start(args, narg);
+    for (int argno = 0; argno < narg; argno++)
+        argarray[argno] = va_arg(args, const char *);
+    va_end(args);
+
+    return MatchesArray(case_sensitive,
+                        previous_words_count, previous_words,
+                        narg, argarray);
+}
+
 /*
  * Check if the final character of 's' is 'c'.
  */
@@ -1906,9 +1997,9 @@ psql_completion(const char *text, int start, int end)
      * "ALTER PUBLICATION <name> ADD TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
         COMPLETE_WITH("(");
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("WHERE", "("))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
         COMPLETE_WITH_ATTR(prev3_wd);
     else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
              !TailMatches("WHERE", "(*)"))
@@ -1926,7 +2017,7 @@ psql_completion(const char *text, int start, int end)
                                  " AND nspname NOT LIKE E'pg\\\\_%%'",
                                  "CURRENT_SCHEMA");
     /* ALTER PUBLICATION <name> SET ( */
-    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny) && TailMatches("SET", "("))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, MatchAnyN, "SET", "("))
         COMPLETE_WITH("publish", "publish_via_partition_root");
     /* ALTER SUBSCRIPTION <name> */
     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny))
@@ -1934,36 +2025,34 @@ psql_completion(const char *text, int start, int end)
                       "RENAME TO", "REFRESH PUBLICATION", "SET", "SKIP (",
                       "ADD PUBLICATION", "DROP PUBLICATION");
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("REFRESH", "PUBLICATION"))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION"))
         COMPLETE_WITH("WITH (");
     /* ALTER SUBSCRIPTION <name> REFRESH PUBLICATION WITH ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("REFRESH", "PUBLICATION", "WITH", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "REFRESH", "PUBLICATION", "WITH", "("))
         COMPLETE_WITH("copy_data");
     /* ALTER SUBSCRIPTION <name> SET */
     else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, "SET"))
         COMPLETE_WITH("(", "PUBLICATION");
     /* ALTER SUBSCRIPTION <name> SET ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "("))
         COMPLETE_WITH("binary", "disable_on_error", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
                       "streaming", "synchronous_commit", "two_phase");
     /* ALTER SUBSCRIPTION <name> SKIP ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SKIP", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SKIP", "("))
         COMPLETE_WITH("lsn");
     /* ALTER SUBSCRIPTION <name> SET PUBLICATION */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) && TailMatches("SET", "PUBLICATION"))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN, "SET", "PUBLICATION"))
     {
         /* complete with nothing here as this refers to remote publications */
     }
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
+                     "ADD|DROP|SET", "PUBLICATION", MatchAny))
         COMPLETE_WITH("WITH (");
     /* ALTER SUBSCRIPTION <name> ADD|DROP|SET PUBLICATION <name> WITH ( */
-    else if (HeadMatches("ALTER", "SUBSCRIPTION", MatchAny) &&
-             TailMatches("ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
+    else if (Matches("ALTER", "SUBSCRIPTION", MatchAny, MatchAnyN,
+                     "ADD|DROP|SET", "PUBLICATION", MatchAny, "WITH", "("))
         COMPLETE_WITH("copy_data", "refresh");

     /* ALTER SCHEMA <name> */
@@ -2726,7 +2815,7 @@ psql_completion(const char *text, int start, int end)
         else if (TailMatches("VERBOSE|SKIP_LOCKED"))
             COMPLETE_WITH("ON", "OFF");
     }
-    else if (HeadMatches("ANALYZE") && TailMatches("("))
+    else if (Matches("ANALYZE", MatchAnyN, "("))
         /* "ANALYZE (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
     else if (HeadMatches("ANALYZE"))
@@ -3178,11 +3267,11 @@ psql_completion(const char *text, int start, int end)
      * "CREATE PUBLICATION <name> FOR TABLE <name> WHERE (" - complete with
      * table attributes
      */
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE"))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE"))
         COMPLETE_WITH("(");
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE", "("))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "("))
         COMPLETE_WITH_ATTR(prev3_wd);
-    else if (HeadMatches("CREATE", "PUBLICATION", MatchAny) && TailMatches("WHERE", "(*)"))
+    else if (Matches("CREATE", "PUBLICATION", MatchAny, MatchAnyN, "WHERE", "(*)"))
         COMPLETE_WITH(" WITH (");

     /*
@@ -3195,7 +3284,7 @@ psql_completion(const char *text, int start, int end)
     else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLES", "IN", "SCHEMA", MatchAny) &&
(!ends_with(prev_wd,','))) 
         COMPLETE_WITH("WITH (");
     /* Complete "CREATE PUBLICATION <name> [...] WITH" */
-    else if (HeadMatches("CREATE", "PUBLICATION") && TailMatches("WITH", "("))
+    else if (Matches("CREATE", "PUBLICATION", MatchAnyN, "WITH", "("))
         COMPLETE_WITH("publish", "publish_via_partition_root");

 /* CREATE RULE */
@@ -3259,8 +3348,7 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH("ndistinct", "dependencies", "mcv");
     else if (Matches("CREATE", "STATISTICS", MatchAny, "(*)"))
         COMPLETE_WITH("ON");
-    else if (HeadMatches("CREATE", "STATISTICS", MatchAny) &&
-             TailMatches("FROM"))
+    else if (Matches("CREATE", "STATISTICS", MatchAny, MatchAnyN, "FROM"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);

 /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */
@@ -3358,10 +3446,10 @@ psql_completion(const char *text, int start, int end)
     {
         /* complete with nothing here as this refers to remote publications */
     }
-    else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("PUBLICATION", MatchAny))
+    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "PUBLICATION", MatchAny))
         COMPLETE_WITH("WITH (");
     /* Complete "CREATE SUBSCRIPTION <name> ...  WITH ( <opt>" */
-    else if (HeadMatches("CREATE", "SUBSCRIPTION") && TailMatches("WITH", "("))
+    else if (Matches("CREATE", "SUBSCRIPTION", MatchAnyN, "WITH", "("))
         COMPLETE_WITH("binary", "connect", "copy_data", "create_slot",
                       "disable_on_error", "enabled", "failover", "origin",
                       "password_required", "run_as_owner", "slot_name",
@@ -3415,9 +3503,10 @@ psql_completion(const char *text, int start, int end)
     else if (TailMatches("CREATE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON") ||
              TailMatches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAny, "INSTEAD", "OF", MatchAny, "ON"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views);
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("ON", MatchAny))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "ON", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "ON", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
@@ -3426,76 +3515,108 @@ psql_completion(const char *text, int start, int end)
             COMPLETE_WITH("NOT DEFERRABLE", "DEFERRABLE", "INITIALLY",
                           "REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("DEFERRABLE") || TailMatches("INITIALLY", "IMMEDIATE|DEFERRED")))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "DEFERRABLE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "DEFERRABLE") ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "INITIALLY", "IMMEDIATE|DEFERRED") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "INITIALLY", "IMMEDIATE|DEFERRED"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("REFERENCING", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("REFERENCING"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING"))
         COMPLETE_WITH("OLD TABLE", "NEW TABLE");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("OLD|NEW", "TABLE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "OLD|NEW", "TABLE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "OLD|NEW", "TABLE"))
         COMPLETE_WITH("AS");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("NEW TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "NEW", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "NEW", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("OLD TABLE", "FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
-              TailMatches("REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny)))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", "AS", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", "AS", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny) ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "REFERENCING", "OLD|NEW", "TABLE", MatchAny, "OLD|NEW", "TABLE", MatchAny))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("FOR", "WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("FOR"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR"))
         COMPLETE_WITH("EACH", "ROW", "STATEMENT");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("FOR", "EACH"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH"))
         COMPLETE_WITH("ROW", "STATEMENT");
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             (TailMatches("FOR", "EACH", "ROW|STATEMENT") ||
-              TailMatches("FOR", "ROW|STATEMENT")))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH", "ROW|STATEMENT") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "EACH", "ROW|STATEMENT") ||
+             Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "FOR", "ROW|STATEMENT") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "FOR", "ROW|STATEMENT"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("WHEN (", "EXECUTE FUNCTION");
         else
             COMPLETE_WITH("WHEN (", "EXECUTE PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("WHEN", "(*)"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "WHEN", "(*)") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "WHEN", "(*)"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
@@ -3507,18 +3628,20 @@ psql_completion(const char *text, int start, int end)
      * Complete CREATE [ OR REPLACE ] TRIGGER ... EXECUTE with
      * PROCEDURE|FUNCTION.
      */
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("EXECUTE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "EXECUTE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "EXECUTE"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("FUNCTION");
         else
             COMPLETE_WITH("PROCEDURE");
     }
-    else if ((HeadMatches("CREATE", "TRIGGER") ||
-              HeadMatches("CREATE", "OR", "REPLACE", "TRIGGER")) &&
-             TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
+    else if (Matches("CREATE", "TRIGGER", MatchAnyN,
+                     "EXECUTE", "FUNCTION|PROCEDURE") ||
+             Matches("CREATE", "OR", "REPLACE", "TRIGGER", MatchAnyN,
+                     "EXECUTE", "FUNCTION|PROCEDURE"))
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);

 /* CREATE ROLE,USER,GROUP <name> */
@@ -3651,16 +3774,14 @@ psql_completion(const char *text, int start, int end)
         else
             COMPLETE_WITH("WHEN TAG IN (", "EXECUTE PROCEDURE");
     }
-    else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
-             TailMatches("WHEN|AND", MatchAny, "IN", "(*)"))
+    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "WHEN|AND", MatchAny, "IN", "(*)"))
     {
         if (pset.sversion >= 110000)
             COMPLETE_WITH("EXECUTE FUNCTION");
         else
             COMPLETE_WITH("EXECUTE PROCEDURE");
     }
-    else if (HeadMatches("CREATE", "EVENT", "TRIGGER") &&
-             TailMatches("EXECUTE", "FUNCTION|PROCEDURE"))
+    else if (Matches("CREATE", "EVENT", "TRIGGER", MatchAnyN, "EXECUTE", "FUNCTION|PROCEDURE"))
         COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions);

 /* DEALLOCATE */
@@ -3685,27 +3806,27 @@ psql_completion(const char *text, int start, int end)
      * provides, like the syntax of DECLARE command in the documentation
      * indicates.
      */
-    else if (HeadMatches("DECLARE") && TailMatches("BINARY"))
+    else if (Matches("DECLARE", MatchAnyN, "BINARY"))
         COMPLETE_WITH("ASENSITIVE", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
-    else if (HeadMatches("DECLARE") && TailMatches("ASENSITIVE|INSENSITIVE"))
+    else if (Matches("DECLARE", MatchAnyN, "ASENSITIVE|INSENSITIVE"))
         COMPLETE_WITH("SCROLL", "NO SCROLL", "CURSOR");
-    else if (HeadMatches("DECLARE") && TailMatches("SCROLL"))
+    else if (Matches("DECLARE", MatchAnyN, "SCROLL"))
         COMPLETE_WITH("CURSOR");
     /* Complete DECLARE ... [options] NO with SCROLL */
-    else if (HeadMatches("DECLARE") && TailMatches("NO"))
+    else if (Matches("DECLARE", MatchAnyN, "NO"))
         COMPLETE_WITH("SCROLL");

     /*
      * Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and
      * FOR
      */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR"))
         COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
     /* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT"))
         COMPLETE_WITH("HOLD");
     /* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
-    else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT", "HOLD"))
+    else if (Matches("DECLARE", MatchAnyN, "CURSOR", "WITH|WITHOUT", "HOLD"))
         COMPLETE_WITH("FOR");

 /* DELETE --- can be inside EXPLAIN, RULE, etc */
@@ -3930,8 +4051,7 @@ psql_completion(const char *text, int start, int end)
                                  "FROM",
                                  "IN");
     /* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
-    else if (HeadMatches("FETCH|MOVE") &&
-             TailMatches("FROM|IN"))
+    else if (Matches("FETCH|MOVE", MatchAnyN, "FROM|IN"))
         COMPLETE_WITH_QUERY(Query_for_list_of_cursors);

 /* FOREIGN DATA WRAPPER */
@@ -3940,8 +4060,7 @@ psql_completion(const char *text, int start, int end)
              !TailMatches("CREATE", MatchAny, MatchAny, MatchAny))
         COMPLETE_WITH_QUERY(Query_for_list_of_fdws);
     /* applies in CREATE SERVER */
-    else if (TailMatches("FOREIGN", "DATA", "WRAPPER", MatchAny) &&
-             HeadMatches("CREATE", "SERVER"))
+    else if (Matches("CREATE", "SERVER", MatchAnyN, "FOREIGN", "DATA", "WRAPPER", MatchAny))
         COMPLETE_WITH("OPTIONS");

 /* FOREIGN TABLE */
@@ -4128,44 +4247,43 @@ psql_completion(const char *text, int start, int end)
      * Complete "GRANT/REVOKE ... TO/FROM" with username, PUBLIC,
      * CURRENT_ROLE, CURRENT_USER, or SESSION_USER.
      */
-    else if ((HeadMatches("GRANT") && TailMatches("TO")) ||
-             (HeadMatches("REVOKE") && TailMatches("FROM")))
+    else if (Matches("GRANT", MatchAnyN, "TO") ||
+             Matches("REVOKE", MatchAnyN, "FROM"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);

     /*
      * Offer grant options after that.
      */
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny))
         COMPLETE_WITH("WITH ADMIN",
                       "WITH INHERIT",
                       "WITH SET",
                       "WITH GRANT OPTION",
                       "GRANTED BY");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH"))
         COMPLETE_WITH("ADMIN",
                       "INHERIT",
                       "SET",
                       "GRANT OPTION");
-    else if (HeadMatches("GRANT") &&
-             (TailMatches("TO", MatchAny, "WITH", "ADMIN|INHERIT|SET")))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", "ADMIN|INHERIT|SET"))
         COMPLETE_WITH("OPTION", "TRUE", "FALSE");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION"))
         COMPLETE_WITH("GRANTED BY");
-    else if (HeadMatches("GRANT") && TailMatches("TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
+    else if (Matches("GRANT", MatchAnyN, "TO", MatchAny, "WITH", MatchAny, "OPTION", "GRANTED", "BY"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
     /* Complete "ALTER DEFAULT PRIVILEGES ... GRANT/REVOKE ... TO/FROM */
-    else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO|FROM"))
+    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO|FROM"))
         COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_roles,
                                  Keywords_for_list_of_grant_roles);
     /* Offer WITH GRANT OPTION after that */
-    else if (HeadMatches("ALTER", "DEFAULT", "PRIVILEGES") && TailMatches("TO", MatchAny))
+    else if (Matches("ALTER", "DEFAULT", "PRIVILEGES", MatchAnyN, "TO", MatchAny))
         COMPLETE_WITH("WITH GRANT OPTION");
     /* Complete "GRANT/REVOKE ... ON * *" with TO/FROM */
-    else if (HeadMatches("GRANT") && TailMatches("ON", MatchAny, MatchAny))
+    else if (Matches("GRANT", MatchAnyN, "ON", MatchAny, MatchAny))
         COMPLETE_WITH("TO");
-    else if (HeadMatches("REVOKE") && TailMatches("ON", MatchAny, MatchAny))
+    else if (Matches("REVOKE", MatchAnyN, "ON", MatchAny, MatchAny))
         COMPLETE_WITH("FROM");

     /* Complete "GRANT/REVOKE * ON ALL * IN SCHEMA *" with TO/FROM */
@@ -4280,7 +4398,7 @@ psql_completion(const char *text, int start, int end)
         COMPLETE_WITH("IN", "NOWAIT");

     /* Complete LOCK [TABLE] [ONLY] <table> IN with a lock mode */
-    else if (HeadMatches("LOCK") && TailMatches("IN"))
+    else if (Matches("LOCK", MatchAnyN, "IN"))
         COMPLETE_WITH("ACCESS SHARE MODE",
                       "ROW SHARE MODE", "ROW EXCLUSIVE MODE",
                       "SHARE UPDATE EXCLUSIVE MODE", "SHARE MODE",
@@ -4291,16 +4409,16 @@ psql_completion(const char *text, int start, int end)
      * Complete LOCK [TABLE][ONLY] <table> IN ACCESS|ROW with rest of lock
      * mode
      */
-    else if (HeadMatches("LOCK") && TailMatches("IN", "ACCESS|ROW"))
+    else if (Matches("LOCK", MatchAnyN, "IN", "ACCESS|ROW"))
         COMPLETE_WITH("EXCLUSIVE MODE", "SHARE MODE");

     /* Complete LOCK [TABLE] [ONLY] <table> IN SHARE with rest of lock mode */
-    else if (HeadMatches("LOCK") && TailMatches("IN", "SHARE"))
+    else if (Matches("LOCK", MatchAnyN, "IN", "SHARE"))
         COMPLETE_WITH("MODE", "ROW EXCLUSIVE MODE",
                       "UPDATE EXCLUSIVE MODE");

     /* Complete LOCK [TABLE] [ONLY] <table> [IN lockmode MODE] with "NOWAIT" */
-    else if (HeadMatches("LOCK") && TailMatches("MODE"))
+    else if (Matches("LOCK", MatchAnyN, "MODE"))
         COMPLETE_WITH("NOWAIT");

 /* MERGE --- can be inside EXPLAIN */
@@ -4605,9 +4723,7 @@ psql_completion(const char *text, int start, int end)
      * Complete ALTER DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER ... SET
      * <name>
      */
-    else if (HeadMatches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER") &&
-             TailMatches("SET", MatchAny) &&
-             !TailMatches("SCHEMA"))
+    else if (Matches("ALTER", "DATABASE|FUNCTION|PROCEDURE|ROLE|ROUTINE|USER", MatchAnyN, "SET",
MatchAnyExcept("SCHEMA")))
         COMPLETE_WITH("FROM CURRENT", "TO");

     /*
@@ -4683,13 +4799,13 @@ psql_completion(const char *text, int start, int end)
     else if (Matches("TRUNCATE", "TABLE"))
         COMPLETE_WITH_SCHEMA_QUERY_PLUS(Query_for_list_of_truncatables,
                                         "ONLY");
-    else if (HeadMatches("TRUNCATE") && TailMatches("ONLY"))
+    else if (Matches("TRUNCATE", MatchAnyN, "ONLY"))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_truncatables);
     else if (Matches("TRUNCATE", MatchAny) ||
              Matches("TRUNCATE", "TABLE|ONLY", MatchAny) ||
              Matches("TRUNCATE", "TABLE", "ONLY", MatchAny))
         COMPLETE_WITH("RESTART IDENTITY", "CONTINUE IDENTITY", "CASCADE", "RESTRICT");
-    else if (HeadMatches("TRUNCATE") && TailMatches("IDENTITY"))
+    else if (Matches("TRUNCATE", MatchAnyN, "IDENTITY"))
         COMPLETE_WITH("CASCADE", "RESTRICT");

 /* UNLISTEN */
@@ -4770,7 +4886,7 @@ psql_completion(const char *text, int start, int end)
         else if (TailMatches("INDEX_CLEANUP"))
             COMPLETE_WITH("AUTO", "ON", "OFF");
     }
-    else if (HeadMatches("VACUUM") && TailMatches("("))
+    else if (Matches("VACUUM", MatchAnyN, "("))
         /* "VACUUM (" should be caught above, so assume we want columns */
         COMPLETE_WITH_ATTR(prev2_wd);
     else if (HeadMatches("VACUUM"))
--
2.43.5

From 90c2f5b3b126352592a3a33c31cceb3526436246 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 26 Jul 2024 15:47:54 -0400
Subject: [PATCH v3 2/3] Prepare tab-complete.c for preprocessing.

Separate out psql_completion's giant else-if chain of *Matches
tests into a new function.  Add the infrastructure needed for
table-driven checking of the initial match of each completion
rule.  As-is, however, the code continues to operate as it did.
The new behavior applies only if SWITCH_CONVERSION_APPLIED
is #defined, which it is not here.  (The preprocessor added
in the next patch will add a #define for that.)

The first and last couple of bits of psql_completion are not
based on HeadMatches/TailMatches/Matches tests, so they stay
where they are; they won't become part of the switch.

This patch also fixes up a couple of if-conditions that didn't meet
the conditions enumerated in the comment for match_previous_words().
Those restrictions exist to simplify the preprocessor.

Discussion: https://postgr.es/m/2208466.1720729502@sss.pgh.pa.us
---
 src/bin/psql/tab-complete.c      | 344 ++++++++++++++++++++++++-------
 src/tools/pgindent/typedefs.list |   1 +
 2 files changed, 269 insertions(+), 76 deletions(-)

diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8f68bfc54d..cd7640cfec 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -4,6 +4,13 @@
  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
  *
  * src/bin/psql/tab-complete.c
+ *
+ * Note: this will compile and work as-is if SWITCH_CONVERSION_APPLIED
+ * is not defined.  However, the expected usage is that it's first run
+ * through gen_tabcomplete.pl, which will #define that symbol, fill in the
+ * tcpatterns[] array, and convert the else-if chain in match_previous_words()
+ * into a switch.  See comments for match_previous_words() and the header
+ * comment in gen_tabcomplete.pl for more detail.
  */

 /*----------------------------------------------------------------------
@@ -1195,6 +1202,20 @@ static const VersionedQuery Query_for_list_of_subscriptions[] = {
     {0, NULL}
 };

+ /* Known command-starting keywords. */
+static const char *const sql_commands[] = {
+    "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
+    "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
+    "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
+    "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
+    "MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
+    "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
+    "RESET", "REVOKE", "ROLLBACK",
+    "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
+    "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
+    NULL
+};
+
 /*
  * This is a list of all "things" in Pgsql, which can show up after CREATE or
  * DROP; and there is also a query to get a list of them.
@@ -1287,6 +1308,51 @@ static const pgsql_thing_t words_after_create[] = {
     {NULL}                        /* end of list */
 };

+/*
+ * The tcpatterns[] table provides the initial pattern-match rule for each
+ * switch case in match_previous_words().  The contents of the table
+ * are constructed by gen_tabcomplete.pl.
+ */
+
+/* Basic match rules appearing in tcpatterns[].kind */
+enum TCPatternKind
+{
+    Match,
+    MatchCS,
+    HeadMatch,
+    HeadMatchCS,
+    TailMatch,
+    TailMatchCS,
+};
+
+/* Things besides string literals that can appear in tcpatterns[].words */
+#define MatchAny  NULL
+#define MatchAnyExcept(pattern)  ("!" pattern)
+#define MatchAnyN ""
+
+/* One entry in tcpatterns[] */
+typedef struct
+{
+    int            id;                /* case label used in match_previous_words */
+    enum TCPatternKind kind;    /* match kind, see above */
+    int            nwords;            /* length of words[] array */
+    const char *const *words;    /* array of match words */
+} TCPattern;
+
+/* Macro emitted by gen_tabcomplete.pl to fill a tcpatterns[] entry */
+#define TCPAT(id, kind, ...) \
+    { (id), (kind), VA_ARGS_NARGS(__VA_ARGS__), \
+      (const char * const []) { __VA_ARGS__ } }
+
+#ifdef SWITCH_CONVERSION_APPLIED
+
+static const TCPattern tcpatterns[] =
+{
+    /* Insert tab-completion pattern data here. */
+};
+
+#endif                            /* SWITCH_CONVERSION_APPLIED */
+
 /* Storage parameters for CREATE TABLE and ALTER TABLE */
 static const char *const table_storage_parameters[] = {
     "autovacuum_analyze_scale_factor",
@@ -1340,6 +1406,10 @@ static const char *const view_optional_parameters[] = {

 /* Forward declaration of functions */
 static char **psql_completion(const char *text, int start, int end);
+static char **match_previous_words(int pattern_id,
+                                   const char *text, int start, int end,
+                                   char **previous_words,
+                                   int previous_words_count);
 static char *create_command_generator(const char *text, int state);
 static char *drop_command_generator(const char *text, int state);
 static char *alter_command_generator(const char *text, int state);
@@ -1449,10 +1519,6 @@ initialize_readline(void)
  * just be written directly in patterns.)  There is also MatchAnyN, but that
  * is supported only in Matches/MatchesCS and is not handled here.
  */
-#define MatchAny  NULL
-#define MatchAnyExcept(pattern)  ("!" pattern)
-#define MatchAnyN ""
-
 static bool
 word_matches(const char *pattern,
              const char *word,
@@ -1787,20 +1853,6 @@ psql_completion(const char *text, int start, int end)
     HeadMatchesImpl(true, previous_words_count, previous_words, \
                     VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)

-    /* Known command-starting keywords. */
-    static const char *const sql_commands[] = {
-        "ABORT", "ALTER", "ANALYZE", "BEGIN", "CALL", "CHECKPOINT", "CLOSE", "CLUSTER",
-        "COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
-        "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
-        "FETCH", "GRANT", "IMPORT FOREIGN SCHEMA", "INSERT INTO", "LISTEN", "LOAD", "LOCK",
-        "MERGE INTO", "MOVE", "NOTIFY", "PREPARE",
-        "REASSIGN", "REFRESH MATERIALIZED VIEW", "REINDEX", "RELEASE",
-        "RESET", "REVOKE", "ROLLBACK",
-        "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
-        "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
-        NULL
-    };
-
     /* psql's backslash commands. */
     static const char *const backslash_commands[] = {
         "\\a",
@@ -1887,6 +1939,194 @@ psql_completion(const char *text, int start, int end)
     else if (previous_words_count == 0)
         COMPLETE_WITH_LIST(sql_commands);

+    /* Else try completions based on matching patterns of previous words */
+    else
+    {
+#ifdef SWITCH_CONVERSION_APPLIED
+        /*
+         * If we have transformed match_previous_words into a switch, iterate
+         * through tcpatterns[] to see which pattern ids match.
+         *
+         * For now, we have to try the patterns in the order they are stored
+         * (matching the order of switch cases in match_previous_words),
+         * because some of the logic in match_previous_words assumes that
+         * previous matches have been eliminated.  This is fairly
+         * unprincipled, and it is likely that there are undesirable as well
+         * as desirable interactions hidden in the order of the pattern
+         * checks.  TODO: think about a better way to manage that.
+         */
+        for (int tindx = 0; tindx < lengthof(tcpatterns); tindx++)
+        {
+            const TCPattern *tcpat = tcpatterns + tindx;
+            bool        match = false;
+
+            switch (tcpat->kind)
+            {
+                case Match:
+                    match = MatchesArray(false,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case MatchCS:
+                    match = MatchesArray(true,
+                                         previous_words_count,
+                                         previous_words,
+                                         tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatch:
+                    match = HeadMatchesArray(false,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case HeadMatchCS:
+                    match = HeadMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatch:
+                    match = TailMatchesArray(false,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+                case TailMatchCS:
+                    match = TailMatchesArray(true,
+                                             previous_words_count,
+                                             previous_words,
+                                             tcpat->nwords, tcpat->words);
+                    break;
+            }
+            if (match)
+            {
+                matches = match_previous_words(tcpat->id, text, start, end,
+                                               previous_words,
+                                               previous_words_count);
+                if (matches != NULL)
+                    break;
+            }
+        }
+#else                            /* !SWITCH_CONVERSION_APPLIED */
+        /*
+         * If gen_tabcomplete.pl hasn't been applied to this code, just let
+         * match_previous_words scan through all its patterns.
+         */
+        matches = match_previous_words(0, text, start, end,
+                                       previous_words,
+                                       previous_words_count);
+#endif                            /* SWITCH_CONVERSION_APPLIED */
+    }
+
+    /*
+     * Finally, we look through the list of "things", such as TABLE, INDEX and
+     * check if that was the previous word. If so, execute the query to get a
+     * list of them.
+     */
+    if (matches == NULL)
+    {
+        const pgsql_thing_t *wac;
+
+        for (wac = words_after_create; wac->name != NULL; wac++)
+        {
+            if (pg_strcasecmp(prev_wd, wac->name) == 0)
+            {
+                if (wac->query)
+                    COMPLETE_WITH_QUERY_LIST(wac->query,
+                                             wac->keywords);
+                else if (wac->vquery)
+                    COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
+                                                       wac->keywords);
+                else if (wac->squery)
+                    COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
+                                                              wac->keywords);
+                break;
+            }
+        }
+    }
+
+    /*
+     * 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(true, "");
+        /* Also, prevent Readline from appending stuff to the non-match */
+        rl_completion_append_character = '\0';
+#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
+        rl_completion_suppress_quote = 1;
+#endif
+    }
+
+    /* free storage */
+    free(previous_words);
+    free(words_buffer);
+    free(text_copy);
+    free(completion_ref_object);
+    completion_ref_object = NULL;
+    free(completion_ref_schema);
+    completion_ref_schema = NULL;
+
+    /* Return our Grand List O' Matches */
+    return matches;
+}
+
+/*
+ * Subroutine to try matches based on previous_words.
+ *
+ * This can operate in one of two modes.  As presented, the body of the
+ * function is a long if-else-if chain that sequentially tries each known
+ * match rule.  That works, but some C compilers have trouble with such a long
+ * else-if chain, either taking extra time to compile or failing altogether.
+ * Therefore, we prefer to transform the else-if chain into a switch, and then
+ * each call of this function considers just one match rule (under control of
+ * a loop in psql_completion()).  Compilers tend to be more ready to deal
+ * with many-arm switches than many-arm else-if chains.
+ *
+ * Each if-condition in this function must begin with a call of one of the
+ * functions Matches, HeadMatches, TailMatches, MatchesCS, HeadMatchesCS, or
+ * TailMatchesCS.  The preprocessor gen_tabcomplete.pl strips out those
+ * calls and converts them into entries in tcpatterns[], which are evaluated
+ * by the calling loop in psql_completion().  Successful matches result in
+ * calls to this function with the appropriate pattern_id, causing just the
+ * corresponding switch case to be executed.
+ *
+ * If-conditions in this function can be more complex than a single *Matches
+ * function call in one of two ways (but not both!).  They can be OR's
+ * of *Matches calls, such as
+ *  else if (Matches("ALTER", "VIEW", MatchAny, "ALTER", MatchAny) ||
+ *           Matches("ALTER", "VIEW", MatchAny, "ALTER", "COLUMN", MatchAny))
+ * or they can be a *Matches call AND'ed with some other condition, e.g.
+ *  else if (Matches("CREATE", "PUBLICATION", MatchAny, "FOR", "TABLE", MatchAny) &&
+ *           !ends_with(prev_wd, ','))
+ * The former case is transformed into multiple tcpatterns[] entries and
+ * multiple case labels for the same bit of code.  The latter case is
+ * transformed into a case label and a contained if-statement.
+ *
+ * This is split out of psql_completion() primarily to separate code that
+ * gen_tabcomplete.pl should process from code that it should not, although
+ * doing so also helps to avoid extra indentation of this code.
+ *
+ * Returns a matches list, or NULL if no match.
+ */
+static char **
+match_previous_words(int pattern_id,
+                     const char *text, int start, int end,
+                     char **previous_words, int previous_words_count)
+{
+    /* This is the variable we'll return. */
+    char      **matches = NULL;
+
+    /* Dummy statement, allowing all the match rules to look like "else if" */
+    if (0)
+         /* skip */ ;
+
+    /* gen_tabcomplete.pl begins special processing here */
+    /* BEGIN GEN_TABCOMPLETE */
+
 /* CREATE */
     /* complete with something you can create */
     else if (TailMatches("CREATE"))
@@ -1985,9 +2225,10 @@ psql_completion(const char *text, int start, int end)
     /* ALTER PUBLICATION <name> ADD */
     else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD"))
         COMPLETE_WITH("TABLES IN SCHEMA", "TABLE");
-    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") ||
-             (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
-              ends_with(prev_wd, ',')))
+    else if (Matches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE"))
+        COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+    else if (HeadMatches("ALTER", "PUBLICATION", MatchAny, "ADD|SET", "TABLE") &&
+             ends_with(prev_wd, ','))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);

     /*
@@ -2453,8 +2694,7 @@ psql_completion(const char *text, int start, int end)
     }
     /* ALTER TABLE xxx ADD [COLUMN] yyy */
     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN", MatchAny) ||
-             (Matches("ALTER", "TABLE", MatchAny, "ADD", MatchAny) &&
-              !Matches("ALTER", "TABLE", MatchAny, "ADD", "COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
+             Matches("ALTER", "TABLE", MatchAny, "ADD",
MatchAnyExcept("COLUMN|CONSTRAINT|CHECK|UNIQUE|PRIMARY|EXCLUDE|FOREIGN")))
         COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes);
     /* ALTER TABLE xxx ADD CONSTRAINT yyy */
     else if (Matches("ALTER", "TABLE", MatchAny, "ADD", "CONSTRAINT", MatchAny))
@@ -3855,13 +4095,14 @@ psql_completion(const char *text, int start, int end)

"COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW",
                      MatchAny) ||
              Matches("DROP", "ACCESS", "METHOD", MatchAny) ||
-             (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
-              ends_with(prev_wd, ')')) ||
              Matches("DROP", "EVENT", "TRIGGER", MatchAny) ||
              Matches("DROP", "FOREIGN", "DATA", "WRAPPER", MatchAny) ||
              Matches("DROP", "FOREIGN", "TABLE", MatchAny) ||
              Matches("DROP", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
         COMPLETE_WITH("CASCADE", "RESTRICT");
+    else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny, MatchAny) &&
+             ends_with(prev_wd, ')'))
+        COMPLETE_WITH("CASCADE", "RESTRICT");

     /* help completing some of the variants */
     else if (Matches("DROP", "AGGREGATE|FUNCTION|PROCEDURE|ROUTINE", MatchAny))
@@ -5140,58 +5381,9 @@ psql_completion(const char *text, int start, int end)
         matches = rl_completion_matches(text, complete_from_files);
     }

-    /*
-     * Finally, we look through the list of "things", such as TABLE, INDEX and
-     * check if that was the previous word. If so, execute the query to get a
-     * list of them.
-     */
-    else
-    {
-        const pgsql_thing_t *wac;
-
-        for (wac = words_after_create; wac->name != NULL; wac++)
-        {
-            if (pg_strcasecmp(prev_wd, wac->name) == 0)
-            {
-                if (wac->query)
-                    COMPLETE_WITH_QUERY_LIST(wac->query,
-                                             wac->keywords);
-                else if (wac->vquery)
-                    COMPLETE_WITH_VERSIONED_QUERY_LIST(wac->vquery,
-                                                       wac->keywords);
-                else if (wac->squery)
-                    COMPLETE_WITH_VERSIONED_SCHEMA_QUERY_LIST(wac->squery,
-                                                              wac->keywords);
-                break;
-            }
-        }
-    }
-
-    /*
-     * 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(true, "");
-        /* Also, prevent Readline from appending stuff to the non-match */
-        rl_completion_append_character = '\0';
-#ifdef HAVE_RL_COMPLETION_SUPPRESS_QUOTE
-        rl_completion_suppress_quote = 1;
-#endif
-    }
-
-    /* free storage */
-    free(previous_words);
-    free(words_buffer);
-    free(text_copy);
-    free(completion_ref_object);
-    completion_ref_object = NULL;
-    free(completion_ref_schema);
-    completion_ref_schema = NULL;
+    /* gen_tabcomplete.pl ends special processing here */
+    /* END GEN_TABCOMPLETE */

-    /* Return our Grand List O' Matches */
     return matches;
 }

diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index b4d7f9217c..ca5c476cca 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2803,6 +2803,7 @@ TBMSharedIterator
 TBMSharedIteratorState
 TBMStatus
 TBlockState
+TCPattern
 TIDBitmap
 TM_FailureData
 TM_IndexDelete
--
2.43.5

From 7d58f2d9d267830dfb0768d7ac2aeabb0669a219 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 26 Jul 2024 15:53:52 -0400
Subject: [PATCH v3 3/3] Convert tab-complete's long else-if chain to a switch
 statement.

Rename tab-complete.c to tab-complete.in.c, create the preprocessor
script gen_tabcomplete.pl, and install Makefile/meson.build rules
to create tab-complete.c from tab-complete.in.c.  The preprocessor
converts match_previous_words' else-if chain into a switch and
populates tcpatterns[] with the data needed by the driver loop.

The initial HeadMatches/TailMatches/Matches test in each else-if arm
is now performed in a table-driven loop.  Where we get a match, the
corresponding switch case is invoked to see if the match succeeds.
(It might not, if there were additional conditions in the original
else-if test.)

The total number of string comparisons done is just about the
same as it was in the previous coding; however, now that we
have table-driven logic underlying the handmade rules, there
is room to improve that.  For now I haven't bothered because
tab completion is still plenty fast enough for human use.
If the number of rules keeps increasing, we might someday
need to do more in that area.

The immediate benefit of all this thrashing is that C compilers
frequently don't deal well with long else-if chains.  On gcc 8.5.0,
this reduces the compile time of tab-complete.c by about a factor of
four, while MSVC is reported to crash outright with the previous
coding.

Discussion: https://postgr.es/m/2208466.1720729502@sss.pgh.pa.us
---
 src/bin/psql/.gitignore                       |   1 +
 src/bin/psql/Makefile                         |   5 +-
 src/bin/psql/gen_tabcomplete.pl               | 306 ++++++++++++++++++
 src/bin/psql/meson.build                      |  12 +-
 .../{tab-complete.c => tab-complete.in.c}     |   2 +-
 5 files changed, 323 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/psql/gen_tabcomplete.pl
 rename src/bin/psql/{tab-complete.c => tab-complete.in.c} (99%)

diff --git a/src/bin/psql/.gitignore b/src/bin/psql/.gitignore
index 10b6dd3a6b..7272f6e35d 100644
--- a/src/bin/psql/.gitignore
+++ b/src/bin/psql/.gitignore
@@ -1,4 +1,5 @@
 /psqlscanslash.c
+/tab-complete.c
 /sql_help.h
 /sql_help.c
 /psql
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index 374c4c3ab8..62636d2663 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -62,6 +62,9 @@ psqlscanslash.c: FLEXFLAGS = -Cfe -p -p
 psqlscanslash.c: FLEX_NO_BACKUP=yes
 psqlscanslash.c: FLEX_FIX_WARNING=yes

+tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
+    $(PERL) $^ --outfile $@
+
 install: all installdirs
     $(INSTALL_PROGRAM) psql$(X) '$(DESTDIR)$(bindir)/psql$(X)'
     $(INSTALL_DATA) $(srcdir)/psqlrc.sample '$(DESTDIR)$(datadir)/psqlrc.sample'
@@ -75,7 +78,7 @@ uninstall:
 clean distclean:
     rm -f psql$(X) $(OBJS) lex.backup
     rm -rf tmp_check
-    rm -f sql_help.h sql_help.c psqlscanslash.c
+    rm -f sql_help.h sql_help.c psqlscanslash.c tab-complete.c

 check:
     $(prove_check)
diff --git a/src/bin/psql/gen_tabcomplete.pl b/src/bin/psql/gen_tabcomplete.pl
new file mode 100644
index 0000000000..f534ec60da
--- /dev/null
+++ b/src/bin/psql/gen_tabcomplete.pl
@@ -0,0 +1,306 @@
+#----------------------------------------------------------------------
+#
+# gen_tabcomplete.pl
+#    Perl script that transforms tab-complete.in.c to tab-complete.c.
+#
+# This script converts a C else-if chain into a switch statement.
+# The else-if statements to be processed must appear at single-tab-stop
+# indentation between lines reading
+#    /* BEGIN GEN_TABCOMPLETE */
+#    /* END GEN_TABCOMPLETE */
+# The first clause in each if-condition must be a call of one of the
+# functions Matches, HeadMatches, TailMatches, MatchesCS, HeadMatchesCS,
+# or TailMatchesCS.  Its argument(s) must be string literals or macros
+# that expand to string literals or NULL.  These clauses are removed from
+# the code and replaced by "break; case N:", where N is a unique number
+# for each such case label.
+# The BEGIN GEN_TABCOMPLETE and END GEN_TABCOMPLETE lines are replaced
+# by "switch (pattern_id) {" and "}" wrapping to make a valid switch.
+# The remainder of the code is copied verbatim.
+#
+# An if-condition can also be an OR ("||") of several *Matches function
+# calls, or it can be an AND ("&&") of a *Matches call with some other
+# condition.  For example,
+#
+#    else if (HeadMatches("DROP", "DATABASE") && ends_with(prev_wd, '('))
+#
+# will be transformed to
+#
+#        break;
+#    case N:
+#        if (ends_with(prev_wd, '('))
+#
+# In addition, there must be one input line that reads
+#    /* Insert tab-completion pattern data here. */
+# This line is replaced in the output file by macro calls, one for each
+# replaced match condition.  The output for the above example would be
+#    TCPAT(N, HeadMatch, "DROP", "DATABASE"),
+# where N is the replacement case label, "HeadMatch" is the original
+# function name shorn of "es", and the rest are the function arguments.
+# The tab-completion data line must appear before BEGIN GEN_TABCOMPLETE.
+#
+#
+# Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/bin/psql/gen_tabcomplete.pl
+#
+#----------------------------------------------------------------------
+
+use strict;
+use warnings FATAL => 'all';
+use Getopt::Long;
+
+my $outfile = '';
+
+GetOptions('outfile=s' => \$outfile) or die "$0: wrong arguments";
+
+open my $infh, '<', $ARGV[0]
+  or die "$0: could not open input file '$ARGV[0]': $!\n";
+
+my $outfh;
+if ($outfile)
+{
+    open $outfh, '>', $outfile
+      or die "$0: could not open output file '$outfile': $!\n";
+}
+else
+{
+    $outfh = *STDOUT;
+}
+
+# Opening boilerplate for output file.
+printf $outfh <<EOM;
+/*-------------------------------------------------------------------------
+ *
+ * tab-complete.c
+ *    Preprocessed tab-completion code.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * NOTES
+ *  ******************************
+ *  *** DO NOT EDIT THIS FILE! ***
+ *  ******************************
+ *
+ *  It has been GENERATED by src/bin/psql/gen_tabcomplete.pl
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define SWITCH_CONVERSION_APPLIED
+
+#line 1 "tab-complete.in.c"
+EOM
+
+# Scan input file until we find the data-replacement label line.
+# Dump what we scan directly into the output file.
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* Insert tab-completion pattern data here\. \*/\s*$|;
+    print $outfh "$_\n";
+}
+
+# $table_data collects what we will substitute for the "pattern data" line.
+my $table_data = '';
+# $output_code collects code that we can't emit till after $table_data.
+my $output_code = '';
+# last case label assigned
+my $last_case_label = 0;
+
+# We emit #line directives to keep the output file's line numbering in sync
+# with the line numbering of the original, to simplify compiler error message
+# reading and debugging.
+my $next_line_no = $. + 1;
+$output_code .= "#line ${next_line_no} \"tab-complete.in.c\"\n";
+
+# Scan until we find the BEGIN GEN_TABCOMPLETE line.
+# Add the scanned code to $output_code verbatim.
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* BEGIN GEN_TABCOMPLETE \*/\s*$|;
+    $output_code .= $_ . "\n";
+}
+
+# Emit the switch-starting lines.
+$output_code .= "\tswitch (pattern_id)\n";
+$output_code .= "\t{\n";
+
+# Keep line numbering in sync.
+$next_line_no = $. + 1;
+$output_code .= "#line ${next_line_no} \"tab-complete.in.c\"\n";
+
+# Scan input file, collecting outer-level else-if conditions
+# to pass to process_else_if.
+# Lines that aren't else-if conditions go to $output_code verbatim.
+# True if we're handling a multiline else-if condition
+my $in_else_if = 0;
+# The accumulated line
+my $else_if_line;
+my $else_if_lineno;
+
+while (<$infh>)
+{
+    chomp;
+    last if m|^\s*/\* END GEN_TABCOMPLETE \*/\s*$|;
+    if ($in_else_if)
+    {
+        my $rest = $_;
+        # collapse leading whitespace
+        $rest =~ s/^\s+//;
+        $else_if_line .= ' ' . $rest;
+        # Double right paren is currently sufficient to detect completion
+        if ($else_if_line =~ m/\)\)$/)
+        {
+            process_else_if($else_if_line, $else_if_lineno, $.);
+            $in_else_if = 0;
+        }
+    }
+    elsif (m/^\telse if \(/)
+    {
+        $else_if_line = $_;
+        $else_if_lineno = $.;
+        # Double right paren is currently sufficient to detect completion
+        if ($else_if_line =~ m/\)\)$/)
+        {
+            process_else_if($else_if_line, $else_if_lineno, $.);
+        }
+        else
+        {
+            $in_else_if = 1;
+        }
+    }
+    else
+    {
+        $output_code .= $_ . "\n";
+    }
+}
+
+die "unfinished else-if" if $in_else_if;
+
+# Emit the switch-ending lines.
+$output_code .= "\tbreak;\n";
+$output_code .= "\tdefault:\n";
+$output_code .= "\t\tAssert(false);\n";
+$output_code .= "\t\tbreak;\n";
+$output_code .= "\t}\n";
+
+# Keep line numbering in sync.
+$next_line_no = $. + 1;
+$output_code .= "#line ${next_line_no} \"tab-complete.in.c\"\n";
+
+# Scan the rest, adding it to $output_code verbatim.
+while (<$infh>)
+{
+    chomp;
+    $output_code .= $_ . "\n";
+}
+
+# Dump out the table data.
+print $outfh $table_data;
+
+# Dump out the modified code, and we're done!
+print $outfh $output_code;
+
+close($infh);
+close($outfh);
+
+# Disassemble an else-if condition.
+# Add the generated table-contents macro(s) to $table_data,
+# and add the replacement case label(s) to $output_code.
+sub process_else_if
+{
+    my ($else_if_line, $else_if_lineno, $end_lineno) = @_;
+
+    # Strip the initial "else if (", which we know is there
+    $else_if_line =~ s/^\telse if \(//;
+
+    # Handle OR'd conditions
+    my $isfirst = 1;
+    while ($else_if_line =~
+        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\s*\|\|\s*//
+      )
+    {
+        my $typ = $1;
+        my $cs = $2;
+        my $args = $3;
+        process_match($typ, $cs, $args, $else_if_lineno, $isfirst);
+        $isfirst = 0;
+    }
+
+    # Check for AND'd condition
+    if ($else_if_line =~
+        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\s*&&\s*//
+      )
+    {
+        my $typ = $1;
+        my $cs = $2;
+        my $args = $3;
+        warn
+          "could not process OR/ANDed if condition at line $else_if_lineno\n"
+          if !$isfirst;
+        process_match($typ, $cs, $args, $else_if_lineno, $isfirst);
+        $isfirst = 0;
+        # approximate line positioning of AND'd condition
+        $output_code .= "#line ${end_lineno} \"tab-complete.in.c\"\n";
+        $output_code .= "\tif ($else_if_line\n";
+    }
+    elsif ($else_if_line =~
+        s/^(Head|Tail|)Matches(CS|)\((("[^"]*"|MatchAnyExcept\("[^"]*"\)|[A-Za-z,\s])+)\)\)$//
+      )
+    {
+        my $typ = $1;
+        my $cs = $2;
+        my $args = $3;
+        process_match($typ, $cs, $args, $else_if_lineno, $isfirst);
+        $isfirst = 0;
+    }
+    else
+    {
+        warn
+          "could not process if condition at line $else_if_lineno: the rest looks like $else_if_line\n";
+        $output_code .= "\telse if ($else_if_line\n";
+    }
+
+    # Keep line numbering in sync.
+    if ($end_lineno != $else_if_lineno)
+    {
+        my $next_lineno = $end_lineno + 1;
+        $output_code .= "#line ${next_lineno} \"tab-complete.in.c\"\n";
+    }
+}
+
+sub process_match
+{
+    my ($typ, $cs, $args, $lineno, $isfirst) = @_;
+
+    # Assign a new case label only for the first pattern in an OR group.
+    if ($isfirst)
+    {
+        $last_case_label++;
+
+        # We intentionally keep the "break;" and the "case" on one line, so
+        # that they have the same line number as the original "else if"'s
+        # first line.  This avoids misleading displays in, e.g., lcov.
+        $output_code .= "\t";
+        $output_code .= "break; " if $last_case_label > 1;
+        $output_code .= "case $last_case_label:\n";
+    }
+
+    $table_data .=
+      "\tTCPAT(${last_case_label}, ${typ}Match${cs}, ${args}),\n";
+}
+
+
+sub usage
+{
+    die <<EOM;
+Usage: gen_tabcomplete.pl [--outfile/-o <path>] input_file
+    --outfile       Output file (default is stdout)
+
+gen_tabcomplete.pl transforms tab-complete.in.c to tab-complete.c.
+EOM
+}
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f3a6392138..b7c026c900 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -13,7 +13,6 @@ psql_sources = files(
   'prompt.c',
   'startup.c',
   'stringutils.c',
-  'tab-complete.c',
   'variables.c',
 )

@@ -24,6 +23,17 @@ psqlscanslash = custom_target('psqlscanslash',
 generated_sources += psqlscanslash
 psql_sources += psqlscanslash

+tabcomplete = custom_target('tabcomplete',
+  input: 'tab-complete.in.c',
+  output: 'tab-complete.c',
+  command: [
+    perl, files('gen_tabcomplete.pl'), files('tab-complete.in.c'),
+    '--outfile', '@OUTPUT@', '@INPUT@',
+  ],
+)
+generated_sources += tabcomplete
+psql_sources += tabcomplete
+
 sql_help = custom_target('psql_help',
   output: ['sql_help.c', 'sql_help.h'],
   depfile: 'sql_help.dep',
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.in.c
similarity index 99%
rename from src/bin/psql/tab-complete.c
rename to src/bin/psql/tab-complete.in.c
index cd7640cfec..6725e15dcd 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2024, PostgreSQL Global Development Group
  *
- * src/bin/psql/tab-complete.c
+ * src/bin/psql/tab-complete.in.c
  *
  * Note: this will compile and work as-is if SWITCH_CONVERSION_APPLIED
  * is not defined.  However, the expected usage is that it's first run
--
2.43.5