vibesql_executor/select/executor/
arena_execution.rs1use std::cmp::Ordering;
29use std::collections::HashMap;
30
31use vibesql_ast::arena::{ArenaInterner, Expression as ArenaExpression, ExtendedExpr as ArenaExtendedExpr, SelectItem as ArenaSelectItem, SelectStmt as ArenaSelectStmt};
32use vibesql_storage::Row;
33use vibesql_types::SqlValue;
34
35use super::builder::SelectExecutor;
36use crate::errors::ExecutorError;
37use crate::evaluator::ArenaExpressionEvaluator;
38use crate::evaluator::window::compare_values;
39use crate::schema::CombinedSchema;
40
41impl SelectExecutor<'_> {
42 pub fn execute_select_arena<'arena>(
67 &self,
68 stmt: &ArenaSelectStmt<'arena>,
69 params: &[SqlValue],
70 interner: &'arena ArenaInterner<'arena>,
71 ) -> Result<Vec<Row>, ExecutorError> {
72 if stmt.with_clause.is_some() {
74 return Err(ExecutorError::UnsupportedExpression(
75 "Arena execution does not support WITH clause".to_string(),
76 ));
77 }
78
79 if stmt.set_operation.is_some() {
80 return Err(ExecutorError::UnsupportedExpression(
81 "Arena execution does not support set operations".to_string(),
82 ));
83 }
84
85 if stmt.group_by.is_some() || stmt.having.is_some() {
86 return Err(ExecutorError::UnsupportedExpression(
87 "Arena execution does not support GROUP BY/HAVING".to_string(),
88 ));
89 }
90
91 if stmt.distinct {
92 return Err(ExecutorError::UnsupportedExpression(
93 "Arena execution does not support DISTINCT".to_string(),
94 ));
95 }
96
97 if self.has_arena_aggregates(&stmt.select_list) {
99 return Err(ExecutorError::UnsupportedExpression(
100 "Arena execution does not support aggregate functions".to_string(),
101 ));
102 }
103
104 match &stmt.from {
106 Some(from) => self.execute_arena_with_from(stmt, from, params, interner),
107 None => self.execute_arena_without_from(stmt, params, interner),
108 }
109 }
110
111 fn execute_arena_without_from<'arena>(
113 &self,
114 stmt: &ArenaSelectStmt<'arena>,
115 params: &[SqlValue],
116 interner: &'arena ArenaInterner<'arena>,
117 ) -> Result<Vec<Row>, ExecutorError> {
118 let schema = CombinedSchema {
120 table_schemas: HashMap::new(),
121 total_columns: 0,
122 };
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.database.get_table(table_name_str).ok_or_else(|| {
176 ExecutorError::TableNotFound(table_name_str.to_string())
177 })?;
178
179 let schema_alias_str = alias.map(|a| interner.resolve(a)).unwrap_or(table_name_str);
181 let schema = CombinedSchema::from_table(schema_alias_str.to_string(), table.schema.clone());
182
183 let evaluator = ArenaExpressionEvaluator::with_database(&schema, params, self.database, interner);
185
186 let mut results = Vec::new();
188 for row in table.scan() {
189 if let Some(where_clause) = &stmt.where_clause {
191 let filter_result = evaluator.eval(where_clause, row)?;
192 match filter_result {
193 SqlValue::Boolean(true) => {}
194 SqlValue::Boolean(false) | SqlValue::Null => continue,
195 _ => {
196 return Err(ExecutorError::TypeError(format!(
197 "WHERE clause must evaluate to boolean, got {:?}",
198 filter_result
199 )));
200 }
201 }
202 }
203
204 let projected = self.project_arena_row(&stmt.select_list, row, &schema, &evaluator, interner)?;
206 results.push(projected);
207
208 if results.len() % 1000 == 0 {
210 self.check_timeout()?;
211 }
212 }
213
214 if let Some(order_by) = &stmt.order_by {
216 self.sort_arena_results(&mut results, order_by.as_slice(), &schema, params, interner)?;
217 }
218
219 Ok(self.apply_arena_limit_offset(results, stmt.limit, stmt.offset))
221 }
222
223 fn project_arena_row<'arena>(
225 &self,
226 select_list: &[ArenaSelectItem<'arena>],
227 row: &Row,
228 schema: &CombinedSchema,
229 evaluator: &ArenaExpressionEvaluator<'_, 'arena>,
230 interner: &'arena ArenaInterner<'arena>,
231 ) -> Result<Row, ExecutorError> {
232 let mut values = Vec::with_capacity(select_list.len());
233
234 for item in select_list.iter() {
235 match item {
236 ArenaSelectItem::Expression { expr, .. } => {
237 let value = evaluator.eval(expr, row)?;
238 values.push(value);
239 }
240 ArenaSelectItem::Wildcard { .. } => {
241 values.extend(row.values.iter().cloned());
243 }
244 ArenaSelectItem::QualifiedWildcard { qualifier, .. } => {
245 let qualifier_str = interner.resolve(*qualifier);
247 if let Some(&(start, ref tbl_schema)) = schema.table_schemas.get(&qualifier_str.to_lowercase()) {
248 for i in 0..tbl_schema.columns.len() {
249 if let Some(val) = row.get(start + i) {
250 values.push(val.clone());
251 }
252 }
253 } else {
254 values.extend(row.values.iter().cloned());
256 }
257 }
258 }
259 }
260
261 Ok(Row::new(values))
262 }
263
264 fn sort_arena_results<'arena>(
266 &self,
267 results: &mut Vec<Row>,
268 order_by: &[vibesql_ast::arena::OrderByItem<'arena>],
269 schema: &CombinedSchema,
270 params: &[SqlValue],
271 interner: &'arena ArenaInterner<'arena>,
272 ) -> Result<(), ExecutorError> {
273 use vibesql_ast::arena::OrderDirection;
274
275 let evaluator = ArenaExpressionEvaluator::with_database(schema, params, self.database, interner);
277
278 let mut keyed_rows: Vec<(Vec<SqlValue>, Row)> = results
280 .drain(..)
281 .map(|row| {
282 let keys: Result<Vec<_>, _> = order_by
283 .iter()
284 .map(|item| evaluator.eval(&item.expr, &row))
285 .collect();
286 keys.map(|k| (k, row))
287 })
288 .collect::<Result<_, _>>()?;
289
290 keyed_rows.sort_by(|(keys_a, _), (keys_b, _)| {
292 for (i, (key_a, key_b)) in keys_a.iter().zip(keys_b.iter()).enumerate() {
293 let cmp = compare_values(key_a, key_b);
294 if cmp != Ordering::Equal {
295 let asc = order_by.get(i).is_some_and(|o| matches!(o.direction, OrderDirection::Asc));
297 return if asc { cmp } else { cmp.reverse() };
298 }
299 }
300 Ordering::Equal
301 });
302
303 results.extend(keyed_rows.into_iter().map(|(_, row)| row));
305
306 Ok(())
307 }
308
309 fn apply_arena_limit_offset(
311 &self,
312 mut results: Vec<Row>,
313 limit: Option<usize>,
314 offset: Option<usize>,
315 ) -> Vec<Row> {
316 if let Some(off) = offset {
318 if off >= results.len() {
319 return vec![];
320 }
321 results = results.into_iter().skip(off).collect();
322 }
323
324 if let Some(lim) = limit {
326 results.truncate(lim);
327 }
328
329 results
330 }
331
332 fn has_arena_aggregates<'arena>(&self, select_list: &[ArenaSelectItem<'arena>]) -> bool {
334 for item in select_list.iter() {
335 if let ArenaSelectItem::Expression { expr, .. } = item {
336 if self.arena_expr_has_aggregate(expr) {
337 return true;
338 }
339 }
340 }
341 false
342 }
343
344 fn arena_expr_has_aggregate<'arena>(&self, expr: &ArenaExpression<'arena>) -> bool {
346 match expr {
347 ArenaExpression::BinaryOp { left, right, .. } => {
349 self.arena_expr_has_aggregate(left) || self.arena_expr_has_aggregate(right)
350 }
351 ArenaExpression::UnaryOp { expr, .. } => self.arena_expr_has_aggregate(expr),
352 ArenaExpression::IsNull { expr, .. } => self.arena_expr_has_aggregate(expr),
353 ArenaExpression::Conjunction(children) | ArenaExpression::Disjunction(children) => {
354 children.iter().any(|c| self.arena_expr_has_aggregate(c))
355 }
356 ArenaExpression::Literal(_)
357 | ArenaExpression::Placeholder(_)
358 | ArenaExpression::NumberedPlaceholder(_)
359 | ArenaExpression::NamedPlaceholder(_)
360 | ArenaExpression::ColumnRef { .. }
361 | ArenaExpression::Wildcard
362 | ArenaExpression::CurrentDate
363 | ArenaExpression::CurrentTime { .. }
364 | ArenaExpression::CurrentTimestamp { .. }
365 | ArenaExpression::Default => false,
366 ArenaExpression::Extended(ext) => self.arena_extended_has_aggregate(ext),
368 }
369 }
370
371 fn arena_extended_has_aggregate<'arena>(&self, ext: &ArenaExtendedExpr<'arena>) -> bool {
373 match ext {
374 ArenaExtendedExpr::AggregateFunction { .. } => true,
375 ArenaExtendedExpr::Function { args, .. } => {
376 args.iter().any(|a| self.arena_expr_has_aggregate(a))
377 }
378 ArenaExtendedExpr::Case { operand, when_clauses, else_result, .. } => {
379 operand.as_ref().is_some_and(|o| self.arena_expr_has_aggregate(o))
380 || when_clauses.iter().any(|w| {
381 w.conditions.iter().any(|c| self.arena_expr_has_aggregate(c))
382 || self.arena_expr_has_aggregate(&w.result)
383 })
384 || else_result.as_ref().is_some_and(|e| self.arena_expr_has_aggregate(e))
385 }
386 ArenaExtendedExpr::Between { expr, low, high, .. } => {
387 self.arena_expr_has_aggregate(expr)
388 || self.arena_expr_has_aggregate(low)
389 || self.arena_expr_has_aggregate(high)
390 }
391 ArenaExtendedExpr::InList { expr, values, .. } => {
392 self.arena_expr_has_aggregate(expr)
393 || values.iter().any(|v| self.arena_expr_has_aggregate(v))
394 }
395 ArenaExtendedExpr::Cast { expr, .. } => self.arena_expr_has_aggregate(expr),
396 ArenaExtendedExpr::Like { expr, pattern, .. } => {
397 self.arena_expr_has_aggregate(expr) || self.arena_expr_has_aggregate(pattern)
398 }
399 _ => false,
400 }
401 }
402}