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

ASF GitHub Bot updated FLINK-40241:
-----------------------------------
    Labels: pull-request-available  (was: )

> Support VARIANT as column type in the 'raw' format
> --------------------------------------------------
>
>                 Key: FLINK-40241
>                 URL: https://issues.apache.org/jira/browse/FLINK-40241
>             Project: Flink
>          Issue Type: Sub-task
>          Components: Formats (JSON, Avro, Parquet, ORC, SequenceFile)
>            Reporter: Ramin Gharib
>            Assignee: Ramin Gharib
>            Priority: Major
>              Labels: pull-request-available
>
> *part to* FLINK-37922,  *relates to* FLINK-40218 , FLINK-39605,
> The {{raw}} format is the only way to read a topic or a file that carries no 
> schema. VARIANT is the type Flink added for semi-structured data. The two 
> cannot be combined today.
> {code:sql}
> CREATE TABLE events (payload VARIANT) WITH ('format' = 'raw', ...);
> {code}
> fails at plan time with:
> {noformat}
> org.apache.flink.table.api.ValidationException:
>   The 'raw' format doesn't supports 'VARIANT' as column type.
> {noformat}
> {{RawFormatFactory#supportedTypes}} lists CHAR, VARCHAR, BINARY, VARBINARY, 
> RAW, BOOLEAN, TINYINT, SMALLINT, INTEGER, BIGINT, FLOAT and DOUBLE. VARIANT 
> is absent, and the converter switches in {{RawFormatDeserializationSchema}} 
> and {{RawFormatSerializationSchema}} both end in 
> {{UnsupportedOperationException}} for anything off that list.
> Two consequences:
>  # *Reading.* A schemaless JSON topic must be declared as {{{}STRING{}}}, and 
> every query has to call {{PARSE_JSON}} on it. The schema then describes the 
> column as text, which is not what it holds. Catalogs, {{DESCRIBE}} and 
> downstream consumers all see the wrong type, and the parse is repeated per 
> query instead of happening once at the source.
>  # *Writing.* A VARIANT column cannot be written to a schemaless sink at all. 
> {{INSERT INTO raw_sink SELECT v FROM ...}} is rejected, so the value has to 
> be flattened with {{JSON_STRING}} first. Every other format that gains 
> VARIANT support will accept it directly.
> *Proposed change*
> Treat a {{raw}} VARIANT column as "the bytes are a JSON document", decoded 
> with the existing {{raw.charset}} option. Read is {{PARSE_JSON}} over the 
> decoded bytes. Write is {{Variant#toJson}} encoded back with the same charset.
> {code:java}
> // RawFormatFactory#supportedTypes
> LogicalTypeRoot.VARIANT,
> {code}
> {code:java}
> // RawFormatDeserializationSchema#createConverter
> case VARIANT:
>     // new String(data, charset), then
>     // BinaryVariantInternalBuilder.parseJson(json, false)
>     return createVariantConverter(charsetName);
> // RawFormatDeserializationSchema#createDataLengthValidator
> case VARIANT:              // variable length, as CHAR / VARBINARY / RAW
>     return data -> {};
> {code}
> {code:java}
> // RawFormatSerializationSchema#createConverter
> case VARIANT:
>     return row -> row.getVariant(0).toJson().getBytes(charset);
> {code}
> The deserializer returns {{{}org.apache.flink.types.variant.Variant{}}}, 
> which is what {{RowData#getVariant}} already contracts for, so no new data 
> structure is needed. Docs need one row in the type table of 
> {{docs/content/docs/connectors/table/formats/raw.md}} and its {{content.zh}} 
> counterpart.
> *Why JSON text and not the binary variant encoding*
> A variant value is two byte arrays, {{value}} and {{{}metadata{}}}. A {{raw}} 
> column carries exactly one {{{}byte[]{}}}. Interpreting the bytes as the 
> Parquet/Spark variant binary encoding would mean inventing a framing to pack 
> both blobs into one array, which is precisely the kind of wire convention the 
> {{raw}} format exists to avoid. JSON text is the only interpretation that 
> needs no new convention. Formats that can carry two fields, such as Avro or 
> Protobuf, are the right home for the binary encoding.
> *Design notes*
>  * *Duplicate keys.* Parse with {{{}allowDuplicateKeys = false{}}}, matching 
> the single-argument {{PARSE_JSON}} default. A format option could expose this 
> later if asked for.
>  * *Malformed input.* Throw, consistent with how the fixed-width types reject 
> wrong-length input. The {{raw}} format has no {{ignore-parse-errors}} option, 
> and adding one is a separate discussion.
>  * *Options.* {{raw.charset}} applies. {{raw.endianness}} is meaningless for 
> VARIANT, exactly as it is for CHAR and VARBINARY.
>  * *Round-trip is value-lossless, not byte-lossless.* {{PARSE_JSON}} followed 
> by {{toJson}} normalizes whitespace, key order and number spelling, so output 
> bytes need not equal input bytes. Tests must assert on the parsed value, not 
> on the bytes.
>  * *Line delimiter.* The {{raw.line-delimiter}} option from FLINK-39401 
> composes for free, which makes newline-delimited JSON readable as a stream of 
> VARIANT rows.
>  * *Key formats.* A VARIANT column has no canonical byte form, so it is a 
> poor choice for a key format. No special handling is proposed.
>  * *Efficiency.* The direct implementation materializes a {{String}} before 
> parsing, because {{BinaryVariantInternalBuilder#parseJson}} accepts nothing 
> else. FLINK-40218 removes that copy by letting a caller drive the builder 
> from its own token source. This ticket is a direct beneficiary and should 
> adopt it once available.
> *Compatibility*
> Purely additive. No existing type changes behavior. The only new failure mode 
> appears where the plan previously failed with a {{{}ValidationException{}}}.
> *Verifying this change*
>  * {{{}RawFormatFactoryTest{}}}: a VARIANT column is accepted, unsupported 
> types are still rejected, multiple physical columns are still rejected.
>  * {{{}RawFormatSerDeSchemaTest{}}}: round-trip an object, an array and each 
> JSON scalar form. A null message yields a null field. A non-UTF-8 charset 
> works. Malformed JSON raises a deserialization error. Serializing a VARIANT 
> produced by {{PARSE_JSON}} yields bytes that parse back to an equal value.
>  * {{{}RawFormatLineDelimiterTest{}}}: a newline-delimited JSON message 
> produces one VARIANT row per line.



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

Reply via email to