diff options
Diffstat (limited to 'Userland/Libraries/LibSQL')
-rw-r--r-- | Userland/Libraries/LibSQL/AST/AST.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/AST/Expression.cpp | 57 | ||||
-rw-r--r-- | Userland/Libraries/LibSQL/CMakeLists.txt | 2 |
3 files changed, 59 insertions, 1 deletions
diff --git a/Userland/Libraries/LibSQL/AST/AST.h b/Userland/Libraries/LibSQL/AST/AST.h index 7dbcb7c389..20dc9d3184 100644 --- a/Userland/Libraries/LibSQL/AST/AST.h +++ b/Userland/Libraries/LibSQL/AST/AST.h @@ -636,6 +636,7 @@ public: MatchOperator type() const { return m_type; } const RefPtr<Expression>& escape() const { return m_escape; } + virtual Value evaluate(ExecutionContext&) const override; private: MatchOperator m_type; diff --git a/Userland/Libraries/LibSQL/AST/Expression.cpp b/Userland/Libraries/LibSQL/AST/Expression.cpp index ce4c972e0c..ad189c5eb6 100644 --- a/Userland/Libraries/LibSQL/AST/Expression.cpp +++ b/Userland/Libraries/LibSQL/AST/Expression.cpp @@ -4,11 +4,14 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <LibRegex/Regex.h> #include <LibSQL/AST/AST.h> #include <LibSQL/Database.h> namespace SQL::AST { +static const String s_posix_basic_metacharacters = ".^$*[]+\\"; + Value Expression::evaluate(ExecutionContext&) const { return Value::null(); @@ -194,4 +197,58 @@ Value ColumnNameExpression::evaluate(ExecutionContext& context) const return Value::null(); } +Value MatchExpression::evaluate(ExecutionContext& context) const +{ + if (context.result->has_error()) + return Value::null(); + switch (type()) { + case MatchOperator::Like: { + Value lhs_value = lhs()->evaluate(context); + Value rhs_value = rhs()->evaluate(context); + char escape_char = '\0'; + if (escape()) { + auto escape_str = escape()->evaluate(context).to_string(); + if (escape_str.length() != 1) { + context.result->set_error(SQLErrorCode::SyntaxError, "ESCAPE should be a single character"); + return Value::null(); + } + escape_char = escape_str[0]; + } + + // Compile the pattern into a simple regex. + // https://sqlite.org/lang_expr.html#the_like_glob_regexp_and_match_operators + bool escaped = false; + AK::StringBuilder builder; + builder.append('^'); + for (auto c : rhs_value.to_string()) { + if (escape() && c == escape_char && !escaped) { + escaped = true; + } else if (s_posix_basic_metacharacters.contains(c)) { + escaped = false; + builder.append('\\'); + builder.append(c); + } else if (c == '_' && !escaped) { + builder.append('.'); + } else if (c == '%' && !escaped) { + builder.append(".*"); + } else { + escaped = false; + builder.append(c); + } + } + builder.append('$'); + // FIXME: We should probably cache this regex. + auto regex = Regex<PosixBasic>(builder.build()); + auto result = regex.match(lhs_value.to_string(), PosixFlags::Insensitive | PosixFlags::Unicode); + return Value(invert_expression() ? !result.success : result.success); + } + case MatchOperator::Glob: + case MatchOperator::Match: + case MatchOperator::Regexp: + default: + VERIFY_NOT_REACHED(); + } + return Value::null(); +} + } diff --git a/Userland/Libraries/LibSQL/CMakeLists.txt b/Userland/Libraries/LibSQL/CMakeLists.txt index 1bfa86baa1..395890f602 100644 --- a/Userland/Libraries/LibSQL/CMakeLists.txt +++ b/Userland/Libraries/LibSQL/CMakeLists.txt @@ -31,4 +31,4 @@ set(GENERATED_SOURCES ) serenity_lib(LibSQL sql) -target_link_libraries(LibSQL LibCore LibSyntax) +target_link_libraries(LibSQL LibCore LibSyntax LibRegex) |