Ramin Gharib created FLINK-41298:
------------------------------------

             Summary: PARSE_JSON, TRY_PARSE_JSON and the raw format drop 
content after the first JSON value
                 Key: FLINK-41298
                 URL: https://issues.apache.org/jira/browse/FLINK-41298
             Project: Flink
          Issue Type: Bug
          Components: API / Core, Table SQL / Runtime
    Affects Versions: 2.2.0, 2.3.0, 2.1.0
            Reporter: Ramin Gharib


{{PARSE_JSON}} reads the first JSON value in its input and stops. It never 
checks that the input ends there, so anything after that value is dropped 
without an error. Two JSON documents in one string come back as the first one, 
and the second is lost.

{noformat}
SELECT JSON_STRING(PARSE_JSON('{"a":1} xyz'));      -- {"a":1}, expected an 
error
SELECT JSON_STRING(PARSE_JSON('[1]]'));             -- [1], expected an error
SELECT JSON_STRING(PARSE_JSON('{"a":1}{"a":2}'));   -- {"a":1}, expected an 
error
SELECT JSON_STRING(TRY_PARSE_JSON('{"a":1} xyz'));  -- {"a":1}, expected NULL
{noformat}

Output of {{BinaryVariantInternalBuilder.parseJson(input, false)}} on the 2.3.0 
jars:

{noformat}
input                 result
{"a":1} xyz           {"a":1}
[1]]                  [1]
{"a":1}{"a":2}        {"a":1}
{"a":1}\n{"a":2}      {"a":1}
1 2                   1
hello                 JsonParseException: Unrecognized token 'hello'
{noformat}

Two entry points on master reach this code:

||Entry point||Builder method||
|{{PARSE_JSON}}, {{TRY_PARSE_JSON}}|{{parseJson(byte[], boolean)}}, through 
{{SqlJsonUtils.parseJsonToVariant}}|
|{{raw}} format, VARIANT column|{{parseJson(String, boolean)}}, in 
{{RawFormatDeserializationSchema}}|

Both overloads end the same way:

{code:java}
try (JsonParser parser = JSON_FACTORY.createParser(bytes)) {
    parser.nextToken();
    return parseJson(parser, allowDuplicateKeys); // returns after one value
}
{code}

The same class already rejects content glued to a number, with "Unexpected 
content after the JSON number". It does not check what follows the whole value.

*Proposal*

In {{parseJson(String, boolean)}} and {{parseJson(byte[], boolean)}}, call 
{{parser.nextToken()}} once more after the value is built, and throw a 
{{JsonParseException}} if it is not null. Then {{PARSE_JSON}} fails, 
{{TRY_PARSE_JSON}} returns NULL, and the {{raw}} format reports a 
deserialization error for the record. Whitespace after the value stays allowed, 
because Jackson skips it.

{{parseJson(JsonParser, boolean)}} stays as it is. Its caller owns the parser 
and decides what may follow.

*Relation to FLINK-40225*

FLINK-40225 asked for the same check in {{IS JSON}}, {{JSON_VALUE}} and the 
other JSON functions. It was closed as Won't Fix: it breaks running jobs, and 
lenient parsing is the default in Jackson and Spark. This case differs in two 
ways:
# {{PARSE_JSON}} stores what it parsed. The dropped text is lost data, not a 
looser validity check. A {{raw}} record that holds newline-delimited JSON 
documents keeps only the first one.
# The {{raw}} format's VARIANT support is new in 2.4.0, which is not released 
yet. Making it strict now breaks no running job.

*Open question*

{{PARSE_JSON}} has been out since 2.1.0, so rejecting trailing content turns 
input that parses today into an error. Should that part go to the dev@ list 
first, as FLINK-40225 suggests, while the {{raw}} format gets the check for 
2.4.0?




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to