fix: preserve OR grouping when combining pushed-down filters - #21
fix: preserve OR grouping when combining pushed-down filters#21wombatu-kun wants to merge 1 commit into
Conversation
9eac2c7 to
714f3b6
Compare
714f3b6 to
5901c26
Compare
|
This PR doubles as a fix for a pre-existing failure on |
hi @fightBoxing review and merge it please |
| } | ||
| // IN (not supported yet, requires more complex handling) | ||
| // IN: args[0] is the field reference, args[1..n] are literal values | ||
| else if (funcDef == BuiltInFunctionDefinitions.IN) { |
There was a problem hiding this comment.
Measured with EXPLAIN ... WHERE status IN ('a','b','c'): the planner pushes OR(OR(=,=),=) down to the source, so BuiltInFunctionDefinitions.IN never reaches applyFilters and the new branch isn't hit on the SQL or Table API push-down path.
- Impact: IN was already fully pushed down by the existing OR+EQUALS path before this PR (no leftover Calc above the scan), so the new code adds close to nothing.
- Verified: numeric columns, a 60-value list (no SEARCH/Sarg), Table API
.in(), and both Flink 1.18 and 1.20 behave the same;NOT INexpands toand(<>,<>)and is likewise not hit. Not checked on 2.x. - Ask: add a real SQL case proving the branch is hit. If it can't be, neither the branch nor the
getFilters()accessor added for it is needed.
There was a problem hiding this comment.
Done bf2f0ad - the branch and getFilters() are dropped. The new LanceFilterPushDownSqlTest asserts the OR chain reaches the scan with nothing left in a Calc.
| SupportsFilterPushDown.Result result = source.applyFilters(Collections.singletonList(inExpr)); | ||
|
|
||
| assertEquals(1, result.getAcceptedFilters().size(), "IN predicate should be accepted"); | ||
| assertEquals( |
There was a problem hiding this comment.
#55 on main added @Disabled to testInPredicatePushDown (IN push-down didn't exist then), so after merging, the two assertions you added get skipped with it. Measured on the merged tree: Tests run: 24, Skipped: 1.
- Impact: main gains code with no assertion exercising it, and
getFilters()has no live caller. - Ask: drop the
@Disabledand its import when rebasing. Its stated reason cites theIN (not supported yet)comment, which this PR removes. - Note: moot if the IN branch is dropped per the reachability finding.
There was a problem hiding this comment.
Left to #62. This PR no longer touches LanceReadOptimizationsTest, so the two do not conflict.
|
|
||
| assertEquals(1, result.getAcceptedFilters().size(), "IN predicate should be accepted"); | ||
| assertEquals( | ||
| Collections.singletonList("status IN ('active', 'pending', 'completed')"), |
There was a problem hiding this comment.
The new assertion checks the rendered string, but nothing exercises where that string goes (buildFilterExpression -> readFilter -> Lance scan). Pre-existing in this repo, same for = and LIKE.
- Impact: if Lance rejects some syntax, unit tests stay green and it only fails at runtime on real SQL.
- Ask: add a
WHERE status IN (...)case against a real dataset inLanceSqlITCase, asserting the row count, to show Lance accepts the string. - Note: that class currently holds only factory/options assertions, so this would be its first
executeSqlexecution test.
There was a problem hiding this comment.
Done bf2f0ad - LanceFilterPushDownSqlTest creates a dataset through SQL and compares returned rows for string IN, numeric IN, NOT IN and an escaped apostrophe, asserting the plan alongside each so the case still fails if push-down regresses.
Two deviations. It is a *Test class rather than LanceSqlITCase, because surefire has no <includes> and no failsafe plugin is configured, so *ITCase classes never execute. And it needed --add-opens=java.base/java.nio=ALL-UNNAMED in surefire: these are the first tests here to allocate off-heap Arrow memory, and JDK 17/21 otherwise fail in MemoryUtil.
The disjunctive case found a real defect on main: buildFilterExpression() joined accepted terms without parenthesising them, so under SQL precedence AND captured OR operands and silently dropped a constraint. Fixed in the same commit.
5901c26 to
bf2f0ad
Compare
Summary
This PR started as an explicit
BuiltInFunctionDefinitions.INbranch. That branch is unreachable: Calcite expandsINover a literal list intoOR(=, =, ...)while converting SQL to RelNode, so the predicate is already anORin the abstract syntax tree andapplyFiltersnever sees anINcall. SQLINwas already fully pushed down by the existingORandEQUALShandling, so the branch and thegetFilters()accessor added for it are dropped. Writing an end-to-end test to prove that surfaced a real defect, which is what this PR now fixes.The bug
buildFilterExpression()joined accepted filters withString.join(" AND ", filters)without parenthesising them. The planner rewrites predicates into CNF and hands the conjunctive terms over separately, and a term can itself contain a top-levelOR; sinceANDbinds tighter, joining them bare regroups the predicate.WHERE (status = 'active' AND id = 1) OR status = 'archived'arrives as two OR terms and was combined into:which Lance reads as
status = 'active' OR status = 'archived'. Theid = 1constraint is silently dropped and the scan returns rows that do not match the query. Pre-existing onmain, not introduced by this PR. Each term is now wrapped before joining.Coverage
LanceFilterPushDownSqlTestbuilds a Lance dataset through SQL and compares returned rows rather than rendered strings: stringIN, numericIN,NOT IN(covering the non-standard!=the source emits in place of<>), a disjunctive predicate that regression-tests the fix above, and a literal containing an apostrophe. Each case also asserts the plan, since a row assertion on its own stays green if push-down regresses and Flink evaluates the predicate itself.It is a
*Testclass rather than an addition toLanceSqlITCasebecause*ITCaseclasses never execute: surefire has no<includes>and no failsafe plugin is configured.Build change
Surefire gains
--add-opens=java.base/java.nio=ALL-UNNAMED. Arrow'sMemoryUtilreflects intojava.nio.Buffer.address, which JDK 16+ denies, and these are the first tests in the repository to allocate off-heap Arrow memory. The@{argLine}placeholder preserves the JaCoCo agent.Relationship to #62
#62 re-enables
testInPredicatePushDownby asserting the OR chain the planner produces. This PR leaves that file untouched, so the two do not conflict.