[FLINK-40399][state] Separate SavepointKeyFilter runtime contract from push-down planning - #28982
[FLINK-40399][state] Separate SavepointKeyFilter runtime contract from push-down planning#28982soin08 wants to merge 9 commits into
Conversation
…m push-down planning SavepointKeyFilter carried two unrelated responsibilities: the contract the savepoint scan needs, and the algebra used only while combining predicates during filter push-down. The second group was public and @experimental despite being unusable outside push-down translation, to the point that its javadoc had to disclaim it - "Used only while combining filters during push-down translation, not during the scan." Split the two: * org.apache.flink.state.api.filter.SavepointKeyFilter keeps only what the scan needs - test() and getExactKeys() - plus the public factories. * org.apache.flink.state.table.filter.SavepointKeyFilterPlan carries the planning algebra (isEmpty, bounds, intersect, filterKeys, empty), next to the connector that is its only user. SavepointKeyFilterPlan and BoundInfo are public only so the connector in org.apache.flink.state.table can reach them, and are marked @internal. The unit tests are split the same way: the plan algebra cases move to SavepointKeyFilterPlanTest, leaving SavepointFilterTranslatorTest to cover only expression translation. No behaviour change.
Adds cases for savepoint key filter push-down behaviour that had no test, most importantly that predicates which cannot be pushed are returned in remaining() so the runtime still evaluates them. Nothing covered that before: every apply() case asserted remaining() was empty, so a regression there would have silently dropped rows. Also covered: an untranslatable child aborting the whole AND/OR, "key = NULL" whose literal has no readable value, non-Comparable literals on a BYTES key, the two comparison flip directions that were missing, comparison arity, and at SQL level the upper-bound predicates, which had no end-to-end test at all. Finally, every test states the predicate under test as a leading comment, so the SQL shape being exercised is readable without decoding the expression builders.
| } | ||
|
|
||
| @Test | ||
| void testOrOfExactAndRangeOnKeyIsNotPushedDownButReturnsCorrectResult() throws Exception { |
There was a problem hiding this comment.
this will be supported in the next MR
| } | ||
|
|
||
| @Test | ||
| void testOrOfTwoRangesOnKeyIsNotPushedDownButReturnsCorrectResult() throws Exception { |
There was a problem hiding this comment.
also will be supported in the next MR
|
@flinkbot run azure |
|
fyi: merge commit is always a blocker |
|
Thanks for the efforts! Shrinking the public API surface here is the right direction, good call splitting the runtime contract from the push-down-only algebra. That said, Flink's existing filter push-down connectors (e.g. Right now Could this be done without the duplication by keeping the intersect/bounds combining logic as private computation inside |
|
Thanks for fixing, the code looks good but the description is stale. |
What is the purpose of the change
SavepointKeyFiltercurrently combines two responsibilities:testandgetExactKeys); andisEmpty, bounds, intersection, key filtering, andBoundInfo).The planning methods are public even though DataStream API implementations do not use them. This change keeps only the runtime contract on
SavepointKeyFilterand moves predicate-combination state intoSavepointFilterTranslator.There is no change to the supported predicates or their push-down behavior.
Brief change log
SavepointKeyFilterfocused ontest(K),getExactKeys(), and theexact(...)/range(...)factories.BoundInfo, andEmptyKeyFilter, without introducing a separate table-side filter-plan hierarchy.KeyFilterPlaninsideSavepointFilterTranslatorwhile walking expressions. It performs the existing intersection logic and creates the finalSavepointKeyFilter.exact(...)orSavepointKeyFilter.range(...)only after translation.ExactKeyFilterandRangeKeyFilterso they contain only runtime filtering behavior. An empty exact-key set now represents a filter that matches nothing.empty()factory and planning-method references.Verifying this change
The tests cover:
remaining()so they are still evaluated;AND/ORpush-down;The existing limitation is preserved:
ORonly combines finite exact-key predicates. For example,k = 5 OR k < 10and disjunctions of ranges are not pushed down and remain for normal SQL evaluation.Does this pull request potentially affect one of the following parts:
@Experimental). Planning-only methods andempty()are removed fromSavepointKeyFilter; its runtime contract (test,getExactKeys) and exact/range factories remain.Documentation
docs/contentanddocs/content.zhfor the reduced interface.Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Claude Opus 5)
Generated-by: Codex (GPT-5)