Conversation
applyOptimizations combined the remaining WHERE clauses of an outer-join query into one plain AND, dropping the residual marker on predicates that were already pushed into a subquery. The next optimization pass treated them as new predicates and pushed them again, so optimizeQuery only stopped at its iteration limit and left duplicated predicates in the subquery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review. 📝 WalkthroughWalkthroughThe optimizer now combines regular and residual WHERE clauses separately. It preserves residual markers when combining multiple residual clauses. A LEFT JOIN test checks which filters remain in the main query and which filter is pushed into a source subquery. ChangesResidual WHERE handling
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Suggested reviewers: Merge Risk: ⚪ Minimal · up to The change preserves residual predicates to prevent repeated pushdown during optimization. No actionable merge-blocking risk is identified; merge after normal checks pass. Architecture SummaryArchitecture risk: 🔵 Low · up to The change affects 1 system. Changed systems: Architecture concerns Review detailsSystems and components
Before / after behavior
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🎯 Changes
Follow-up to #1976, found while debugging it. When a query has an outer join and two or more WHERE clauses remain,
optimizeQuerynever converges: it pushes the same predicate into the subquery on every pass untilmaxIterations(10).For a left join with one nullable-side predicate and one active-side predicate:
the optimized
partsubquery ended up as:Cause
In
applyOptimizations, a predicate pushed into an outer-join source is kept as a residual clause (createResidualWhere).applySingleLevelOptimizationskips residual clauses so they aren't pushed again. When more than one clause remains, though, they're merged with:getWhereExpressionunwraps the residual marker, so the mergedand(...)is a plain clause. On the next pass it's split again and the active-side predicate is pushed down again. This repeats every iteration.The query result is still correct (the duplicated conjuncts are idempotent), but it costs up to 10 optimizer passes and leaves redundant filter work in the compiled pipeline.
Fix
Regular and residual clauses are combined separately. The residual group stays wrapped in
createResidualWhere, so the next pass leaves it alone and the loop converges after one push.Tests
New case in
tests/query/optimizer.test.ts(JOIN semantics preservation): a left join with one nullable-side and one active-side predicate. The test asserts that the active-side predicate is pushed into the subquery exactly once and stays residual in the outer query. It fails onmainand passes with this change.✅ Checklist
pnpm test. I ran the fullpackages/dbvitest suite. Two property-test files hit the 5 s timeout on my machine, onmaintoo. With--testTimeout=120000the only failure iscollection-subscription-lifecycle-publicationwith random seed-533529595, which fails the same way onmain.🚀 Release Impact
🤖 Generated with Claude Code
Summary by CodeRabbit