/src/serenity/Userland/Libraries/LibSQL/AST/Select.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021, Jan de Visser <jan@de-visser.net> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/NumericLimits.h> |
8 | | #include <LibSQL/AST/AST.h> |
9 | | #include <LibSQL/Database.h> |
10 | | #include <LibSQL/Meta.h> |
11 | | #include <LibSQL/Row.h> |
12 | | |
13 | | namespace SQL::AST { |
14 | | |
15 | | static ByteString result_column_name(ResultColumn const& column, size_t column_index) |
16 | 0 | { |
17 | 0 | auto fallback_column_name = [column_index]() { |
18 | 0 | return ByteString::formatted("Column{}", column_index); |
19 | 0 | }; |
20 | |
|
21 | 0 | if (auto const& alias = column.column_alias(); !alias.is_empty()) |
22 | 0 | return alias; |
23 | | |
24 | 0 | if (column.select_from_expression()) { |
25 | 0 | if (is<ColumnNameExpression>(*column.expression())) { |
26 | 0 | auto const& column_name_expression = verify_cast<ColumnNameExpression>(*column.expression()); |
27 | 0 | return column_name_expression.column_name(); |
28 | 0 | } |
29 | | |
30 | | // FIXME: Generate column names from other result column expressions. |
31 | 0 | return fallback_column_name(); |
32 | 0 | } |
33 | | |
34 | 0 | VERIFY(column.select_from_table()); |
35 | | |
36 | | // FIXME: Generate column names from select-from-table result columns. |
37 | 0 | return fallback_column_name(); |
38 | 0 | } |
39 | | |
40 | | ResultOr<ResultSet> Select::execute(ExecutionContext& context) const |
41 | 0 | { |
42 | 0 | Vector<NonnullRefPtr<ResultColumn const>> columns; |
43 | 0 | Vector<ByteString> column_names; |
44 | |
|
45 | 0 | auto const& result_column_list = this->result_column_list(); |
46 | 0 | VERIFY(!result_column_list.is_empty()); |
47 | | |
48 | 0 | for (auto& table_descriptor : table_or_subquery_list()) { |
49 | 0 | if (!table_descriptor->is_table()) |
50 | 0 | return Result { SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"sv }; |
51 | | |
52 | 0 | auto table_def = TRY(context.database->get_table(table_descriptor->schema_name(), table_descriptor->table_name())); |
53 | | |
54 | 0 | if (result_column_list.size() == 1 && result_column_list[0]->type() == ResultType::All) { |
55 | 0 | TRY(columns.try_ensure_capacity(columns.size() + table_def->columns().size())); |
56 | 0 | TRY(column_names.try_ensure_capacity(column_names.size() + table_def->columns().size())); |
57 | | |
58 | 0 | for (auto& col : table_def->columns()) { |
59 | 0 | columns.unchecked_append( |
60 | 0 | create_ast_node<ResultColumn>( |
61 | 0 | create_ast_node<ColumnNameExpression>(table_def->parent()->name(), table_def->name(), col->name()), |
62 | 0 | "")); |
63 | |
|
64 | 0 | column_names.unchecked_append(col->name()); |
65 | 0 | } |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | 0 | if (result_column_list.size() != 1 || result_column_list[0]->type() != ResultType::All) { |
70 | 0 | TRY(columns.try_ensure_capacity(result_column_list.size())); |
71 | 0 | TRY(column_names.try_ensure_capacity(result_column_list.size())); |
72 | | |
73 | 0 | for (size_t i = 0; i < result_column_list.size(); ++i) { |
74 | 0 | auto const& col = result_column_list[i]; |
75 | |
|
76 | 0 | if (col->type() == ResultType::All) { |
77 | | // FIXME can have '*' for example in conjunction with computed columns |
78 | 0 | return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "*"sv }; |
79 | 0 | } |
80 | | |
81 | 0 | columns.unchecked_append(col); |
82 | 0 | column_names.unchecked_append(result_column_name(col, i)); |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | 0 | ResultSet result { SQLCommand::Select, move(column_names) }; |
87 | |
|
88 | 0 | auto descriptor = adopt_ref(*new TupleDescriptor); |
89 | 0 | Tuple tuple(descriptor); |
90 | 0 | Vector<Tuple> rows; |
91 | 0 | descriptor->empend("__unity__"sv); |
92 | 0 | tuple.append(Value { true }); |
93 | 0 | rows.append(tuple); |
94 | |
|
95 | 0 | for (auto& table_descriptor : table_or_subquery_list()) { |
96 | 0 | if (!table_descriptor->is_table()) |
97 | 0 | return Result { SQLCommand::Select, SQLErrorCode::NotYetImplemented, "Sub-selects are not yet implemented"sv }; |
98 | | |
99 | 0 | auto table_def = TRY(context.database->get_table(table_descriptor->schema_name(), table_descriptor->table_name())); |
100 | 0 | if (table_def->num_columns() == 0) |
101 | 0 | continue; |
102 | | |
103 | 0 | auto old_descriptor_size = descriptor->size(); |
104 | 0 | descriptor->extend(table_def->to_tuple_descriptor()); |
105 | |
|
106 | 0 | while (!rows.is_empty() && (rows.first().size() == old_descriptor_size)) { |
107 | 0 | auto cartesian_row = rows.take_first(); |
108 | 0 | auto table_rows = TRY(context.database->select_all(*table_def)); |
109 | | |
110 | 0 | for (auto& table_row : table_rows) { |
111 | 0 | auto new_row = cartesian_row; |
112 | 0 | new_row.extend(table_row); |
113 | 0 | rows.append(new_row); |
114 | 0 | } |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | 0 | bool has_ordering { false }; |
119 | 0 | auto sort_descriptor = adopt_ref(*new TupleDescriptor); |
120 | 0 | for (auto& term : m_ordering_term_list) { |
121 | 0 | sort_descriptor->append(TupleElementDescriptor { .order = term->order() }); |
122 | 0 | has_ordering = true; |
123 | 0 | } |
124 | 0 | Tuple sort_key(sort_descriptor); |
125 | |
|
126 | 0 | for (auto& row : rows) { |
127 | 0 | context.current_row = &row; |
128 | |
|
129 | 0 | if (where_clause()) { |
130 | 0 | auto where_result = TRY(where_clause()->evaluate(context)).to_bool(); |
131 | 0 | if (!where_result.has_value() || !where_result.value()) |
132 | 0 | continue; |
133 | 0 | } |
134 | | |
135 | 0 | tuple.clear(); |
136 | |
|
137 | 0 | for (auto& col : columns) { |
138 | 0 | auto value = TRY(col->expression()->evaluate(context)); |
139 | 0 | tuple.append(value); |
140 | 0 | } |
141 | | |
142 | 0 | if (has_ordering) { |
143 | 0 | sort_key.clear(); |
144 | 0 | for (auto& term : m_ordering_term_list) { |
145 | 0 | auto value = TRY(term->expression()->evaluate(context)); |
146 | 0 | sort_key.append(value); |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | 0 | result.insert_row(tuple, sort_key); |
151 | 0 | } |
152 | | |
153 | 0 | if (m_limit_clause != nullptr) { |
154 | 0 | size_t limit_value = NumericLimits<size_t>::max(); |
155 | 0 | size_t offset_value = 0; |
156 | |
|
157 | 0 | auto limit = TRY(m_limit_clause->limit_expression()->evaluate(context)); |
158 | 0 | if (!limit.is_null()) { |
159 | 0 | auto limit_value_maybe = limit.to_int<size_t>(); |
160 | 0 | if (!limit_value_maybe.has_value()) |
161 | 0 | return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "LIMIT clause must evaluate to an integer value"sv }; |
162 | | |
163 | 0 | limit_value = limit_value_maybe.value(); |
164 | 0 | } |
165 | | |
166 | 0 | if (m_limit_clause->offset_expression() != nullptr) { |
167 | 0 | auto offset = TRY(m_limit_clause->offset_expression()->evaluate(context)); |
168 | 0 | if (!offset.is_null()) { |
169 | 0 | auto offset_value_maybe = offset.to_int<size_t>(); |
170 | 0 | if (!offset_value_maybe.has_value()) |
171 | 0 | return Result { SQLCommand::Select, SQLErrorCode::SyntaxError, "OFFSET clause must evaluate to an integer value"sv }; |
172 | | |
173 | 0 | offset_value = offset_value_maybe.value(); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | 0 | result.limit(offset_value, limit_value); |
178 | 0 | } |
179 | | |
180 | 0 | return result; |
181 | 0 | } |
182 | | |
183 | | } |