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