1#include "duckdb/optimizer/rule/like_optimizations.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
7#include <regex>
8
9using namespace duckdb;
10using namespace std;
11
12LikeOptimizationRule::LikeOptimizationRule(ExpressionRewriter &rewriter) : Rule(rewriter) {
13 // match on a FunctionExpression that has a foldable ConstantExpression
14 auto func = make_unique<FunctionExpressionMatcher>();
15 func->matchers.push_back(make_unique<ConstantExpressionMatcher>());
16 func->matchers.push_back(make_unique<ExpressionMatcher>());
17 func->policy = SetMatcher::Policy::SOME;
18 // we only match on LIKE ("~~")
19 func->function = make_unique<SpecificFunctionMatcher>("~~");
20 root = move(func);
21}
22
23unique_ptr<Expression> LikeOptimizationRule::Apply(LogicalOperator &op, vector<Expression *> &bindings,
24 bool &changes_made) {
25 auto root = (BoundFunctionExpression *)bindings[0];
26 auto constant_expr = (BoundConstantExpression *)bindings[1];
27 assert(root->children.size() == 2);
28 if (constant_expr->value.is_null) {
29 return make_unique<BoundConstantExpression>(Value(root->return_type));
30 }
31
32 // the constant_expr is a scalar expression that we have to fold
33 if (!constant_expr->IsFoldable()) {
34 return root->Copy();
35 }
36
37 auto constant_value = ExpressionExecutor::EvaluateScalar(*constant_expr);
38 assert(constant_value.type == constant_expr->return_type);
39 string patt_str = string(((string_t)constant_value.str_value).GetData());
40
41 if (std::regex_match(patt_str, std::regex("[^%_]*[%]+"))) {
42 // Prefix LIKE pattern : [^%_]*[%]+, ignoring underscore
43
44 return ApplyRule(root, PrefixFun::GetFunction(), patt_str);
45
46 } else if (std::regex_match(patt_str, std::regex("[%]+[^%_]*"))) {
47 // Suffix LIKE pattern: [%]+[^%_]*, ignoring underscore
48
49 return ApplyRule(root, SuffixFun::GetFunction(), patt_str);
50
51 } else if (std::regex_match(patt_str, std::regex("[%]+[^%_]*[%]+"))) {
52 // Contains LIKE pattern: [%]+[^%_]*[%]+, ignoring underscore
53
54 return ApplyRule(root, ContainsFun::GetFunction(), patt_str);
55 }
56
57 return nullptr;
58}
59
60unique_ptr<Expression> LikeOptimizationRule::ApplyRule(BoundFunctionExpression *expr, ScalarFunction function,
61 string pattern) {
62 // replace LIKE by an optimized function
63 expr->function = function;
64
65 // removing "%" from the pattern
66 pattern.erase(std::remove(pattern.begin(), pattern.end(), '%'), pattern.end());
67
68 expr->children[1] = make_unique<BoundConstantExpression>(Value(pattern));
69
70 return expr->Copy();
71}
72