Be more careful about newline-chomping in pgbench.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Sep 2017 21:25:31 +0000 (17:25 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Sep 2017 21:25:31 +0000 (17:25 -0400)
process_backslash_command would drop the last character of the input
command on the assumption that it was a newline.  Given a non newline
terminated input file, this could result in dropping the last character
of the command.  Fix that by doing an actual test that we're removing
a newline.

While at it, allow for Windows newlines (\r\n), and suppress multiple
newlines if any.  I do not think either of those cases really occur,
since (a) we read script files in text mode and (b) the lexer stops
when it hits a newline.  But it's cheap enough and it provides a
stronger guarantee about what the result string looks like.

This is just cosmetic, I think, since the possibly-overly-chomped
line was only used for display not for further processing.  So
it doesn't seem necessary to back-patch.

Fabien Coelho, reviewed by Nikolay Shaplov, whacked around a bit by me

Discussion: https://postgr.es/m/alpine.DEB.2.20.1704171422500.4025@lancre

src/bin/pgbench/exprscan.l
src/bin/pgbench/pgbench.c
src/bin/pgbench/pgbench.h

index 1862fe40302b25cba5cd6e13c7e0a34a00748bae..9bf6d237f570c3dcf0078ee22f827a0a9b6adac0 100644 (file)
@@ -197,7 +197,6 @@ expr_yyerror_more(yyscan_t yyscanner, const char *message, const char *more)
    int         error_detection_offset = expr_scanner_offset(state) - 1;
    YYSTYPE     lval;
    char       *full_line;
-   size_t      l;
 
    /*
     * While parsing an expression, we may not have collected the whole line
@@ -210,13 +209,11 @@ expr_yyerror_more(yyscan_t yyscanner, const char *message, const char *more)
             /* skip */ ;
    }
 
+   /* Extract the line, trimming trailing newline if any */
    full_line = expr_scanner_get_substring(state,
                                           expr_start_offset,
-                                          expr_scanner_offset(state));
-   /* Trim trailing newline if any */
-   l = strlen(full_line);
-   while (l > 0 && full_line[l - 1] == '\n')
-       full_line[--l] = '\0';
+                                          expr_scanner_offset(state),
+                                          true);
 
    syntax_error(expr_source, expr_lineno, full_line, expr_command,
                 message, more, error_detection_offset - expr_start_offset);
@@ -341,19 +338,30 @@ expr_scanner_offset(PsqlScanState state)
 
 /*
  * Get a malloc'd copy of the lexer input string from start_offset
- * to just before end_offset.
+ * to just before end_offset.  If chomp is true, drop any trailing
+ * newline(s).
  */
 char *
 expr_scanner_get_substring(PsqlScanState state,
-                          int start_offset, int end_offset)
+                          int start_offset, int end_offset,
+                          bool chomp)
 {
    char       *result;
+   const char *scanptr = state->scanbuf + start_offset;
    int         slen = end_offset - start_offset;
 
    Assert(slen >= 0);
    Assert(end_offset <= strlen(state->scanbuf));
+
+   if (chomp)
+   {
+       while (slen > 0 &&
+              (scanptr[slen - 1] == '\n' || scanptr[slen - 1] == '\r'))
+           slen--;
+   }
+
    result = (char *) pg_malloc(slen + 1);
-   memcpy(result, state->scanbuf + start_offset, slen);
+   memcpy(result, scanptr, slen);
    result[slen] = '\0';
 
    return result;
index 364e25447e431330aab526e989eb28ffe6366ab6..e37496c971459f07ecae968a8d48bf3cd038b7ad 100644 (file)
@@ -3065,8 +3065,7 @@ process_backslash_command(PsqlScanState sstate, const char *source)
    PQExpBufferData word_buf;
    int         word_offset;
    int         offsets[MAX_ARGS];  /* offsets of argument words */
-   int         start_offset,
-               end_offset;
+   int         start_offset;
    int         lineno;
    int         j;
 
@@ -3120,13 +3119,11 @@ process_backslash_command(PsqlScanState sstate, const char *source)
 
        my_command->expr = expr_parse_result;
 
-       /* Get location of the ending newline */
-       end_offset = expr_scanner_offset(sstate) - 1;
-
-       /* Save line */
+       /* Save line, trimming any trailing newline */
        my_command->line = expr_scanner_get_substring(sstate,
                                                      start_offset,
-                                                     end_offset);
+                                                     expr_scanner_offset(sstate),
+                                                     true);
 
        expr_scanner_finish(yyscanner);
 
@@ -3147,13 +3144,11 @@ process_backslash_command(PsqlScanState sstate, const char *source)
        my_command->argc++;
    }
 
-   /* Get location of the ending newline */
-   end_offset = expr_scanner_offset(sstate) - 1;
-
-   /* Save line */
+   /* Save line, trimming any trailing newline */
    my_command->line = expr_scanner_get_substring(sstate,
                                                  start_offset,
-                                                 end_offset);
+                                                 expr_scanner_offset(sstate),
+                                                 true);
 
    if (pg_strcasecmp(my_command->argv[0], "sleep") == 0)
    {
index abc13e9463423714ac70968a4fd8e243b4d995e5..fd428af274f26e98e611b4f5e7fdcce6c2a76b5b 100644 (file)
@@ -128,7 +128,8 @@ extern yyscan_t expr_scanner_init(PsqlScanState state,
 extern void expr_scanner_finish(yyscan_t yyscanner);
 extern int expr_scanner_offset(PsqlScanState state);
 extern char *expr_scanner_get_substring(PsqlScanState state,
-                          int start_offset, int end_offset);
+                          int start_offset, int end_offset,
+                          bool chomp);
 extern int expr_scanner_get_lineno(PsqlScanState state, int offset);
 
 extern void syntax_error(const char *source, int lineno, const char *line,