From eb213acfe2a51ab3dc6d9bbe02d5d8a903366779 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 22 Jan 2015 18:10:47 -0500 Subject: [PATCH] Prevent duplicate escape-string warnings when using pg_stat_statements. 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.) --- .../pg_stat_statements/pg_stat_statements.c | 3 +++ src/backend/parser/scan.l | 18 +++++++++++------- src/include/parser/scanner.h | 10 ++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 2629bfca34..95616b3691 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -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++) { diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index 21a6f30e10..a78ce03fbd 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -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 . {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"), diff --git a/src/include/parser/scanner.h b/src/include/parser/scanner.h index 02e8764213..e6724bc588 100644 --- a/src/include/parser/scanner.h +++ b/src/include/parser/scanner.h @@ -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