1#include "duckdb/common/exception.hpp"
2#include "duckdb/common/vector_operations/vector_operations.hpp"
3#include "duckdb/function/scalar/string_functions.hpp"
4
5using namespace std;
6
7namespace duckdb {
8
9static bool like_operator(const char *s, const char *pattern, const char *escape);
10
11struct LikeEscapeOperator {
12 template <class TA, class TB, class TC> static inline bool Operation(TA str, TB pattern, TC escape) {
13 // Only one escape character should be allowed
14 if (escape.GetSize() > 1) {
15 throw SyntaxException("Invalid escape string. Escape string must be empty or one character.");
16 }
17 return like_operator(str.GetData(), pattern.GetData(), escape.GetData());
18 }
19};
20
21struct NotLikeEscapeOperator {
22 template <class TA, class TB, class TC> static inline bool Operation(TA str, TB pattern, TC escape) {
23 return !LikeEscapeOperator::Operation(str, pattern, escape);
24 }
25};
26
27struct LikeOperator {
28 template <class TA, class TB, class TR> static inline TR Operation(TA str, TB pattern) {
29 return like_operator(str.GetData(), pattern.GetData(), nullptr);
30 }
31};
32
33struct NotLikeOperator {
34 template <class TA, class TB, class TR> static inline TR Operation(TA str, TB pattern) {
35 return !like_operator(str.GetData(), pattern.GetData(), nullptr);
36 ;
37 }
38};
39
40bool like_operator(const char *s, const char *pattern, const char *escape) {
41 const char *t, *p;
42
43 t = s;
44 for (p = pattern; *p && *t; p++) {
45 if (escape && *p == *escape) {
46 p++;
47 if (*p != *t) {
48 return false;
49 }
50 t++;
51 } else if (*p == '_') {
52 t++;
53 } else if (*p == '%') {
54 p++;
55 while (*p == '%') {
56 p++;
57 }
58 if (*p == 0) {
59 return true; /* tail is acceptable */
60 }
61 for (; *p && *t; t++) {
62 if (like_operator(t, p, escape)) {
63 return true;
64 }
65 }
66 if (*p == 0 && *t == 0) {
67 return true;
68 }
69 return false;
70 } else if (*p == *t) {
71 t++;
72 } else {
73 return false;
74 }
75 }
76 if (*p == '%' && *(p + 1) == 0) {
77 return true;
78 }
79 return *t == 0 && *p == 0;
80} // namespace duckdb
81
82// This can be moved to the scalar_function class
83template <typename Func> static void like_escape_function(DataChunk &args, ExpressionState &state, Vector &result) {
84 assert(args.column_count() == 3 && args.data[0].type == TypeId::VARCHAR && args.data[1].type == TypeId::VARCHAR &&
85 args.data[2].type == TypeId::VARCHAR);
86 auto &str = args.data[0];
87 auto &pattern = args.data[1];
88 auto &escape = args.data[2];
89
90 TernaryExecutor::Execute<string_t, string_t, string_t, bool>(
91 str, pattern, escape, result, args.size(), Func::template Operation<string_t, string_t, string_t>);
92}
93
94void LikeFun::RegisterFunction(BuiltinFunctions &set) {
95 set.AddFunction(ScalarFunction("~~", {SQLType::VARCHAR, SQLType::VARCHAR}, SQLType::BOOLEAN,
96 ScalarFunction::BinaryFunction<string_t, string_t, bool, LikeOperator, true>));
97 set.AddFunction(ScalarFunction("!~~", {SQLType::VARCHAR, SQLType::VARCHAR}, SQLType::BOOLEAN,
98 ScalarFunction::BinaryFunction<string_t, string_t, bool, NotLikeOperator, true>));
99}
100
101void LikeEscapeFun::RegisterFunction(BuiltinFunctions &set) {
102 set.AddFunction({"like_escape"}, ScalarFunction({SQLType::VARCHAR, SQLType::VARCHAR, SQLType::VARCHAR},
103 SQLType::BOOLEAN, like_escape_function<LikeEscapeOperator>));
104 set.AddFunction({"not_like_escape"}, ScalarFunction({SQLType::VARCHAR, SQLType::VARCHAR, SQLType::VARCHAR},
105 SQLType::BOOLEAN, like_escape_function<NotLikeEscapeOperator>));
106}
107} // namespace duckdb
108