On Tue, 3 Jan 2023 at 12:30, vignesh C <[email protected]> wrote:
>
> The patch does not apply on top of HEAD as in [1], please post a rebased
> patch:
>
This is because 0001 has been committed.
Re-uploading 0002, to keep the CF-bot happy.
Reviewing 0002...
I'm not entirely convinced that the PartialMatches() changes are
really necessary. As far as I can see USING followed by ON doesn't
appear anywhere else in the grammar, and the later completions
involving WHEN [NOT] MATCHED are definitely unique to MERGE.
Nonetheless, I guess it's not a bad thing to check that it really is a
MERGE. Also the new matching function might prove useful for other
cases.
Some more detailed code comments:
I find the name PartialMatches() a little off, since "partial" doesn't
really accurately describe what it's doing. HeadMatches() and
TailMatches() are also partial matches (matching the head and tail
parts). So perhaps MidMatches() would be a better name.
I also found the comment description of PartialMatchesImpl() misleading:
/*
* Implementation of PartialMatches and PartialMatchesCS macros: do parts of
* the words in previous_words match the variadic arguments?
*/
I think a more accurate description would be:
/*
* Implementation of MidMatches and MidMatchesCS macros: do any N consecutive
* words in previous_words match the variadic arguments?
*/
Similarly, instead of:
/* Match N words on the line partially, case-insensitively. */
how about:
/* Match N consecutive words anywhere on the line, case-insensitively. */
In PartialMatchesImpl()'s main loop:
if (previous_words_count - startpos < narg)
{
va_end(args);
return false;
}
couldn't that just be built into the loop's termination clause (i.e.,
loop while startpos <= previous_words_count - narg)?
For the first block of changes using the new function:
else if (PartialMatches("MERGE", "INTO", MatchAny, "USING") ||
PartialMatches("MERGE", "INTO", MatchAny, "AS", MatchAny,
"USING") ||
PartialMatches("MERGE", "INTO", MatchAny, MatchAny, "USING"))
{
/* Complete MERGE INTO ... ON with target table attributes */
if (TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"))
COMPLETE_WITH_ATTR(prev4_wd);
else if (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"))
COMPLETE_WITH_ATTR(prev6_wd);
wouldn't it be simpler to just include "MERGE" in the TailMatches()
arguments, and leave these 3 cases outside the new code block. I.e.:
/* Complete MERGE INTO ... ON with target table attributes */
else if (TailMatches("MERGE", "INTO", MatchAny, "USING", MatchAny, "ON"))
COMPLETE_WITH_ATTR(prev4_wd);
else if (TailMatches("MERGE", "INTO", MatchAny, "AS", MatchAny,
"USING", MatchAny, "AS", MatchAny, "ON"))
COMPLETE_WITH_ATTR(prev8_wd);
else if (TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING",
MatchAny, MatchAny, "ON"))
COMPLETE_WITH_ATTR(prev6_wd);
Regards,
Dean
From 8c7e25e4a2d939a32751bd9a0d487c510ec66191 Mon Sep 17 00:00:00 2001
From: Fujii Masao <[email protected]>
Date: Wed, 21 Sep 2022 13:58:50 +0900
Subject: [PATCH 2/2] psql: Add PartialMatches() macro for better
tab-completion.
---
src/bin/psql/tab-complete.c | 152 +++++++++++++++++++++++++++---------
1 file changed, 113 insertions(+), 39 deletions(-)
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 820f47d23a..0b8c252615 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1582,6 +1582,65 @@ HeadMatchesImpl(bool case_sensitive,
return true;
}
+/*
+ * Implementation of PartialMatches and PartialMatchesCS macros: do parts of
+ * the words in previous_words match the variadic arguments?
+ */
+static bool
+PartialMatchesImpl(bool case_sensitive,
+ int previous_words_count, char **previous_words,
+ int narg,...)
+{
+ va_list args;
+ char *firstarg = NULL;
+
+ if (previous_words_count < narg)
+ return false;
+
+ for (int startpos = 0; startpos < previous_words_count; startpos++)
+ {
+ int argno;
+
+ if (firstarg == NULL)
+ {
+ va_start(args, narg);
+ firstarg = va_arg(args, char *);
+ }
+
+ if (!word_matches(firstarg,
+ previous_words[previous_words_count - startpos - 1],
+ case_sensitive))
+ continue;
+
+ if (previous_words_count - startpos < narg)
+ {
+ va_end(args);
+ return false;
+ }
+
+ for (argno = 1; argno < narg; argno++)
+ {
+ const char *arg = va_arg(args, const char *);
+
+ if (!word_matches(arg,
+ previous_words[previous_words_count - argno - startpos - 1],
+ case_sensitive))
+ break;
+ }
+
+ va_end(args);
+ firstarg = NULL;
+
+ if (argno == narg)
+ return true;
+ }
+
+ if (firstarg != NULL)
+ va_end(args);
+
+ return false;
+}
+
/*
* Check if the final character of 's' is 'c'.
*/
@@ -1663,6 +1722,16 @@ psql_completion(const char *text, int start, int end)
HeadMatchesImpl(true, previous_words_count, previous_words, \
VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+ /* Match N words on the line partially, case-insensitively. */
+#define PartialMatches(...) \
+ PartialMatchesImpl(false, previous_words_count, previous_words, \
+ VA_ARGS_NARGS(__VA_ARGS__), __VA_ARGS__)
+
+ /* Match N words on the line partially, case-sensitively. */
+#define PartialMatchesCS(...) \
+ PartialMatchesImpl(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",
@@ -4108,46 +4177,51 @@ psql_completion(const char *text, int start, int end)
TailMatches("MERGE", "INTO", MatchAny, MatchAny, "USING", MatchAny, MatchAnyExcept("ON|AS")))
COMPLETE_WITH("ON");
- /* Complete MERGE INTO ... ON with target table attributes */
- else if (TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"))
- COMPLETE_WITH_ATTR(prev4_wd);
- else if (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"))
- COMPLETE_WITH_ATTR(prev6_wd);
+ else if (PartialMatches("MERGE", "INTO", MatchAny, "USING") ||
+ PartialMatches("MERGE", "INTO", MatchAny, "AS", MatchAny, "USING") ||
+ PartialMatches("MERGE", "INTO", MatchAny, MatchAny, "USING"))
+ {
+ /* Complete MERGE INTO ... ON with target table attributes */
+ if (TailMatches("INTO", MatchAny, "USING", MatchAny, "ON"))
+ COMPLETE_WITH_ATTR(prev4_wd);
+ else if (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"))
+ COMPLETE_WITH_ATTR(prev6_wd);
- /*
- * 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")))
- 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"))
- COMPLETE_WITH("MATCHED", "NOT MATCHED");
-
- /* Complete ... WHEN [NOT] MATCHED with THEN/AND */
- else if (TailMatches("WHEN", "MATCHED") ||
- TailMatches("WHEN", "NOT", "MATCHED"))
- COMPLETE_WITH("THEN", "AND");
-
- /* Complete ... WHEN MATCHED THEN with UPDATE SET/DELETE/DO NOTHING */
- else if (TailMatches("WHEN", "MATCHED", "THEN"))
- COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
-
- /* Complete ... WHEN NOT MATCHED THEN with INSERT/DO NOTHING */
- else if (TailMatches("WHEN", "NOT", "MATCHED", "THEN"))
- COMPLETE_WITH("INSERT", "DO NOTHING");
+ /*
+ * 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")))
+ 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"))
+ COMPLETE_WITH("MATCHED", "NOT MATCHED");
+
+ /* Complete ... WHEN [NOT] MATCHED with THEN/AND */
+ else if (TailMatches("WHEN", "MATCHED") ||
+ TailMatches("WHEN", "NOT", "MATCHED"))
+ COMPLETE_WITH("THEN", "AND");
+
+ /* Complete ... WHEN MATCHED THEN with UPDATE SET/DELETE/DO NOTHING */
+ else if (TailMatches("WHEN", "MATCHED", "THEN"))
+ COMPLETE_WITH("UPDATE SET", "DELETE", "DO NOTHING");
+
+ /* Complete ... WHEN NOT MATCHED THEN with INSERT/DO NOTHING */
+ else if (TailMatches("WHEN", "NOT", "MATCHED", "THEN"))
+ COMPLETE_WITH("INSERT", "DO NOTHING");
+ }
/* NOTIFY --- can be inside EXPLAIN, RULE, etc */
else if (TailMatches("NOTIFY"))
--
2.37.1