1#include "duckdb/optimizer/rule/empty_needle_removal.hpp"
2
3#include "duckdb/execution/expression_executor.hpp"
4#include "duckdb/planner/expression/bound_function_expression.hpp"
5#include "duckdb/planner/expression/bound_constant_expression.hpp"
6#include "duckdb/planner/expression/bound_operator_expression.hpp"
7#include "duckdb/planner/expression/bound_case_expression.hpp"
8#include "duckdb/optimizer/expression_rewriter.hpp"
9
10namespace duckdb {
11
12EmptyNeedleRemovalRule::EmptyNeedleRemovalRule(ExpressionRewriter &rewriter) : Rule(rewriter) {
13 // match on a FunctionExpression that has a foldable ConstantExpression
14 auto func = make_uniq<FunctionExpressionMatcher>();
15 func->matchers.push_back(x: make_uniq<ExpressionMatcher>());
16 func->matchers.push_back(x: make_uniq<ExpressionMatcher>());
17 func->policy = SetMatcher::Policy::SOME;
18
19 unordered_set<string> functions = {"prefix", "contains", "suffix"};
20 func->function = make_uniq<ManyFunctionMatcher>(args&: functions);
21 root = std::move(func);
22}
23
24unique_ptr<Expression> EmptyNeedleRemovalRule::Apply(LogicalOperator &op, vector<reference<Expression>> &bindings,
25 bool &changes_made, bool is_root) {
26 auto &root = bindings[0].get().Cast<BoundFunctionExpression>();
27 D_ASSERT(root.children.size() == 2);
28 auto &prefix_expr = bindings[2].get();
29
30 // the constant_expr is a scalar expression that we have to fold
31 if (!prefix_expr.IsFoldable()) {
32 return nullptr;
33 }
34 D_ASSERT(root.return_type.id() == LogicalTypeId::BOOLEAN);
35
36 auto prefix_value = ExpressionExecutor::EvaluateScalar(context&: GetContext(), expr: prefix_expr);
37
38 if (prefix_value.IsNull()) {
39 return make_uniq<BoundConstantExpression>(args: Value(LogicalType::BOOLEAN));
40 }
41
42 D_ASSERT(prefix_value.type() == prefix_expr.return_type);
43 auto &needle_string = StringValue::Get(value: prefix_value);
44
45 // PREFIX('xyz', '') is TRUE
46 // PREFIX(NULL, '') is NULL
47 // so rewrite PREFIX(x, '') to TRUE_OR_NULL(x)
48 if (needle_string.empty()) {
49 return ExpressionRewriter::ConstantOrNull(child: std::move(root.children[0]), value: Value::BOOLEAN(value: true));
50 }
51 return nullptr;
52}
53
54} // namespace duckdb
55