[FLINK-40241][table-runtime] Support VARIANT as column type in the 'raw' format - #29010
[FLINK-40241][table-runtime] Support VARIANT as column type in the 'raw' format#29010raminqaf wants to merge 1 commit into
Conversation
31f70e1 to
3b54633
Compare
| return parseFromBytes | ||
| ? BinaryVariantInternalBuilder.parseJson(data, false) | ||
| : BinaryVariantInternalBuilder.parseJson( | ||
| new String(data, charset), false); |
There was a problem hiding this comment.
I'm curious why you're not using the charset in both paths.
If the user configures anything that isn't UTF-8, we'd respect and enforce what they declared.
If the user configures it to use UTF-8, we give the data to Jackson which IIUC will do it's own pattern-based encoding detection. So if Jackson doesn't think the data looks like UTF-8, the UTF-8 config would be ignored.
Is that intentional? (Am I mis-reading the intent of the config?)
There was a problem hiding this comment.
Good catch, and no, that wasn't intentional. You read it right.
The byte path handed raw bytes to Jackson, whose createParser(byte[]) runs its own RFC 4627 encoding auto-detection. I confirmed it parses UTF-16 bytes even when raw.charset is UTF-8, so the declared charset was not enforced on that path, while every non-UTF-8 charset was.
Fixed by always decoding with the declared charset before parsing. UTF-8 is now enforced exactly like every other charset, and read is symmetric with write.
I also dropped the now-unused parseJson(byte[]) overload, so this no longer touches flink-core.
There was a problem hiding this comment.
Do you think we need to update the catch here to cover VariantTypeException that would be possible now?
There was a problem hiding this comment.
Good point. VariantTypeException extends RuntimeException, so it wasn't caught here. Added
3b54633 to
1fd016f
Compare
…aw' format The 'raw' format now accepts a single VARIANT column, treating the bytes as a JSON document. On read the bytes are decoded with 'raw.charset' and parsed like PARSE_JSON with duplicate keys rejected; on write the value is rendered with Variant#toJson and encoded with the same charset. The round trip is value-lossless but not byte-lossless: insignificant whitespace is dropped and object keys are ordered. 'raw.endianness' does not apply to VARIANT. Combined with 'raw.line-delimiter' this reads and writes newline-delimited JSON, one row per line.
1fd016f to
6ebb456
Compare
|
@flinkbot run azure |
What is the purpose of the change
The
rawformat is for reading and writing schemaless topics and files, andVARIANTis Flink's type for semi-structured data, but the two could not be combined:CREATE TABLE t (payload VARIANT) WITH ('format' = 'raw', ...)failed at plan time. Users had to declare the column asSTRINGand callPARSE_JSONin every query on read, and flatten withJSON_STRINGon write, which misrepresents the column type in catalogs and downstream consumers.This change makes the
rawformat accept a singleVARIANTcolumn, treating the bytes as a JSON document. The change is purely additive; no existing type behavior changes.Brief change log
LogicalTypeRoot.VARIANTtoRawFormatFactory's supported types.Variant, matchingPARSE_JSONdefaults (duplicate object keys rejected, malformed JSON fails the job). For the default UTF-8 the bytes are parsed directly via a new@Internal BinaryVariantInternalBuilder#parseJson(byte[])overload, letting Jackson decode straight from the byte array with no intermediateString. Other charsets are decoded to aStringwithraw.charsetfirst, so the option stays honored and read is symmetric with write.VariantwithVariant#toJson, encoded withraw.charset.Verifying this change
This change added tests and can be verified as follows:
RawFormatFactoryTest: aVARIANTcolumn is accepted and produces the expected schemas.RawFormatSerDeSchemaTest: value-lossless round trips for objects, arrays, and JSON scalars; SQLNULL; a UTF-16 round trip (exercises the decode-then-parse branch); malformed JSON, duplicate keys, and empty message all raiseDeserializationException.RawFormatLineDelimiterTest: newline-delimited JSON round trips one row per line when combined withraw.line-delimiter.Does this pull request potentially affect one of the following parts:
@Public(Evolving): no (the new builder method is@Internal)VARIANTcolumns on the raw read/write path)Documentation
Notes for reviewers
Variant#toJsondrops insignificant whitespace and orders object keys.raw.endiannessdoes not apply toVARIANT.Variant#toJson().getBytes(charset). A byte-direct emitter would avoid the intermediateString, but it needs a new flink-coreVariantAPI with a byte-level JSON escaper, so it is deferred as a follow-up. The read-side byte path was taken here becausecreateParser(byte[])already exists and is algorithmically faster, not just one fewer allocation.Variantfrom a token source) is complementary but targets callers that have already tokenized their JSON; it does not help this bytes-in path or the write side, so it is out of scope here.Was generative AI tooling used to co-author this PR?