On Thu, Jan 7, 2021 at 1:30 PM Fujii Masao <masao.fu...@oss.nttdata.com> wrote:
>
>
>
> On 2021/01/07 12:42, Masahiko Sawada wrote:
> > On Thu, Jan 7, 2021 at 10:59 AM Fujii Masao <masao.fu...@oss.nttdata.com> 
> > wrote:
> >>
> >>
> >>
> >> On 2021/01/07 10:01, Masahiko Sawada wrote:
> >>> On Wed, Jan 6, 2021 at 3:37 PM <shinya11.k...@nttdata.com> wrote:
> >>>>
> >>>>> +#define Query_for_list_of_cursors \
> >>>>> +" SELECT name FROM pg_cursors"\
> >>>>>
> >>>>> This query should be the following?
> >>>>>
> >>>>> " SELECT pg_catalog.quote_ident(name) "\
> >>>>> "   FROM pg_catalog.pg_cursors "\
> >>>>> "  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"
> >>>>>
> >>>>> +/* CLOSE */
> >>>>> +      else if (Matches("CLOSE"))
> >>>>> +              COMPLETE_WITH_QUERY(Query_for_list_of_cursors
> >>>>> +                                                      " UNION ALL 
> >>>>> SELECT 'ALL'");
> >>>>>
> >>>>> "UNION ALL" should be "UNION"?
> >>>>
> >>>> Thank you for your advice, and I corrected them.
> >>>>
> >>>>>> +               COMPLETE_WITH_QUERY(Query_for_list_of_cursors
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'ABSOLUTE'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'BACKWARD'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'FORWARD'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'RELATIVE'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'ALL'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'NEXT'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'PRIOR'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'FIRST'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'LAST'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'FROM'"
> >>>>>> +                                                       " UNION SELECT 
> >>>>>> 'IN'");
> >>>>>>
> >>>>>> This change makes psql unexpectedly output "FROM" and "IN" just after 
> >>>>>> "FETCH".
> >>>>>
> >>>>> I think "FROM" and "IN" can be placed just after "FETCH". According to
> >>>>> the documentation, the direction can be empty. For instance, we can do
> >>>>> like:
> >>>>
> >>>> Thank you!
> >>>>
> >>>>> I've attached the patch improving the tab completion for DECLARE as an
> >>>>> example. What do you think?
> >>>>>
> >>>>> BTW according to the documentation, the options of DECLARE statement
> >>>>> (BINARY, INSENSITIVE, SCROLL, and NO SCROLL) are order-sensitive.
> >>>>>
> >>>>> DECLARE name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
> >>>>>      CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
> >>>>>
> >>>>> But I realized that these options are actually order-insensitive. For
> >>>>> instance, we can declare a cursor like:
> >>>>>
> >>>>> =# declare abc scroll binary cursor for select * from pg_class;
> >>>>> DECLARE CURSOR
> >>>>>
> >>>>> The both parser code and documentation has been unchanged from 2003.
> >>>>> Is it a documentation bug?
> >>>>
> >>>> Thank you for your patch, and it is good.
> >>>> I cannot find the description "(BINARY, INSENSITIVE, SCROLL, and NO 
> >>>> SCROLL) are order-sensitive."
> >>>> I saw "The key words BINARY, INSENSITIVE, and SCROLL can appear in any 
> >>>> order." , according to the documentation.
> >>>
> >>> Thanks, you're right. I missed that sentence. I still don't think the
> >>> syntax of DECLARE statement in the documentation doesn't match its
> >>> implementation but I agree that it's order-insensitive.
> >>>
> >>>> I made a new patch, but the amount of codes was large due to 
> >>>> order-insensitive.
> >>>
> >>> Thank you for updating the patch!
> >>>
> >>> Yeah, I'm also afraid a bit that conditions will exponentially
> >>> increase when a new option is added to DECLARE statement in the
> >>> future. Looking at the parser code for DECLARE statement, we can put
> >>> the same options multiple times (that's also why I don't think it
> >>> matches). For instance,
> >>>
> >>> postgres(1:44758)=# begin;
> >>> BEGIN
> >>> postgres(1:44758)=# declare test binary binary binary cursor for
> >>> select * from pg_class;
> >>> DECLARE CURSOR
> >>>
> >>> So how about simplify the above code as follows?
> >>>
> >>> @@ -3005,8 +3014,23 @@ psql_completion(const char *text, int start, int 
> >>> end)
> >>>       else if (Matches("DECLARE", MatchAny))
> >>>           COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
> >>>                         "CURSOR");
> >>> +   /*
> >>> +    * Complete DECLARE <name> with one of BINARY, INSENSITIVE, SCROLL,
> >>> +    * NO SCROLL, and CURSOR. The tail doesn't match any keywords for
> >>> +    * DECLARE, assume we want options.
> >>> +    */
> >>> +   else if (HeadMatches("DECLARE", MatchAny, "*") &&
> >>> +            TailMatches(MatchAnyExcept("CURSOR|WITH|WITHOUT|HOLD|FOR")))
> >>> +       COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
> >>> +                     "CURSOR");
> >>
> >> This change seems to cause "DECLARE ... CURSOR FOR SELECT <tab>" to
> >> unexpectedly output BINARY, INSENSITIVE, etc.
> >
> > Indeed. Is the following not complete but much better?
>
> Yes, I think that's better.
>
> >
> > @@ -3002,11 +3011,18 @@ psql_completion(const char *text, int start, int 
> > end)
> >                              " UNION SELECT 'ALL'");
> >
> >   /* DECLARE */
> > -   else if (Matches("DECLARE", MatchAny))
> > -       COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
> > -                     "CURSOR");
> > +   /*
> > +   * Complete DECLARE <name> with one of BINARY, INSENSITIVE, SCROLL,
> > +   * NO SCROLL, and CURSOR. If the tail is any one of options, assume we
> > +   * still want options.
> > +   */
> > +   else if (Matches("DECLARE", MatchAny) ||
> > +            TailMatches("BINARY|INSENSITIVE|SCROLL|NO"))
> > +       COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", 
> > "CURSOR");
>
> This change seems to cause "DECLARE ... NO <tab>" to unexpectedly output
> "NO SCROLL". Also this change seems to cause "COPY ... (FORMAT BINARY <tab>"
> to unexpectedly output BINARY, CURSOR, etc.

Oops, I missed the HeadMatches(). Thank you for pointing this out.

I've attached the updated patch including Kato-san's changes. I
think the tab completion support for DECLARE added by this patch
works better.


Regards,

--
Masahiko Sawada
EnterpriseDB:  https://www.enterprisedb.com/
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 9dcab0d2fa..5838319676 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -976,6 +976,11 @@ static const SchemaQuery Query_for_list_of_statistics = {
 "       and pg_catalog.pg_table_is_visible(c2.oid)"\
 "       and c2.relispartition = 'true'"
 
+#define Query_for_list_of_cursors \
+" SELECT pg_catalog.quote_ident(name) "\
+"   FROM pg_catalog.pg_cursors "\
+"  WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s'"
+
 /*
  * These object types were introduced later than our support cutoff of
  * server version 7.4.  We use the VersionedQuery infrastructure so that
@@ -2284,6 +2289,10 @@ psql_completion(const char *text, int start, int end)
 		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures, NULL);
 	else if (Matches("CALL", MatchAny))
 		COMPLETE_WITH("(");
+/* CLOSE */
+	else if (Matches("CLOSE"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_cursors
+							" UNION SELECT 'ALL'");
 /* CLUSTER */
 	else if (Matches("CLUSTER"))
 		COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, "UNION SELECT 'VERBOSE'");
@@ -3002,11 +3011,37 @@ psql_completion(const char *text, int start, int end)
 							" UNION SELECT 'ALL'");
 
 /* DECLARE */
+   /*
+	* Complete DECLARE <name> with one of BINARY, INSENSITIVE, SCROLL, NO SCROLL,
+	* and CURSOR.
+	*/
 	else if (Matches("DECLARE", MatchAny))
 		COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL",
 					  "CURSOR");
+	/* Complete with more options */
+	else if (HeadMatches("DECLARE", MatchAny, "BINARY|INSENSITIVE|SCROLL|NO") &&
+			 TailMatches("BINARY|INSENSITIVE|SCROLL|NO"))
+	{
+		/* Complete DECLARE <name> [options] NO with SCROLL */
+		if (TailMatches("NO"))
+			COMPLETE_WITH("SCROLL");
+		/*
+		 * Complete DECLARE <name> [options] with one of BINARY, INSENSITIVE, SCROLL,
+		 * NO SCROLL, and CURSOR. If the tail is any one of options, assume we
+		 * still want options.
+		 */
+		else if (TailMatches("BINARY|INSENSITIVE|SCROLL"))
+			COMPLETE_WITH("BINARY", "INSENSITIVE", "SCROLL", "NO SCROLL", "CURSOR");
+	}
+	/* Complete DECLARE ... CURSOR with one of WITH HOLD, WITHOUT HOLD, and FOR */
 	else if (HeadMatches("DECLARE") && TailMatches("CURSOR"))
 		COMPLETE_WITH("WITH HOLD", "WITHOUT HOLD", "FOR");
+	/* Complete DECLARE ... CURSOR WITH|WITHOUT with HOLD */
+	else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT"))
+		COMPLETE_WITH("HOLD");
+	/* Complete DECLARE ... CURSOR WITH|WITHOUT HOLD with FOR */
+	else if (HeadMatches("DECLARE") && TailMatches("CURSOR", "WITH|WITHOUT", "HOLD"))
+		COMPLETE_WITH("FOR");
 
 /* DELETE --- can be inside EXPLAIN, RULE, etc */
 	/* ... despite which, only complete DELETE with FROM at start of line */
@@ -3167,15 +3202,31 @@ psql_completion(const char *text, int start, int end)
 
 	/*
 	 * Complete FETCH with one of ABSOLUTE, BACKWARD, FORWARD, RELATIVE, ALL,
-	 * NEXT, PRIOR, FIRST, LAST
+	 * NEXT, PRIOR, FIRST, LAST, FROM, IN, and a list of cursors
 	 */
 	else if (Matches("FETCH|MOVE"))
-		COMPLETE_WITH("ABSOLUTE", "BACKWARD", "FORWARD", "RELATIVE",
-					  "ALL", "NEXT", "PRIOR", "FIRST", "LAST");
+		COMPLETE_WITH_QUERY(Query_for_list_of_cursors
+							" UNION SELECT 'ABSOLUTE'"
+							" UNION SELECT 'BACKWARD'"
+							" UNION SELECT 'FORWARD'"
+							" UNION SELECT 'RELATIVE'"
+							" UNION SELECT 'ALL'"
+							" UNION SELECT 'NEXT'"
+							" UNION SELECT 'PRIOR'"
+							" UNION SELECT 'FIRST'"
+							" UNION SELECT 'LAST'"
+							" UNION SELECT 'FROM'"
+							" UNION SELECT 'IN'");
 
-	/* Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN */
+	/*
+	 * Complete FETCH BACKWARD or FORWARD with one of ALL, FROM, IN,
+	 * and a list of cursors
+	 */
 	else if (Matches("FETCH|MOVE", "BACKWARD|FORWARD"))
-		COMPLETE_WITH("ALL", "FROM", "IN");
+		COMPLETE_WITH_QUERY(Query_for_list_of_cursors
+							" UNION SELECT 'ALL'"
+							" UNION SELECT 'FROM'"
+							" UNION SELECT 'IN'");
 
 	/*
 	 * Complete FETCH <direction> with "FROM" or "IN". These are equivalent,
@@ -3185,7 +3236,13 @@ psql_completion(const char *text, int start, int end)
 	else if (Matches("FETCH|MOVE", "ABSOLUTE|BACKWARD|FORWARD|RELATIVE",
 					 MatchAnyExcept("FROM|IN")) ||
 			 Matches("FETCH|MOVE", "ALL|NEXT|PRIOR|FIRST|LAST"))
-		COMPLETE_WITH("FROM", "IN");
+		COMPLETE_WITH_QUERY(Query_for_list_of_cursors
+							" UNION SELECT 'FROM'"
+							" UNION SELECT 'IN'");
+	/* Complete FETCH <direction> "FROM" or "IN" with a list of cursors */
+	else if (HeadMatches("FETCH|MOVE") &&
+			 TailMatches("FROM|IN"))
+		COMPLETE_WITH_QUERY(Query_for_list_of_cursors);
 
 /* FOREIGN DATA WRAPPER */
 	/* applies in ALTER/DROP FDW and in CREATE SERVER */

Reply via email to