1use alopex_core::kv::KVStore;
2
3use crate::ast::LITERAL_TABLE;
4use crate::catalog::{Catalog, StorageType};
5use crate::executor::evaluator::EvalContext;
6use crate::executor::memory::MemoryPolicy;
7use crate::executor::{ExecutionResult, ExecutorError, QueryResult, QueryRowIterator, Result};
8use crate::planner::logical_plan::LogicalPlan;
9use crate::planner::typed_expr::{Projection, SortExpr};
10use crate::storage::{SqlTxn, SqlValue};
11
12use super::{ColumnInfo, Row};
13
14pub mod aggregate;
15pub mod columnar_scan;
16pub mod iterator;
17pub mod join;
18mod knn;
19mod project;
20mod scan;
21pub mod subquery;
22
23pub use columnar_scan::{ColumnarScanIterator, create_columnar_scan_iterator};
24pub use iterator::{FilterIterator, LimitIterator, RowIterator, ScanIterator, SortIterator};
25pub use scan::create_scan_iterator;
26
27pub fn execute_query<'txn, S: KVStore + 'txn, C: Catalog + ?Sized, T: SqlTxn<'txn, S>>(
39 txn: &mut T,
40 catalog: &C,
41 plan: LogicalPlan,
42) -> Result<ExecutionResult> {
43 execute_query_with_policy(txn, catalog, plan, None)
44}
45
46pub fn execute_query_with_policy<
47 'txn,
48 S: KVStore + 'txn,
49 C: Catalog + ?Sized,
50 T: SqlTxn<'txn, S>,
51>(
52 txn: &mut T,
53 catalog: &C,
54 plan: LogicalPlan,
55 memory: Option<&MemoryPolicy>,
56) -> Result<ExecutionResult> {
57 if let Some((pattern, projection, filter)) = knn::extract_knn_context(&plan) {
58 return knn::execute_knn_query(txn, catalog, &pattern, &projection, filter.as_ref());
59 }
60
61 let result = execute_query_result_with_outer_and_policy(txn, catalog, plan, None, memory)?;
62 Ok(ExecutionResult::Query(result))
63}
64
65pub(crate) fn execute_query_result_with_outer<
66 'txn,
67 S: KVStore + 'txn,
68 C: Catalog + ?Sized,
69 T: SqlTxn<'txn, S>,
70>(
71 txn: &mut T,
72 catalog: &C,
73 plan: LogicalPlan,
74 outer: Option<&Row>,
75) -> Result<QueryResult> {
76 execute_query_result_with_outer_and_policy(txn, catalog, plan, outer, None)
77}
78
79fn execute_query_result_with_outer_and_policy<
80 'txn,
81 S: KVStore + 'txn,
82 C: Catalog + ?Sized,
83 T: SqlTxn<'txn, S>,
84>(
85 txn: &mut T,
86 catalog: &C,
87 plan: LogicalPlan,
88 outer: Option<&Row>,
89 memory: Option<&MemoryPolicy>,
90) -> Result<QueryResult> {
91 let (mut iter, projection, schema) =
92 build_iterator_pipeline_with_outer(txn, catalog, plan, memory, outer)?;
93 let mut rows = Vec::new();
94 while let Some(result) = iter.next_row() {
95 rows.push(result?);
96 }
97 execute_project_with_subqueries(txn, catalog, rows, &projection, &schema, outer)
98}
99
100pub fn execute_query_streaming<'txn, S: KVStore + 'txn, C: Catalog + ?Sized, T: SqlTxn<'txn, S>>(
116 txn: &mut T,
117 catalog: &C,
118 plan: LogicalPlan,
119) -> Result<QueryRowIterator<'static>> {
120 execute_query_streaming_with_policy(txn, catalog, plan, None)
121}
122
123pub fn execute_query_streaming_with_policy<
124 'txn,
125 S: KVStore + 'txn,
126 C: Catalog + ?Sized,
127 T: SqlTxn<'txn, S>,
128>(
129 txn: &mut T,
130 catalog: &C,
131 plan: LogicalPlan,
132 memory: Option<&MemoryPolicy>,
133) -> Result<QueryRowIterator<'static>> {
134 if knn::extract_knn_context(&plan).is_some() {
136 let result = execute_query_with_policy(txn, catalog, plan, memory)?;
138 if let ExecutionResult::Query(qr) = result {
139 let (iter, projection, schema) = materialize_query_result(qr);
140 return Ok(QueryRowIterator::new(iter, projection, schema));
141 }
142 return Err(ExecutorError::InvalidOperation {
143 operation: "execute_query_streaming".into(),
144 reason: "KNN query did not return Query result".into(),
145 });
146 }
147
148 if subquery::plan_contains_subquery(&plan) {
153 let result = execute_query_result_with_outer_and_policy(txn, catalog, plan, None, memory)?;
154 let (iter, projection, schema) = materialize_query_result(result);
155 return Ok(QueryRowIterator::new(iter, projection, schema));
156 }
157
158 let (iter, projection, schema) = build_iterator_pipeline(txn, catalog, plan, memory)?;
159
160 Ok(QueryRowIterator::new(iter, projection, schema))
161}
162
163fn materialize_query_result(
168 result: QueryResult,
169) -> (
170 Box<dyn RowIterator>,
171 Projection,
172 Vec<crate::catalog::ColumnMetadata>,
173) {
174 let column_names: Vec<String> = result.columns.iter().map(|c| c.name.clone()).collect();
175 let schema: Vec<crate::catalog::ColumnMetadata> = result
176 .columns
177 .iter()
178 .map(|c| crate::catalog::ColumnMetadata::new(&c.name, c.data_type.clone()))
179 .collect();
180 let rows: Vec<Row> = result
181 .rows
182 .into_iter()
183 .enumerate()
184 .map(|(i, values)| Row::new(i as u64, values))
185 .collect();
186 let iter = iterator::VecIterator::new(rows, schema.clone());
187 (Box::new(iter), Projection::All(column_names), schema)
188}
189
190fn build_iterator_pipeline<'txn, S: KVStore + 'txn, C: Catalog + ?Sized, T: SqlTxn<'txn, S>>(
197 txn: &mut T,
198 catalog: &C,
199 plan: LogicalPlan,
200 memory: Option<&MemoryPolicy>,
201) -> Result<(
202 Box<dyn RowIterator>,
203 Projection,
204 Vec<crate::catalog::ColumnMetadata>,
205)> {
206 build_iterator_pipeline_with_outer(txn, catalog, plan, memory, None)
207}
208
209fn build_iterator_pipeline_with_outer<
210 'txn,
211 S: KVStore + 'txn,
212 C: Catalog + ?Sized,
213 T: SqlTxn<'txn, S>,
214>(
215 txn: &mut T,
216 catalog: &C,
217 plan: LogicalPlan,
218 memory: Option<&MemoryPolicy>,
219 outer: Option<&Row>,
220) -> Result<(
221 Box<dyn RowIterator>,
222 Projection,
223 Vec<crate::catalog::ColumnMetadata>,
224)> {
225 match plan {
226 LogicalPlan::Scan { table, projection } => {
227 if table == LITERAL_TABLE {
228 let schema = Vec::new();
229 let rows = vec![Row::new(0, Vec::new())];
230 let iter = iterator::VecIterator::new(rows, schema.clone());
231 return Ok((Box::new(iter), projection, schema));
232 }
233 let table_meta = catalog
234 .get_table(&table)
235 .cloned()
236 .ok_or_else(|| ExecutorError::TableNotFound(table.clone()))?;
237
238 if table_meta.storage_options.storage_type == StorageType::Columnar {
239 let columnar_scan = columnar_scan::build_columnar_scan(&table_meta, &projection);
240 let rows = columnar_scan::execute_columnar_scan(txn, &table_meta, &columnar_scan)?;
241 let schema = table_meta.columns.clone();
242 let iter = iterator::VecIterator::new(rows, schema.clone());
243 return Ok((Box::new(iter), projection, schema));
244 }
245
246 let rows = scan::execute_scan(txn, &table_meta)?;
250 let schema = table_meta.columns.clone();
251
252 let iter = iterator::VecIterator::new(rows, schema.clone());
254 Ok((Box::new(iter), projection, schema))
255 }
256 LogicalPlan::Filter { input, predicate } => {
257 if let LogicalPlan::Scan { table, projection } = input.as_ref()
258 && let Some(table_meta) = catalog.get_table(table)
259 && table_meta.storage_options.storage_type == StorageType::Columnar
260 {
261 let columnar_scan = columnar_scan::build_columnar_scan_for_filter(
262 table_meta,
263 projection.clone(),
264 &predicate,
265 );
266 let rows = columnar_scan::execute_columnar_scan(txn, table_meta, &columnar_scan)?;
267 let schema = table_meta.columns.clone();
268 let iter = iterator::VecIterator::new(rows, schema.clone());
269 return Ok((Box::new(iter), projection.clone(), schema));
270 }
271 let (mut input_iter, projection, schema) =
272 build_iterator_pipeline_with_outer(txn, catalog, *input, memory, outer)?;
273 if outer.is_some() || subquery::contains_subquery(&predicate) {
274 let mut rows = Vec::new();
275 while let Some(result) = input_iter.next_row() {
276 let row = result?;
277 let eval_row = combine_outer_for_eval(&row, outer);
278 if let SqlValue::Boolean(true) = subquery::evaluate_expr_with_subqueries(
279 txn, catalog, &predicate, &eval_row,
280 )? {
281 rows.push(row);
282 }
283 }
284 let iter = iterator::VecIterator::new(rows, schema.clone());
285 return Ok((Box::new(iter), projection, schema));
286 }
287 let filter_iter = FilterIterator::new(input_iter, predicate);
288 Ok((Box::new(filter_iter), projection, schema))
289 }
290 LogicalPlan::Project { input, projection } => {
291 let (mut input_iter, _input_projection, schema) =
292 build_iterator_pipeline_with_outer(txn, catalog, *input, memory, outer)?;
293 let mut rows = Vec::new();
294 while let Some(result) = input_iter.next_row() {
295 rows.push(result?);
296 }
297 let projected =
298 execute_project_with_subqueries(txn, catalog, rows, &projection, &schema, outer)?;
299 let output_schema = projected
300 .columns
301 .iter()
302 .map(|col| crate::catalog::ColumnMetadata::new(&col.name, col.data_type.clone()))
303 .collect::<Vec<_>>();
304 let rows = projected
305 .rows
306 .into_iter()
307 .enumerate()
308 .map(|(idx, values)| Row::new(idx as u64, values))
309 .collect::<Vec<_>>();
310 let output_projection =
311 Projection::All(output_schema.iter().map(|col| col.name.clone()).collect());
312 let iter = iterator::VecIterator::new(rows, output_schema.clone());
313 Ok((Box::new(iter), output_projection, output_schema))
314 }
315 LogicalPlan::Join {
316 left,
317 right,
318 join_type,
319 condition,
320 using: _,
321 } => {
322 let (mut left_iter, _left_projection, left_schema) =
323 build_iterator_pipeline_with_outer(txn, catalog, *left, memory, outer)?;
324 let (mut right_iter, _right_projection, right_schema) =
325 build_iterator_pipeline_with_outer(txn, catalog, *right, memory, outer)?;
326 let mut left_rows = Vec::new();
327 while let Some(result) = left_iter.next_row() {
328 left_rows.push(result?);
329 }
330 let mut right_rows = Vec::new();
331 while let Some(result) = right_iter.next_row() {
332 right_rows.push(result?);
333 }
334 let left_width = left_schema.len();
335 let right_width = right_schema.len();
336 let rows = join::execute_join_with_widths(
337 left_rows,
338 right_rows,
339 join_type,
340 condition.as_ref(),
341 left_width,
342 right_width,
343 )?;
344 let mut schema = left_schema;
345 schema.extend(right_schema);
346 let projection = Projection::All(schema.iter().map(|col| col.name.clone()).collect());
347 let iter = iterator::VecIterator::new(rows, schema.clone());
348 Ok((Box::new(iter), projection, schema))
349 }
350 LogicalPlan::Aggregate {
351 input,
352 group_keys,
353 aggregates,
354 having,
355 projection,
356 } => {
357 let (input_iter, _projection, _schema) =
358 build_iterator_pipeline_with_outer(txn, catalog, *input, memory, outer)?;
359 let schema = aggregate::build_aggregate_schema(&group_keys, &aggregates);
360 if let Some(policy) = memory
361 && policy.spill_directory().is_some()
362 {
363 if group_keys.is_empty() {
364 let iter = aggregate::StreamingAggregateIterator::new(
365 input_iter,
366 group_keys,
367 aggregates,
368 having,
369 schema.clone(),
370 );
371 return Ok((Box::new(iter), projection, schema));
372 }
373 let order_by = group_keys
374 .iter()
375 .cloned()
376 .map(|expr| SortExpr {
377 expr,
378 asc: true,
379 nulls_first: false,
380 })
381 .collect::<Vec<_>>();
382 let sort_iter =
383 SortIterator::new_with_policy(input_iter, &order_by, Some(policy.clone()))?;
384 let iter = aggregate::StreamingAggregateIterator::new(
385 Box::new(sort_iter),
386 group_keys,
387 aggregates,
388 having,
389 schema.clone(),
390 );
391 return Ok((Box::new(iter), projection, schema));
392 }
393
394 let mut iter = aggregate::AggregateIterator::new(
395 input_iter,
396 group_keys,
397 aggregates,
398 having,
399 schema.clone(),
400 );
401 if let Some(policy) = memory {
402 iter = iter.with_memory_policy(Some(policy.clone()));
403 }
404 Ok((Box::new(iter), projection, schema))
405 }
406 LogicalPlan::Sort { input, order_by } => {
407 let (input_iter, projection, schema) =
408 build_iterator_pipeline_with_outer(txn, catalog, *input, memory, outer)?;
409 let sort_iter = if let Some(policy) = memory {
410 SortIterator::new_with_policy(input_iter, &order_by, Some(policy.clone()))?
411 } else {
412 SortIterator::new(input_iter, &order_by)?
413 };
414 Ok((Box::new(sort_iter), projection, schema))
415 }
416 LogicalPlan::Limit {
417 input,
418 limit,
419 offset,
420 } => {
421 let (input_iter, projection, schema) =
422 build_iterator_pipeline_with_outer(txn, catalog, *input, memory, outer)?;
423 let limit_iter = LimitIterator::new(input_iter, limit, offset);
424 Ok((Box::new(limit_iter), projection, schema))
425 }
426 other => Err(ExecutorError::UnsupportedOperation(format!(
427 "unsupported query plan: {other:?}"
428 ))),
429 }
430}
431
432pub fn build_streaming_pipeline<
444 'a,
445 'txn: 'a,
446 S: KVStore + 'txn,
447 C: Catalog + ?Sized,
448 T: SqlTxn<'txn, S>,
449>(
450 txn: &'a mut T,
451 catalog: &C,
452 plan: LogicalPlan,
453) -> Result<(
454 Box<dyn RowIterator + 'a>,
455 Projection,
456 Vec<crate::catalog::ColumnMetadata>,
457)> {
458 build_streaming_pipeline_with_policy(txn, catalog, plan, None)
459}
460
461pub fn build_streaming_pipeline_with_policy<
462 'a,
463 'txn: 'a,
464 S: KVStore + 'txn,
465 C: Catalog + ?Sized,
466 T: SqlTxn<'txn, S>,
467>(
468 txn: &'a mut T,
469 catalog: &C,
470 plan: LogicalPlan,
471 memory: Option<&MemoryPolicy>,
472) -> Result<(
473 Box<dyn RowIterator + 'a>,
474 Projection,
475 Vec<crate::catalog::ColumnMetadata>,
476)> {
477 if subquery::plan_contains_subquery(&plan) {
483 let result = execute_query_result_with_outer_and_policy(txn, catalog, plan, None, memory)?;
484 return Ok(materialize_query_result(result));
485 }
486
487 build_streaming_pipeline_inner(txn, catalog, plan, memory)
488}
489
490fn build_streaming_pipeline_inner<
492 'a,
493 'txn: 'a,
494 S: KVStore + 'txn,
495 C: Catalog + ?Sized,
496 T: SqlTxn<'txn, S>,
497>(
498 txn: &'a mut T,
499 catalog: &C,
500 plan: LogicalPlan,
501 memory: Option<&MemoryPolicy>,
502) -> Result<(
503 Box<dyn RowIterator + 'a>,
504 Projection,
505 Vec<crate::catalog::ColumnMetadata>,
506)> {
507 match plan {
508 LogicalPlan::Scan { table, projection } => {
509 if table == LITERAL_TABLE {
510 let schema = Vec::new();
511 let rows = vec![Row::new(0, Vec::new())];
512 let iter = iterator::VecIterator::new(rows, schema.clone());
513 return Ok((Box::new(iter), projection, schema));
514 }
515 let table_meta = catalog
516 .get_table(&table)
517 .cloned()
518 .ok_or_else(|| ExecutorError::TableNotFound(table.clone()))?;
519
520 if table_meta.storage_options.storage_type == StorageType::Columnar {
521 let columnar_scan = columnar_scan::build_columnar_scan(&table_meta, &projection);
523 let schema = table_meta.columns.clone();
524 let iter =
525 columnar_scan::create_columnar_scan_iterator(txn, &table_meta, &columnar_scan)?;
526 return Ok((Box::new(iter), projection, schema));
527 }
528
529 let schema = table_meta.columns.clone();
531 let scan_iter = scan::create_scan_iterator(txn, &table_meta)?;
532 Ok((Box::new(scan_iter), projection, schema))
533 }
534 LogicalPlan::Filter { input, predicate } => {
535 if let LogicalPlan::Scan { table, projection } = input.as_ref()
536 && let Some(table_meta) = catalog.get_table(table)
537 && table_meta.storage_options.storage_type == StorageType::Columnar
538 {
539 let columnar_scan = columnar_scan::build_columnar_scan_for_filter(
541 table_meta,
542 projection.clone(),
543 &predicate,
544 );
545 let schema = table_meta.columns.clone();
546 let iter =
547 columnar_scan::create_columnar_scan_iterator(txn, table_meta, &columnar_scan)?;
548 return Ok((Box::new(iter), projection.clone(), schema));
549 }
550 let (input_iter, projection, schema) =
551 build_streaming_pipeline_inner(txn, catalog, *input, memory)?;
552 let filter_iter = FilterIterator::new(input_iter, predicate);
553 Ok((Box::new(filter_iter), projection, schema))
554 }
555 LogicalPlan::Project { input, projection } => {
556 let (mut input_iter, _input_projection, schema) =
557 build_streaming_pipeline_inner(txn, catalog, *input, memory)?;
558 let mut rows = Vec::new();
559 while let Some(result) = input_iter.next_row() {
560 rows.push(result?);
561 }
562 let projected = project::execute_project(rows, &projection, &schema)?;
563 let output_schema = projected
564 .columns
565 .iter()
566 .map(|col| crate::catalog::ColumnMetadata::new(&col.name, col.data_type.clone()))
567 .collect::<Vec<_>>();
568 let rows = projected
569 .rows
570 .into_iter()
571 .enumerate()
572 .map(|(idx, values)| Row::new(idx as u64, values))
573 .collect::<Vec<_>>();
574 let output_projection =
575 Projection::All(output_schema.iter().map(|col| col.name.clone()).collect());
576 let iter = iterator::VecIterator::new(rows, output_schema.clone());
577 Ok((Box::new(iter), output_projection, output_schema))
578 }
579 LogicalPlan::Join {
580 left,
581 right,
582 join_type,
583 condition,
584 using: _,
585 } => {
586 let (mut left_iter, _left_projection, left_schema) =
587 build_streaming_pipeline_inner(txn, catalog, *left, memory)?;
588 let mut left_rows = Vec::new();
589 while let Some(result) = left_iter.next_row() {
590 left_rows.push(result?);
591 }
592 drop(left_iter);
593 let (mut right_iter, _right_projection, right_schema) =
594 build_streaming_pipeline_inner(txn, catalog, *right, memory)?;
595 let mut right_rows = Vec::new();
596 while let Some(result) = right_iter.next_row() {
597 right_rows.push(result?);
598 }
599 let rows = join::execute_join_with_widths(
600 left_rows,
601 right_rows,
602 join_type,
603 condition.as_ref(),
604 left_schema.len(),
605 right_schema.len(),
606 )?;
607 let mut schema = left_schema;
608 schema.extend(right_schema);
609 let projection = Projection::All(schema.iter().map(|col| col.name.clone()).collect());
610 let iter = iterator::VecIterator::new(rows, schema.clone());
611 Ok((Box::new(iter), projection, schema))
612 }
613 LogicalPlan::Aggregate {
614 input,
615 group_keys,
616 aggregates,
617 having,
618 projection,
619 } => {
620 let (input_iter, _projection, _schema) =
621 build_streaming_pipeline_inner(txn, catalog, *input, memory)?;
622 let schema = aggregate::build_aggregate_schema(&group_keys, &aggregates);
623 if let Some(policy) = memory
624 && policy.spill_directory().is_some()
625 {
626 if group_keys.is_empty() {
627 let iter = aggregate::StreamingAggregateIterator::new(
628 input_iter,
629 group_keys,
630 aggregates,
631 having,
632 schema.clone(),
633 );
634 return Ok((Box::new(iter), projection, schema));
635 }
636 let order_by = group_keys
637 .iter()
638 .cloned()
639 .map(|expr| SortExpr {
640 expr,
641 asc: true,
642 nulls_first: false,
643 })
644 .collect::<Vec<_>>();
645 let sort_iter =
646 SortIterator::new_with_policy(input_iter, &order_by, Some(policy.clone()))?;
647 let iter = aggregate::StreamingAggregateIterator::new(
648 Box::new(sort_iter),
649 group_keys,
650 aggregates,
651 having,
652 schema.clone(),
653 );
654 return Ok((Box::new(iter), projection, schema));
655 }
656
657 let mut iter = aggregate::AggregateIterator::new(
658 input_iter,
659 group_keys,
660 aggregates,
661 having,
662 schema.clone(),
663 );
664 if let Some(policy) = memory {
665 iter = iter.with_memory_policy(Some(policy.clone()));
666 }
667 Ok((Box::new(iter), projection, schema))
668 }
669 LogicalPlan::Sort { input, order_by } => {
670 let (input_iter, projection, schema) =
671 build_streaming_pipeline_inner(txn, catalog, *input, memory)?;
672 let sort_iter = if let Some(policy) = memory {
673 SortIterator::new_with_policy(input_iter, &order_by, Some(policy.clone()))?
674 } else {
675 SortIterator::new(input_iter, &order_by)?
676 };
677 Ok((Box::new(sort_iter), projection, schema))
678 }
679 LogicalPlan::Limit {
680 input,
681 limit,
682 offset,
683 } => {
684 let (input_iter, projection, schema) =
685 build_streaming_pipeline_inner(txn, catalog, *input, memory)?;
686 let limit_iter = LimitIterator::new(input_iter, limit, offset);
687 Ok((Box::new(limit_iter), projection, schema))
688 }
689 other => Err(ExecutorError::UnsupportedOperation(format!(
690 "unsupported query plan: {other:?}"
691 ))),
692 }
693}
694
695fn eval_expr(expr: &crate::planner::typed_expr::TypedExpr, row: &Row) -> Result<SqlValue> {
697 let ctx = EvalContext::new(&row.values);
698 crate::executor::evaluator::evaluate(expr, &ctx)
699}
700
701fn combine_outer_for_eval(row: &Row, outer: Option<&Row>) -> Row {
702 let Some(outer) = outer else {
703 return row.clone();
704 };
705 let mut values = Vec::with_capacity(row.len() + outer.len());
706 values.extend(row.values.clone());
707 values.extend(outer.values.clone());
708 Row::new(row.row_id, values)
709}
710
711fn execute_project_with_subqueries<
712 'txn,
713 S: KVStore + 'txn,
714 C: Catalog + ?Sized,
715 T: SqlTxn<'txn, S>,
716>(
717 txn: &mut T,
718 catalog: &C,
719 rows: Vec<Row>,
720 projection: &Projection,
721 schema: &[crate::catalog::ColumnMetadata],
722 outer: Option<&Row>,
723) -> Result<QueryResult> {
724 match projection {
725 Projection::All(_) => project::execute_project(rows, projection, schema),
726 Projection::Columns(cols)
727 if outer.is_some() || cols.iter().any(|c| subquery::contains_subquery(&c.expr)) =>
728 {
729 let columns: Vec<_> = cols
730 .iter()
731 .enumerate()
732 .map(|(i, c)| column_info_from_projection(c, i))
733 .collect();
734 let mut projected_rows = Vec::with_capacity(rows.len());
735 for row in rows {
736 let eval_row = combine_outer_for_eval(&row, outer);
737 let mut values = Vec::with_capacity(cols.len());
738 for col in cols {
739 values.push(subquery::evaluate_expr_with_subqueries(
740 txn, catalog, &col.expr, &eval_row,
741 )?);
742 }
743 projected_rows.push(values);
744 }
745 Ok(QueryResult::new(columns, projected_rows))
746 }
747 Projection::Columns(_) => project::execute_project(rows, projection, schema),
748 }
749}
750
751fn column_name_from_projection(
753 projected: &crate::planner::typed_expr::ProjectedColumn,
754 idx: usize,
755) -> String {
756 projected
757 .alias
758 .clone()
759 .or_else(|| match &projected.expr.kind {
760 crate::planner::typed_expr::TypedExprKind::ColumnRef { column, .. } => {
761 Some(column.clone())
762 }
763 _ => None,
764 })
765 .unwrap_or_else(|| format!("col_{idx}"))
766}
767
768fn column_info_from_projection(
770 projected: &crate::planner::typed_expr::ProjectedColumn,
771 idx: usize,
772) -> ColumnInfo {
773 ColumnInfo::new(
774 column_name_from_projection(projected, idx),
775 projected.expr.resolved_type.clone(),
776 )
777}
778
779fn column_infos_from_all(
781 schema: &[crate::catalog::ColumnMetadata],
782 names: &[String],
783) -> Result<Vec<ColumnInfo>> {
784 names
785 .iter()
786 .map(|name| {
787 let col = schema
788 .iter()
789 .find(|c| &c.name == name)
790 .ok_or_else(|| ExecutorError::ColumnNotFound(name.clone()))?;
791 Ok(ColumnInfo::new(name.clone(), col.data_type.clone()))
792 })
793 .collect()
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799 use crate::catalog::{ColumnMetadata, MemoryCatalog, TableMetadata};
800 use crate::executor::ddl::create_table::execute_create_table;
801 use crate::planner::typed_expr::TypedExpr;
802 use crate::planner::types::ResolvedType;
803 use crate::storage::TxnBridge;
804 use alopex_core::kv::memory::MemoryKV;
805 use std::sync::Arc;
806
807 #[test]
808 fn execute_query_scan_only_returns_rows() {
809 let bridge = TxnBridge::new(Arc::new(MemoryKV::new()));
810 let mut catalog = MemoryCatalog::new();
811 let table = TableMetadata::new(
812 "users",
813 vec![
814 ColumnMetadata::new("id", ResolvedType::Integer),
815 ColumnMetadata::new("name", ResolvedType::Text),
816 ],
817 );
818 let mut ddl_txn = bridge.begin_write().unwrap();
819 execute_create_table(&mut ddl_txn, &mut catalog, table.clone(), vec![], false).unwrap();
820 ddl_txn.commit().unwrap();
821
822 let mut txn = bridge.begin_write().unwrap();
823 crate::executor::dml::execute_insert(
824 &mut txn,
825 &catalog,
826 "users",
827 vec!["id".into(), "name".into()],
828 vec![vec![
829 TypedExpr::literal(
830 crate::ast::expr::Literal::Number("1".into()),
831 ResolvedType::Integer,
832 crate::Span::default(),
833 ),
834 TypedExpr::literal(
835 crate::ast::expr::Literal::String("alice".into()),
836 ResolvedType::Text,
837 crate::Span::default(),
838 ),
839 ]],
840 )
841 .unwrap();
842
843 let result = execute_query(
844 &mut txn,
845 &catalog,
846 LogicalPlan::scan(
847 "users".into(),
848 Projection::All(vec!["id".into(), "name".into()]),
849 ),
850 )
851 .unwrap();
852
853 match result {
854 ExecutionResult::Query(q) => {
855 assert_eq!(q.rows.len(), 1);
856 assert_eq!(q.columns.len(), 2);
857 assert_eq!(
858 q.rows[0],
859 vec![SqlValue::Integer(1), SqlValue::Text("alice".into())]
860 );
861 }
862 other => panic!("unexpected result {other:?}"),
863 }
864 }
865}