1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:48:11 +00:00

LibSQL: Implement LIKE SQL expressions

This commit is contained in:
Guilherme Gonçalves 2021-12-29 11:47:29 -03:00 committed by Ali Mohammad Pur
parent e957c078d5
commit f91d471843
5 changed files with 133 additions and 1 deletions

View file

@ -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();
}
}