summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibSQL
diff options
context:
space:
mode:
authorGuilherme Gonçalves <ggoncalves@google.com>2021-12-29 11:47:29 -0300
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2022-01-07 10:50:39 +0330
commitf91d471843fe097603a9af42d9cd5a8f84f58f83 (patch)
tree849f3ea8648339ed7c0fbeda41807617166720b0 /Userland/Libraries/LibSQL
parente957c078d58d176a01fb59d9a8c45f3022a532bf (diff)
downloadserenity-f91d471843fe097603a9af42d9cd5a8f84f58f83.zip
LibSQL: Implement LIKE SQL expressions
Diffstat (limited to 'Userland/Libraries/LibSQL')
-rw-r--r--Userland/Libraries/LibSQL/AST/AST.h1
-rw-r--r--Userland/Libraries/LibSQL/AST/Expression.cpp57
-rw-r--r--Userland/Libraries/LibSQL/CMakeLists.txt2
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)