Repository navigation
ADBM-3574: Fix JSON support for pgbackrest recovery filters - #113
VoidZeroNull0 wants to merge 5 commits into
Conversation
Try the first solution.
This test now passes, it is not needed.
Was wrong this->json++.
Get back this->json++, add this->json--.
Make a separate function, add tests for error branches.
|
Tests seem to fail because of missing docker image. Nothing related to the patch. |
| else if (codepoint <= 0x7FF) | ||
| { | ||
| strCatChr(result, (char)(0xC0 | (codepoint >> 6))); | ||
| strCatChr(result, (char)(0x80 | (codepoint & 0x3F))); | ||
| } | ||
| else if (codepoint <= 0xFFFF) | ||
| { | ||
| strCatChr(result, (char)(0xE0 | (codepoint >> 12))); | ||
| strCatChr(result, (char)(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| strCatChr(result, (char)(0x80 | (codepoint & 0x3F))); | ||
| } | ||
| else | ||
| { | ||
| strCatChr(result, (char)(0xF0 | (codepoint >> 18))); | ||
| strCatChr(result, (char)(0x80 | ((codepoint >> 12) & 0x3F))); | ||
| strCatChr(result, (char)(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| strCatChr(result, (char)(0x80 | (codepoint & 0x3F))); | ||
| } |
There was a problem hiding this comment.
these lines are not covered by tests
There was a problem hiding this comment.
This is precisely why the CI is red: it checks the coverage of the ENTIRE codebase.
| // Unicode escape | ||
| FN_EXTERN void jsonDecodeUnicode(JsonRead *this, String *result); | ||
|
|
||
| // Convert JSON to Variant |
There was a problem hiding this comment.
Why did you make this function public?
| FN_EXTERN void | ||
| jsonDecodeUnicode(JsonRead *const this, String *const result) | ||
| { | ||
| this->json++; // get past /u |
There was a problem hiding this comment.
add FUNCTION_TEST_BEGIN/PARAM/RETURN and ASSERT which CODING.md requires
| strCatChr(result, (char)cvtZSubNToUIntBase(this->json, 0, 2, 16)); | ||
| this->json++; | ||
|
|
||
| jsonDecodeUnicode(this, result); |
There was a problem hiding this comment.
Now jsonReadStr does not validate the input data, and jsonDecodeUnicode does not check it either.
There is no test for the original bug: |
| /**********************************************************************************************************************************/ | ||
| FN_EXTERN void | ||
| jsonDecodeUnicode(JsonRead *const this, String *const result) | ||
| { | ||
| this->json++; // get past /u | ||
| const unsigned int high = cvtZSubNToUIntBase(this->json, 0, 4, 16); | ||
| unsigned int codepoint = high; | ||
| this->json += 4; | ||
|
|
||
| // Check for supplementary planes | ||
| if (high >= 0xD800 && high <= 0xDBFF) | ||
| { | ||
| if (*this->json != '\\' || *(this->json + 1) != 'u') | ||
| THROW_FMT(JsonFormatError, "missing low surrogate for high surrogate at: %s", this->json - 6); | ||
|
|
||
| this->json += 2; | ||
| const unsigned int low = cvtZSubNToUIntBase(this->json, 0, 4, 16); | ||
| this->json += 4; | ||
|
|
||
| if (low < 0xDC00 || low > 0xDFFF) | ||
| THROW_FMT(JsonFormatError, "invalid low surrogate in unicode escape at: %s", this->json - 6); | ||
|
|
||
| codepoint = 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00); | ||
| } | ||
| else if (high >= 0xDC00 && high <= 0xDFFF) | ||
| THROW_FMT(JsonFormatError, "unexpected low surrogate without high surrogate at: %s", this->json - 4); | ||
|
|
||
| // Append 1-4 bytes to the result | ||
| if (codepoint <= 0x7F) | ||
| { | ||
| strCatChr(result, (char)codepoint); | ||
| } | ||
| else if (codepoint <= 0x7FF) | ||
| { | ||
| strCatChr(result, (char)(0xC0 | (codepoint >> 6))); | ||
| strCatChr(result, (char)(0x80 | (codepoint & 0x3F))); | ||
| } | ||
| else if (codepoint <= 0xFFFF) | ||
| { | ||
| strCatChr(result, (char)(0xE0 | (codepoint >> 12))); | ||
| strCatChr(result, (char)(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| strCatChr(result, (char)(0x80 | (codepoint & 0x3F))); | ||
| } | ||
| else | ||
| { | ||
| strCatChr(result, (char)(0xF0 | (codepoint >> 18))); | ||
| strCatChr(result, (char)(0x80 | ((codepoint >> 12) & 0x3F))); | ||
| strCatChr(result, (char)(0x80 | ((codepoint >> 6) & 0x3F))); | ||
| strCatChr(result, (char)(0x80 | (codepoint & 0x3F))); | ||
| } | ||
|
|
||
| this->json--; | ||
| } | ||
|
|
There was a problem hiding this comment.
jsonDecodeUnicode() lacks validation for 4 hex digits (it reads past the \0 terminator, throws FormatError instead of JsonFormatError, and strtoull accepts extraneous input).
| for (; digitIdx < 4; digitIdx++) | ||
| { | ||
| if (!isdigit(this->json[digitIdx])) | ||
| if (!isxdigit(this->json[digitIdx])) |
There was a problem hiding this comment.
A \ before \0 in jsonReadSkipStr causes the pointer to move past the end of the string (fixed upstream in pgbackrest/pgbackrest@b623071a2).
|
|
| codepoint = 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00); | ||
| } | ||
| else if (high >= 0xDC00 && high <= 0xDFFF) | ||
| THROW_FMT(JsonFormatError, "unexpected low surrogate without high surrogate at: %s", this->json - 4); |
There was a problem hiding this comment.
| THROW_FMT(JsonFormatError, "unexpected low surrogate without high surrogate at: %s", this->json - 4); | |
| THROW_FMT(JsonFormatError, "unexpected low surrogate without high surrogate at: %s", this->json - 6); |
to add \u in error message like above
|
Do we really need to actually decode UTF-16 strings? During WAL filtering, we do not use text table names. |
What happened?
Restore failed when a --filter value contained a "\uXXXX" escape
outside the ASCII range - e.g. a surrogate pair like "\uD83E\uDD52".
The parser only supported "\u00XX" and threw on anything else.
How do we fix this?
Decode \uXXXX escapes into UTF-8, including surrogate pairs: combine
high/low surrogates into one code point and encode it as 1–4 UTF-8
bytes. Extracted the logic into jsonDecodeUnicode() function.
Tests?
One success test for the surrogate-pair path was added. One test per
error branch was also added.
Ticket: ADBM-3574