[core][flink][python] Support nullable primary keys - #9094
Conversation
leaves12138
left a comment
There was a problem hiding this comment.
I found a blocking Flink compatibility issue with the default streaming read path.
Because a nullable primary key is intentionally omitted from the Flink CatalogTable schema, Flink sees no primary-key constraint. However, BaseDataTableSource#getChangelogMode() still returns an upsert changelog for the default unbounded primary-key source (UPDATE_AFTER/DELETE without UPDATE_BEFORE). Flink therefore rejects even a plain streaming SELECT * FROM T during planning:
Table 'PAIMON.default.T' produces a changelog stream that contains UPDATE_AFTER but no UPDATE_BEFORE. This requires defining a primary key constraint on the table.
I reproduced this on this PR with a table created using 'primary-key'='id' and 'primary-key.nullable'='true'. The same query can be planned with changelog-producer=input, because that mode includes UPDATE_BEFORE, but the default streaming path is currently unusable. Please adapt the changelog/planner integration and add a Flink IT case, or explicitly constrain the feature if nullable keys cannot support default streaming reads.
There is also a cross-language compatibility gap worth addressing or documenting: current paimon-rust and paimon-cpp schema creation normalize primary-key fields to non-nullable unconditionally. Their comparators already handle nulls, but tables created or evolved through those clients cannot currently preserve the nullable-primary-key schema semantics. Please define the compatibility/versioning expectation and add cross-implementation coverage or coordinated follow-ups, especially for Java/PyPaimon/Rust/C++ table interchange.
|
Thanks for catching this. Addressed in For Flink, I chose to constrain the unsupported combination instead of advertising I added both a direct changelog-mode test and a Flink planner IT case covering the rejected default path and the supported For Rust and C++, agreed that schema normalization and cross-implementation interchange coverage are still needed. I will coordinate those as follow-up work so this PR can remain focused on the Java/PyPaimon/Flink implementation. |
leaves12138
left a comment
There was a problem hiding this comment.
Verified the follow-up fix in b3e73b725d.
The early rejection is the correct behavior for the default nullable-key streaming path: it avoids advertising UPDATE_BEFORE records that the source does not produce, while preserving batch reads, insert-only reads, and full-changelog producers. The new planner and changelog-mode tests pass locally with both the Flink 1.x and Flink 2.x profiles.
The Rust/C++ schema-normalization work can be handled in coordinated follow-ups as discussed. LGTM.
6fe2438 to
11ed5a8
Compare
bcaf300 to
b067b42
Compare
What changed
primary-key.nullabletable option, disabled by defaultWhy
Some upstream systems can produce null components in logical merge keys. Paimon previously rewrote every primary-key field to
NOT NULL, preventing true primary-key tables from representing and consistently merging these records.With the option enabled, null components use null-safe equality, so repeated records for keys such as
(1, NULL)merge as the same key.User impact
Existing tables remain unchanged because the option defaults to
false. Tables that opt in can write, compact, read, update, and delete records whose primary-key components are null. The option is immutable after the table has snapshots.For Flink, nullable primary keys are declared with the
primary-keytable option because Flink SQL primary-key constraints implyNOT NULL.Validation
SchemaBuilderTest,SchemaValidationTest#testNullablePrimaryKeyRequiresPrimaryKeyTable, andPrimaryKeySimpleTableTest#testNullablePrimaryKey(7 tests)FlinkCatalogTest#testNullablePrimaryKeyExposedAsTableOptionpaimon-docsgenerate-docsprofile