|
29 | 29 | #include "duckdb/planner/expression/bound_comparison_expression.hpp" |
30 | 30 | #include "duckdb/planner/expression/bound_conjunction_expression.hpp" |
31 | 31 | #include "duckdb/planner/expression/bound_constant_expression.hpp" |
| 32 | +#include "duckdb/planner/expression/bound_operator_expression.hpp" |
32 | 33 | #include "duckdb/planner/operator/logical_get.hpp" |
33 | 34 |
|
34 | 35 | #include "paimon_catalog.hpp" |
@@ -104,6 +105,72 @@ static std::shared_ptr<paimon::Predicate> TryConvertComparison(const BoundCompar |
104 | 105 | } |
105 | 106 | } |
106 | 107 |
|
| 108 | +static std::shared_ptr<paimon::Predicate> TryConvertOperator(const BoundOperatorExpression &op, LogicalGet &get) { |
| 109 | + // Validate children count per operator type. |
| 110 | + switch (op.type) { |
| 111 | + case ExpressionType::COMPARE_IN: |
| 112 | + case ExpressionType::COMPARE_NOT_IN: |
| 113 | + D_ASSERT(op.children.size() >= 2); |
| 114 | + break; |
| 115 | + default: |
| 116 | + return nullptr; |
| 117 | + } |
| 118 | + |
| 119 | + // We can only deal with column ref as the first child. |
| 120 | + if (op.children[0]->GetExpressionClass() != ExpressionClass::BOUND_COLUMN_REF) { |
| 121 | + return nullptr; |
| 122 | + } |
| 123 | + |
| 124 | + // Get column index and name. |
| 125 | + auto filter_binding_idx = op.children[0]->Cast<BoundColumnRefExpression>().binding.column_index; |
| 126 | + auto col_idx = get.GetColumnIds()[filter_binding_idx]; |
| 127 | + auto paimon_type = PaimonTypeUtils::ConvertFieldType(get.GetColumnType(col_idx)); |
| 128 | + auto field_index = col_idx.GetPrimaryIndex(); |
| 129 | + auto &field_name = get.GetColumnName(col_idx); |
| 130 | + |
| 131 | + switch (op.type) { |
| 132 | + case ExpressionType::COMPARE_IN: |
| 133 | + case ExpressionType::COMPARE_NOT_IN: { |
| 134 | + // Collect literals from children[1..n]. |
| 135 | + std::vector<paimon::Literal> literals; |
| 136 | + for (idx_t i = 1; i < op.children.size(); i++) { |
| 137 | + if (op.children[i]->GetExpressionClass() != ExpressionClass::BOUND_CONSTANT) { |
| 138 | + // Best effort pushdown. |
| 139 | + if (op.type == ExpressionType::COMPARE_NOT_IN) { |
| 140 | + continue; |
| 141 | + } else { |
| 142 | + return nullptr; |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + auto val = op.children[i]->Cast<BoundConstantExpression>().value; |
| 147 | + auto literal = PaimonTypeUtils::ConvertLiteral(val, paimon_type); |
| 148 | + if (!literal) { |
| 149 | + // Same reason as above: best effort pushdown. |
| 150 | + if (op.type == ExpressionType::COMPARE_NOT_IN) { |
| 151 | + continue; |
| 152 | + } else { |
| 153 | + return nullptr; |
| 154 | + } |
| 155 | + } |
| 156 | + literals.push_back(std::move(literal.value())); |
| 157 | + } |
| 158 | + |
| 159 | + if (literals.empty()) { |
| 160 | + return nullptr; |
| 161 | + } |
| 162 | + |
| 163 | + if (op.type == ExpressionType::COMPARE_IN) { |
| 164 | + return paimon::PredicateBuilder::In(field_index, field_name, paimon_type, literals); |
| 165 | + } else { |
| 166 | + return paimon::PredicateBuilder::NotIn(field_index, field_name, paimon_type, literals); |
| 167 | + } |
| 168 | + } |
| 169 | + default: |
| 170 | + return nullptr; |
| 171 | + } |
| 172 | +} |
| 173 | + |
107 | 174 | // Forward declaration for mutual recursion with TryConvertConjunction. |
108 | 175 | static std::shared_ptr<paimon::Predicate> TryConvertExpression(const Expression &expr, LogicalGet &get); |
109 | 176 |
|
@@ -149,6 +216,8 @@ static std::shared_ptr<paimon::Predicate> TryConvertExpression(const Expression |
149 | 216 | return TryConvertComparison(expr.Cast<BoundComparisonExpression>(), get); |
150 | 217 | case ExpressionClass::BOUND_CONJUNCTION: |
151 | 218 | return TryConvertConjunction(expr.Cast<BoundConjunctionExpression>(), get); |
| 219 | + case ExpressionClass::BOUND_OPERATOR: |
| 220 | + return TryConvertOperator(expr.Cast<BoundOperatorExpression>(), get); |
152 | 221 | default: |
153 | 222 | return nullptr; |
154 | 223 | } |
|
0 commit comments