[FLINK-40490][table] Add runtime serialization and codegen for the UUID type - #29087
[FLINK-40490][table] Add runtime serialization and codegen for the UUID type#29087raminqaf wants to merge 2 commits into
Conversation
…ID type Wire the UUID logical type through the table runtime and code generation so UUID values can be materialized, serialized, and returned to users. Internally a UUID is stored as its canonical 16-byte big-endian encoding, reusing the byte[] representation and getBinary/writeBinary access of BINARY/VARBINARY; the external conversion class is java.util.UUID. This covers InternalSerializers, the binary row and array writers and getters (including the interpreted RowData and ArrayData field getters), CodeGenUtils/GenerateUtils, RexLiteral conversion, the data structure converter, planner size estimation, and constant reduction handling. For the DataStream API this adds UuidSerializer and UuidComparator plus Types.UUID. The comparator orders values by unsigned big-endian bytes, matching the type's defined order rather than the signed java.util.UUID.compareTo. UUID is intentionally not registered for automatic reflective extraction in the DataStream API so that the serializer of existing java.util.UUID fields does not change; users opt in via Types.UUID. Table and SQL do auto-extract UUID via ClassDataTypeConverter, since a plain field otherwise requires an explicit RAW annotation. Casts (FLINK-40487) and comparison and ordering in SQL (FLINK-40488) are handled in separate subtasks.
bbacb85 to
2fc07d2
Compare
| // UUID is intentionally not registered here. Automatic reflective extraction of | ||
| // java.util.UUID to UUID_TYPE_INFO in the DataStream API stays opt-in via Types.UUID to | ||
| // avoid silently changing the serializer of existing java.util.UUID fields (currently | ||
| // handled by Kryo). This is planned to change in the next major Flink version. |
There was a problem hiding this comment.
who and how will remember it when next major Flink version starts?
Should we better have a jira with label something like Flink-3.0?
There was a problem hiding this comment.
| addDefaultDataType( | ||
| java.time.Period.class, DataTypes.INTERVAL(DataTypes.YEAR(4), DataTypes.MONTH())); | ||
| addDefaultDataType(ColumnList.class, DataTypes.DESCRIPTOR()); | ||
| addDefaultDataType(java.util.UUID.class, DataTypes.UUID()); |
There was a problem hiding this comment.
any specific reason for absolute name here?
There was a problem hiding this comment.
Not sure what the convention here Is. most of the java classes here have the absolute name
| // UUID is intentionally not registered here. Automatic reflective extraction of | ||
| // java.util.UUID to UUID_TYPE_INFO in the DataStream API stays opt-in via Types.UUID to | ||
| // avoid silently changing the serializer of existing java.util.UUID fields (currently | ||
| // handled by Kryo). This is planned to change in the next major Flink version. |
There was a problem hiding this comment.
Please open a ticket for this with Fixed Version 3.0
There was a problem hiding this comment.
| /** Returns type information for {@link java.time.Instant}. Supports a null value. */ | ||
| public static final TypeInformation<Instant> INSTANT = BasicTypeInfo.INSTANT_TYPE_INFO; | ||
|
|
||
| /** Returns type information for {@link java.util.UUID}. Supports a null value. */ |
There was a problem hiding this comment.
| /** Returns type information for {@link java.util.UUID}. Supports a null value. */ | |
| /** Returns type information for {@link java.util.UUID}. */ |
the serializer code says:
so there is no reserved value for {@code null}; nullability is handled by wrapping serializers
| import static org.junit.jupiter.params.provider.Arguments.of; | ||
|
|
||
| /** Runtime tests for the {@code UUID} type through the full plan/codegen/execution stack. */ | ||
| class UuidITCase { |
There was a problem hiding this comment.
Never just implement plain ITCases, usually they should extend from a test base. Otherwise a full Flink local cluster is being used instead of performant testing clusters. I would suggest to use SemanticTestBase here or BuiltInFunctionTestBase?
There was a problem hiding this comment.
Removed and converted into semantic tests
| putConverter(LogicalTypeRoot.STRUCTURED_TYPE, RowData.class, identity()); | ||
| putConverter(LogicalTypeRoot.RAW, byte[].class, RawByteArrayConverter::create); | ||
| putConverter(LogicalTypeRoot.RAW, RawValueData.class, identity()); | ||
| putConverter(LogicalTypeRoot.UUID, UUID.class, constructor(UuidUuidConverter::new)); |
There was a problem hiding this comment.
| putConverter(LogicalTypeRoot.UUID, UUID.class, constructor(UuidUuidConverter::new)); | |
| putConverter(LogicalTypeRoot.UUID, UUID.class, constructor(UuidUuidConverter::new)); | |
| putConverter(LogicalTypeRoot.UUID, byte[].class, identity()); |
There was a problem hiding this comment.
Added byte[] to the inputOutputConvertion
- Document the UUID internal representation (byte[]) in the RowData javadoc. - Support byte[] as a conversion class for UUID (its internal representation) and register the identity data structure converter. - Remove the inaccurate "supports a null value" note from Types.UUID, since the serializer has no reserved null value. - Annotate UuidSerializerSnapshot with @internal to satisfy the API annotation architecture rule. - Replace the plain UUID ITCase with a SemanticTestBase based UuidSemanticTest and move the test programs into UuidTestPrograms.
What is the purpose of the change
Second subtask of FLIP-604, building directly on FLINK-40486 (merged as #29041), which introduced the
UUIDlogical type at the type-system layer only. After that change aUUIDvalue still could not be materialized, serialized, or returned at runtime. This change wiresUUIDthrough the table runtime, code generation, and the DataStream type stack soUUIDcolumns work end to end.
Internally a
UUIDis stored as its canonical 16-byte big-endian encoding, reusing thembyte[]representation andgetBinary/writeBinaryaccess already used byBINARY/VARBINARY. The external/default conversion class isjava.util.UUID.Brief change log
LogicalTypeUtils.toInternalConversionClassreturnsbyte[];InternalSerializers,BinaryWriter,BinaryArrayWriter, the interpretedRowData/ArrayDatafield getters, andBinaryArrayDatasizing reuse theBINARY/VARBINARYpath.CodeGenUtils(type term, hashing, row read/write) andGenerateUtilsliteral handling emit thebyte[]path forUUID;RexLiteralUtilconverts ajava.util.UUIDliteral to 16 big-endian bytes.UuidUuidConverter(byte[]<->java.util.UUID), registered inDataStructureConverters.UuidSerializerandUuidComparator, plusTypes.UUID. The comparator uses unsigned big-endian byte order (the type's defined order), not the signedjava.util.UUID.compareTo.UUIDis auto-extracted for Table/SQL viaClassDataTypeConverterbut intentionally not auto-registered for the DataStream API (BasicTypeInfo.TYPES); see below.FlinkRelMdSize(16-byte estimate) andExpressionReducer(skip constant folding, as forVARIANT).Verifying this change
This change added tests and can be verified as follows:
UuidSerializerTest/UuidComparatorTest(flink-core): serialization round-trip and unsigned ordering, including normalized-key consistency; the sorted test data spans the signed-long boundary (0x7FFF...vs0x8000...).DataStructureConvertersTest:byte[]<->java.util.UUIDround-trip.ClassDataTypeConverterTest/DataTypeExtractorTest: Table/SQL auto-extraction of ajava.util.UUIDfield toDataTypes.UUID().UuidITCase(flink-table-planner): end-to-endSELECT,ARRAY[...], multi-rowVALUES,UNION/CASE,MAPvalue, and nestedROW, collectingjava.util.UUIDback through the full plan/codegen/execution stack.Design notes / trade-offs for reviewers
java.util.UUIDfor automatic reflective extraction in the DataStream API would silently switch the serializer of existingjava.util.UUIDfields from Kryo toUuidSerializer, breaking savepoint compatibility on upgrade. DataStream users therefore opt in viaTypes.UUID; automatic extraction is planned for the next major version. Table/SQL does auto-extract, since a plainUUIDfield there otherwise requires an explicit RAW annotation, so there is no silent change.java.util.UUID.compareTo(signed per half).Related tickets (FLIP-604)
UUID <-> STRING/BINARY(16)(note:.print()depends on theto-string cast).
ORDER BY/GROUP BY/ joins);TypeCheckUtils.isComparablestill excludesUUIDin this PR.UUID_V4/UUID_V7functions.RexNodeliteral serde, left as a follow-up as it was forVARIANT.Does this pull request potentially affect one of the following parts:
@Public(Evolving): yes — addsTypes.UUIDandBasicTypeInfo.UUID_TYPE_INFOUuidSerializer;UUIDinternal state uses the byte-array serializerwriters and generated field access for
UUIDCheckpointing, Kubernetes/Yarn, ZooKeeper: no (new type; no state migration for existing jobs)
Documentation
handled in a separate FLIP-604 subtask (FLINK-40494)