Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pbmR1-0000QC-0d for pgsql-hackers@arkaria.postgresql.org; Mon, 13 Mar 2023 17:58:11 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1pbmQz-0008K4-DV for pgsql-hackers@arkaria.postgresql.org; Mon, 13 Mar 2023 17:58:09 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pbmQy-0008Jo-SS for pgsql-hackers@lists.postgresql.org; Mon, 13 Mar 2023 17:58:09 +0000 Received: from sss.pgh.pa.us ([66.207.139.130]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pbmQv-0007FL-Nd for pgsql-hackers@postgresql.org; Mon, 13 Mar 2023 17:58:07 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 32DHw2Cc2000263; Mon, 13 Mar 2023 13:58:02 -0400 From: Tom Lane To: Nikolay Shaplov cc: pgsql-hackers Subject: Re: Bug in jsonb_in function (14 & 15 version are affected) In-reply-to: <7332649.x5DLKWyVIX@thinkpad-pgpro> References: <7332649.x5DLKWyVIX@thinkpad-pgpro> Comments: In-reply-to Nikolay Shaplov message dated "Mon, 13 Mar 2023 17:18:15 +0300" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <2000253.1678730277.0@sss.pgh.pa.us> Date: Mon, 13 Mar 2023 13:58:02 -0400 Message-ID: <2000262.1678730282@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <2000253.1678730277.1@sss.pgh.pa.us> Content-Transfer-Encoding: quoted-printable Nikolay Shaplov writes: > I found a bug in jsonb_in function (it converts json from sting represen= tation > into jsonb internal representation). Yeah. Looks like json_lex_string is failing to honor the invariant that it needs to set token_terminator ... although the documentation of the function certainly isn't helping. I think we need the attached. A nice side benefit is that the error context reports get a lot more useful --- somebody should have inquired before as to why they were so bogus. regards, tom lane ------- =_aaaaaaaaaa0 Content-Type: text/x-diff; name="fix-json-lex-string-error-cases.patch"; charset="us-ascii" Content-ID: <2000253.1678730277.2@sss.pgh.pa.us> Content-Description: fix-json-lex-string-error-cases.patch Content-Transfer-Encoding: quoted-printable diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jso= nfuncs.c index bdfc48cdf5..7a36f74dad 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -675,6 +675,7 @@ report_json_context(JsonLexContext *lex) line_start =3D lex->line_start; context_start =3D line_start; context_end =3D lex->token_terminator; + Assert(context_end >=3D context_start); = /* Advance until we are close enough to context_end */ while (context_end - context_start >=3D 50) diff --git a/src/common/jsonapi.c b/src/common/jsonapi.c index e4ff3f3602..4e2a664603 100644 --- a/src/common/jsonapi.c +++ b/src/common/jsonapi.c @@ -697,6 +697,14 @@ json_lex(JsonLexContext *lex) = /* * The next token in the input stream is known to be a string; lex it. + * + * If lex->strval isn't NULL, fill it with the decoded string. + * Set lex->token_terminator to the end of the decoded input, and in + * success cases, transfer its previous value to lex->prev_token_terminat= or. + * Return JSON_SUCCESS or an error code. + * + * Note: be careful that all error exits advance lex->token_terminator + * to the point after the character we detected the error on. */ static inline JsonParseErrorType json_lex_string(JsonLexContext *lex) @@ -705,6 +713,19 @@ json_lex_string(JsonLexContext *lex) char *const end =3D lex->input + lex->input_length; int hi_surrogate =3D -1; = + /* Convenience macros */ +#define FAIL_AT_CHAR_START(code) \ + do { \ + lex->token_terminator =3D s; \ + return code; \ + } while (0) +#define FAIL_AT_CHAR_END(code) \ + do { \ + lex->token_terminator =3D \ + s + pg_encoding_mblen_bounded(lex->input_encoding, s); \ + return code; \ + } while (0) + if (lex->strval !=3D NULL) resetStringInfo(lex->strval); = @@ -715,10 +736,7 @@ json_lex_string(JsonLexContext *lex) s++; /* Premature end of the string. */ if (s >=3D end) - { - lex->token_terminator =3D s; - return JSON_INVALID_TOKEN; - } + FAIL_AT_CHAR_START(JSON_INVALID_TOKEN); else if (*s =3D=3D '"') break; else if (*s =3D=3D '\\') @@ -726,10 +744,7 @@ json_lex_string(JsonLexContext *lex) /* OK, we have an escape character. */ s++; if (s >=3D end) - { - lex->token_terminator =3D s; - return JSON_INVALID_TOKEN; - } + FAIL_AT_CHAR_START(JSON_INVALID_TOKEN); else if (*s =3D=3D 'u') { int i; @@ -739,10 +754,7 @@ json_lex_string(JsonLexContext *lex) { s++; if (s >=3D end) - { - lex->token_terminator =3D s; - return JSON_INVALID_TOKEN; - } + FAIL_AT_CHAR_START(JSON_INVALID_TOKEN); else if (*s >=3D '0' && *s <=3D '9') ch =3D (ch * 16) + (*s - '0'); else if (*s >=3D 'a' && *s <=3D 'f') @@ -750,10 +762,7 @@ json_lex_string(JsonLexContext *lex) else if (*s >=3D 'A' && *s <=3D 'F') ch =3D (ch * 16) + (*s - 'A') + 10; else - { - lex->token_terminator =3D s + pg_encoding_mblen_bounded(lex->input_= encoding, s); - return JSON_UNICODE_ESCAPE_FORMAT; - } + FAIL_AT_CHAR_END(JSON_UNICODE_ESCAPE_FORMAT); } if (lex->strval !=3D NULL) { @@ -763,20 +772,20 @@ json_lex_string(JsonLexContext *lex) if (is_utf16_surrogate_first(ch)) { if (hi_surrogate !=3D -1) - return JSON_UNICODE_HIGH_SURROGATE; + FAIL_AT_CHAR_END(JSON_UNICODE_HIGH_SURROGATE); hi_surrogate =3D ch; continue; } else if (is_utf16_surrogate_second(ch)) { if (hi_surrogate =3D=3D -1) - return JSON_UNICODE_LOW_SURROGATE; + FAIL_AT_CHAR_END(JSON_UNICODE_LOW_SURROGATE); ch =3D surrogate_pair_to_codepoint(hi_surrogate, ch); hi_surrogate =3D -1; } = if (hi_surrogate !=3D -1) - return JSON_UNICODE_LOW_SURROGATE; + FAIL_AT_CHAR_END(JSON_UNICODE_LOW_SURROGATE); = /* * Reject invalid cases. We can't have a value above @@ -786,7 +795,7 @@ json_lex_string(JsonLexContext *lex) if (ch =3D=3D 0) { /* We can't allow this, since our TEXT type doesn't */ - return JSON_UNICODE_CODE_POINT_ZERO; + FAIL_AT_CHAR_END(JSON_UNICODE_CODE_POINT_ZERO); } = /* @@ -800,7 +809,7 @@ json_lex_string(JsonLexContext *lex) char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1]; = if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf)) - return JSON_UNICODE_UNTRANSLATABLE; + FAIL_AT_CHAR_END(JSON_UNICODE_UNTRANSLATABLE); appendStringInfoString(lex->strval, cbuf); } #else @@ -820,14 +829,14 @@ json_lex_string(JsonLexContext *lex) appendStringInfoChar(lex->strval, (char) ch); } else - return JSON_UNICODE_HIGH_ESCAPE; + FAIL_AT_CHAR_END(JSON_UNICODE_HIGH_ESCAPE); #endif /* FRONTEND */ } } else if (lex->strval !=3D NULL) { if (hi_surrogate !=3D -1) - return JSON_UNICODE_LOW_SURROGATE; + FAIL_AT_CHAR_END(JSON_UNICODE_LOW_SURROGATE); = switch (*s) { @@ -852,10 +861,14 @@ json_lex_string(JsonLexContext *lex) appendStringInfoChar(lex->strval, '\t'); break; default: - /* Not a valid string escape, so signal error. */ + + /* + * Not a valid string escape, so signal error. We + * adjust token_start so that just the escape sequence + * is reported, not the whole string. + */ lex->token_start =3D s; - lex->token_terminator =3D s + pg_encoding_mblen_bounded(lex->input_= encoding, s); - return JSON_ESCAPING_INVALID; + FAIL_AT_CHAR_END(JSON_ESCAPING_INVALID); } } else if (strchr("\"\\/bfnrt", *s) =3D=3D NULL) @@ -868,8 +881,7 @@ json_lex_string(JsonLexContext *lex) * shown it's not a performance win. */ lex->token_start =3D s; - lex->token_terminator =3D s + pg_encoding_mblen_bounded(lex->input_en= coding, s); - return JSON_ESCAPING_INVALID; + FAIL_AT_CHAR_END(JSON_ESCAPING_INVALID); } } else @@ -877,7 +889,7 @@ json_lex_string(JsonLexContext *lex) char *p =3D s; = if (hi_surrogate !=3D -1) - return JSON_UNICODE_LOW_SURROGATE; + FAIL_AT_CHAR_END(JSON_UNICODE_LOW_SURROGATE); = /* * Skip to the first byte that requires special handling, so we @@ -917,12 +929,18 @@ json_lex_string(JsonLexContext *lex) } = if (hi_surrogate !=3D -1) + { + lex->token_terminator =3D s + 1; return JSON_UNICODE_LOW_SURROGATE; + } = /* Hooray, we found the end of the string! */ lex->prev_token_terminator =3D lex->token_terminator; lex->token_terminator =3D s + 1; return JSON_SUCCESS; + +#undef FAIL_AT_CHAR_START +#undef FAIL_AT_CHAR_END } = /* diff --git a/src/test/regress/expected/json_encoding.out b/src/test/regres= s/expected/json_encoding.out index f18ba9ebb2..fe729db8c9 100644 --- a/src/test/regress/expected/json_encoding.out +++ b/src/test/regress/expected/json_encoding.out @@ -56,19 +56,19 @@ select json '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> = 'a' as correct_in_utf8; select json '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a = row ERROR: invalid input syntax for type json DETAIL: Unicode high surrogate must not follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83d\ud83d... select json '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong ord= er ERROR: invalid input syntax for type json DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... select json '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate ERROR: invalid input syntax for type json DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83dX... select json '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate ERROR: invalid input syntax for type json DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... --handling of simple unicode escapes select json '{ "a": "the Copyright \u00a9 sign" }' as correct_in_utf8; correct_in_utf8 = @@ -121,7 +121,7 @@ select json '{ "a": "dollar \\u0024 character" }' ->>= 'a' as not_an_escape; select json '{ "a": "null \u0000 escape" }' ->> 'a' as fails; ERROR: unsupported Unicode escape sequence DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "null \u0000... select json '{ "a": "null \\u0000 escape" }' ->> 'a' as not_an_escape; not_an_escape = -------------------- @@ -159,7 +159,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT '"\u0000"'::jsonb; ^ DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: ... +CONTEXT: JSON data, line 1: "\u0000... -- use octet_length here so we don't get an odd unicode char in the -- output SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower = case both OK @@ -180,25 +180,25 @@ ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ud83d\ud83d" }' -> 'a'; ^ DETAIL: Unicode high surrogate must not follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83d\ud83d... SELECT jsonb '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong or= der ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ude04\ud83d" }' -> 'a'; ^ DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... SELECT jsonb '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ud83dX" }' -> 'a'; ^ DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83dX... SELECT jsonb '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ude04X" }' -> 'a'; ^ DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... -- handling of simple unicode escapes SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' as correct_in_utf8; correct_in_utf8 = @@ -223,7 +223,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT jsonb '{ "a": "null \u0000 escape" }' as fails; ^ DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "null \u0000... SELECT jsonb '{ "a": "null \\u0000 escape" }' as not_an_escape; not_an_escape = ------------------------------ @@ -253,7 +253,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' as fai... ^ DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "null \u0000... SELECT jsonb '{ "a": "null \\u0000 escape" }' ->> 'a' as not_an_escape; not_an_escape = -------------------- diff --git a/src/test/regress/expected/json_encoding_1.out b/src/test/regr= ess/expected/json_encoding_1.out index 77bdaf63a1..5c8d91ad0b 100644 --- a/src/test/regress/expected/json_encoding_1.out +++ b/src/test/regress/expected/json_encoding_1.out @@ -50,23 +50,23 @@ SELECT '"\uaBcD"'::json; -- OK, uppercase and lower c= ase both OK select json '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> 'a' as correct_in_u= tf8; ERROR: unsupported Unicode escape sequence DETAIL: Unicode escape value could not be translated to the server's enc= oding SQL_ASCII. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83d\ude04... select json '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a = row ERROR: invalid input syntax for type json DETAIL: Unicode high surrogate must not follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83d\ud83d... select json '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong ord= er ERROR: invalid input syntax for type json DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... select json '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate ERROR: invalid input syntax for type json DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83dX... select json '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate ERROR: invalid input syntax for type json DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... --handling of simple unicode escapes select json '{ "a": "the Copyright \u00a9 sign" }' as correct_in_utf8; correct_in_utf8 = @@ -101,7 +101,7 @@ select json '{ "a": "null \\u0000 escape" }' as not_a= n_escape; select json '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' as correct_in= _utf8; ERROR: unsupported Unicode escape sequence DETAIL: Unicode escape value could not be translated to the server's enc= oding SQL_ASCII. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "the Copyright \u00a9... select json '{ "a": "dollar \u0024 character" }' ->> 'a' as correct_ever= ywhere; correct_everywhere = -------------------- @@ -117,7 +117,7 @@ select json '{ "a": "dollar \\u0024 character" }' ->>= 'a' as not_an_escape; select json '{ "a": "null \u0000 escape" }' ->> 'a' as fails; ERROR: unsupported Unicode escape sequence DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "null \u0000... select json '{ "a": "null \\u0000 escape" }' ->> 'a' as not_an_escape; not_an_escape = -------------------- @@ -155,7 +155,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT '"\u0000"'::jsonb; ^ DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: ... +CONTEXT: JSON data, line 1: "\u0000... -- use octet_length here so we don't get an odd unicode char in the -- output SELECT octet_length('"\uaBcD"'::jsonb::text); -- OK, uppercase and lower = case both OK @@ -163,45 +163,45 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT octet_length('"\uaBcD"'::jsonb::text); ^ DETAIL: Unicode escape value could not be translated to the server's enc= oding SQL_ASCII. -CONTEXT: JSON data, line 1: ... +CONTEXT: JSON data, line 1: "\uaBcD... -- handling of unicode surrogate pairs SELECT octet_length((jsonb '{ "a": "\ud83d\ude04\ud83d\udc36" }' -> 'a')= ::text) AS correct_in_utf8; ERROR: unsupported Unicode escape sequence LINE 1: SELECT octet_length((jsonb '{ "a": "\ud83d\ude04\ud83d\udc3... ^ DETAIL: Unicode escape value could not be translated to the server's enc= oding SQL_ASCII. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83d\ude04... SELECT jsonb '{ "a": "\ud83d\ud83d" }' -> 'a'; -- 2 high surrogates in a= row ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ud83d\ud83d" }' -> 'a'; ^ DETAIL: Unicode high surrogate must not follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83d\ud83d... SELECT jsonb '{ "a": "\ude04\ud83d" }' -> 'a'; -- surrogates in wrong or= der ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ude04\ud83d" }' -> 'a'; ^ DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... SELECT jsonb '{ "a": "\ud83dX" }' -> 'a'; -- orphan high surrogate ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ud83dX" }' -> 'a'; ^ DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ud83dX... SELECT jsonb '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate ERROR: invalid input syntax for type json LINE 1: SELECT jsonb '{ "a": "\ude04X" }' -> 'a'; ^ DETAIL: Unicode low surrogate must follow a high surrogate. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "\ude04... -- handling of simple unicode escapes SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' as correct_in_utf8; ERROR: unsupported Unicode escape sequence LINE 1: SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' as corr... ^ DETAIL: Unicode escape value could not be translated to the server's enc= oding SQL_ASCII. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "the Copyright \u00a9... SELECT jsonb '{ "a": "dollar \u0024 character" }' as correct_everywhere; correct_everywhere = ----------------------------- @@ -219,7 +219,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT jsonb '{ "a": "null \u0000 escape" }' as fails; ^ DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "null \u0000... SELECT jsonb '{ "a": "null \\u0000 escape" }' as not_an_escape; not_an_escape = ------------------------------ @@ -231,7 +231,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' ->> 'a'... ^ DETAIL: Unicode escape value could not be translated to the server's enc= oding SQL_ASCII. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "the Copyright \u00a9... SELECT jsonb '{ "a": "dollar \u0024 character" }' ->> 'a' as correct_eve= rywhere; correct_everywhere = -------------------- @@ -249,7 +249,7 @@ ERROR: unsupported Unicode escape sequence LINE 1: SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' as fai... ^ DETAIL: \u0000 cannot be converted to text. -CONTEXT: JSON data, line 1: { "a":... +CONTEXT: JSON data, line 1: { "a": "null \u0000... SELECT jsonb '{ "a": "null \\u0000 escape" }' ->> 'a' as not_an_escape; not_an_escape = -------------------- ------- =_aaaaaaaaaa0--