CREATE tab completion

Started by Ken Katoabout 4 years ago8 messages
#1Ken Kato
katouknl@oss.nttdata.com
1 attachment(s)

Hi hackers,

I noticed that there are some tab completions missing for the following
commands:
-CREATE CONVERSION : missing FOR, TO, FROM
-CREATE DOMAIN : missing after AS
-CREATE LANGUAGE : missing after HANDLER
-CREATE SCHEMA : missing AUTHORIZATION, IF NOT EXISTS
-CREATE SEQUENCE : missing AS
-CREATE TRANSFORM : missing after FOR

I made a patch for this, so please have a look.

Best wishes,

--
Ken Kato
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION

Attachments:

create_tab_complete.patchtext/x-diff; name=create_tab_complete.patchDownload
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 4f724e4428..0216b50946 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2572,6 +2572,20 @@ psql_completion(const char *text, int start, int end)
 			COMPLETE_WITH("true", "false");
 	}
 
+	/* CREATE CONVERSION */
+	else if (Matches("CREATE", "CONVERSION", MatchAny))
+		COMPLETE_WITH("FOR");
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny))
+		COMPLETE_WITH("TO");
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny, "TO"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny, "TO", MatchAny))
+		COMPLETE_WITH("FROM");
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny, "TO", MatchAny, "FROM"))
+		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
 	/* CREATE DATABASE */
 	else if (Matches("CREATE", "DATABASE", MatchAny))
 		COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2582,6 +2596,17 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
 
+	/* CREATE DOMAIN */
+	else if (Matches("CREATE", "DOMAIN", MatchAny))
+		COMPLETE_WITH("AS");
+	else if (Matches("CREATE", "DOMAIN", MatchAny, "AS"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+	else if (Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny))
+		COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
+					  "NOT NULL", "NULL", "CHECK (");
+	else if (Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations, NULL);
+
 	/* CREATE EXTENSION */
 	/* Complete with available extensions rather than installed ones. */
 	else if (Matches("CREATE", "EXTENSION"))
@@ -2661,6 +2686,12 @@ psql_completion(const char *text, int start, int end)
 			 !TailMatches("FOR", MatchAny, MatchAny, MatchAny))
 		COMPLETE_WITH("(");
 
+	/* CREATE LANGUAGE */
+	else if (Matches("CREATE", "LANGUAGE", MatchAny))
+		COMPLETE_WITH("HANDLER");
+	else if (Matches("CREATE", "LANGUAGE", MatchAny, "HANDLER", MatchAny))
+		COMPLETE_WITH("INLINE", "VALIDATOR");
+
 	/* CREATE OR REPLACE */
 	else if (Matches("CREATE", "OR"))
 		COMPLETE_WITH("REPLACE");
@@ -2802,11 +2833,23 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
+/* CREATE SCHEMA */
+	else if (Matches("CREATE", "SCHEMA"))
+		COMPLETE_WITH("AUTHORIZATION", "IF NOT EXISTS");
+	else if (Matches("CREATE", "SCHEMA", "IF", "NOT", "EXISTS"))
+	{
+		completion_info_charp = prev2_wd;
+		COMPLETE_WITH_QUERY(Query_for_list_of_schemas
+							" UNION SELECT 'AUTORIZATION'");
+	}
+	else if (Matches("CREATE", "SCHEMA") && TailMatches("AUTHORIZATION"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+
 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	else if (TailMatches("CREATE", "SEQUENCE", MatchAny) ||
 			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
-		COMPLETE_WITH("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");
 	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "NO") ||
 			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
@@ -2882,6 +2925,19 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("CREATE", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
 		COMPLETE_WITH("(");
 
+/* CREATE TRANSFORM */
+	else if (Matches("CREATE", "TRANSFORM"))
+		COMPLETE_WITH("FOR");
+	else if (Matches("CREATE", "TRANSFORM", "FOR"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+	else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny)
+		COMPLETE_WITH("LANGUAGE")
+	else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE")
+	{
+		completion_info_charp = prev2_wd;
+		COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+	}
+
 /* CREATE SUBSCRIPTION */
 	else if (Matches("CREATE", "SUBSCRIPTION", MatchAny))
 		COMPLETE_WITH("CONNECTION");
#2Michael Paquier
michael@paquier.xyz
In reply to: Ken Kato (#1)
Re: CREATE tab completion

On Wed, Nov 17, 2021 at 10:44:44AM +0900, Ken Kato wrote:

I made a patch for this, so please have a look.

+       else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny)
+               COMPLETE_WITH("LANGUAGE")
+       else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE")

Those three lines are wrong, for two different reasons and three
mistakes. You may want to compile your code before sending it :)

"CREATE [TEMP|TEMPORARY] SEQUENCE name AS" could be completed with the
supported types. There are three of them.

+               COMPLETE_WITH_QUERY(Query_for_list_of_schemas
+                                                       " UNION SELECT 'AUTORIZATION'");
Incorrect completion here, s/AUTORIZATION/AUTHORIZATION/.
+       else if (Matches("CREATE", "CONVERSION", MatchAny))
+               COMPLETE_WITH("FOR");
Why didn't you consider DEFAULT for the set of changes with
conversions?
+       else if (Matches("CREATE", "SCHEMA"))
+               COMPLETE_WITH("AUTHORIZATION", "IF NOT EXISTS");
+       else if (Matches("CREATE", "SCHEMA", "IF", "NOT", "EXISTS"))
We don't do any completion for INE or IE in the other object types.
+       /* CREATE LANGUAGE */
+       else if (Matches("CREATE", "LANGUAGE", MatchAny))
+               COMPLETE_WITH("HANDLER");
+       else if (Matches("CREATE", "LANGUAGE", MatchAny, "HANDLER", MatchAny))
+               COMPLETE_WITH("INLINE", "VALIDATOR");
It looks like you forgot the case of "OR REPLACE" here?  This is done
for triggers, for example.
--
Michael
#3Ken Kato
katouknl@oss.nttdata.com
In reply to: Michael Paquier (#2)
1 attachment(s)
Re: CREATE tab completion
+       else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny)
+               COMPLETE_WITH("LANGUAGE")
+       else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, 
"LANGUAGE")

Those three lines are wrong, for two different reasons and three
mistakes. You may want to compile your code before sending it :)

+               COMPLETE_WITH_QUERY(Query_for_list_of_schemas
+                                                       " UNION SELECT
'AUTORIZATION'");
Incorrect completion here, s/AUTORIZATION/AUTHORIZATION/.

Thank you for the comments.

I am sorry for the compile error and a typo. I will make sure to compile
before sending it and double check typos.

"CREATE [TEMP|TEMPORARY] SEQUENCE name AS" could be completed with the
supported types. There are three of them.

For this part, I did the following:
+	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
+			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, 
"AS"))
+		COMPLETE_WITH("smallint", "integer", "bigint");

Am I doing this right? or Are there better ways to do it?

Best wishes,

--
Ken Kato
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION

Attachments:

create_tab_completion.patchtext/x-diff; name=create_tab_completion.patchDownload
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 4f724e4428..efa7bb1f5f 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2572,6 +2572,26 @@ psql_completion(const char *text, int start, int end)
 			COMPLETE_WITH("true", "false");
 	}
 
+	/* CREATE CONVERSION */
+	else if (Matches("CREATE", "CONVERSION", MatchAny) ||
+			 Matches("CREATE", "DEFAULT", "CONVERSION", MatchAny))
+		COMPLETE_WITH("FOR");
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR") ||
+			 Matches("CREATE", "DEFAULT", "CONVERSION", MatchAny, "FOR"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny) ||
+			 Matches("CREATE", "DEFAULT", "CONVERSION", MatchAny, "FOR", MatchAny))
+		COMPLETE_WITH("TO");
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny, "TO") ||
+			 Matches("CREATE", "DEFAULT", "CONVERSION", MatchAny, "FOR", MatchAny, "TO"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny, "TO", MatchAny) ||
+			 Matches("CREATE", "DEFAULT", "CONVERSION", MatchAny, "FOR", MatchAny, "TO", MatchAny))
+		COMPLETE_WITH("FROM");
+	else if (Matches("CREATE", "CONVERSION", MatchAny, "FOR", MatchAny, "TO", MatchAny, "FROM") ||
+			 Matches("CREATE", "DEFAULT", "CONVERSION", MatchAny, "FOR", MatchAny, "TO", MatchAny, "FROM"))
+		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
+
 	/* CREATE DATABASE */
 	else if (Matches("CREATE", "DATABASE", MatchAny))
 		COMPLETE_WITH("OWNER", "TEMPLATE", "ENCODING", "TABLESPACE",
@@ -2582,6 +2602,17 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("CREATE", "DATABASE", MatchAny, "TEMPLATE"))
 		COMPLETE_WITH_QUERY(Query_for_list_of_template_databases);
 
+	/* CREATE DOMAIN */
+	else if (Matches("CREATE", "DOMAIN", MatchAny))
+		COMPLETE_WITH("AS");
+	else if (Matches("CREATE", "DOMAIN", MatchAny, "AS"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+	else if (Matches("CREATE", "DOMAIN", MatchAny, "AS", MatchAny))
+		COMPLETE_WITH("COLLATE", "DEFAULT", "CONSTRAINT",
+					  "NOT NULL", "NULL", "CHECK (");
+	else if (Matches("CREATE", "DOMAIN", MatchAny, "COLLATE"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_collations, NULL);
+
 	/* CREATE EXTENSION */
 	/* Complete with available extensions rather than installed ones. */
 	else if (Matches("CREATE", "EXTENSION"))
@@ -2661,6 +2692,14 @@ psql_completion(const char *text, int start, int end)
 			 !TailMatches("FOR", MatchAny, MatchAny, MatchAny))
 		COMPLETE_WITH("(");
 
+	/* CREATE LANGUAGE */
+	else if (Matches("CREATE", "LANGUAGE", MatchAny) ||
+			 Matches("CREATE", "OR", "REPLACE", "LANGUAGE", MatchAny))
+		COMPLETE_WITH("HANDLER");
+	else if (Matches("CREATE", "LANGUAGE", MatchAny, "HANDLER", MatchAny) ||
+			 Matches("CREATE", "OR", "REPLACE", "LANGUAGE", MatchAny, "HANDLER", MatchAny))
+		COMPLETE_WITH("INLINE", "VALIDATOR");
+
 	/* CREATE OR REPLACE */
 	else if (Matches("CREATE", "OR"))
 		COMPLETE_WITH("REPLACE");
@@ -2802,11 +2841,20 @@ psql_completion(const char *text, int start, int end)
 	else if (TailMatches("AS", "ON", "SELECT|UPDATE|INSERT|DELETE", "TO"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
 
+/* CREATE SCHEMA */
+	else if (Matches("CREATE", "SCHEMA"))
+		COMPLETE_WITH("AUTHORIZATION");
+	else if (Matches("CREATE", "SCHEMA") && TailMatches("AUTHORIZATION"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+
 /* CREATE SEQUENCE --- is allowed inside CREATE SCHEMA, so use TailMatches */
 	else if (TailMatches("CREATE", "SEQUENCE", MatchAny) ||
 			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny))
-		COMPLETE_WITH("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");
+	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
+			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"))
+		COMPLETE_WITH("smallint", "integer", "bigint");
 	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "NO") ||
 			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "NO"))
 		COMPLETE_WITH("MINVALUE", "MAXVALUE", "CYCLE");
@@ -2882,6 +2930,19 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("CREATE", "TEXT", "SEARCH", "CONFIGURATION|DICTIONARY|PARSER|TEMPLATE", MatchAny))
 		COMPLETE_WITH("(");
 
+/* CREATE TRANSFORM */
+	else if (Matches("CREATE", "TRANSFORM"))
+		COMPLETE_WITH("FOR");
+	else if (Matches("CREATE", "TRANSFORM", "FOR"))
+		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_datatypes, NULL);
+	else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny))
+		COMPLETE_WITH("LANGUAGE");
+	else if (Matches("CREATE", "TRANSFORM", "FOR", MatchAny, "LANGUAGE"))
+	{
+		completion_info_charp = prev2_wd;
+		COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+	}
+
 /* CREATE SUBSCRIPTION */
 	else if (Matches("CREATE", "SUBSCRIPTION", MatchAny))
 		COMPLETE_WITH("CONNECTION");
#4Michael Paquier
michael@paquier.xyz
In reply to: Ken Kato (#3)
Re: CREATE tab completion

On Thu, Nov 18, 2021 at 04:25:30PM +0900, Ken Kato wrote:

For this part, I did the following:
+	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
+			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"))
+		COMPLETE_WITH("smallint", "integer", "bigint");

Am I doing this right? or Are there better ways to do it?

That looks fine per se.

+/* CREATE SCHEMA */
+	else if (Matches("CREATE", "SCHEMA"))
+		COMPLETE_WITH("AUTHORIZATION");
+	else if (Matches("CREATE", "SCHEMA") && TailMatches("AUTHORIZATION"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_roles);

The part about CREATE SCHEMA was itching me a bit, until I recalled
this recent thread which has a more complete logic:
/messages/by-id/87im0efqhp.fsf@wibble.ilmari.org

The rest looks good, I'll take care of that in a bit.
--
Michael

#5Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: Michael Paquier (#4)
Re: CREATE tab completion

At Thu, 18 Nov 2021 17:17:25 +0900, Michael Paquier <michael@paquier.xyz> wrote in

On Thu, Nov 18, 2021 at 04:25:30PM +0900, Ken Kato wrote:

For this part, I did the following:
+	else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") ||
+			 TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS"))
+		COMPLETE_WITH("smallint", "integer", "bigint");

Am I doing this right? or Are there better ways to do it?

That looks fine per se.

FWIW, I would be a bit perplexed to see type names suggested in
upper-cases, even if it is acceptable by the parser. If you type-in
the following phrase:

=# type boo<tab>

it is completed to "boolean" but,

=# type BOO<tab>

doesn't respond.

So we could use COMPLETE_WITH_CS instead so that CREATE SEQUENCE
behavess the same way with the existing behavior of TYPE.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#6Michael Paquier
michael@paquier.xyz
In reply to: Kyotaro Horiguchi (#5)
Re: CREATE tab completion

On Thu, Nov 18, 2021 at 05:20:58PM +0900, Kyotaro Horiguchi wrote:

So we could use COMPLETE_WITH_CS instead so that CREATE SEQUENCE
behavess the same way with the existing behavior of TYPE.

Makes sense.

Another issue I have noticed with the patch is that it forgets to
apply quotes for the FROM/TO clauses of CREATE CONVERSION, making the
queries fail. \encoding does not care about that but we do for this
set of DDLs. So I have discarded this part.

One extra issue was in CREATE TRANSFORM, where we should handle "OR
REPLACE" like the others. And a last issue I had was with CREATE
LANGUAGE where we would miss TRUSTED. This last one cannot be
completed, but we'd better allow its completion if we can. That makes
things a bit tense if you add on top of that the handling of "OR
REPLACE".

I have fixed (or discarded) that, and the parts for sequences, domains
and transforms remained. That looked like good enough on its own, so
applied those parts of the patch.
--
Michael

#7Ken Kato
katouknl@oss.nttdata.com
In reply to: Michael Paquier (#6)
Re: CREATE tab completion

I have fixed (or discarded) that, and the parts for sequences, domains
and transforms remained. That looked like good enough on its own, so
applied those parts of the patch.
--
Michael

Thank you very much!

I assume Michael has committed the modified version of the patch.
Therefore, I changed the status to"committed" in Commitfest 2022-01.
https://commitfest.postgresql.org/36/3418/

Best wishes,

--
Ken Kato
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION

#8Michael Paquier
michael@paquier.xyz
In reply to: Ken Kato (#7)
Re: CREATE tab completion

On Fri, Nov 19, 2021 at 04:19:01PM +0900, Ken Kato wrote:

I assume Michael has committed the modified version of the patch.
Therefore, I changed the status to"committed" in Commitfest 2022-01.
https://commitfest.postgresql.org/36/3418/

Thanks, I did not notice that :)
--
Michael