diff --git a/src/optimizer/pushdown/pushdown_cross_product.cpp b/src/optimizer/pushdown/pushdown_cross_product.cpp index a05da2c27b8..73f7b47ac8f 100644 --- a/src/optimizer/pushdown/pushdown_cross_product.cpp +++ b/src/optimizer/pushdown/pushdown_cross_product.cpp @@ -1,4 +1,6 @@ #include "duckdb/optimizer/filter_pushdown.hpp" +#include "duckdb/planner/expression/bound_comparison_expression.hpp" +#include "duckdb/planner/expression_iterator.hpp" #include "duckdb/planner/operator/logical_comparison_join.hpp" #include "duckdb/planner/operator/logical_cross_product.hpp" @@ -22,6 +24,10 @@ unique_ptr FilterPushdown::PushdownCrossProduct(unique_ptr left_bindings, right_bindings; if (!filters.empty()) { + // We can only push down right side AsOf expressions + // that do not reference the inequality column + vector> asof_filters; + optional_ptr asof_compare; // check to see into which side we should push the filters // first get the LHS and RHS bindings LogicalJoin::GetTableReferences(*op->children[0], left_bindings); @@ -37,16 +43,36 @@ unique_ptr FilterPushdown::PushdownCrossProduct(unique_ptrfilter)); + asof_filters.push_back(std::move(f)); } else { right_pushdown.filters.push_back(std::move(f)); } } else { D_ASSERT(side == JoinSide::BOTH || side == JoinSide::NONE); + // Record the inequality binding for AsOf + if (join_ref_type == JoinRefType::ASOF && ExpressionType::COMPARE_LESSTHAN <= f->filter->type && + f->filter->type <= ExpressionType::COMPARE_GREATERTHANOREQUALTO) { + asof_compare = f->filter->Cast().right.get(); + } // bindings match both: turn into join condition join_expressions.push_back(std::move(f->filter)); } } + // Now that we know what the right side AsOf inequality expression is, + // we can push down any predicates that don't refer to it + for (auto &f : asof_filters) { + bool referenced = false; + ExpressionIterator::EnumerateExpression(f->filter, [&](const Expression &child) { + if (child.Equals(*asof_compare)) { + referenced = true; + } + }); + if (referenced) { + join_expressions.push_back(std::move(f->filter)); + } else { + right_pushdown.filters.push_back(std::move(f)); + } + } } op->children[0] = left_pushdown.Rewrite(std::move(op->children[0])); diff --git a/test/sql/join/asof/test_asof_join_pushdown.test b/test/sql/join/asof/test_asof_join_pushdown.test index ba298378c49..7bcbda159bb 100644 --- a/test/sql/join/asof/test_asof_join_pushdown.test +++ b/test/sql/join/asof/test_asof_join_pushdown.test @@ -79,3 +79,46 @@ ORDER BY ALL ---- 5 6 10 11 + +statement ok +CREATE TABLE issue13899(seq_no INT, amount DECIMAL(10,2)); + +statement ok +INSERT INTO issue13899 VALUES + (1,1.00), + (2,null), + (3,null), + (4,null), + (5,2.00), + (6,null), + (7,null), + (8,3.00), + (9,null), + (10,null), + (11,5.00); + +# Without the pushdown, the filter does not get applied properly +statement ok +PRAGMA disable_verification + +query III +SELECT + a.seq_no, + a.amount, + b.amount +FROM issue13899 AS a +ASOF JOIN issue13899 AS b + ON a.seq_no>=b.seq_no + AND b.amount IS NOT NULL; +---- +1 1.00 1.00 +2 NULL 1.00 +3 NULL 1.00 +4 NULL 1.00 +5 2.00 2.00 +6 NULL 2.00 +7 NULL 2.00 +8 3.00 3.00 +9 NULL 3.00 +10 NULL 3.00 +11 5.00 5.00