1 | #include "duckdb/common/exception.hpp" |
2 | #include "duckdb/common/vector_operations/vector_operations.hpp" |
3 | #include "duckdb/function/scalar/string_functions.hpp" |
4 | |
5 | using namespace std; |
6 | |
7 | namespace duckdb { |
8 | |
9 | static bool like_operator(const char *s, const char *pattern, const char *escape); |
10 | |
11 | struct 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 | |
21 | struct 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 | |
27 | struct 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 | |
33 | struct 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 | |
40 | bool 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 |
83 | template <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 | |
94 | void 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 | |
101 | void 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 | |