vibesql_executor/select/executor/
arena_execution.rs1use std::cmp::Ordering;
29use std::collections::HashMap;
30
31use vibesql_ast::arena::{
32 ArenaInterner, Expression as ArenaExpression, ExtendedExpr as ArenaExtendedExpr,
33 SelectItem as ArenaSelectItem, SelectStmt as ArenaSelectStmt,
34};
35use vibesql_storage::Row;
36use vibesql_types::SqlValue;
37
38use super::builder::SelectExecutor;
39use crate::errors::ExecutorError;
40use crate::evaluator::window::compare_values;
41use crate::evaluator::ArenaExpressionEvaluator;
42use crate::schema::CombinedSchema;
43
44impl SelectExecutor<'_> {
45 pub fn execute_select_arena<'arena>(
70 &self,
71 stmt: &ArenaSelectStmt<'arena>,
72 params: &[SqlValue],
73 interner: &'arena ArenaInterner<'arena>,
74 ) -> Result<Vec<Row>, ExecutorError> {
75 if stmt.with_clause.is_some() {
77 return Err(ExecutorError::UnsupportedExpression(
78 "Arena execution does not support WITH clause".to_string(),
79 ));
80 }
81
82 if stmt.set_operation.is_some() {
83 return Err(ExecutorError::UnsupportedExpression(
84 "Arena execution does not support set operations".to_string(),
85 ));
86 }
87
88 if stmt.group_by.is_some() || stmt.having.is_some() {
89 return Err(ExecutorError::UnsupportedExpression(
90 "Arena execution does not support GROUP BY/HAVING".to_string(),
91 ));
92 }
93
94 if stmt.distinct {
95 return Err(ExecutorError::UnsupportedExpression(
96 "Arena execution does not support DISTINCT".to_string(),
97 ));
98 }
99
100 if self.has_arena_aggregates(&stmt.select_list) {
102 return Err(ExecutorError::UnsupportedExpression(
103 "Arena execution does not support aggregate functions".to_string(),
104 ));
105 }
106
107 match &stmt.from {
109 Some(from) => self.execute_arena_with_from(stmt, from, params, interner),
110 None => self.execute_arena_without_from(stmt, params, interner),
111 }
112 }
113
114 fn execute_arena_without_from<'arena>(
116 &self,
117 stmt: &ArenaSelectStmt<'arena>,
118 params: &[SqlValue],
119 interner: &'arena ArenaInterner<'arena>,
120 ) -> Result<Vec<Row>, ExecutorError> {
121 let schema = CombinedSchema { table_schemas: HashMap::new(), total_columns: 0 };
123 let empty_row = Row::new(vec![]);
124 let evaluator = ArenaExpressionEvaluator::new(&schema, params, interner);
125
126 let mut values = Vec::with_capacity(stmt.select_list.len());
128 for item in stmt.select_list.iter() {
129 match item {
130 ArenaSelectItem::Expression { expr, .. } => {
131 let value = evaluator.eval(expr, &empty_row)?;
132 values.push(value);
133 }
134 ArenaSelectItem::Wildcard { .. } | ArenaSelectItem::QualifiedWildcard { .. } => {
135 continue;
137 }
138 }
139 }
140
141 let rows = vec![Row::new(values)];
143 Ok(self.apply_arena_limit_offset(rows, stmt.limit, stmt.offset))
144 }
145
146 fn execute_arena_with_from<'arena>(
148 &self,
149 stmt: &ArenaSelectStmt<'arena>,
150 from: &vibesql_ast::arena::FromClause<'arena>,
151 params: &[SqlValue],
152 interner: &'arena ArenaInterner<'arena>,
153 ) -> Result<Vec<Row>, ExecutorError> {
154 use vibesql_ast::arena::FromClause;
155
156 let (table_name, alias) = match from {
158 FromClause::Table { name, alias, .. } => (*name, *alias),
159 FromClause::Join { .. } => {
160 return Err(ExecutorError::UnsupportedExpression(
161 "Arena execution does not support JOINs yet".to_string(),
162 ));
163 }
164 FromClause::Subquery { .. } => {
165 return Err(ExecutorError::UnsupportedExpression(
166 "Arena execution does not support subqueries in FROM".to_string(),
167 ));
168 }
169 };
170
171 let table_name_str = interner.resolve(table_name);
173
174 let table = self
176 .database
177 .get_table(table_name_str)
178 .ok_or_else(|| ExecutorError::TableNotFound(table_name_str.to_string()))?;
179
180 let schema_alias_str = alias.map(|a| interner.resolve(a)).unwrap_or(table_name_str);
182 let schema = CombinedSchema::from_table(schema_alias_str.to_string(), table.schema.clone());
183
184 let evaluator =
186 ArenaExpressionEvaluator::with_database(&schema, params, self.database, interner);
187
188 let mut results = Vec::new();
191 for (_, row) in table.scan_live() {
192 if let Some(where_clause) = &stmt.where_clause {
194 let filter_result = evaluator.eval(where_clause, row)?;
195 match filter_result {
196 SqlValue::Boolean(true) => {}
197 SqlValue::Boolean(false) | SqlValue::Null => continue,
198 _ => {
199 return Err(ExecutorError::TypeError(format!(
200 "WHERE clause must evaluate to boolean, got {:?}",
201 filter_result
202 )));
203 }
204 }
205 }
206
207 let projected =
209 self.project_arena_row(&stmt.select_list, row, &schema, &evaluator, interner)?;
210 results.push(projected);
211
212 if results.len() % 1000 == 0 {
214 self.check_timeout()?;
215 }
216 }
217
218 if let Some(order_by) = &stmt.order_by {
220 self.sort_arena_results(&mut results, order_by.as_slice(), &schema, params, interner)?;
221 }
222
223 Ok(self.apply_arena_limit_offset(results, stmt.limit, stmt.offset))
225 }
226
227 fn project_arena_row<'arena>(
229 &self,
230 select_list: &[ArenaSelectItem<'arena>],
231 row: &Row,
232 schema: &CombinedSchema,
233 evaluator: &ArenaExpressionEvaluator<'_, 'arena>,
234 interner: &'arena ArenaInterner<'arena>,
235 ) -> Result<Row, ExecutorError> {
236 let mut values = Vec::with_capacity(select_list.len());
237
238 for item in select_list.iter() {
239 match item {
240 ArenaSelectItem::Expression { expr, .. } => {
241 let value = evaluator.eval(expr, row)?;
242 values.push(value);
243 }
244 ArenaSelectItem::Wildcard { .. } => {
245 values.extend(row.values.iter().cloned());
247 }
248 ArenaSelectItem::QualifiedWildcard { qualifier, .. } => {
249 let qualifier_str = interner.resolve(*qualifier);
251 if let Some(&(start, ref tbl_schema)) = schema.get_table(qualifier_str) {
252 for i in 0..tbl_schema.columns.len() {
253 if let Some(val) = row.get(start + i) {
254 values.push(val.clone());
255 }
256 }
257 } else {
258 values.extend(row.values.iter().cloned());
260 }
261 }
262 }
263 }
264
265 Ok(Row::new(values))
266 }
267
268 fn sort_arena_results<'arena>(
270 &self,
271 results: &mut Vec<Row>,
272 order_by: &[vibesql_ast::arena::OrderByItem<'arena>],
273 schema: &CombinedSchema,
274 params: &[SqlValue],
275 interner: &'arena ArenaInterner<'arena>,
276 ) -> Result<(), ExecutorError> {
277 use vibesql_ast::arena::OrderDirection;
278
279 let evaluator =
281 ArenaExpressionEvaluator::with_database(schema, params, self.database, interner);
282
283 let mut keyed_rows: Vec<(Vec<SqlValue>, Row)> = results
285 .drain(..)
286 .map(|row| {
287 let keys: Result<Vec<_>, _> =
288 order_by.iter().map(|item| evaluator.eval(&item.expr, &row)).collect();
289 keys.map(|k| (k, row))
290 })
291 .collect::<Result<_, _>>()?;
292
293 keyed_rows.sort_by(|(keys_a, _), (keys_b, _)| {
295 for (i, (key_a, key_b)) in keys_a.iter().zip(keys_b.iter()).enumerate() {
296 let cmp = compare_values(key_a, key_b);
297 if cmp != Ordering::Equal {
298 let asc =
300 order_by.get(i).is_some_and(|o| matches!(o.direction, OrderDirection::Asc));
301 return if asc { cmp } else { cmp.reverse() };
302 }
303 }
304 Ordering::Equal
305 });
306
307 results.extend(keyed_rows.into_iter().map(|(_, row)| row));
309
310 Ok(())
311 }
312
313 fn apply_arena_limit_offset(
315 &self,
316 mut results: Vec<Row>,
317 limit: Option<usize>,
318 offset: Option<usize>,
319 ) -> Vec<Row> {
320 if let Some(off) = offset {
322 if off >= results.len() {
323 return vec![];
324 }
325 results = results.into_iter().skip(off).collect();
326 }
327
328 if let Some(lim) = limit {
330 results.truncate(lim);
331 }
332
333 results
334 }
335
336 fn has_arena_aggregates<'arena>(&self, select_list: &[ArenaSelectItem<'arena>]) -> bool {
338 for item in select_list.iter() {
339 if let ArenaSelectItem::Expression { expr, .. } = item {
340 if self.arena_expr_has_aggregate(expr) {
341 return true;
342 }
343 }
344 }
345 false
346 }
347
348 fn arena_expr_has_aggregate<'arena>(&self, expr: &ArenaExpression<'arena>) -> bool {
350 match expr {
351 ArenaExpression::BinaryOp { left, right, .. } => {
353 self.arena_expr_has_aggregate(left) || self.arena_expr_has_aggregate(right)
354 }
355 ArenaExpression::UnaryOp { expr, .. } => self.arena_expr_has_aggregate(expr),
356 ArenaExpression::IsNull { expr, .. } => self.arena_expr_has_aggregate(expr),
357 ArenaExpression::Conjunction(children) | ArenaExpression::Disjunction(children) => {
358 children.iter().any(|c| self.arena_expr_has_aggregate(c))
359 }
360 ArenaExpression::Literal(_)
361 | ArenaExpression::Placeholder(_)
362 | ArenaExpression::NumberedPlaceholder(_)
363 | ArenaExpression::NamedPlaceholder(_)
364 | ArenaExpression::ColumnRef { .. }
365 | ArenaExpression::Wildcard
366 | ArenaExpression::CurrentDate
367 | ArenaExpression::CurrentTime { .. }
368 | ArenaExpression::CurrentTimestamp { .. }
369 | ArenaExpression::Default => false,
370 ArenaExpression::Extended(ext) => self.arena_extended_has_aggregate(ext),
372 }
373 }
374
375 fn arena_extended_has_aggregate<'arena>(&self, ext: &ArenaExtendedExpr<'arena>) -> bool {
377 match ext {
378 ArenaExtendedExpr::AggregateFunction { .. } => true,
379 ArenaExtendedExpr::Function { args, .. } => {
380 args.iter().any(|a| self.arena_expr_has_aggregate(a))
381 }
382 ArenaExtendedExpr::Case { operand, when_clauses, else_result, .. } => {
383 operand.as_ref().is_some_and(|o| self.arena_expr_has_aggregate(o))
384 || when_clauses.iter().any(|w| {
385 w.conditions.iter().any(|c| self.arena_expr_has_aggregate(c))
386 || self.arena_expr_has_aggregate(&w.result)
387 })
388 || else_result.as_ref().is_some_and(|e| self.arena_expr_has_aggregate(e))
389 }
390 ArenaExtendedExpr::Between { expr, low, high, .. } => {
391 self.arena_expr_has_aggregate(expr)
392 || self.arena_expr_has_aggregate(low)
393 || self.arena_expr_has_aggregate(high)
394 }
395 ArenaExtendedExpr::InList { expr, values, .. } => {
396 self.arena_expr_has_aggregate(expr)
397 || values.iter().any(|v| self.arena_expr_has_aggregate(v))
398 }
399 ArenaExtendedExpr::Cast { expr, .. } => self.arena_expr_has_aggregate(expr),
400 ArenaExtendedExpr::Like { expr, pattern, .. } => {
401 self.arena_expr_has_aggregate(expr) || self.arena_expr_has_aggregate(pattern)
402 }
403 _ => false,
404 }
405 }
406}