Allow noise semicolons ending psql \sf, \ef, \sv, \ev commands.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 10 Jan 2024 19:20:09 +0000 (14:20 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 10 Jan 2024 19:20:09 +0000 (14:20 -0500)
Many psql backslash commands tolerate trailing semicolons, even
though that's not part of the official syntax.  These did not.
They tried to, by passing semicolon = true to psql_scan_slash_option,
but that function ignored this parameter in OT_WHOLE_LINE mode.
Teach it to do the right thing, and remove the now-duplicative
logic in exec_command_help.

Discussion: https://postgr.es/m/2012251.1704746912@sss.pgh.pa.us

src/bin/psql/command.c
src/bin/psql/psqlscanslash.l
src/test/regress/expected/psql.out
src/test/regress/sql/psql.sql

index eb216b7c09efb45bf9812b22121496ab284dc290..9ad911e28d58d7c3895f42e9f33c6b6c3b3d40a6 100644 (file)
@@ -1640,18 +1640,7 @@ exec_command_help(PsqlScanState scan_state, bool active_branch)
    if (active_branch)
    {
        char       *opt = psql_scan_slash_option(scan_state,
-                                                OT_WHOLE_LINE, NULL, false);
-       size_t      len;
-
-       /* strip any trailing spaces and semicolons */
-       if (opt)
-       {
-           len = strlen(opt);
-           while (len > 0 &&
-                  (isspace((unsigned char) opt[len - 1])
-                   || opt[len - 1] == ';'))
-               opt[--len] = '\0';
-       }
+                                                OT_WHOLE_LINE, NULL, true);
 
        helpSQL(opt, pset.popt.topt.pager);
        free(opt);
@@ -3151,6 +3140,10 @@ ignore_slash_filepipe(PsqlScanState scan_state)
  * This *MUST* be used for inactive-branch processing of any slash command
  * that takes an OT_WHOLE_LINE option.  Otherwise we might consume a different
  * amount of option text in active and inactive cases.
+ *
+ * Note: although callers might pass "semicolon" as either true or false,
+ * we need not duplicate that here, since it doesn't affect the amount of
+ * input text consumed.
  */
 static void
 ignore_slash_whole_line(PsqlScanState scan_state)
index 514977e59dc81fff28b5c50b5fb215f40ef364a6..e1ae8627dbf274c97b4564da64af9014776be10f 100644 (file)
@@ -18,6 +18,8 @@
  */
 #include "postgres_fe.h"
 
+#include <ctype.h>
+
 #include "common.h"
 #include "psqlscanslash.h"
 
@@ -608,7 +610,7 @@ psql_scan_slash_option(PsqlScanState state,
            /* empty arg */
            break;
        case xslasharg:
-           /* Strip any unquoted trailing semi-colons if requested */
+           /* Strip any unquoted trailing semicolons if requested */
            if (semicolon)
            {
                while (unquoted_option_chars-- > 0 &&
@@ -640,7 +642,22 @@ psql_scan_slash_option(PsqlScanState state,
            termPQExpBuffer(&mybuf);
            return NULL;
        case xslashwholeline:
-           /* always okay */
+           /*
+            * In whole-line mode, we interpret semicolon = true as stripping
+            * trailing whitespace as well as semicolons; this gives the
+            * nearest equivalent to what semicolon = true does in normal
+            * mode.  Note there's no concept of quoting in this mode.
+            */
+           if (semicolon)
+           {
+               while (mybuf.len > 0 &&
+                      (mybuf.data[mybuf.len - 1] == ';' ||
+                       (isascii((unsigned char) mybuf.data[mybuf.len - 1]) &&
+                        isspace((unsigned char) mybuf.data[mybuf.len - 1]))))
+               {
+                   mybuf.data[--mybuf.len] = '\0';
+               }
+           }
            break;
        default:
            /* can't get here */
index 5d61e4c7bb9384961f54548fcc0a84298398fb37..4f3fd4642078d7e85f949489fc4588973c69f8b5 100644 (file)
@@ -5323,7 +5323,7 @@ END
          LANGUAGE sql
          IMMUTABLE PARALLEL SAFE STRICT COST 1
 1       RETURN ($2 + $1)
-\sf ts_debug(text)
+\sf ts_debug(text);
 CREATE OR REPLACE FUNCTION pg_catalog.ts_debug(document text, OUT alias text, OUT description text, OUT token text, OUT dictionaries regdictionary[], OUT dictionary regdictionary, OUT lexemes text[])
  RETURNS SETOF record
  LANGUAGE sql
index f199d624d3b5e7e899888d7824cc2fe3ddfa9861..c997106b9f3c47c1d062ea880cf143bfcb9e7185 100644 (file)
@@ -1315,7 +1315,7 @@ drop role regress_psql_user;
 \sf information_schema._pg_index_position
 \sf+ information_schema._pg_index_position
 \sf+ interval_pl_time
-\sf ts_debug(text)
+\sf ts_debug(text);
 \sf+ ts_debug(text)
 
 -- AUTOCOMMIT