Coverage Report

Created: 2026-06-07 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibSQL/AST/Expression.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Jan de Visser <jan@de-visser.net>
3
 * Copyright (c) 2022, the SerenityOS developers.
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <AK/StringView.h>
9
#include <LibRegex/Regex.h>
10
#include <LibSQL/AST/AST.h>
11
#include <LibSQL/Database.h>
12
13
namespace SQL::AST {
14
15
static constexpr auto s_posix_basic_metacharacters = ".^$*[]+\\"sv;
16
17
ResultOr<Value> NumericLiteral::evaluate(ExecutionContext&) const
18
0
{
19
0
    return Value { value() };
20
0
}
21
22
ResultOr<Value> StringLiteral::evaluate(ExecutionContext&) const
23
0
{
24
0
    return Value { value() };
25
0
}
26
27
ResultOr<Value> BooleanLiteral::evaluate(ExecutionContext&) const
28
0
{
29
0
    return Value { value() };
30
0
}
31
32
ResultOr<Value> NullLiteral::evaluate(ExecutionContext&) const
33
0
{
34
0
    return Value {};
35
0
}
36
37
ResultOr<Value> Placeholder::evaluate(ExecutionContext& context) const
38
0
{
39
0
    if (parameter_index() >= context.placeholder_values.size())
40
0
        return Result { SQLCommand::Unknown, SQLErrorCode::InvalidNumberOfPlaceholderValues };
41
0
    return context.placeholder_values[parameter_index()];
42
0
}
43
44
ResultOr<Value> NestedExpression::evaluate(ExecutionContext& context) const
45
0
{
46
0
    return expression()->evaluate(context);
47
0
}
48
49
ResultOr<Value> ChainedExpression::evaluate(ExecutionContext& context) const
50
0
{
51
0
    Vector<Value> values;
52
0
    TRY(values.try_ensure_capacity(expressions().size()));
53
54
0
    for (auto& expression : expressions())
55
0
        values.unchecked_append(TRY(expression->evaluate(context)));
56
57
0
    return Value::create_tuple(move(values));
58
0
}
59
60
ResultOr<Value> BinaryOperatorExpression::evaluate(ExecutionContext& context) const
61
0
{
62
0
    Value lhs_value = TRY(lhs()->evaluate(context));
63
0
    Value rhs_value = TRY(rhs()->evaluate(context));
64
65
0
    switch (type()) {
66
0
    case BinaryOperator::Concatenate: {
67
0
        if (lhs_value.type() != SQLType::Text)
68
0
            return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type()) };
69
70
0
        AK::StringBuilder builder;
71
0
        builder.append(lhs_value.to_byte_string());
72
0
        builder.append(rhs_value.to_byte_string());
73
0
        return Value(builder.to_byte_string());
74
0
    }
75
0
    case BinaryOperator::Multiplication:
76
0
        return lhs_value.multiply(rhs_value);
77
0
    case BinaryOperator::Division:
78
0
        return lhs_value.divide(rhs_value);
79
0
    case BinaryOperator::Modulo:
80
0
        return lhs_value.modulo(rhs_value);
81
0
    case BinaryOperator::Plus:
82
0
        return lhs_value.add(rhs_value);
83
0
    case BinaryOperator::Minus:
84
0
        return lhs_value.subtract(rhs_value);
85
0
    case BinaryOperator::ShiftLeft:
86
0
        return lhs_value.shift_left(rhs_value);
87
0
    case BinaryOperator::ShiftRight:
88
0
        return lhs_value.shift_right(rhs_value);
89
0
    case BinaryOperator::BitwiseAnd:
90
0
        return lhs_value.bitwise_and(rhs_value);
91
0
    case BinaryOperator::BitwiseOr:
92
0
        return lhs_value.bitwise_or(rhs_value);
93
0
    case BinaryOperator::LessThan:
94
0
        return Value(lhs_value.compare(rhs_value) < 0);
95
0
    case BinaryOperator::LessThanEquals:
96
0
        return Value(lhs_value.compare(rhs_value) <= 0);
97
0
    case BinaryOperator::GreaterThan:
98
0
        return Value(lhs_value.compare(rhs_value) > 0);
99
0
    case BinaryOperator::GreaterThanEquals:
100
0
        return Value(lhs_value.compare(rhs_value) >= 0);
101
0
    case BinaryOperator::Equals:
102
0
        return Value(lhs_value.compare(rhs_value) == 0);
103
0
    case BinaryOperator::NotEquals:
104
0
        return Value(lhs_value.compare(rhs_value) != 0);
105
0
    case BinaryOperator::And: {
106
0
        auto lhs_bool_maybe = lhs_value.to_bool();
107
0
        auto rhs_bool_maybe = rhs_value.to_bool();
108
0
        if (!lhs_bool_maybe.has_value() || !rhs_bool_maybe.has_value())
109
0
            return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type()) };
110
111
0
        return Value(lhs_bool_maybe.release_value() && rhs_bool_maybe.release_value());
112
0
    }
113
0
    case BinaryOperator::Or: {
114
0
        auto lhs_bool_maybe = lhs_value.to_bool();
115
0
        auto rhs_bool_maybe = rhs_value.to_bool();
116
0
        if (!lhs_bool_maybe.has_value() || !rhs_bool_maybe.has_value())
117
0
            return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, BinaryOperator_name(type()) };
118
119
0
        return Value(lhs_bool_maybe.release_value() || rhs_bool_maybe.release_value());
120
0
    }
121
0
    default:
122
0
        VERIFY_NOT_REACHED();
123
0
    }
124
0
}
125
126
ResultOr<Value> UnaryOperatorExpression::evaluate(ExecutionContext& context) const
127
0
{
128
0
    Value expression_value = TRY(NestedExpression::evaluate(context));
129
130
0
    switch (type()) {
131
0
    case UnaryOperator::Plus:
132
0
        if (expression_value.type() == SQLType::Integer || expression_value.type() == SQLType::Float)
133
0
            return expression_value;
134
0
        return Result { SQLCommand::Unknown, SQLErrorCode::NumericOperatorTypeMismatch, UnaryOperator_name(type()) };
135
0
    case UnaryOperator::Minus:
136
0
        return expression_value.negate();
137
0
    case UnaryOperator::Not:
138
0
        if (expression_value.type() == SQLType::Boolean) {
139
0
            expression_value = !expression_value.to_bool().value();
140
0
            return expression_value;
141
0
        }
142
0
        return Result { SQLCommand::Unknown, SQLErrorCode::BooleanOperatorTypeMismatch, UnaryOperator_name(type()) };
143
0
    case UnaryOperator::BitwiseNot:
144
0
        return expression_value.bitwise_not();
145
0
    default:
146
0
        VERIFY_NOT_REACHED();
147
0
    }
148
0
}
149
150
ResultOr<Value> ColumnNameExpression::evaluate(ExecutionContext& context) const
151
0
{
152
0
    if (!context.current_row)
153
0
        return Result { SQLCommand::Unknown, SQLErrorCode::SyntaxError, column_name() };
154
155
0
    auto& descriptor = *context.current_row->descriptor();
156
0
    VERIFY(context.current_row->size() == descriptor.size());
157
0
    Optional<size_t> index_in_row;
158
0
    for (auto ix = 0u; ix < context.current_row->size(); ix++) {
159
0
        auto& column_descriptor = descriptor[ix];
160
0
        if (!table_name().is_empty() && column_descriptor.table != table_name())
161
0
            continue;
162
0
        if (column_descriptor.name == column_name()) {
163
0
            if (index_in_row.has_value())
164
0
                return Result { SQLCommand::Unknown, SQLErrorCode::AmbiguousColumnName, column_name() };
165
166
0
            index_in_row = ix;
167
0
        }
168
0
    }
169
0
    if (index_in_row.has_value())
170
0
        return (*context.current_row)[index_in_row.value()];
171
172
0
    return Result { SQLCommand::Unknown, SQLErrorCode::ColumnDoesNotExist, column_name() };
173
0
}
174
175
ResultOr<Value> MatchExpression::evaluate(ExecutionContext& context) const
176
0
{
177
0
    switch (type()) {
178
0
    case MatchOperator::Like: {
179
0
        Value lhs_value = TRY(lhs()->evaluate(context));
180
0
        Value rhs_value = TRY(rhs()->evaluate(context));
181
182
0
        char escape_char = '\0';
183
0
        if (escape()) {
184
0
            auto escape_str = TRY(escape()->evaluate(context)).to_byte_string();
185
0
            if (escape_str.length() != 1)
186
0
                return Result { SQLCommand::Unknown, SQLErrorCode::SyntaxError, "ESCAPE should be a single character" };
187
0
            escape_char = escape_str[0];
188
0
        }
189
190
        // Compile the pattern into a simple regex.
191
        // https://sqlite.org/lang_expr.html#the_like_glob_regexp_and_match_operators
192
0
        bool escaped = false;
193
0
        AK::StringBuilder builder;
194
0
        builder.append('^');
195
0
        for (auto c : rhs_value.to_byte_string()) {
196
0
            if (escape() && c == escape_char && !escaped) {
197
0
                escaped = true;
198
0
            } else if (s_posix_basic_metacharacters.contains(c)) {
199
0
                escaped = false;
200
0
                builder.append('\\');
201
0
                builder.append(c);
202
0
            } else if (c == '_' && !escaped) {
203
0
                builder.append('.');
204
0
            } else if (c == '%' && !escaped) {
205
0
                builder.append(".*"sv);
206
0
            } else {
207
0
                escaped = false;
208
0
                builder.append(c);
209
0
            }
210
0
        }
211
0
        builder.append('$');
212
213
        // FIXME: We should probably cache this regex.
214
0
        auto regex = Regex<PosixBasic>(builder.to_byte_string());
215
0
        auto result = regex.match(lhs_value.to_byte_string(), PosixFlags::Insensitive | PosixFlags::Unicode);
216
0
        return Value(invert_expression() ? !result.success : result.success);
217
0
    }
218
0
    case MatchOperator::Regexp: {
219
0
        Value lhs_value = TRY(lhs()->evaluate(context));
220
0
        Value rhs_value = TRY(rhs()->evaluate(context));
221
222
0
        auto regex = Regex<PosixExtended>(rhs_value.to_byte_string());
223
0
        auto err = regex.parser_result.error;
224
0
        if (err != regex::Error::NoError) {
225
0
            StringBuilder builder;
226
0
            builder.append("Regular expression: "sv);
227
0
            builder.append(get_error_string(err));
228
229
0
            return Result { SQLCommand::Unknown, SQLErrorCode::SyntaxError, builder.to_byte_string() };
230
0
        }
231
232
0
        auto result = regex.match(lhs_value.to_byte_string(), PosixFlags::Insensitive | PosixFlags::Unicode);
233
0
        return Value(invert_expression() ? !result.success : result.success);
234
0
    }
235
0
    case MatchOperator::Glob:
236
0
        return Result { SQLCommand::Unknown, SQLErrorCode::NotYetImplemented, "GLOB expression is not yet implemented"sv };
237
0
    case MatchOperator::Match:
238
0
        return Result { SQLCommand::Unknown, SQLErrorCode::NotYetImplemented, "MATCH expression is not yet implemented"sv };
239
0
    default:
240
0
        VERIFY_NOT_REACHED();
241
0
    }
242
0
}
243
244
}