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();
190 for row in table.scan() {
191 if let Some(where_clause) = &stmt.where_clause {
193 let filter_result = evaluator.eval(where_clause, row)?;
194 match filter_result {
195 SqlValue::Boolean(true) => {}
196 SqlValue::Boolean(false) | SqlValue::Null => continue,
197 _ => {
198 return Err(ExecutorError::TypeError(format!(
199 "WHERE clause must evaluate to boolean, got {:?}",
200 filter_result
201 )));
202 }
203 }
204 }
205
206 let projected =
208 self.project_arena_row(&stmt.select_list, row, &schema, &evaluator, interner)?;
209 results.push(projected);
210
211 if results.len() % 1000 == 0 {
213 self.check_timeout()?;
214 }
215 }
216
217 if let Some(order_by) = &stmt.order_by {
219 self.sort_arena_results(&mut results, order_by.as_slice(), &schema, params, interner)?;
220 }
221
222 Ok(self.apply_arena_limit_offset(results, stmt.limit, stmt.offset))
224 }
225
226 fn project_arena_row<'arena>(
228 &self,
229 select_list: &[ArenaSelectItem<'arena>],
230 row: &Row,
231 schema: &CombinedSchema,
232 evaluator: &ArenaExpressionEvaluator<'_, 'arena>,
233 interner: &'arena ArenaInterner<'arena>,
234 ) -> Result<Row, ExecutorError> {
235 let mut values = Vec::with_capacity(select_list.len());
236
237 for item in select_list.iter() {
238 match item {
239 ArenaSelectItem::Expression { expr, .. } => {
240 let value = evaluator.eval(expr, row)?;
241 values.push(value);
242 }
243 ArenaSelectItem::Wildcard { .. } => {
244 values.extend(row.values.iter().cloned());
246 }
247 ArenaSelectItem::QualifiedWildcard { qualifier, .. } => {
248 let qualifier_str = interner.resolve(*qualifier);
250 if let Some(&(start, ref tbl_schema)) =
251 schema.table_schemas.get(&qualifier_str.to_lowercase())
252 {
253 for i in 0..tbl_schema.columns.len() {
254 if let Some(val) = row.get(start + i) {
255 values.push(val.clone());
256 }
257 }
258 } else {
259 values.extend(row.values.iter().cloned());
261 }
262 }
263 }
264 }
265
266 Ok(Row::new(values))
267 }
268
269 fn sort_arena_results<'arena>(
271 &self,
272 results: &mut Vec<Row>,
273 order_by: &[vibesql_ast::arena::OrderByItem<'arena>],
274 schema: &CombinedSchema,
275 params: &[SqlValue],
276 interner: &'arena ArenaInterner<'arena>,
277 ) -> Result<(), ExecutorError> {
278 use vibesql_ast::arena::OrderDirection;
279
280 let evaluator =
282 ArenaExpressionEvaluator::with_database(schema, params, self.database, interner);
283
284 let mut keyed_rows: Vec<(Vec<SqlValue>, Row)> = results
286 .drain(..)
287 .map(|row| {
288 let keys: Result<Vec<_>, _> =
289 order_by.iter().map(|item| evaluator.eval(&item.expr, &row)).collect();
290 keys.map(|k| (k, row))
291 })
292 .collect::<Result<_, _>>()?;
293
294 keyed_rows.sort_by(|(keys_a, _), (keys_b, _)| {
296 for (i, (key_a, key_b)) in keys_a.iter().zip(keys_b.iter()).enumerate() {
297 let cmp = compare_values(key_a, key_b);
298 if cmp != Ordering::Equal {
299 let asc =
301 order_by.get(i).is_some_and(|o| matches!(o.direction, OrderDirection::Asc));
302 return if asc { cmp } else { cmp.reverse() };
303 }
304 }
305 Ordering::Equal
306 });
307
308 results.extend(keyed_rows.into_iter().map(|(_, row)| row));
310
311 Ok(())
312 }
313
314 fn apply_arena_limit_offset(
316 &self,
317 mut results: Vec<Row>,
318 limit: Option<usize>,
319 offset: Option<usize>,
320 ) -> Vec<Row> {
321 if let Some(off) = offset {
323 if off >= results.len() {
324 return vec![];
325 }
326 results = results.into_iter().skip(off).collect();
327 }
328
329 if let Some(lim) = limit {
331 results.truncate(lim);
332 }
333
334 results
335 }
336
337 fn has_arena_aggregates<'arena>(&self, select_list: &[ArenaSelectItem<'arena>]) -> bool {
339 for item in select_list.iter() {
340 if let ArenaSelectItem::Expression { expr, .. } = item {
341 if self.arena_expr_has_aggregate(expr) {
342 return true;
343 }
344 }
345 }
346 false
347 }
348
349 fn arena_expr_has_aggregate<'arena>(&self, expr: &ArenaExpression<'arena>) -> bool {
351 match expr {
352 ArenaExpression::BinaryOp { left, right, .. } => {
354 self.arena_expr_has_aggregate(left) || self.arena_expr_has_aggregate(right)
355 }
356 ArenaExpression::UnaryOp { expr, .. } => self.arena_expr_has_aggregate(expr),
357 ArenaExpression::IsNull { expr, .. } => self.arena_expr_has_aggregate(expr),
358 ArenaExpression::Conjunction(children) | ArenaExpression::Disjunction(children) => {
359 children.iter().any(|c| self.arena_expr_has_aggregate(c))
360 }
361 ArenaExpression::Literal(_)
362 | ArenaExpression::Placeholder(_)
363 | ArenaExpression::NumberedPlaceholder(_)
364 | ArenaExpression::NamedPlaceholder(_)
365 | ArenaExpression::ColumnRef { .. }
366 | ArenaExpression::Wildcard
367 | ArenaExpression::CurrentDate
368 | ArenaExpression::CurrentTime { .. }
369 | ArenaExpression::CurrentTimestamp { .. }
370 | ArenaExpression::Default => false,
371 ArenaExpression::Extended(ext) => self.arena_extended_has_aggregate(ext),
373 }
374 }
375
376 fn arena_extended_has_aggregate<'arena>(&self, ext: &ArenaExtendedExpr<'arena>) -> bool {
378 match ext {
379 ArenaExtendedExpr::AggregateFunction { .. } => true,
380 ArenaExtendedExpr::Function { args, .. } => {
381 args.iter().any(|a| self.arena_expr_has_aggregate(a))
382 }
383 ArenaExtendedExpr::Case { operand, when_clauses, else_result, .. } => {
384 operand.as_ref().is_some_and(|o| self.arena_expr_has_aggregate(o))
385 || when_clauses.iter().any(|w| {
386 w.conditions.iter().any(|c| self.arena_expr_has_aggregate(c))
387 || self.arena_expr_has_aggregate(&w.result)
388 })
389 || else_result.as_ref().is_some_and(|e| self.arena_expr_has_aggregate(e))
390 }
391 ArenaExtendedExpr::Between { expr, low, high, .. } => {
392 self.arena_expr_has_aggregate(expr)
393 || self.arena_expr_has_aggregate(low)
394 || self.arena_expr_has_aggregate(high)
395 }
396 ArenaExtendedExpr::InList { expr, values, .. } => {
397 self.arena_expr_has_aggregate(expr)
398 || values.iter().any(|v| self.arena_expr_has_aggregate(v))
399 }
400 ArenaExtendedExpr::Cast { expr, .. } => self.arena_expr_has_aggregate(expr),
401 ArenaExtendedExpr::Like { expr, pattern, .. } => {
402 self.arena_expr_has_aggregate(expr) || self.arena_expr_has_aggregate(pattern)
403 }
404 _ => false,
405 }
406 }
407}