[ 
https://issues.apache.org/jira/browse/FLINK-31917?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jane Chan updated FLINK-31917:
------------------------------
    Description: 
JsonSerDeTestUtil#testJsonRoundTrip only checks the equality between spec and 
deserialized object. Some corner cases are detected when serializing the 
deserialized object again.
{code:java}
static <T> T testJsonRoundTrip(SerdeContext serdeContext, T spec, Class<T> 
clazz)
            throws IOException {
        String actualJson = toJson(serdeContext, spec);
        T actual = toObject(serdeContext, actualJson, clazz);

        assertThat(actual).isEqualTo(spec);
        assertThat(actualJson).isEqualTo(toJson(serdeContext, actual)); // this 
will eval some corner cases
        return actual;
    }
{code}
The discovered corner cases are listed as follows.
h5. 1. SerDe for AggregateCall

When deserializing the aggregate call, we should check the JsonNodeType to 
avoid converting null to "null" string.
[https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/AggregateCallJsonDeserializer.java#L64]
h5. Suggested Fix
{code:java}
JsonNode nameNode = jsonNode.required(FIELD_NAME_NAME);
final String name = JsonNodeType.NULL ? null : nameNode.asText();
{code}
h5. 2. SerDe for RexNode

RexNodeJsonSerdeTest#testSystemFunction should create the temporary system 
function instead of the temporary catalog function.
[https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/serde/RexNodeJsonSerdeTest.java#L209]
h5. Suggested Fix

Use functionCatalog#registerTemporarySystemFunction to test.
h5. 3. About RexLiteral type

RexNodeJsonSerdeTest#testRexNodeSerde has a test spec as follows
{code:java}
//This will create the literal with DOUBLE as the literal type, and DECIMAL as 
the broad type of this literal. You can refer to Calcite for more details
rexBuilder.makeExactLiteral(BigDecimal.valueOf(Double.MAX_VALUE), 
FACTORY.createSqlType(SqlTypeName.DOUBLE))
{code}
The RexNodeJsonSerializer uses `typeName`(which is DECIMAL) as the literal's 
type, as a result, the rel data type is serialized as double, but the value is 
serialized as a string (in case lost the precision)
[https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/RexNodeJsonSerializer.java#L197]

And then, during the deserialization, according to the JSON, the deserialized 
literal will assign DOUBLE as the literal type and the broad type of the 
literal.
This will cause the comparison failure
{code:java}
expected: {"kind": "LITERAL", "value": "1.7976931348623157E+308"}
actual: {"kind": "LITERAL", "value": 1.7976931348623157E+308}
{code}
h5. Suggested Fix

SARG is a special case and can be coped first, and for the rest type, we can 
use literal.getType().getSqlTypeName() instead of literal.getTypeName().
{code:java}
// first cope with SARG type
if (literal.getTypeName() == SARG) {
    serializeSargValue(
        (Sarg<?>) value, literal.getType().getSqlTypeName(), gen, 
serializerProvider);
} else {
    serializeLiteralValue(
        value,
        literal.getType().getSqlTypeName(),
        gen,
        serializerProvider);
}
{code}

  was:
JsonSerDeTestUtil#testJsonRoundTrip only checks the equality between spec and 
deserialized object. Some corner cases are detected when serializing the 
deserialized object again.
{code:java}
static <T> T testJsonRoundTrip(SerdeContext serdeContext, T spec, Class<T> 
clazz)
            throws IOException {
        String actualJson = toJson(serdeContext, spec);
        T actual = toObject(serdeContext, actualJson, clazz);

        assertThat(actual).isEqualTo(spec);
        assertThat(actualJson).isEqualTo(toJson(serdeContext, actual)); // this 
will eval some corner cases
        return actual;
    }
{code}
The discovered corner cases are listed as follows.
h5. 1. SerDe for AggregateCall

When deserializing the aggregate call, we should check the JsonNodeType to 
avoid converting null to "null" string.
[https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/AggregateCallJsonDeserializer.java#L64]
h5. Suggested Fix
{code:java}
JsonNode nameNode = jsonNode.required(FIELD_NAME_NAME);
final String name = JsonNodeType.NULL ? null : nameNode.asText();
{code}
h5. 2. SerDe for RexNode

RexNodeJsonSerdeTest#testSystemFunction should create the temporary system 
function instead of the temporary catalog function.
[https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/serde/RexNodeJsonSerdeTest.java#L209]

h5. 3. About RexLiteral type 
RexNodeJsonSerdeTest#testRexNodeSerde has a test spec as follows
{code:java}
//This will create the literal with DOUBLE as the literal type, and DECIMAL as 
the broad type of this literal. You can refer to Calcite for more details
rexBuilder.makeExactLiteral(BigDecimal.valueOf(Double.MAX_VALUE), 
FACTORY.createSqlType(SqlTypeName.DOUBLE))
{code}

The RexNodeJsonSerializer uses `typeName`(which is DECIMAL) as the literal's 
type, as a result, the rel data type is serialized as double, but the value is 
serialized as a string (in case lost the precision)
[https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/RexNodeJsonSerializer.java#L197]

And then, during the deserialization, according to the JSON, the deserialized 
literal will assign DOUBLE as the literal type and the broad type of the 
literal.
This will cause the comparison failure
{code}
expected: {"kind": "LITERAL", "value": "1.7976931348623157E+308"}
actual: {"kind": "LITERAL", "value": 1.7976931348623157E+308}
{code}


> Loss of Idempotence in JsonSerDe Round Trip for AggregateCall and RexNode
> -------------------------------------------------------------------------
>
>                 Key: FLINK-31917
>                 URL: https://issues.apache.org/jira/browse/FLINK-31917
>             Project: Flink
>          Issue Type: Bug
>          Components: Table SQL / Planner
>    Affects Versions: 1.18.0
>            Reporter: Jane Chan
>            Priority: Major
>             Fix For: 1.18.0
>
>
> JsonSerDeTestUtil#testJsonRoundTrip only checks the equality between spec and 
> deserialized object. Some corner cases are detected when serializing the 
> deserialized object again.
> {code:java}
> static <T> T testJsonRoundTrip(SerdeContext serdeContext, T spec, Class<T> 
> clazz)
>             throws IOException {
>         String actualJson = toJson(serdeContext, spec);
>         T actual = toObject(serdeContext, actualJson, clazz);
>         assertThat(actual).isEqualTo(spec);
>         assertThat(actualJson).isEqualTo(toJson(serdeContext, actual)); // 
> this will eval some corner cases
>         return actual;
>     }
> {code}
> The discovered corner cases are listed as follows.
> h5. 1. SerDe for AggregateCall
> When deserializing the aggregate call, we should check the JsonNodeType to 
> avoid converting null to "null" string.
> [https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/AggregateCallJsonDeserializer.java#L64]
> h5. Suggested Fix
> {code:java}
> JsonNode nameNode = jsonNode.required(FIELD_NAME_NAME);
> final String name = JsonNodeType.NULL ? null : nameNode.asText();
> {code}
> h5. 2. SerDe for RexNode
> RexNodeJsonSerdeTest#testSystemFunction should create the temporary system 
> function instead of the temporary catalog function.
> [https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/nodes/exec/serde/RexNodeJsonSerdeTest.java#L209]
> h5. Suggested Fix
> Use functionCatalog#registerTemporarySystemFunction to test.
> h5. 3. About RexLiteral type
> RexNodeJsonSerdeTest#testRexNodeSerde has a test spec as follows
> {code:java}
> //This will create the literal with DOUBLE as the literal type, and DECIMAL 
> as the broad type of this literal. You can refer to Calcite for more details
> rexBuilder.makeExactLiteral(BigDecimal.valueOf(Double.MAX_VALUE), 
> FACTORY.createSqlType(SqlTypeName.DOUBLE))
> {code}
> The RexNodeJsonSerializer uses `typeName`(which is DECIMAL) as the literal's 
> type, as a result, the rel data type is serialized as double, but the value 
> is serialized as a string (in case lost the precision)
> [https://github.com/apache/flink/blob/f9b3e0b7bc0432001b4a197539a0712b16e0b33b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/nodes/exec/serde/RexNodeJsonSerializer.java#L197]
> And then, during the deserialization, according to the JSON, the deserialized 
> literal will assign DOUBLE as the literal type and the broad type of the 
> literal.
> This will cause the comparison failure
> {code:java}
> expected: {"kind": "LITERAL", "value": "1.7976931348623157E+308"}
> actual: {"kind": "LITERAL", "value": 1.7976931348623157E+308}
> {code}
> h5. Suggested Fix
> SARG is a special case and can be coped first, and for the rest type, we can 
> use literal.getType().getSqlTypeName() instead of literal.getTypeName().
> {code:java}
> // first cope with SARG type
> if (literal.getTypeName() == SARG) {
>     serializeSargValue(
>         (Sarg<?>) value, literal.getType().getSqlTypeName(), gen, 
> serializerProvider);
> } else {
>     serializeLiteralValue(
>         value,
>         literal.getType().getSqlTypeName(),
>         gen,
>         serializerProvider);
> }
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to