| 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 | |