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
9#include <regex>
10
11using namespace duckdb;
12using namespace std;
13
14EmptyNeedleRemovalRule::EmptyNeedleRemovalRule(ExpressionRewriter &rewriter) : Rule(rewriter) {
15 // match on a FunctionExpression that has a foldable ConstantExpression
16 auto func = make_unique<FunctionExpressionMatcher>();
17 func->matchers.push_back(make_unique<ExpressionMatcher>());
18 func->matchers.push_back(make_unique<ExpressionMatcher>());
19 func->policy = SetMatcher::Policy::SOME;
20
21 unordered_set<string> functions = {"prefix", "contains", "suffix"};
22 func->function = make_unique<ManyFunctionMatcher>(functions);
23 root = move(func);
24}
25
26unique_ptr<Expression> EmptyNeedleRemovalRule::Apply(LogicalOperator &op, vector<Expression *> &bindings,
27 bool &changes_made) {
28 auto root = (BoundFunctionExpression *)bindings[0];
29 assert(root->children.size() == 2);
30 auto prefix_expr = bindings[2];
31
32 // the constant_expr is a scalar expression that we have to fold
33 if (!prefix_expr->IsFoldable()) {
34 return nullptr;
35 }
36 assert(root->return_type == TypeId::BOOL);
37
38 auto prefix_value = ExpressionExecutor::EvaluateScalar(*prefix_expr);
39
40 if (prefix_value.is_null) {
41 return make_unique<BoundConstantExpression>(Value(TypeId::BOOL));
42 }
43
44 assert(prefix_value.type == prefix_expr->return_type);
45 string needle_string = string(((string_t)prefix_value.str_value).GetData());
46
47 /* PREFIX('xyz', '') is TRUE, PREFIX(NULL, '') is NULL, so rewrite PREFIX(x, '') to (CASE WHEN x IS NOT NULL THEN
48 * TRUE ELSE NULL END) */
49 if (needle_string.empty()) {
50 auto if_ = make_unique<BoundOperatorExpression>(ExpressionType::OPERATOR_IS_NOT_NULL, TypeId::BOOL);
51 if_->children.push_back(bindings[1]->Copy());
52 auto case_ =
53 make_unique<BoundCaseExpression>(move(if_), make_unique<BoundConstantExpression>(Value::BOOLEAN(true)),
54 make_unique<BoundConstantExpression>(Value(TypeId::BOOL)));
55 return move(case_);
56 }
57
58 return nullptr;
59}
60