From 562f29aedb0207420a8e3aeb807a9a1b18095fda Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 30 Dec 2025 15:42:22 -0500
Subject: [PATCH v1 2/4] Remove server-side support for
 !standard_conforming_strings.

Mostly this consists of dropping scan.l's support for
escape_string_warning, which is no longer reachable.
(To see that, note that the warning subroutines are
only called within the <xe> exclusive state, and after
the change in the {xqstart} rule, <xe> state cannot be
reached with yyextra->warn_on_first_escape true.
So the subroutines can never warn.)

This leaves the escape_string_warning GUC nonfunctional,
but cleaning up that situation will come later.

Also adjust assorted comments to reflect the fact that
standard_conforming_strings = false is no longer possible
locally, but we still have to allow for the possibility
that a remote server might have it.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/3279216.1767072538@sss.pgh.pa.us
---
 .../pg_stat_statements/pg_stat_statements.c   |  3 -
 contrib/test_decoding/test_decoding.c         |  4 +-
 src/backend/parser/scan.l                     | 76 ++-----------------
 src/backend/utils/adt/quote.c                 |  8 +-
 src/backend/utils/adt/ruleutils.c             |  6 +-
 src/backend/utils/misc/guc_tables.c           |  5 +-
 src/include/parser/parser.h                   |  4 +-
 src/include/parser/scanner.h                  |  5 +-
 8 files changed, 17 insertions(+), 94 deletions(-)

diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 39208f80b5b..58892d12246 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2991,9 +2991,6 @@ fill_in_constant_lengths(JumbleState *jstate, const char *query,
 							 &ScanKeywords,
 							 ScanKeywordTokens);
 
-	/* we don't want to re-emit any escape string warnings */
-	yyextra.escape_string_warning = false;
-
 	/* Search for each constant, in sequence */
 	for (int i = 0; i < jstate->clocations_count; i++)
 	{
diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c
index 47094f86f5f..4c92f0eeffa 100644
--- a/contrib/test_decoding/test_decoding.c
+++ b/contrib/test_decoding/test_decoding.c
@@ -474,8 +474,8 @@ pg_decode_filter(LogicalDecodingContext *ctx,
  * Print literal `outputstr' already represented as string of type `typid'
  * into stringbuf `s'.
  *
- * Some builtin types aren't quoted, the rest is quoted. Escaping is done as
- * if standard_conforming_strings were enabled.
+ * Some builtin types aren't quoted, the rest is quoted. Escaping is done
+ * per standard SQL rules.
  */
 static void
 print_literal(StringInfo s, Oid typid, char *outputstr)
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index a67815339b7..cc88eea3708 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -60,14 +60,13 @@ fprintf_to_ereport(const char *fmt, const char *msg)
 }
 
 /*
- * GUC variables.  This is a DIRECT violation of the warning given at the
+ * GUC variable.  This is a DIRECT violation of the warning given at the
  * head of gram.y, ie flex/bison code must not depend on any GUC variables;
- * as such, changing their values can induce very unintuitive behavior.
- * But we shall have to live with it until we can remove these variables.
+ * as such, changing its value can induce very unintuitive behavior.
+ * In practice, backslash_quote is not too awful since it only controls
+ * whether to throw an error: it cannot change non-error results.
  */
 int			backslash_quote = BACKSLASH_QUOTE_SAFE_ENCODING;
-bool		escape_string_warning = true;
-bool		standard_conforming_strings = true;
 
 /*
  * Constant data exported from this file.  This array maps from the
@@ -127,9 +126,6 @@ static void addunicode(char32_t c, yyscan_t yyscanner);
 
 #define lexer_errposition()  scanner_errposition(*(yylloc), yyscanner)
 
-static void check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner);
-static void check_escape_warning(core_yyscan_t yyscanner);
-
 %}
 
 %option reentrant
@@ -543,17 +539,12 @@ other			.
 				}
 
 {xqstart}		{
-					yyextra->warn_on_first_escape = true;
 					yyextra->saw_non_ascii = false;
 					SET_YYLLOC();
-					if (yyextra->standard_conforming_strings)
-						BEGIN(xq);
-					else
-						BEGIN(xe);
+					BEGIN(xq);
 					startlit();
 				}
 {xestart}		{
-					yyextra->warn_on_first_escape = false;
 					yyextra->saw_non_ascii = false;
 					SET_YYLLOC();
 					BEGIN(xe);
@@ -561,12 +552,6 @@ other			.
 				}
 {xusstart}		{
 					SET_YYLLOC();
-					if (!yyextra->standard_conforming_strings)
-						ereport(ERROR,
-								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-								 errmsg("unsafe use of string constant with Unicode escapes"),
-								 errdetail("String constants with Unicode escapes cannot be used when \"standard_conforming_strings\" is off."),
-								 lexer_errposition()));
 					BEGIN(xus);
 					startlit();
 				}
@@ -642,13 +627,6 @@ other			.
 <xe>{xeunicode} {
 					char32_t	c = strtoul(yytext + 2, NULL, 16);
 
-					/*
-					 * For consistency with other productions, issue any
-					 * escape warning with cursor pointing to start of string.
-					 * We might want to change that, someday.
-					 */
-					check_escape_warning(yyscanner);
-
 					/* Remember start of overall string token ... */
 					PUSH_YYLLOC();
 					/* ... and set the error cursor to point at this esc seq */
@@ -715,14 +693,12 @@ other			.
 									 errhint("Use '' to write quotes in strings. \\' is insecure in client-only encodings."),
 									 lexer_errposition()));
 					}
-					check_string_escape_warning(yytext[1], yyscanner);
 					addlitchar(unescape_single_char(yytext[1], yyscanner),
 							   yyscanner);
 				}
 <xe>{xeoctesc}  {
 					unsigned char c = strtoul(yytext + 1, NULL, 8);
 
-					check_escape_warning(yyscanner);
 					addlitchar(c, yyscanner);
 					if (c == '\0' || IS_HIGHBIT_SET(c))
 						yyextra->saw_non_ascii = true;
@@ -730,7 +706,6 @@ other			.
 <xe>{xehexesc}  {
 					unsigned char c = strtoul(yytext + 2, NULL, 16);
 
-					check_escape_warning(yyscanner);
 					addlitchar(c, yyscanner);
 					if (c == '\0' || IS_HIGHBIT_SET(c))
 						yyextra->saw_non_ascii = true;
@@ -1263,8 +1238,6 @@ scanner_init(const char *str,
 	yyext->keyword_tokens = keyword_tokens;
 
 	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.
@@ -1420,45 +1393,6 @@ unescape_single_char(unsigned char c, core_yyscan_t yyscanner)
 	}
 }
 
-static void
-check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner)
-{
-	if (ychar == '\'')
-	{
-		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"),
-					 errhint("Use '' to write quotes in strings, or use the escape string syntax (E'...')."),
-					 lexer_errposition()));
-		yyextra->warn_on_first_escape = false;	/* warn only once per string */
-	}
-	else if (ychar == '\\')
-	{
-		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"),
-					 errhint("Use the escape string syntax for backslashes, e.g., E'\\\\'."),
-					 lexer_errposition()));
-		yyextra->warn_on_first_escape = false;	/* warn only once per string */
-	}
-	else
-		check_escape_warning(yyscanner);
-}
-
-static void
-check_escape_warning(core_yyscan_t yyscanner)
-{
-	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"),
-				 errhint("Use the escape string syntax for escapes, e.g., E'\\r\\n'."),
-				 lexer_errposition()));
-	yyextra->warn_on_first_escape = false;	/* warn only once per string */
-}
-
 /*
  * Interface functions to make flex use palloc() instead of malloc().
  * It'd be better to make these static, but flex insists otherwise.
diff --git a/src/backend/utils/adt/quote.c b/src/backend/utils/adt/quote.c
index 551de59a07f..e62bf04a9f9 100644
--- a/src/backend/utils/adt/quote.c
+++ b/src/backend/utils/adt/quote.c
@@ -37,11 +37,9 @@ quote_ident(PG_FUNCTION_ARGS)
  * quote_literal_internal -
  *	  helper function for quote_literal and quote_literal_cstr
  *
- * NOTE: think not to make this function's behavior change with
- * standard_conforming_strings.  We don't know where the result
- * literal will be used, and so we must generate a result that
- * will work with either setting.  Take a look at what dblink
- * uses this for before thinking you know better.
+ * NOTE: This must produce output that will work in old servers with
+ * standard_conforming_strings = off.  It's used for example by
+ * dblink, which may send the result to another server.
  */
 static size_t
 quote_literal_internal(char *dst, const char *src, size_t len)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9f85eb86da1..61028db020f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -11828,16 +11828,14 @@ simple_quote_literal(StringInfo buf, const char *val)
 	const char *valptr;
 
 	/*
-	 * We form the string literal according to the prevailing setting of
-	 * standard_conforming_strings; we never use E''. User is responsible for
-	 * making sure result is used correctly.
+	 * We always form the string literal according to standard SQL rules.
 	 */
 	appendStringInfoChar(buf, '\'');
 	for (valptr = val; *valptr; valptr++)
 	{
 		char		ch = *valptr;
 
-		if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
+		if (SQL_STR_DOUBLE(ch, false))
 			appendStringInfoChar(buf, ch);
 		appendStringInfoChar(buf, ch);
 	}
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 04ab0a26608..beea9b00657 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -529,10 +529,11 @@ bool		row_security;
 bool		check_function_bodies = true;
 
 /*
- * This GUC exists solely for backward compatibility, check its definition for
- * details.
+ * These GUCs exist solely for backward compatibility.
  */
 static bool default_with_oids = false;
+static bool standard_conforming_strings = true;
+static bool escape_string_warning = true;
 
 bool		current_role_is_superuser;
 
diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h
index 350196cd641..cbf9a704dfb 100644
--- a/src/include/parser/parser.h
+++ b/src/include/parser/parser.h
@@ -52,10 +52,8 @@ typedef enum
 	BACKSLASH_QUOTE_SAFE_ENCODING,
 }			BackslashQuoteType;
 
-/* GUC variables in scan.l (every one of these is a bad idea :-() */
+/* GUC variable in scan.l */
 extern PGDLLIMPORT int backslash_quote;
-extern PGDLLIMPORT bool escape_string_warning;
-extern PGDLLIMPORT bool standard_conforming_strings;
 
 
 /* Primary entry point for the raw parsing functions */
diff --git a/src/include/parser/scanner.h b/src/include/parser/scanner.h
index 8d202d5b284..f5806feb91d 100644
--- a/src/include/parser/scanner.h
+++ b/src/include/parser/scanner.h
@@ -85,8 +85,6 @@ typedef struct core_yy_extra_type
 	 * 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
@@ -110,8 +108,7 @@ typedef struct core_yy_extra_type
 	/* first part of UTF16 surrogate pair for Unicode escapes */
 	int32		utf16_first_part;
 
-	/* state variables for literal-lexing warnings */
-	bool		warn_on_first_escape;
+	/* true if we need to verify valid encoding of current literal string */
 	bool		saw_non_ascii;
 } core_yy_extra_type;
 
-- 
2.43.7

