Prevent duplicate escape-string warnings when using pg_stat_statements.
authorTom Lane <[email protected]>
Thu, 22 Jan 2015 23:10:47 +0000 (18:10 -0500)
committerTom Lane <[email protected]>
Thu, 22 Jan 2015 23:11:00 +0000 (18:11 -0500)
contrib/pg_stat_statements will sometimes run the core lexer a second time
on submitted statements.  Formerly, if you had standard_conforming_strings
turned off, this led to sometimes getting two copies of any warnings
enabled by escape_string_warning.  While this is probably no longer a big
deal in the field, it's a pain for regression testing.

To fix, change the lexer so it doesn't consult the escape_string_warning
GUC variable directly, but looks at a copy in the core_yy_extra_type state
struct.  Then, pg_stat_statements can change that copy to disable warnings
while it's redoing the lexing.

It seemed like a good idea to make this happen for all three of the GUCs
consulted by the lexer, not just escape_string_warning.  There's not an
immediate use-case for callers to adjust the other two AFAIK, but making
it possible is easy enough and seems like good future-proofing.

Arguably this is a bug fix, but there doesn't seem to be enough interest to
justify a back-patch.  We'd not be able to back-patch exactly as-is anyway,
for fear of breaking ABI compatibility of the struct.  (We could perhaps
back-patch the addition of only escape_string_warning by adding it at the
end of the struct, where there's currently alignment padding space.)

contrib/pg_stat_statements/pg_stat_statements.c
src/backend/parser/scan.l
src/include/parser/scanner.h

index 2629bfca34f1b77793bcf37b9bd7f5cc269b2a5a..95616b36914d52f710d64299c3207fe3e9508fd8 100644 (file)
@@ -2816,6 +2816,9 @@ fill_in_constant_lengths(pgssJumbleState *jstate, const char *query)
                             ScanKeywords,
                             NumScanKeywords);
 
+   /* we don't want to re-emit any escape string warnings */
+   yyextra.escape_string_warning = false;
+
    /* Search for each constant, in sequence */
    for (i = 0; i < jstate->clocations_count; i++)
    {
index 21a6f30e10cb94d3b878ce527a84b303ee6109de..a78ce03fbd5a4ef31a0b8984819366a60fe28849 100644 (file)
@@ -505,7 +505,7 @@ other           .
                    yyextra->warn_on_first_escape = true;
                    yyextra->saw_non_ascii = false;
                    SET_YYLLOC();
-                   if (standard_conforming_strings)
+                   if (yyextra->standard_conforming_strings)
                        BEGIN(xq);
                    else
                        BEGIN(xe);
@@ -520,7 +520,7 @@ other           .
                }
 {xusstart}     {
                    SET_YYLLOC();
-                   if (!standard_conforming_strings)
+                   if (!yyextra->standard_conforming_strings)
                        ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("unsafe use of string constant with Unicode escapes"),
@@ -622,8 +622,8 @@ other           .
 <xe>{xeescape}  {
                    if (yytext[1] == '\'')
                    {
-                       if (backslash_quote == BACKSLASH_QUOTE_OFF ||
-                           (backslash_quote == BACKSLASH_QUOTE_SAFE_ENCODING &&
+                       if (yyextra->backslash_quote == BACKSLASH_QUOTE_OFF ||
+                           (yyextra->backslash_quote == BACKSLASH_QUOTE_SAFE_ENCODING &&
                             PG_ENCODING_IS_CLIENT_ONLY(pg_get_client_encoding())))
                            ereport(ERROR,
                                    (errcode(ERRCODE_NONSTANDARD_USE_OF_ESCAPE_CHARACTER),
@@ -1074,6 +1074,10 @@ scanner_init(const char *str,
    yyext->keywords = keywords;
    yyext->num_keywords = num_keywords;
 
+   yyext->backslash_quote = backslash_quote;
+   yyext->escape_string_warning = escape_string_warning;
+   yyext->standard_conforming_strings = standard_conforming_strings;
+
    /*
     * Make a scan buffer with special termination needed by flex.
     */
@@ -1433,7 +1437,7 @@ check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner)
 {
    if (ychar == '\'')
    {
-       if (yyextra->warn_on_first_escape && escape_string_warning)
+       if (yyextra->warn_on_first_escape && yyextra->escape_string_warning)
            ereport(WARNING,
                    (errcode(ERRCODE_NONSTANDARD_USE_OF_ESCAPE_CHARACTER),
                     errmsg("nonstandard use of \\' in a string literal"),
@@ -1443,7 +1447,7 @@ check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner)
    }
    else if (ychar == '\\')
    {
-       if (yyextra->warn_on_first_escape && escape_string_warning)
+       if (yyextra->warn_on_first_escape && yyextra->escape_string_warning)
            ereport(WARNING,
                    (errcode(ERRCODE_NONSTANDARD_USE_OF_ESCAPE_CHARACTER),
                     errmsg("nonstandard use of \\\\ in a string literal"),
@@ -1458,7 +1462,7 @@ check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner)
 static void
 check_escape_warning(core_yyscan_t yyscanner)
 {
-   if (yyextra->warn_on_first_escape && escape_string_warning)
+   if (yyextra->warn_on_first_escape && yyextra->escape_string_warning)
        ereport(WARNING,
                (errcode(ERRCODE_NONSTANDARD_USE_OF_ESCAPE_CHARACTER),
                 errmsg("nonstandard use of escape in a string literal"),
index 02e8764213504a8402e49ce006c05974cf35c15f..e6724bc588ea07b542fdb1f98af65cba48dba352 100644 (file)
@@ -77,6 +77,16 @@ typedef struct core_yy_extra_type
    const ScanKeyword *keywords;
    int         num_keywords;
 
+   /*
+    * Scanner settings to use.  These are initialized from the corresponding
+    * GUC variables by scanner_init().  Callers can modify them after
+    * scanner_init() if they don't want the scanner's behavior to follow the
+    * prevailing GUC settings.
+    */
+   int         backslash_quote;
+   bool        escape_string_warning;
+   bool        standard_conforming_strings;
+
    /*
     * literalbuf is used to accumulate literal values when multiple rules are
     * needed to parse a single literal.  Call startlit() to reset buffer to