1use crate::ast::*;
4use crate::cancel::CancelCheck;
5use crate::plan::*;
6use crate::planner::{
7 extract_single_bound, range_scan_for_target, try_extract_eq_index_key, RangeBound, RangeTarget,
8};
9use crate::result::{QueryError, QueryResult};
10use powdb_storage::catalog::{Catalog, ExpressionIndexMeta, IndexOrderDirection};
11use powdb_storage::row::{decode_column, decode_row, patch_var_column_in_place, RowLayout};
12use powdb_storage::stored_json_path::StoredJsonPathV1;
13use powdb_storage::types::*;
14use std::cmp::Reverse;
15use std::collections::{BinaryHeap, HashSet};
16use std::ops::ControlFlow;
17
18use super::compiled::*;
19use super::eval::*;
20use super::row_body_base;
21use super::{check_join_limit, mem_budget, Engine, MAX_SORT_ROWS};
22use powdb_storage::view::ViewDef;
23
24const CANCELLABLE_SORT_RUN: usize = 2_048;
28
29#[cfg(test)]
33thread_local! {
34 static GENERIC_RID_MATCH_CALLS: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
35}
36
37#[cfg(test)]
38pub(super) fn reset_generic_rid_match_calls() {
39 GENERIC_RID_MATCH_CALLS.with(|calls| calls.set(0));
40}
41
42#[cfg(test)]
43pub(super) fn generic_rid_match_calls() -> u64 {
44 GENERIC_RID_MATCH_CALLS.with(std::cell::Cell::get)
45}
46
47pub(super) fn compare_order_values(
53 left: &Value,
54 right: &Value,
55 descending: bool,
56) -> std::cmp::Ordering {
57 use std::cmp::Ordering;
58
59 match (left, right) {
60 (Value::Empty, Value::Empty) => Ordering::Equal,
61 (Value::Empty, _) => Ordering::Greater,
62 (_, Value::Empty) => Ordering::Less,
63 _ if descending => left.cmp(right).reverse(),
64 _ => left.cmp(right),
65 }
66}
67
68pub(super) fn cooperative_stable_sort_by<T, F>(
77 values: &mut [T],
78 memory_limit: usize,
79 compare: F,
80) -> Result<(), QueryError>
81where
82 F: Fn(&T, &T) -> std::cmp::Ordering,
83{
84 crate::cancel::check()?;
85 let len = values.len();
86 if len < 2 {
87 return Ok(());
88 }
89
90 let scratch_bytes = len
91 .saturating_mul(std::mem::size_of::<usize>())
92 .saturating_mul(2);
93 mem_budget::charge(scratch_bytes, memory_limit)?;
94
95 let mut order: Vec<usize> = (0..len).collect();
96 let mut scratch = vec![0usize; len];
97
98 for run in order.chunks_mut(CANCELLABLE_SORT_RUN) {
99 crate::cancel::check()?;
100 run.sort_by(|&a, &b| compare(&values[a], &values[b]));
101 crate::cancel::check()?;
102 }
103
104 let mut cancel = CancelCheck::new();
105 let mut width = CANCELLABLE_SORT_RUN;
106 while width < len {
107 let step = width.saturating_mul(2);
108 let mut start = 0usize;
109 while start < len {
110 let mid = start.saturating_add(width).min(len);
111 let end = start.saturating_add(step).min(len);
112 let (mut left, mut right, mut out) = (start, mid, start);
113
114 while left < mid && right < end {
115 cancel.tick()?;
116 if compare(&values[order[left]], &values[order[right]])
117 != std::cmp::Ordering::Greater
118 {
119 scratch[out] = order[left];
120 left += 1;
121 } else {
122 scratch[out] = order[right];
123 right += 1;
124 }
125 out += 1;
126 }
127 while left < mid {
128 cancel.tick()?;
129 scratch[out] = order[left];
130 left += 1;
131 out += 1;
132 }
133 while right < end {
134 cancel.tick()?;
135 scratch[out] = order[right];
136 right += 1;
137 out += 1;
138 }
139 start = start.saturating_add(step);
140 }
141 std::mem::swap(&mut order, &mut scratch);
142 width = step;
143 }
144
145 for (new_position, &old_position) in order.iter().enumerate() {
148 cancel.tick()?;
149 scratch[old_position] = new_position;
150 }
151 drop(order);
152 for position in 0..len {
153 while scratch[position] != position {
154 cancel.tick()?;
155 let destination = scratch[position];
156 values.swap(position, destination);
157 scratch.swap(position, destination);
158 }
159 }
160 Ok(())
161}
162
163pub(super) fn for_each_row_raw_cancellable(
166 catalog: &Catalog,
167 table: &str,
168 mut f: impl FnMut(RowId, &[u8]),
169) -> Result<(), QueryError> {
170 if !crate::cancel::has_active_install() {
176 return catalog
177 .for_each_row_raw(table, f)
178 .map_err(|err| QueryError::StorageError(err.to_string()));
179 }
180
181 let mut cancel = CancelCheck::new();
182 let mut cancel_err: Option<QueryError> = None;
183 catalog
184 .try_for_each_row_raw(table, |rid, data| {
185 if let Err(err) = cancel.tick() {
186 cancel_err = Some(err);
187 return ControlFlow::Break(());
188 }
189 f(rid, data);
190 ControlFlow::Continue(())
191 })
192 .map_err(|err| QueryError::StorageError(err.to_string()))?;
193 match cancel_err {
194 Some(err) => Err(err),
195 None => Ok(()),
196 }
197}
198
199fn resolve_expression_index(
200 catalog: &Catalog,
201 table: &str,
202 path: &StoredJsonPathV1,
203) -> Option<ExpressionIndexMeta> {
204 catalog
205 .expression_index_metadata(table)?
206 .into_iter()
207 .find(|metadata| metadata.canonical_version == 1 && metadata.json_path == *path)
208}
209
210fn expression_index_fallback(plan: &PlanNode) -> Option<PlanNode> {
211 match plan {
212 PlanNode::ExprIndexScan { table, path, key } => Some(PlanNode::Filter {
213 input: Box::new(PlanNode::SeqScan {
214 table: table.clone(),
215 }),
216 predicate: Expr::BinaryOp(
217 Box::new(stored_json_path_expr(path)),
218 BinOp::Eq,
219 Box::new(key.clone()),
220 ),
221 }),
222 PlanNode::ExprRangeScan {
223 table,
224 path,
225 start,
226 end,
227 } => Some(PlanNode::Filter {
228 input: Box::new(PlanNode::SeqScan {
229 table: table.clone(),
230 }),
231 predicate: synthesize_expr_range_predicate(path, start, end),
232 }),
233 PlanNode::OrderedExprIndexScan {
234 table,
235 path,
236 descending,
237 limit,
238 offset,
239 } => {
240 let sorted = PlanNode::Sort {
241 input: Box::new(PlanNode::SeqScan {
242 table: table.clone(),
243 }),
244 keys: vec![SortKey {
245 expr: stored_json_path_expr(path),
246 descending: *descending,
247 }],
248 };
249 let sliced = match offset {
250 Some(count) => PlanNode::Offset {
251 input: Box::new(sorted),
252 count: count.clone(),
253 },
254 None => sorted,
255 };
256 Some(PlanNode::Limit {
257 input: Box::new(sliced),
258 count: limit.clone(),
259 })
260 }
261 _ => None,
262 }
263}
264
265#[derive(Debug)]
266pub(super) struct ProvenanceRows {
267 pub(super) columns: Vec<String>,
268 pub(super) rows: Vec<Vec<Value>>,
269 source_aliases: Vec<String>,
270 provenance: Vec<Vec<Option<RowId>>>,
271}
272
273impl ProvenanceRows {
274 fn source_index(&self, alias: &str) -> Option<usize> {
275 self.source_aliases
276 .iter()
277 .position(|source| source == alias)
278 }
279}
280
281impl Engine {
282 pub(super) fn execute_expression_index_plan(
283 &self,
284 plan: &PlanNode,
285 projected_fields: Option<&[ProjectField]>,
286 ) -> Result<Option<QueryResult>, QueryError> {
287 let (table, path) = match plan {
288 PlanNode::ExprIndexScan { table, path, .. }
289 | PlanNode::ExprRangeScan { table, path, .. }
290 | PlanNode::OrderedExprIndexScan { table, path, .. } => (table, path),
291 _ => return Ok(None),
292 };
293 let Some(index) = resolve_expression_index(&self.catalog, table, path) else {
294 return Ok(None);
295 };
296 let schema = self
297 .catalog
298 .schema(table)
299 .ok_or_else(|| QueryError::TableNotFound(table.clone()))?
300 .clone();
301 let all_columns: Vec<String> = schema
302 .columns
303 .iter()
304 .map(|column| column.name.clone())
305 .collect();
306
307 let projection = match projected_fields {
308 Some(fields) => {
309 if !fields
310 .iter()
311 .all(|field| matches!(field.expr, Expr::Field(_)))
312 {
313 return Ok(None);
314 }
315 let mut indices = Vec::with_capacity(fields.len());
316 let mut columns = Vec::with_capacity(fields.len());
317 for field in fields {
318 let Expr::Field(name) = &field.expr else {
319 unreachable!("plain-field projection checked above")
320 };
321 let index =
322 schema
323 .column_index(name)
324 .ok_or_else(|| QueryError::ColumnNotFound {
325 table: table.clone(),
326 column: name.clone(),
327 })?;
328 indices.push(index);
329 columns.push(field.alias.clone().unwrap_or_else(|| name.clone()));
330 }
331 Some((indices, columns))
332 }
333 None => None,
334 };
335
336 let (rids, range) = match plan {
337 PlanNode::ExprIndexScan { key, .. } => {
338 let key = literal_to_value(key)?;
339 let rids = if key.is_empty() {
340 self.catalog
341 .expression_index_btree(table, index.index_id)
342 .ok_or_else(|| {
343 QueryError::Execution("expression index disappeared".to_string())
344 })?
345 .empty_rids()
346 .to_vec()
347 } else {
348 self.catalog
349 .expression_index_lookup_all(table, index.index_id, &key)
350 .map_err(|error| QueryError::StorageError(error.to_string()))?
351 };
352 (rids, None)
353 }
354 PlanNode::ExprRangeScan { start, end, .. } => {
355 let start_value = start
356 .as_ref()
357 .map(|(expr, _)| literal_to_value(expr))
358 .transpose()?;
359 let end_value = end
360 .as_ref()
361 .map(|(expr, _)| literal_to_value(expr))
362 .transpose()?;
363 let rids = self
364 .catalog
365 .expression_index_range_rids(
366 table,
367 index.index_id,
368 start_value.as_ref(),
369 end_value.as_ref(),
370 )
371 .map_err(|error| QueryError::StorageError(error.to_string()))?;
372 (
373 rids,
374 Some((
375 start_value,
376 start.as_ref().is_none_or(|(_, inclusive)| *inclusive),
377 end_value,
378 end.as_ref().is_none_or(|(_, inclusive)| *inclusive),
379 )),
380 )
381 }
382 PlanNode::OrderedExprIndexScan {
383 descending,
384 limit,
385 offset,
386 ..
387 } => {
388 let Expr::Literal(Literal::Int(limit)) = limit else {
389 return Err(QueryError::Execution(
390 "expression-index limit must be a non-negative integer".to_string(),
391 ));
392 };
393 let offset = match offset {
394 Some(Expr::Literal(Literal::Int(offset))) if *offset >= 0 => *offset as usize,
395 None => 0,
396 _ => {
397 return Err(QueryError::Execution(
398 "expression-index offset must be a non-negative integer".to_string(),
399 ));
400 }
401 };
402 if *limit < 0 {
403 return Err(QueryError::Execution(
404 "expression-index limit must be a non-negative integer".to_string(),
405 ));
406 }
407 let rids = self
408 .catalog
409 .expression_index_ordered_rids_bounded(
410 table,
411 index.index_id,
412 if *descending {
413 IndexOrderDirection::Desc
414 } else {
415 IndexOrderDirection::Asc
416 },
417 offset,
418 *limit as usize,
419 )
420 .map_err(|error| QueryError::StorageError(error.to_string()))?;
421 (rids, None)
422 }
423 _ => unreachable!("expression-index plan checked above"),
424 };
425
426 let root_index =
427 schema
428 .column_index(&path.column)
429 .ok_or_else(|| QueryError::ColumnNotFound {
430 table: table.clone(),
431 column: path.column.clone(),
432 })?;
433 let path_expr = stored_json_path_expr(path);
434 let mut rows = Vec::with_capacity(rids.len());
435 let mut cancel = CancelCheck::new();
436 for rid in rids {
437 cancel.tick()?;
438 match &projection {
439 Some((projected_indices, _)) => {
440 let mut fetch_indices = projected_indices.clone();
441 let root_position = fetch_indices.iter().position(|index| *index == root_index);
442 let root_position = match root_position {
443 Some(position) => position,
444 None => {
445 fetch_indices.push(root_index);
446 fetch_indices.len() - 1
447 }
448 };
449 let Some(mut fetched) = self
450 .catalog
451 .get_projected(table, rid, &fetch_indices)
452 .map_err(|error| QueryError::StorageError(error.to_string()))?
453 else {
454 continue;
455 };
456 if let Some((start, start_inclusive, end, end_inclusive)) = &range {
457 let value = eval_expr(
458 &path_expr,
459 std::slice::from_ref(&fetched[root_position]),
460 std::slice::from_ref(&path.column),
461 );
462 if value.is_empty()
463 || !range_matches(&value, start, *start_inclusive, end, *end_inclusive)
464 {
465 continue;
466 }
467 }
468 fetched.truncate(projected_indices.len());
469 rows.push(fetched);
470 }
471 None => {
472 let Some(row) = self.catalog.get(table, rid) else {
473 continue;
474 };
475 if let Some((start, start_inclusive, end, end_inclusive)) = &range {
476 let value = eval_expr(&path_expr, &row, &all_columns);
477 if value.is_empty()
478 || !range_matches(&value, start, *start_inclusive, end, *end_inclusive)
479 {
480 continue;
481 }
482 }
483 rows.push(row);
484 }
485 }
486 }
487
488 let columns = projection
489 .map(|(_, columns)| columns)
490 .unwrap_or(all_columns);
491 Ok(Some(QueryResult::Rows { columns, rows }))
492 }
493
494 pub(super) fn try_filter_index_residual_fast(
508 &self,
509 input: &PlanNode,
510 predicate: &Expr,
511 ) -> Result<Option<QueryResult>, QueryError> {
512 if contains_subquery(predicate) {
515 return Ok(None);
516 }
517 let (table, rids) = match input {
518 PlanNode::IndexScan { table, column, key } => {
519 let Some(tbl) = self.catalog.get_table(table) else {
520 return Ok(None);
521 };
522 if !tbl.has_index(column) {
523 return Ok(None);
524 }
525 let key_value = literal_to_value(key)?;
526 (table.as_str(), tbl.index_lookup_all(column, &key_value))
527 }
528 PlanNode::ExprIndexScan { table, path, key } => {
529 let Some(index) = resolve_expression_index(&self.catalog, table, path) else {
530 return Ok(None);
531 };
532 let key_value = literal_to_value(key)?;
533 let rids = if key_value.is_empty() {
534 self.catalog
535 .expression_index_btree(table, index.index_id)
536 .ok_or_else(|| {
537 QueryError::Execution("expression index disappeared".to_string())
538 })?
539 .empty_rids()
540 .to_vec()
541 } else {
542 self.catalog
543 .expression_index_lookup_all(table, index.index_id, &key_value)
544 .map_err(|error| QueryError::StorageError(error.to_string()))?
545 };
546 (table.as_str(), rids)
547 }
548 _ => return Ok(None),
549 };
550
551 let schema = self
552 .catalog
553 .schema(table)
554 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
555 .clone();
556 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
557 let residual_indices = predicate_column_indices_json(predicate, &all_columns);
560 let residual_names: Vec<String> = residual_indices
561 .iter()
562 .map(|&index| all_columns[index].clone())
563 .collect();
564
565 let mut rows: Vec<Vec<Value>> = Vec::new();
566 let mut cancel = CancelCheck::new();
569 for rid in rids {
570 cancel.tick()?;
571 let Some(sparse) = self
572 .catalog
573 .get_projected(table, rid, &residual_indices)
574 .map_err(|error| QueryError::StorageError(error.to_string()))?
575 else {
576 continue;
577 };
578 if eval_predicate(predicate, &sparse, &residual_names) {
579 if let Some(full) = self.catalog.get(table, rid) {
580 rows.push(full);
581 }
582 }
583 }
584 Ok(Some(QueryResult::Rows {
585 columns: all_columns,
586 rows,
587 }))
588 }
589
590 fn charge_provenance(&self, rows: &ProvenanceRows) -> Result<(), QueryError> {
591 let aliases =
592 rows.source_aliases
593 .iter()
594 .fold(std::mem::size_of::<Vec<String>>(), |total, alias| {
595 total
596 .saturating_add(std::mem::size_of::<String>())
597 .saturating_add(alias.capacity())
598 });
599 let per_row = std::mem::size_of::<Vec<Option<RowId>>>().saturating_add(
600 rows.source_aliases
601 .len()
602 .saturating_mul(std::mem::size_of::<Option<RowId>>()),
603 );
604 mem_budget::charge(
605 aliases.saturating_add(rows.provenance.len().saturating_mul(per_row)),
606 self.query_memory_limit(),
607 )
608 }
609
610 fn provenance_scan(
611 &self,
612 table: &str,
613 alias: &str,
614 qualify_columns: bool,
615 ) -> Result<ProvenanceRows, QueryError> {
616 let schema = self
617 .catalog
618 .schema(table)
619 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
620 .clone();
621 let columns = schema
622 .columns
623 .iter()
624 .map(|column| {
625 if qualify_columns {
626 format!("{alias}.{}", column.name)
627 } else {
628 column.name.clone()
629 }
630 })
631 .collect();
632 let mut rows = Vec::new();
633 let mut provenance = Vec::new();
634 let mut cancel = CancelCheck::new();
635 for (rid, row) in self
636 .catalog
637 .scan(table)
638 .map_err(|error| QueryError::StorageError(error.to_string()))?
639 {
640 cancel.tick()?;
641 rows.push(row);
642 provenance.push(vec![Some(rid)]);
643 }
644 let result = ProvenanceRows {
645 columns,
646 rows,
647 source_aliases: vec![alias.to_string()],
648 provenance,
649 };
650 Ok(result)
651 }
652
653 pub(super) fn materialize_rows_with_provenance(
654 &self,
655 plan: &PlanNode,
656 ) -> Result<ProvenanceRows, QueryError> {
657 let result = match plan {
658 PlanNode::SeqScan { table } => self.provenance_scan(table, table, false)?,
659 PlanNode::AliasScan { table, alias } => self.provenance_scan(table, alias, true)?,
660 PlanNode::IndexScan { table, column, key } => {
661 let fallback = PlanNode::Filter {
662 input: Box::new(PlanNode::SeqScan {
663 table: table.clone(),
664 }),
665 predicate: Expr::BinaryOp(
666 Box::new(Expr::Field(column.clone())),
667 BinOp::Eq,
668 Box::new(key.clone()),
669 ),
670 };
671 self.materialize_rows_with_provenance(&fallback)?
672 }
673 PlanNode::RangeScan {
674 table,
675 column,
676 start,
677 end,
678 } => {
679 let fallback = PlanNode::Filter {
680 input: Box::new(PlanNode::SeqScan {
681 table: table.clone(),
682 }),
683 predicate: synthesize_range_predicate(column, start, end),
684 };
685 self.materialize_rows_with_provenance(&fallback)?
686 }
687 PlanNode::ExprIndexScan { .. }
688 | PlanNode::ExprRangeScan { .. }
689 | PlanNode::OrderedExprIndexScan { .. } => {
690 let fallback = expression_index_fallback(plan)
691 .expect("expression-index branch always has a fallback");
692 self.materialize_rows_with_provenance(&fallback)?
693 }
694 PlanNode::Filter { input, predicate } => {
695 if contains_subquery(predicate) {
696 return Err(QueryError::Execution(
697 "symmetric aggregation over a subquery filter is not supported; use raw"
698 .to_string(),
699 ));
700 }
701 let input = self.materialize_rows_with_provenance(input)?;
702 let mut rows = Vec::new();
703 let mut provenance = Vec::new();
704 let mut cancel = CancelCheck::new();
705 for (row, row_provenance) in input.rows.into_iter().zip(input.provenance) {
706 cancel.tick()?;
707 if eval_predicate(predicate, &row, &input.columns) {
708 rows.push(row);
709 provenance.push(row_provenance);
710 }
711 }
712 ProvenanceRows {
713 columns: input.columns,
714 rows,
715 source_aliases: input.source_aliases,
716 provenance,
717 }
718 }
719 PlanNode::Project { input, fields } => {
720 let input = self.materialize_rows_with_provenance(input)?;
721 let columns = fields
722 .iter()
723 .map(|field| {
724 field.alias.clone().unwrap_or_else(|| match &field.expr {
725 Expr::Field(name) => name.clone(),
726 Expr::QualifiedField { qualifier, field } => {
727 format!("{qualifier}.{field}")
728 }
729 _ => expression_output_name(&field.expr),
730 })
731 })
732 .collect();
733 let mut rows = Vec::with_capacity(input.rows.len());
734 let mut cancel = CancelCheck::new();
735 for row in &input.rows {
736 cancel.tick()?;
737 rows.push(
738 fields
739 .iter()
740 .map(|field| eval_expr(&field.expr, row, &input.columns))
741 .collect(),
742 );
743 }
744 ProvenanceRows {
745 columns,
746 rows,
747 source_aliases: input.source_aliases,
748 provenance: input.provenance,
749 }
750 }
751 PlanNode::Sort { input, keys } => {
752 let input = self.materialize_rows_with_provenance(input)?;
753 if input.rows.len() > MAX_SORT_ROWS {
754 return Err(QueryError::SortLimitExceeded);
755 }
756 self.charge_rows(&input.rows)?;
757 let mut paired: Vec<_> = input.rows.into_iter().zip(input.provenance).collect();
758 cooperative_stable_sort_by(
759 &mut paired,
760 self.query_memory_limit(),
761 |(left, _), (right, _)| {
762 for key in keys {
763 let left_value = eval_expr(&key.expr, left, &input.columns);
764 let right_value = eval_expr(&key.expr, right, &input.columns);
765 let comparison =
766 compare_order_values(&left_value, &right_value, key.descending);
767 if comparison != std::cmp::Ordering::Equal {
768 return comparison;
769 }
770 }
771 std::cmp::Ordering::Equal
772 },
773 )?;
774 let (rows, provenance) = paired.into_iter().unzip();
775 ProvenanceRows {
776 columns: input.columns,
777 rows,
778 source_aliases: input.source_aliases,
779 provenance,
780 }
781 }
782 PlanNode::Limit { input, count } | PlanNode::Offset { input, count } => {
783 let input_rows = self.materialize_rows_with_provenance(input)?;
784 let Expr::Literal(Literal::Int(count)) = count else {
785 return Err(QueryError::Execution(
786 "limit/offset must be an integer literal".to_string(),
787 ));
788 };
789 let count = *count as usize;
790 let is_limit = matches!(plan, PlanNode::Limit { .. });
791 let iterator = input_rows.rows.into_iter().zip(input_rows.provenance);
792 let (rows, provenance) = if is_limit {
793 iterator.take(count).unzip()
794 } else {
795 iterator.skip(count).unzip()
796 };
797 ProvenanceRows {
798 columns: input_rows.columns,
799 rows,
800 source_aliases: input_rows.source_aliases,
801 provenance,
802 }
803 }
804 PlanNode::Distinct { input } => {
805 let input = self.materialize_rows_with_provenance(input)?;
806 let mut seen = HashSet::new();
807 let mut rows = Vec::new();
808 let mut provenance = Vec::new();
809 let mut cancel = CancelCheck::new();
810 for (row, row_provenance) in input.rows.into_iter().zip(input.provenance) {
811 cancel.tick()?;
812 if seen.insert(row.clone()) {
813 rows.push(row);
814 provenance.push(row_provenance);
815 }
816 }
817 ProvenanceRows {
818 columns: input.columns,
819 rows,
820 source_aliases: input.source_aliases,
821 provenance,
822 }
823 }
824 PlanNode::Union { left, right, all } => {
825 let mut left_rows = self.materialize_rows_with_provenance(left)?;
826 let right_rows = self.materialize_rows_with_provenance(right)?;
827 if left_rows.columns.len() != right_rows.columns.len() {
828 return Err(QueryError::Execution(
829 "union sides must have the same number of columns".to_string(),
830 ));
831 }
832 if left_rows.source_aliases != right_rows.source_aliases {
833 return Err(QueryError::Execution(
834 "symmetric aggregation over union requires matching source aliases; use raw"
835 .to_string(),
836 ));
837 }
838 left_rows.rows.extend(right_rows.rows);
839 left_rows.provenance.extend(right_rows.provenance);
840 if !all {
841 let mut seen = HashSet::new();
842 let mut rows = Vec::new();
843 let mut provenance = Vec::new();
844 for (row, row_provenance) in
845 left_rows.rows.into_iter().zip(left_rows.provenance)
846 {
847 if seen.insert(row.clone()) {
848 rows.push(row);
849 provenance.push(row_provenance);
850 }
851 }
852 left_rows.rows = rows;
853 left_rows.provenance = provenance;
854 }
855 left_rows
856 }
857 PlanNode::NestedLoopJoin {
858 left,
859 right,
860 on,
861 kind,
862 } => {
863 let left = self.materialize_rows_with_provenance(left)?;
864 let right = self.materialize_rows_with_provenance(right)?;
865 execute_provenance_join(
866 left,
867 right,
868 on.as_ref(),
869 *kind,
870 self.nested_loop_pair_limit,
871 )?
872 }
873 _ => {
874 return Err(QueryError::Execution(
875 "symmetric aggregation input shape is not supported; use raw".to_string(),
876 ));
877 }
878 };
879 self.charge_provenance(&result)?;
880 Ok(result)
881 }
882
883 pub(super) fn introspect_list_types(&self) -> Result<QueryResult, QueryError> {
886 let rows: Vec<Vec<Value>> = self
887 .catalog
888 .list_tables()
889 .iter()
890 .map(|name| {
891 let cols = self
892 .catalog
893 .schema(name)
894 .map(|s| s.columns.len())
895 .unwrap_or(0) as i64;
896 vec![Value::Str((*name).to_string()), Value::Int(cols)]
897 })
898 .collect();
899 Ok(QueryResult::Rows {
900 columns: vec!["name".to_string(), "columns".to_string()],
901 rows,
902 })
903 }
904
905 pub(super) fn introspect_describe(&self, table: &str) -> Result<QueryResult, QueryError> {
908 let schema = self
909 .catalog
910 .schema(table)
911 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
912 let rows: Vec<Vec<Value>> = schema
913 .columns
914 .iter()
915 .map(|c| {
916 let index = if self.catalog.has_index(table, &c.name) {
917 match self.catalog.is_index_unique(table, &c.name) {
918 Some(true) => "unique",
919 _ => "index",
920 }
921 } else {
922 ""
923 };
924 vec![
925 Value::Str(c.name.clone()),
926 Value::Str(type_id_to_name(c.type_id).to_string()),
927 Value::Bool(!c.required),
928 Value::Str(index.to_string()),
929 ]
930 })
931 .collect();
932 Ok(QueryResult::Rows {
933 columns: vec![
934 "column".to_string(),
935 "type".to_string(),
936 "nullable".to_string(),
937 "index".to_string(),
938 ],
939 rows,
940 })
941 }
942
943 pub fn execute_plan(&mut self, plan: &PlanNode) -> Result<QueryResult, QueryError> {
944 validate_no_stray_aggregates(plan)?;
950 validate_json_path_types(&self.catalog, plan)?;
951 match plan {
952 PlanNode::ExprIndexScan { .. }
953 | PlanNode::ExprRangeScan { .. }
954 | PlanNode::OrderedExprIndexScan { .. } => {
955 if let Some(result) = self.execute_expression_index_plan(plan, None)? {
956 return Ok(result);
957 }
958 let fallback = expression_index_fallback(plan)
959 .expect("expression-index branch always has a fallback");
960 self.execute_plan(&fallback)
961 }
962 PlanNode::SeqScan { table } => {
963 if self.view_registry.is_dirty(table) {
965 self.refresh_view(table)?;
966 }
967 let schema = self
968 .catalog
969 .schema(table)
970 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
971 .clone();
972 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
973 let mut cancel = CancelCheck::new();
976 let mut rows: Vec<Vec<Value>> = Vec::new();
977 for (_, row) in self
978 .catalog
979 .scan(table)
980 .map_err(|e| QueryError::StorageError(e.to_string()))?
981 {
982 cancel.tick()?;
983 rows.push(row);
984 }
985 Ok(QueryResult::Rows { columns, rows })
986 }
987
988 PlanNode::Filter { input, predicate } => {
989 let materialized;
993 let predicate = if contains_subquery(predicate) {
994 materialized = self.materialize_subqueries(predicate)?;
995 &materialized
996 } else {
997 predicate
998 };
999
1000 if contains_subquery(predicate) {
1002 let result = self.execute_plan(input)?;
1003 return match result {
1004 QueryResult::Rows { columns, rows } => {
1005 let mut filtered = Vec::new();
1006 let mut cancel = CancelCheck::new();
1009 for row in rows {
1010 cancel.tick()?;
1011 let row_pred =
1012 self.materialize_correlated_for_row(predicate, &row, &columns)?;
1013 if eval_predicate(&row_pred, &row, &columns) {
1014 filtered.push(row);
1015 }
1016 }
1017 Ok(QueryResult::Rows {
1018 columns,
1019 rows: filtered,
1020 })
1021 }
1022 _ => Err("filter requires row input".into()),
1023 };
1024 }
1025
1026 if matches!(
1030 input.as_ref(),
1031 PlanNode::IndexScan { .. } | PlanNode::ExprIndexScan { .. }
1032 ) {
1033 if let Some(result) = self.try_filter_index_residual_fast(input, predicate)? {
1034 return Ok(result);
1035 }
1036 }
1037
1038 if let PlanNode::SeqScan { table } = input.as_ref() {
1046 if !self.catalog.table_has_overflow(table) {
1047 if self.view_registry.is_dirty(table) {
1049 self.refresh_view(table)?;
1050 }
1051 let schema = self
1052 .catalog
1053 .schema(table)
1054 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
1055 .clone();
1056 let columns: Vec<String> =
1057 schema.columns.iter().map(|c| c.name.clone()).collect();
1058 let fast = FastLayout::new(&schema);
1059 let row_layout = RowLayout::new(&schema);
1060 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
1064
1065 let mut cancel = CancelCheck::new();
1072 let mut cancel_err: Option<QueryError> = None;
1073 if let Some(compiled) =
1074 compile_predicate(predicate, &columns, &fast, &schema)
1075 {
1076 self.catalog
1077 .try_for_each_row_raw(table, |_rid, data| {
1078 if let Err(e) = cancel.tick() {
1079 cancel_err = Some(e);
1080 return ControlFlow::Break(());
1081 }
1082 if compiled(data) {
1083 rows.push(decode_row(&schema, data));
1084 }
1085 ControlFlow::Continue(())
1086 })
1087 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1088 } else {
1089 let pred_cols = predicate_column_indices_json(predicate, &columns);
1090 self.catalog
1091 .try_for_each_row_raw(table, |_rid, data| {
1092 if let Err(e) = cancel.tick() {
1093 cancel_err = Some(e);
1094 return ControlFlow::Break(());
1095 }
1096 let pred_row =
1097 decode_selective(&schema, &row_layout, data, &pred_cols);
1098 if eval_predicate(predicate, &pred_row, &columns) {
1099 rows.push(decode_row(&schema, data));
1100 }
1101 ControlFlow::Continue(())
1102 })
1103 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1104 }
1105 if let Some(e) = cancel_err {
1106 return Err(e);
1107 }
1108
1109 return Ok(QueryResult::Rows { columns, rows });
1110 }
1111 }
1112
1113 let result = self.execute_plan(input)?;
1115 match result {
1116 QueryResult::Rows { columns, rows } => {
1117 let mut cancel = CancelCheck::new();
1118 let mut filtered: Vec<Vec<Value>> = Vec::new();
1119 for row in rows {
1120 cancel.tick()?;
1121 if eval_predicate(predicate, &row, &columns) {
1122 filtered.push(row);
1123 }
1124 }
1125 Ok(QueryResult::Rows {
1126 columns,
1127 rows: filtered,
1128 })
1129 }
1130 _ => Err("filter requires row input".into()),
1131 }
1132 }
1133
1134 PlanNode::Project { input, fields } => {
1135 if matches!(
1136 input.as_ref(),
1137 PlanNode::ExprIndexScan { .. }
1138 | PlanNode::ExprRangeScan { .. }
1139 | PlanNode::OrderedExprIndexScan { .. }
1140 ) {
1141 if let Some(result) = self.execute_expression_index_plan(input, Some(fields))? {
1142 return Ok(result);
1143 }
1144 }
1145 if let PlanNode::IndexScan { table, column, key } = input.as_ref() {
1148 let schema = self
1149 .catalog
1150 .schema(table)
1151 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
1152 .clone();
1153 let all_columns: Vec<String> =
1154 schema.columns.iter().map(|c| c.name.clone()).collect();
1155 let key_value = literal_to_value(key)?;
1156 let tbl = self
1157 .catalog
1158 .get_table(table)
1159 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1160
1161 let proj_columns: Vec<String> = fields
1162 .iter()
1163 .map(|f| {
1164 f.alias.clone().unwrap_or_else(|| match &f.expr {
1165 Expr::Field(name) => name.clone(),
1166 _ => "?".into(),
1167 })
1168 })
1169 .collect();
1170
1171 let proj_indices: Vec<usize> = fields
1173 .iter()
1174 .filter_map(|f| {
1175 if let Expr::Field(name) = &f.expr {
1176 all_columns.iter().position(|c| c == name)
1177 } else {
1178 None
1179 }
1180 })
1181 .collect();
1182
1183 let all_plain_fields = fields.iter().all(|f| matches!(f.expr, Expr::Field(_)));
1188 if tbl.has_index(column) && all_plain_fields {
1189 let rids = tbl.index_lookup_all(column, &key_value);
1190 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(rids.len());
1191 let mut cancel = CancelCheck::new();
1192 for rid in rids {
1193 cancel.tick()?;
1194 if let Some(full) = tbl.get(rid) {
1200 let row: Vec<Value> =
1201 proj_indices.iter().map(|&ci| full[ci].clone()).collect();
1202 rows.push(row);
1203 }
1204 }
1205 return Ok(QueryResult::Rows {
1206 columns: proj_columns,
1207 rows,
1208 });
1209 }
1210 }
1211
1212 if let PlanNode::Limit {
1217 input: inner,
1218 count: limit_expr,
1219 } = input.as_ref()
1220 {
1221 if let PlanNode::Sort {
1222 input: sort_input,
1223 keys,
1224 } = inner.as_ref()
1225 {
1226 if keys.len() == 1 {
1228 if let Expr::Field(sort_field) = &keys[0].expr {
1229 let descending = keys[0].descending;
1230 let limit = match limit_expr {
1231 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
1232 _ => usize::MAX,
1233 };
1234 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
1235 match sort_input.as_ref() {
1236 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
1237 PlanNode::Filter {
1238 input: fi,
1239 predicate,
1240 } => {
1241 if let PlanNode::SeqScan { table } = fi.as_ref() {
1242 (Some(table.as_str()), Some(predicate))
1243 } else {
1244 (None, None)
1245 }
1246 }
1247 _ => (None, None),
1248 };
1249 if let Some(table) = table_opt {
1250 if let Some(result) = self.project_filter_sort_limit_fast(
1251 table, fields, sort_field, descending, limit, pred_opt,
1252 )? {
1253 return Ok(result);
1254 }
1255 }
1256 }
1257 }
1258 }
1259 if let PlanNode::Filter {
1262 input: fi,
1263 predicate,
1264 } = inner.as_ref()
1265 {
1266 if let PlanNode::SeqScan { table } = fi.as_ref() {
1267 let limit = match limit_expr {
1268 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
1269 _ => usize::MAX,
1270 };
1271 if let Some(result) = self.project_filter_limit_fast(
1272 table,
1273 fields,
1274 limit,
1275 Some(predicate),
1276 )? {
1277 return Ok(result);
1278 }
1279 }
1280 }
1281 if let PlanNode::SeqScan { table } = inner.as_ref() {
1283 let limit = match limit_expr {
1284 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
1285 _ => usize::MAX,
1286 };
1287 if let Some(result) =
1288 self.project_filter_limit_fast(table, fields, limit, None)?
1289 {
1290 return Ok(result);
1291 }
1292 }
1293 }
1294
1295 if let PlanNode::Filter {
1306 input: fi,
1307 predicate,
1308 } = input.as_ref()
1309 {
1310 if let PlanNode::SeqScan { table } = fi.as_ref() {
1311 if let Some(result) = self.project_filter_limit_fast(
1312 table,
1313 fields,
1314 usize::MAX,
1315 Some(predicate),
1316 )? {
1317 return Ok(result);
1318 }
1319 }
1320 }
1321
1322 if let PlanNode::SeqScan { table } = input.as_ref() {
1326 if let Some(result) =
1327 self.project_filter_limit_fast(table, fields, usize::MAX, None)?
1328 {
1329 return Ok(result);
1330 }
1331 }
1332
1333 let result = self.execute_plan(input)?;
1334 match result {
1335 QueryResult::Rows { columns, rows } => {
1336 let proj_columns: Vec<String> = fields
1337 .iter()
1338 .map(|f| {
1339 f.alias.clone().unwrap_or_else(|| match &f.expr {
1340 Expr::Field(name) => name.clone(),
1341 Expr::QualifiedField { qualifier, field } => {
1345 format!("{qualifier}.{field}")
1346 }
1347 _ => "?".into(),
1348 })
1349 })
1350 .collect();
1351 let mut cancel = CancelCheck::new();
1352 let mut proj_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1353 for row in &rows {
1354 cancel.tick()?;
1355 proj_rows.push(
1356 fields
1357 .iter()
1358 .map(|f| eval_expr(&f.expr, row, &columns))
1359 .collect(),
1360 );
1361 }
1362 Ok(QueryResult::Rows {
1363 columns: proj_columns,
1364 rows: proj_rows,
1365 })
1366 }
1367 _ => Err("project requires row input".into()),
1368 }
1369 }
1370
1371 PlanNode::Sort { input, keys } => {
1372 let result = self.execute_plan(input)?;
1373 match result {
1374 QueryResult::Rows { columns, mut rows } => {
1375 if rows.len() > MAX_SORT_ROWS {
1379 return Err(QueryError::SortLimitExceeded);
1380 }
1381 self.charge_rows(&rows)?;
1382 let key_specs: Vec<(Option<usize>, &Expr, bool)> = keys
1383 .iter()
1384 .map(|k| {
1385 let stored_name = match &k.expr {
1386 Expr::Field(name) => Some(name.clone()),
1387 Expr::QualifiedField { qualifier, field } => {
1388 Some(format!("{qualifier}.{field}"))
1389 }
1390 _ => None,
1391 };
1392 let index = stored_name
1393 .as_ref()
1394 .and_then(|name| columns.iter().position(|c| c == name));
1395 if let Some(name) = stored_name {
1396 if index.is_none() {
1397 return Err(QueryError::ColumnNotFound {
1398 table: String::new(),
1399 column: name,
1400 });
1401 }
1402 }
1403 Ok((index, &k.expr, k.descending))
1404 })
1405 .collect::<Result<_, QueryError>>()?;
1406 cooperative_stable_sort_by(&mut rows, self.query_memory_limit, |a, b| {
1407 for &(col_idx, expr, descending) in &key_specs {
1408 let (left_value, right_value) = match col_idx {
1409 Some(index) => (&a[index], &b[index]),
1410 None => {
1411 let left = eval_expr(expr, a, &columns);
1412 let right = eval_expr(expr, b, &columns);
1413 let cmp = compare_order_values(&left, &right, descending);
1414 if cmp != std::cmp::Ordering::Equal {
1415 return cmp;
1416 }
1417 continue;
1418 }
1419 };
1420 let cmp = compare_order_values(left_value, right_value, descending);
1421 if cmp != std::cmp::Ordering::Equal {
1422 return cmp;
1423 }
1424 }
1425 std::cmp::Ordering::Equal
1426 })?;
1427 Ok(QueryResult::Rows { columns, rows })
1428 }
1429 _ => Err("sort requires row input".into()),
1430 }
1431 }
1432
1433 PlanNode::Limit { input, count } => {
1434 let result = self.execute_plan(input)?;
1435 let n = match count {
1436 Expr::Literal(Literal::Int(v)) => *v as usize,
1437 _ => return Err("limit must be integer literal".into()),
1438 };
1439 match result {
1440 QueryResult::Rows { columns, rows } => {
1441 let mut cancel = CancelCheck::new();
1442 let mut limited = Vec::with_capacity(n.min(rows.len()));
1443 for row in rows.into_iter().take(n) {
1444 cancel.tick()?;
1445 limited.push(row);
1446 }
1447 Ok(QueryResult::Rows {
1448 columns,
1449 rows: limited,
1450 })
1451 }
1452 _ => Err("limit requires row input".into()),
1453 }
1454 }
1455
1456 PlanNode::Offset { input, count } => {
1457 let result = self.execute_plan(input)?;
1458 let n = match count {
1459 Expr::Literal(Literal::Int(v)) => *v as usize,
1460 _ => return Err("offset must be integer literal".into()),
1461 };
1462 match result {
1463 QueryResult::Rows { columns, rows } => {
1464 let mut cancel = CancelCheck::new();
1465 let mut offset = Vec::with_capacity(rows.len().saturating_sub(n));
1466 for (index, row) in rows.into_iter().enumerate() {
1467 cancel.tick()?;
1468 if index >= n {
1469 offset.push(row);
1470 }
1471 }
1472 Ok(QueryResult::Rows {
1473 columns,
1474 rows: offset,
1475 })
1476 }
1477 _ => Err("offset requires row input".into()),
1478 }
1479 }
1480
1481 PlanNode::Aggregate {
1482 input,
1483 function,
1484 argument,
1485 mode: _,
1486 provenance_alias,
1487 } => {
1488 if let Some(provenance_alias) = provenance_alias {
1489 let input = self.materialize_rows_with_provenance(input)?;
1490 self.charge_rows(&input.rows)?;
1491 return aggregate_rows_with_provenance(
1492 *function,
1493 argument.as_ref(),
1494 &input,
1495 provenance_alias,
1496 self.query_memory_limit(),
1497 );
1498 }
1499 if *function == AggFunc::Count {
1501 if let PlanNode::SeqScan { table } = input.as_ref() {
1505 if !self.catalog.table_has_overflow(table) {
1506 if self.view_registry.is_dirty(table) {
1510 self.refresh_view(table)?;
1511 }
1512 let mut count: i64 = 0;
1513 for_each_row_raw_cancellable(&self.catalog, table, |_rid, _data| {
1514 count += 1;
1515 })?;
1516 return Ok(QueryResult::Scalar(Value::Int(count)));
1517 }
1518 }
1519 if let PlanNode::Filter {
1527 input: inner,
1528 predicate,
1529 } = input.as_ref()
1530 {
1531 if let PlanNode::SeqScan { table } = inner.as_ref() {
1532 if self.view_registry.is_dirty(table) {
1533 self.refresh_view(table)?;
1534 }
1535 }
1536 if let (PlanNode::SeqScan { table }, false) =
1537 (inner.as_ref(), contains_subquery(predicate))
1538 {
1539 if !self.catalog.table_has_overflow(table) {
1540 let schema = self
1541 .catalog
1542 .schema(table)
1543 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
1544 .clone();
1545 let columns: Vec<String> =
1546 schema.columns.iter().map(|c| c.name.clone()).collect();
1547 let fast = FastLayout::new(&schema);
1548 let row_layout = RowLayout::new(&schema);
1549
1550 if let Some(compiled) =
1553 compile_predicate(predicate, &columns, &fast, &schema)
1554 {
1555 let mut count: i64 = 0;
1556 for_each_row_raw_cancellable(
1557 &self.catalog,
1558 table,
1559 |_rid, data| {
1560 if compiled(data) {
1561 count += 1;
1562 }
1563 },
1564 )?;
1565 return Ok(QueryResult::Scalar(Value::Int(count)));
1566 }
1567
1568 let pred_cols = predicate_column_indices_json(predicate, &columns);
1570 let mut count: i64 = 0;
1571 for_each_row_raw_cancellable(
1572 &self.catalog,
1573 table,
1574 |_rid, data| {
1575 let pred_row = decode_selective(
1576 &schema,
1577 &row_layout,
1578 data,
1579 &pred_cols,
1580 );
1581 if eval_predicate(predicate, &pred_row, &columns) {
1582 count += 1;
1583 }
1584 },
1585 )?;
1586
1587 return Ok(QueryResult::Scalar(Value::Int(count)));
1588 }
1589 }
1590 }
1591 }
1592
1593 if matches!(
1597 function,
1598 AggFunc::Sum
1599 | AggFunc::Avg
1600 | AggFunc::Min
1601 | AggFunc::Max
1602 | AggFunc::CountDistinct
1603 ) {
1604 if let Some(Expr::Field(col)) = argument.as_ref() {
1605 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
1607 match input.as_ref() {
1608 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
1609 PlanNode::Filter {
1610 input: inner,
1611 predicate,
1612 } => {
1613 if let PlanNode::SeqScan { table } = inner.as_ref() {
1614 (Some(table.as_str()), Some(predicate))
1615 } else {
1616 (None, None)
1617 }
1618 }
1619 _ => (None, None),
1620 };
1621 if let Some(table) = table_opt {
1622 if let Some(result) =
1623 self.agg_single_col_fast(table, col, *function, pred_opt)?
1624 {
1625 return Ok(result);
1626 }
1627 }
1628 }
1629 }
1630
1631 let result = self.execute_plan(input)?;
1636 match result {
1637 QueryResult::Rows { columns, rows } => {
1638 aggregate_rows(*function, argument.as_ref(), &columns, &rows)
1639 }
1640 _ => Err("aggregate requires row input".into()),
1641 }
1642 }
1643
1644 PlanNode::Insert {
1645 table,
1646 rows,
1647 returning,
1648 } => {
1649 let mut returning_columns: Vec<String> = Vec::new();
1654 let all_values: Vec<Vec<Value>> = {
1655 let schema = self
1656 .catalog
1657 .schema(table)
1658 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1659 if *returning {
1660 returning_columns = schema.columns.iter().map(|c| c.name.clone()).collect();
1661 }
1662 let defaults = self.catalog.column_defaults(table).unwrap_or(&[]);
1663 let auto = self.catalog.auto_columns(table).unwrap_or(&[]);
1664 let mut all = Vec::with_capacity(rows.len());
1665 for assignments in rows {
1666 let mut values = vec![Value::Empty; schema.columns.len()];
1667 for a in assignments {
1668 let idx = schema.column_index(&a.field).ok_or_else(|| {
1669 QueryError::ColumnNotFound {
1670 table: String::new(),
1671 column: a.field.clone(),
1672 }
1673 })?;
1674 let raw = literal_to_value(&a.value)?;
1675 values[idx] = coerce_value(raw, &schema.columns[idx])?;
1676 }
1677 for (i, slot) in values.iter_mut().enumerate() {
1681 if slot.is_empty() {
1682 if let Some(Some(d)) = defaults.get(i) {
1683 *slot = d.clone();
1684 }
1685 }
1686 }
1687 for col in &schema.columns {
1688 let pos = col.position as usize;
1689 let is_auto = auto.get(pos).copied().unwrap_or(false);
1692 if col.required && !is_auto && matches!(values[pos], Value::Empty) {
1693 return Err(QueryError::Execution(format!(
1694 "column '{}' is required but no value was provided",
1695 col.name
1696 )));
1697 }
1698 }
1699 all.push(values);
1700 }
1701 all
1702 };
1703 let mut all_values = all_values;
1708 for values in all_values.iter_mut() {
1709 self.catalog.assign_auto_columns(table, values);
1710 }
1711 self.charge_rows(&all_values)?;
1717 let n = all_values.len() as u64;
1718 for values in &all_values {
1719 self.catalog
1720 .insert(table, values)
1721 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1722 }
1723 self.view_registry.mark_dependents_dirty(table);
1724 if *returning {
1725 Ok(QueryResult::Rows {
1726 columns: returning_columns,
1727 rows: all_values,
1728 })
1729 } else {
1730 Ok(QueryResult::Modified(n))
1731 }
1732 }
1733
1734 PlanNode::Upsert {
1735 table,
1736 key_column,
1737 assignments,
1738 on_conflict,
1739 } => {
1740 let (values, key_idx) = {
1741 let schema = self
1742 .catalog
1743 .schema(table)
1744 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1745 let mut values = vec![Value::Empty; schema.columns.len()];
1746 for a in assignments {
1747 let idx = schema.column_index(&a.field).ok_or_else(|| {
1748 QueryError::ColumnNotFound {
1749 table: String::new(),
1750 column: a.field.clone(),
1751 }
1752 })?;
1753 let raw = literal_to_value(&a.value)?;
1754 values[idx] = coerce_value(raw, &schema.columns[idx])?;
1755 }
1756 let defaults = self.catalog.column_defaults(table).unwrap_or(&[]);
1759 for (i, slot) in values.iter_mut().enumerate() {
1760 if slot.is_empty() {
1761 if let Some(Some(d)) = defaults.get(i) {
1762 *slot = d.clone();
1763 }
1764 }
1765 }
1766 for col in &schema.columns {
1767 if col.required && matches!(values[col.position as usize], Value::Empty) {
1768 return Err(QueryError::Execution(format!(
1769 "column '{}' is required but no value was provided",
1770 col.name
1771 )));
1772 }
1773 }
1774 let key_idx = schema
1775 .column_index(key_column)
1776 .ok_or_else(|| format!("key column '{key_column}' not found"))?;
1777 (values, key_idx)
1778 };
1779
1780 if self.catalog.is_index_unique(table, key_column) != Some(true) {
1784 return Err(QueryError::Execution(format!(
1785 "upsert on .{key_column} requires a unique column (declare it with \
1786 `unique {key_column}: <type>` or `alter {table} add unique .{key_column}`)"
1787 )));
1788 }
1789
1790 let key_value = values[key_idx].clone();
1791
1792 let existing = {
1794 let tbl = self
1795 .catalog
1796 .get_table(table)
1797 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1798 let rids = tbl.index_lookup_all(key_column, &key_value);
1801 rids.into_iter()
1804 .next()
1805 .and_then(|rid| tbl.get(rid).map(|row| (rid, row)))
1806 };
1807
1808 if let Some((rid, mut existing_row)) = existing {
1809 let update_assignments = if on_conflict.is_empty() {
1811 assignments
1812 } else {
1813 on_conflict
1814 };
1815 let changed_cols: Vec<usize> = {
1816 let schema = self
1817 .catalog
1818 .schema(table)
1819 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1820 let mut indices = Vec::new();
1821 for a in update_assignments {
1822 let idx = schema.column_index(&a.field).ok_or_else(|| {
1823 QueryError::ColumnNotFound {
1824 table: String::new(),
1825 column: a.field.clone(),
1826 }
1827 })?;
1828 if idx != key_idx {
1829 existing_row[idx] =
1834 coerce_value(literal_to_value(&a.value)?, &schema.columns[idx])
1835 .map_err(QueryError::TypeError)?;
1836 indices.push(idx);
1837 }
1838 }
1839 indices
1840 };
1841 self.catalog
1842 .update_hinted(table, rid, &existing_row, Some(&changed_cols))
1843 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1844 self.view_registry.mark_dependents_dirty(table);
1845 Ok(QueryResult::Modified(1))
1846 } else {
1847 self.catalog
1849 .insert(table, &values)
1850 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1851 self.view_registry.mark_dependents_dirty(table);
1852 Ok(QueryResult::Modified(1))
1853 }
1854 }
1855
1856 PlanNode::Update {
1857 input,
1858 table,
1859 assignments,
1860 returning,
1861 } => {
1862 let (col_indices, literal_vals, target_cols): (
1868 Vec<usize>,
1869 Option<Vec<Value>>,
1870 Vec<ColumnDef>,
1871 ) = {
1872 let schema_ref = self
1873 .catalog
1874 .schema(table)
1875 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1876 let indices: Vec<usize> = assignments
1877 .iter()
1878 .map(|a| {
1879 schema_ref.column_index(&a.field).ok_or_else(|| {
1880 QueryError::ColumnNotFound {
1881 table: String::new(),
1882 column: a.field.clone(),
1883 }
1884 })
1885 })
1886 .collect::<Result<_, _>>()?;
1887 let target_cols: Vec<ColumnDef> = indices
1891 .iter()
1892 .map(|&idx| schema_ref.columns[idx].clone())
1893 .collect();
1894 let raw_vals: Result<Vec<Value>, _> = assignments
1898 .iter()
1899 .map(|a| literal_to_value(&a.value))
1900 .collect();
1901 let coerced = match raw_vals {
1910 Ok(raws) => {
1911 let mut out = Vec::with_capacity(raws.len());
1912 for (raw, &idx) in raws.into_iter().zip(indices.iter()) {
1913 out.push(
1914 coerce_value(raw, &schema_ref.columns[idx])
1915 .map_err(QueryError::TypeError)?,
1916 );
1917 }
1918 Some(out)
1919 }
1920 Err(_) => None,
1921 };
1922 (indices, coerced, target_cols)
1923 };
1924 let resolved_assignments: Option<Vec<(usize, Value)>> =
1925 literal_vals.map(|vals| col_indices.iter().copied().zip(vals).collect());
1926
1927 let changed_cols: Vec<usize> = col_indices.clone();
1930
1931 if *returning {
1938 let columns: Vec<String> = {
1939 let schema_ref = self
1940 .catalog
1941 .schema(table)
1942 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1943 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
1944 };
1945 let matching_rids = self.collect_rids_for_mutation(input, table)?;
1946 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(matching_rids.len());
1947 crate::cancel::check()?;
1954 for rid in matching_rids {
1955 let mut row = match self.catalog.get(table, rid) {
1956 Some(r) => r,
1957 None => continue,
1958 };
1959 match &resolved_assignments {
1960 Some(resolved) => {
1962 for (idx, val) in resolved.iter() {
1963 row[*idx] = val.clone();
1964 }
1965 }
1966 None => {
1972 for (i, asgn) in assignments.iter().enumerate() {
1973 let val = eval_expr(&asgn.value, &row, &columns);
1974 row[col_indices[i]] = coerce_value(val, &target_cols[i])
1975 .map_err(QueryError::TypeError)?;
1976 }
1977 }
1978 }
1979 self.catalog
1980 .update_hinted(table, rid, &row, Some(&changed_cols))
1981 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1982 out_rows.push(row);
1983 }
1984 self.view_registry.mark_dependents_dirty(table);
1985 return Ok(QueryResult::Rows {
1986 columns,
1987 rows: out_rows,
1988 });
1989 }
1990
1991 if let Some(ref resolved_assignments) = resolved_assignments {
1998 if let PlanNode::Filter {
1999 input: inner,
2000 predicate,
2001 } = input.as_ref()
2002 {
2003 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
2004 if t == table {
2005 crate::cancel::check()?;
2011 let fused_result = self.try_fused_scan_update(
2012 table,
2013 predicate,
2014 resolved_assignments,
2015 &changed_cols,
2016 );
2017 if let Some(result) = fused_result {
2018 return result;
2019 }
2020 }
2021 }
2022 }
2023 }
2024
2025 let matching_rids = self.collect_rids_for_mutation(input, table)?;
2027 crate::cancel::check()?;
2030
2031 if let Some(ref resolved_assignments) = resolved_assignments {
2033 let fast_patch: Option<Vec<FastPatch>> = {
2039 let tbl = self
2040 .catalog
2041 .get_table(table)
2042 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2043 let schema = tbl.schema();
2044 let all_fixed_nonnull = !tbl.has_overflow_rows()
2048 && resolved_assignments.iter().all(|(idx, val)| {
2049 is_fixed_size(schema.columns[*idx].type_id) && !val.is_empty()
2050 });
2051 let no_indexed = !resolved_assignments
2052 .iter()
2053 .any(|(idx, _)| tbl.has_indexed_col(*idx));
2054
2055 if all_fixed_nonnull && no_indexed {
2056 let layout = RowLayout::new(schema);
2057 let bitmap_size = layout.bitmap_size();
2058 let patches: Vec<FastPatch> = resolved_assignments
2059 .iter()
2060 .map(|(idx, val)| {
2061 let fixed_off = layout
2062 .fixed_offset(*idx)
2063 .expect("is_fixed_size already checked");
2064 let field_off = 2 + bitmap_size + fixed_off;
2065 let bytes: FixedBytes = match val {
2066 Value::Int(v) => FixedBytes::I64(v.to_le_bytes()),
2067 Value::Float(v) => FixedBytes::F64(v.to_le_bytes()),
2068 Value::Bool(v) => FixedBytes::Bool(if *v { 1 } else { 0 }),
2069 Value::DateTime(v) => FixedBytes::I64(v.to_le_bytes()),
2070 Value::Uuid(v) => FixedBytes::Uuid(*v),
2071 _ => unreachable!("all_fixed_nonnull guard lied"),
2072 };
2073 FastPatch {
2074 field_off,
2075 bitmap_byte_off: 2 + idx / 8,
2076 bit_mask: 1u8 << (idx % 8),
2077 bytes,
2078 }
2079 })
2080 .collect();
2081 Some(patches)
2082 } else {
2083 None
2084 }
2085 };
2086
2087 if let Some(patches) = fast_patch {
2088 let mut count = 0u64;
2089 let mut fallback_rids: Vec<RowId> = Vec::new();
2090 for rid in &matching_rids {
2091 let ok = self
2106 .catalog
2107 .update_row_bytes_logged(table, *rid, |row| {
2108 let base = row_body_base(row);
2109 for p in &patches {
2110 row[base + p.bitmap_byte_off] &= !p.bit_mask;
2111 let field_bytes = p.bytes.as_slice();
2112 row[base + p.field_off
2113 ..base + p.field_off + field_bytes.len()]
2114 .copy_from_slice(field_bytes);
2115 }
2116 })
2117 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2118 if ok {
2119 count += 1;
2120 } else {
2121 fallback_rids.push(*rid);
2122 }
2123 }
2124 for rid in fallback_rids {
2125 let mut row = match self.catalog.get(table, rid) {
2126 Some(r) => r,
2127 None => continue,
2128 };
2129 for (idx, val) in resolved_assignments.iter() {
2130 row[*idx] = val.clone();
2131 }
2132 self.catalog
2133 .update_hinted(table, rid, &row, Some(&changed_cols))
2134 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2135 count += 1;
2136 }
2137 self.view_registry.mark_dependents_dirty(table);
2138 return Ok(QueryResult::Modified(count));
2139 }
2140
2141 let var_fast: Option<(usize, Option<Vec<u8>>)> = {
2143 let tbl = self
2144 .catalog
2145 .get_table(table)
2146 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2147 let schema = tbl.schema();
2148 let is_single = resolved_assignments.len() == 1 && !tbl.has_overflow_rows();
2152 let is_var_col = is_single
2153 && !is_fixed_size(schema.columns[resolved_assignments[0].0].type_id);
2154 let no_indexed = !resolved_assignments
2155 .iter()
2156 .any(|(idx, _)| tbl.has_indexed_col(*idx));
2157
2158 if is_single && is_var_col && no_indexed {
2159 let (idx, val) = &resolved_assignments[0];
2160 let bytes_opt: Option<Vec<u8>> = match val {
2161 Value::Str(s) => Some(s.as_bytes().to_vec()),
2162 Value::Bytes(b) => Some(b.clone()),
2163 Value::Json(b) => Some(b.to_vec()),
2167 Value::Empty => None,
2168 _ => {
2169 return Err(QueryError::TypeError(format!(
2170 "cannot assign non-var value to var column '{}'",
2171 schema.columns[*idx].name
2172 )))
2173 }
2174 };
2175 Some((*idx, bytes_opt))
2176 } else {
2177 None
2178 }
2179 };
2180
2181 if let Some((col_idx, new_bytes_opt)) = var_fast {
2182 let new_bytes_ref: Option<&[u8]> = new_bytes_opt.as_deref();
2183 let mut count = 0u64;
2184 let mut fallback_rids: Vec<RowId> = Vec::new();
2185 for rid in &matching_rids {
2186 let ok = self
2192 .catalog
2193 .patch_var_col_logged(table, *rid, col_idx, new_bytes_ref)
2194 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2195 if ok {
2196 count += 1;
2197 } else {
2198 fallback_rids.push(*rid);
2199 }
2200 }
2201 for rid in fallback_rids {
2202 let mut row = match self.catalog.get(table, rid) {
2203 Some(r) => r,
2204 None => continue,
2205 };
2206 for (idx, val) in resolved_assignments.iter() {
2207 row[*idx] = val.clone();
2208 }
2209 self.catalog
2210 .update_hinted(table, rid, &row, Some(&changed_cols))
2211 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2212 count += 1;
2213 }
2214 self.view_registry.mark_dependents_dirty(table);
2215 return Ok(QueryResult::Modified(count));
2216 }
2217
2218 let mut count = 0u64;
2220 for rid in matching_rids {
2221 let mut row = match self.catalog.get(table, rid) {
2222 Some(r) => r,
2223 None => continue,
2224 };
2225 for (idx, val) in resolved_assignments.iter() {
2226 row[*idx] = val.clone();
2227 }
2228 self.catalog
2229 .update_hinted(table, rid, &row, Some(&changed_cols))
2230 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2231 count += 1;
2232 }
2233 self.view_registry.mark_dependents_dirty(table);
2234 return Ok(QueryResult::Modified(count));
2235 } let col_names: Vec<String> = {
2241 let schema_ref = self
2242 .catalog
2243 .schema(table)
2244 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2245 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
2246 };
2247 let mut count = 0u64;
2248 for rid in matching_rids {
2249 let mut row = match self.catalog.get(table, rid) {
2250 Some(r) => r,
2251 None => continue,
2252 };
2253 for (i, asgn) in assignments.iter().enumerate() {
2254 let val = eval_expr(&asgn.value, &row, &col_names);
2255 row[col_indices[i]] =
2261 coerce_value(val, &target_cols[i]).map_err(QueryError::TypeError)?;
2262 }
2263 self.catalog
2264 .update_hinted(table, rid, &row, Some(&changed_cols))
2265 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2266 count += 1;
2267 }
2268 self.view_registry.mark_dependents_dirty(table);
2269 Ok(QueryResult::Modified(count))
2270 }
2271
2272 PlanNode::Delete {
2273 input,
2274 table,
2275 returning,
2276 } => {
2277 if *returning {
2284 let columns: Vec<String> = {
2285 let schema_ref = self
2286 .catalog
2287 .schema(table)
2288 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2289 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
2290 };
2291 let matching_rids = self.collect_rids_for_mutation(input, table)?;
2292 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(matching_rids.len());
2293 let mut cancel = CancelCheck::new();
2297 for rid in &matching_rids {
2298 cancel.tick()?;
2299 if let Some(row) = self.catalog.get(table, *rid) {
2300 out_rows.push(row);
2301 }
2302 }
2303 crate::cancel::check()?;
2304 self.catalog
2305 .delete_many(table, &matching_rids)
2306 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2307 self.view_registry.mark_dependents_dirty(table);
2308 return Ok(QueryResult::Rows {
2309 columns,
2310 rows: out_rows,
2311 });
2312 }
2313
2314 let delete_overflow = self.catalog.table_has_overflow(table);
2338 if let PlanNode::Filter {
2339 input: inner,
2340 predicate,
2341 } = input.as_ref()
2342 {
2343 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
2344 if t == table && !delete_overflow {
2345 let schema = self
2346 .catalog
2347 .schema(table)
2348 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2349 let columns: Vec<String> =
2350 schema.columns.iter().map(|c| c.name.clone()).collect();
2351 let fast = FastLayout::new(schema);
2352 if let Some(compiled) =
2353 compile_predicate(predicate, &columns, &fast, schema)
2354 {
2355 crate::cancel::check()?;
2361 let count = self
2362 .catalog
2363 .scan_delete_matching_logged(table, |data| compiled(data))
2364 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2365 self.view_registry.mark_dependents_dirty(table);
2366 return Ok(QueryResult::Modified(count));
2367 }
2368 }
2369 }
2370 } else if let PlanNode::SeqScan { table: t } = input.as_ref() {
2371 if t == table && !delete_overflow {
2372 crate::cancel::check()?;
2376 let count = self
2377 .catalog
2378 .scan_delete_matching_logged(table, |_| true)
2379 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2380 self.view_registry.mark_dependents_dirty(table);
2381 return Ok(QueryResult::Modified(count));
2382 }
2383 }
2384
2385 let matching_rids = self.collect_rids_for_mutation(input, table)?;
2386 crate::cancel::check()?;
2387 let count = self
2388 .catalog
2389 .delete_many(table, &matching_rids)
2390 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2391 self.view_registry.mark_dependents_dirty(table);
2392 Ok(QueryResult::Modified(count))
2393 }
2394
2395 PlanNode::AliasScan { table, alias } => {
2396 let schema = self
2406 .catalog
2407 .schema(table)
2408 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
2409 .clone();
2410 let columns: Vec<String> = schema
2411 .columns
2412 .iter()
2413 .map(|c| format!("{alias}.{}", c.name))
2414 .collect();
2415 let mut cancel = CancelCheck::new();
2416 let mut rows: Vec<Vec<Value>> = Vec::new();
2417 for (_, row) in self
2418 .catalog
2419 .scan(table)
2420 .map_err(|e| QueryError::StorageError(e.to_string()))?
2421 {
2422 cancel.tick()?;
2423 rows.push(row);
2424 }
2425 Ok(QueryResult::Rows { columns, rows })
2426 }
2427
2428 PlanNode::NestedLoopJoin {
2429 left,
2430 right,
2431 on,
2432 kind,
2433 } => {
2434 let left_result = self.execute_plan(left)?;
2445 let right_result = self.execute_plan(right)?;
2446 let (left_columns, left_rows) = match left_result {
2447 QueryResult::Rows { columns, rows } => (columns, rows),
2448 _ => return Err("join left side must produce rows".into()),
2449 };
2450 let (right_columns, right_rows) = match right_result {
2451 QueryResult::Rows { columns, rows } => (columns, rows),
2452 _ => return Err("join right side must produce rows".into()),
2453 };
2454
2455 self.charge_rows(&left_rows)?;
2459 self.charge_rows(&right_rows)?;
2460
2461 execute_materialized_join(
2462 left_columns,
2463 left_rows,
2464 right_columns,
2465 right_rows,
2466 on.as_ref(),
2467 *kind,
2468 self.nested_loop_pair_limit,
2469 )
2470 }
2471
2472 PlanNode::Distinct { input } => {
2473 let result = self.execute_plan(input)?;
2474 match result {
2475 QueryResult::Rows { columns, rows } => {
2476 let mut seen = std::collections::HashSet::new();
2477 let mut unique_rows = Vec::new();
2478 let mut cancel = CancelCheck::new();
2479 for row in rows {
2480 cancel.tick()?;
2481 if seen.insert(row.clone()) {
2482 unique_rows.push(row);
2483 }
2484 }
2485 Ok(QueryResult::Rows {
2486 columns,
2487 rows: unique_rows,
2488 })
2489 }
2490 other => Ok(other),
2491 }
2492 }
2493
2494 PlanNode::GroupBy {
2495 input,
2496 keys,
2497 aggregates,
2498 having,
2499 } => {
2500 if aggregates
2501 .iter()
2502 .any(|aggregate| aggregate.provenance_alias.is_some())
2503 {
2504 let input = self.materialize_rows_with_provenance(input)?;
2505 self.charge_rows(&input.rows)?;
2506 return exec_group_by_with_provenance(
2507 input,
2508 keys,
2509 aggregates,
2510 having,
2511 self.query_memory_limit(),
2512 );
2513 }
2514 let result = self.execute_plan(input)?;
2515 match result {
2516 QueryResult::Rows { columns, rows } => {
2517 self.charge_rows(&rows)?;
2520 exec_group_by(columns, rows, keys, aggregates, having)
2521 }
2522 _ => Err("group by requires row input".into()),
2523 }
2524 }
2525
2526 PlanNode::CreateTable {
2527 name,
2528 fields,
2529 if_not_exists,
2530 } => {
2531 if self.catalog.schema(name).is_some() {
2535 if *if_not_exists {
2536 return Ok(QueryResult::Executed {
2537 message: format!("type '{name}' already exists (skipped)"),
2538 });
2539 }
2540 return Err(QueryError::Execution(format!(
2543 "cannot create type '{name}': it already exists"
2544 )));
2545 }
2546 let columns: Vec<ColumnDef> = fields
2547 .iter()
2548 .enumerate()
2549 .map(|(i, f)| -> Result<ColumnDef, QueryError> {
2550 Ok(ColumnDef {
2551 name: f.name.clone(),
2552 type_id: type_name_to_id(&f.type_name)
2553 .map_err(QueryError::TypeError)?,
2554 required: f.required,
2555 position: i as u16,
2556 })
2557 })
2558 .collect::<Result<Vec<_>, _>>()?;
2559 let mut defaults: Vec<Option<Value>> = vec![None; columns.len()];
2563 let mut auto_cols: Vec<bool> = vec![false; columns.len()];
2564 for (i, f) in fields.iter().enumerate() {
2565 if let Some(lit) = &f.default {
2566 let raw = literal_value_from(lit);
2567 defaults[i] = Some(coerce_value(raw, &columns[i])?);
2568 }
2569 if f.auto {
2570 if columns[i].type_id != TypeId::Int {
2574 return Err(QueryError::TypeError(format!(
2575 "auto column '{}' must be of type int",
2576 f.name
2577 )));
2578 }
2579 if f.default.is_some() {
2580 return Err(QueryError::TypeError(format!(
2581 "auto column '{}' cannot also declare a default",
2582 f.name
2583 )));
2584 }
2585 auto_cols[i] = true;
2586 }
2587 }
2588 let schema = Schema {
2589 table_name: name.clone(),
2590 columns,
2591 };
2592 self.catalog
2593 .create_table_full(schema, defaults, auto_cols)
2594 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2595 for f in fields.iter().filter(|f| f.unique) {
2598 self.catalog
2599 .create_index_unique(name, &f.name, true)
2600 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2601 }
2602 Ok(QueryResult::Created(name.clone()))
2603 }
2604
2605 PlanNode::AlterTable { table, action } => match action {
2606 AlterAction::AddColumn {
2607 name,
2608 type_name,
2609 required,
2610 } => {
2611 let position = self
2612 .catalog
2613 .schema(table)
2614 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
2615 .columns
2616 .len() as u16;
2617 let col = ColumnDef {
2618 name: name.clone(),
2619 type_id: type_name_to_id(type_name).map_err(QueryError::TypeError)?,
2620 required: *required,
2621 position,
2622 };
2623 self.catalog
2624 .alter_table_add_column(table, col)
2625 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2626 Ok(QueryResult::Executed {
2627 message: format!("column '{name}' added to '{table}'"),
2628 })
2629 }
2630 AlterAction::DropColumn { name, if_exists } => {
2631 if *if_exists {
2634 let present = self
2635 .catalog
2636 .schema(table)
2637 .map(|s| s.column_index(name).is_some())
2638 .unwrap_or(false);
2639 if !present {
2640 return Ok(QueryResult::Executed {
2641 message: format!(
2642 "column '{name}' does not exist on '{table}' (skipped)"
2643 ),
2644 });
2645 }
2646 }
2647 self.catalog
2648 .alter_table_drop_column(table, name)
2649 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2650 Ok(QueryResult::Executed {
2651 message: format!("column '{name}' dropped from '{table}'"),
2652 })
2653 }
2654 AlterAction::AddIndex {
2655 target,
2656 if_not_exists: _,
2657 } => {
2658 let IndexTarget::Column(column) = target else {
2659 let IndexTarget::JsonPath(path) = target else {
2660 unreachable!("index target variants are exhaustive")
2661 };
2662 if let Some(existing) = resolve_expression_index(&self.catalog, table, path)
2663 {
2664 return Ok(QueryResult::Executed {
2665 message: format!(
2666 "expression index {} on '{}' already exists (skipped)",
2667 existing.index_id, table
2668 ),
2669 });
2670 }
2671 crate::cancel::check()?;
2672 let index_id = self
2673 .catalog
2674 .create_expression_index_metadata(
2675 table,
2676 1,
2677 path.canonical_text(),
2678 path.clone(),
2679 false,
2680 )
2681 .map_err(|error| QueryError::StorageError(error.to_string()))?;
2682 return Ok(QueryResult::Executed {
2683 message: format!("expression index {index_id} on '{}' created", table),
2684 });
2685 };
2686 crate::cancel::check()?;
2690 self.catalog
2691 .create_index(table, column)
2692 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2693 Ok(QueryResult::Executed {
2694 message: format!("index on '{table}.{column}' created"),
2695 })
2696 }
2697 AlterAction::AddUnique {
2698 target,
2699 if_not_exists,
2700 } => {
2701 let IndexTarget::Column(column) = target else {
2702 let IndexTarget::JsonPath(path) = target else {
2703 unreachable!("index target variants are exhaustive")
2704 };
2705 if let Some(existing) = resolve_expression_index(&self.catalog, table, path)
2706 {
2707 if *if_not_exists {
2708 return Ok(QueryResult::Executed {
2709 message: format!(
2710 "expression index {} on '{}' already exists (skipped)",
2711 existing.index_id, table
2712 ),
2713 });
2714 }
2715 return Err(QueryError::Execution(format!(
2716 "cannot add unique expression index on {}: path already indexed",
2717 table
2718 )));
2719 }
2720 crate::cancel::check()?;
2721 let index_id = self
2722 .catalog
2723 .create_expression_index_metadata(
2724 table,
2725 1,
2726 path.canonical_text(),
2727 path.clone(),
2728 true,
2729 )
2730 .map_err(|error| QueryError::StorageError(error.to_string()))?;
2731 return Ok(QueryResult::Executed {
2732 message: format!(
2733 "unique expression index {index_id} on '{}' created",
2734 table
2735 ),
2736 });
2737 };
2738 if self.catalog.has_index(table, column) {
2741 if *if_not_exists {
2742 return Ok(QueryResult::Executed {
2743 message: format!(
2744 "index on '{table}.{column}' already exists (skipped)"
2745 ),
2746 });
2747 }
2748 return Err(QueryError::Execution(format!(
2751 "cannot add unique on {table}.{column}: column already indexed"
2752 )));
2753 }
2754 {
2757 let tbl = self
2758 .catalog
2759 .get_table(table)
2760 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2761 let col_idx = tbl.schema().column_index(column).ok_or_else(|| {
2762 QueryError::ColumnNotFound {
2763 table: table.to_string(),
2764 column: column.clone(),
2765 }
2766 })?;
2767 let mut seen = std::collections::HashSet::new();
2768 let mut cancel = CancelCheck::new();
2769 for (_, row) in tbl.scan() {
2770 cancel.tick()?;
2771 let v = &row[col_idx];
2772 if v.is_empty() {
2773 continue;
2774 }
2775 if !seen.insert(v.clone()) {
2776 return Err(QueryError::Execution(format!(
2777 "cannot add unique on {table}.{column}: \
2778 duplicate value {v:?} exists"
2779 )));
2780 }
2781 }
2782 }
2783 crate::cancel::check()?;
2784 self.catalog
2785 .create_index_unique(table, column, true)
2786 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2787 Ok(QueryResult::Executed {
2788 message: format!("unique index on '{table}.{column}' created"),
2789 })
2790 }
2791 AlterAction::DropIndex { target, if_exists } => {
2792 let IndexTarget::JsonPath(path) = target else {
2793 return Err(QueryError::Execution(
2794 "dropping stored-column indexes is not supported".to_string(),
2795 ));
2796 };
2797 let Some(existing) = resolve_expression_index(&self.catalog, table, path)
2798 else {
2799 if *if_exists {
2800 return Ok(QueryResult::Executed {
2801 message: format!(
2802 "expression index on '{}' does not exist (skipped)",
2803 table
2804 ),
2805 });
2806 }
2807 return Err(QueryError::Execution(format!(
2808 "expression index on '{}' does not exist",
2809 table
2810 )));
2811 };
2812 crate::cancel::check()?;
2813 self.catalog
2814 .drop_expression_index(table, existing.index_id)
2815 .map_err(|error| QueryError::StorageError(error.to_string()))?;
2816 Ok(QueryResult::Executed {
2817 message: format!(
2818 "expression index {} on '{}' dropped",
2819 existing.index_id, table
2820 ),
2821 })
2822 }
2823 },
2824
2825 PlanNode::DropTable { name, if_exists } => {
2826 if *if_exists && self.catalog.schema(name).is_none() {
2827 return Ok(QueryResult::Executed {
2828 message: format!("type '{name}' does not exist (skipped)"),
2829 });
2830 }
2831 self.catalog
2832 .drop_table(name)
2833 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2834 Ok(QueryResult::Executed {
2835 message: format!("table '{name}' dropped"),
2836 })
2837 }
2838
2839 PlanNode::ListTypes => self.introspect_list_types(),
2840
2841 PlanNode::Describe { table } => self.introspect_describe(table),
2842
2843 PlanNode::CreateView { name, query_text } => {
2844 self.create_view(name, query_text)?;
2845 Ok(QueryResult::Executed {
2846 message: format!("materialized view '{name}' created"),
2847 })
2848 }
2849
2850 PlanNode::RefreshView { name } => {
2851 self.refresh_view(name)?;
2852 Ok(QueryResult::Executed {
2853 message: format!("materialized view '{name}' refreshed"),
2854 })
2855 }
2856
2857 PlanNode::DropView { name, if_exists } => {
2858 if *if_exists && !self.view_registry.is_view(name) {
2859 return Ok(QueryResult::Executed {
2860 message: format!("view '{name}' does not exist (skipped)"),
2861 });
2862 }
2863 self.drop_view(name)?;
2864 Ok(QueryResult::Executed {
2865 message: format!("materialized view '{name}' dropped"),
2866 })
2867 }
2868
2869 PlanNode::Window { input, windows } => {
2870 let result = self.execute_plan(input)?;
2871 execute_window(result, windows, self.query_memory_limit)
2872 }
2873
2874 PlanNode::Union { left, right, all } => {
2875 let left_result = self.execute_plan(left)?;
2876 let right_result = self.execute_plan(right)?;
2877 let (left_cols, left_rows) = match left_result {
2878 QueryResult::Rows { columns, rows } => (columns, rows),
2879 _ => return Err("UNION requires query results on left side".into()),
2880 };
2881 let (_, right_rows) = match right_result {
2882 QueryResult::Rows { columns, rows } => (columns, rows),
2883 _ => return Err("UNION requires query results on right side".into()),
2884 };
2885 let mut combined = left_rows;
2886 let mut cancel = CancelCheck::new();
2887 if *all {
2888 for row in right_rows {
2890 cancel.tick()?;
2891 combined.push(row);
2892 }
2893 } else {
2894 let mut seen = std::collections::HashSet::new();
2897 for row in &combined {
2898 cancel.tick()?;
2899 seen.insert(row.clone());
2900 }
2901 for row in right_rows {
2902 cancel.tick()?;
2903 if seen.insert(row.clone()) {
2904 combined.push(row);
2905 }
2906 }
2907 }
2908 Ok(QueryResult::Rows {
2909 columns: left_cols,
2910 rows: combined,
2911 })
2912 }
2913
2914 PlanNode::Explain { input } => {
2915 let text = format_plan_tree(&self.catalog, input, 0);
2919 Ok(QueryResult::Rows {
2920 columns: vec!["plan".to_string()],
2921 rows: text
2922 .lines()
2923 .map(|line| vec![Value::Str(line.to_string())])
2924 .collect(),
2925 })
2926 }
2927
2928 PlanNode::Begin => {
2929 if self.in_transaction {
2930 return Err(QueryError::Execution(
2931 "already in a transaction (nested transactions not supported)".into(),
2932 ));
2933 }
2934 self.catalog
2935 .begin_transaction()
2936 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2937 self.in_transaction = true;
2938 Ok(QueryResult::Executed {
2939 message: "transaction started".to_string(),
2940 })
2941 }
2942
2943 PlanNode::Commit => {
2944 if !self.in_transaction {
2945 return Err(QueryError::Execution(
2946 "no active transaction to commit".into(),
2947 ));
2948 }
2949 self.catalog
2950 .commit_transaction()
2951 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2952 self.in_transaction = false;
2953 Ok(QueryResult::Executed {
2954 message: "transaction committed".to_string(),
2955 })
2956 }
2957
2958 PlanNode::Rollback => {
2959 if !self.in_transaction {
2960 return Err(QueryError::Execution(
2961 "no active transaction to roll back".into(),
2962 ));
2963 }
2964 self.rollback_transaction_preserving_wal_archive()
2965 }
2966
2967 PlanNode::IndexScan { table, column, key } => {
2968 let key_value = literal_to_value(key)?;
2969 let tbl = self
2970 .catalog
2971 .get_table(table)
2972 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2973 let columns: Vec<String> = tbl
2974 .schema()
2975 .columns
2976 .iter()
2977 .map(|c| c.name.clone())
2978 .collect();
2979
2980 if tbl.has_index(column) {
2984 let rids = tbl.index_lookup_all(column, &key_value);
2985 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(rids.len());
2986 let mut cancel = CancelCheck::new();
2987 for rid in rids {
2988 cancel.tick()?;
2989 if let Some(row) = tbl.get(rid) {
2993 rows.push(row);
2994 }
2995 }
2996 return Ok(QueryResult::Rows { columns, rows });
2997 }
2998
2999 let schema = tbl.schema();
3007 let fast = FastLayout::new(schema);
3008 let synth_pred = Expr::BinaryOp(
3009 Box::new(Expr::Field(column.clone())),
3010 BinOp::Eq,
3011 Box::new(key.clone()),
3012 );
3013 if !tbl.has_overflow_rows() {
3016 if let Some(compiled) = compile_predicate(&synth_pred, &columns, &fast, schema)
3017 {
3018 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
3020 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3021 if compiled(data) {
3022 rows.push(decode_row(schema, data));
3023 }
3024 })?;
3025 return Ok(QueryResult::Rows { columns, rows });
3026 }
3027 }
3028
3029 let col_idx =
3031 schema
3032 .column_index(column)
3033 .ok_or_else(|| QueryError::ColumnNotFound {
3034 table: String::new(),
3035 column: column.clone(),
3036 })?;
3037 let mut cancel = CancelCheck::new();
3038 let mut rows: Vec<Vec<Value>> = Vec::new();
3039 for (_, row) in tbl.scan() {
3040 cancel.tick()?;
3041 if row[col_idx] == key_value {
3042 rows.push(row);
3043 }
3044 }
3045 Ok(QueryResult::Rows { columns, rows })
3046 }
3047
3048 PlanNode::RangeScan {
3049 table,
3050 column,
3051 start,
3052 end,
3053 } => {
3054 let tbl = self
3055 .catalog
3056 .get_table(table)
3057 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
3058 let columns: Vec<String> = tbl
3059 .schema()
3060 .columns
3061 .iter()
3062 .map(|c| c.name.clone())
3063 .collect();
3064 let schema = tbl.schema();
3065
3066 let start_val = match start {
3067 Some((expr, _)) => Some(literal_to_value(expr)?),
3068 None => None,
3069 };
3070 let end_val = match end {
3071 Some((expr, _)) => Some(literal_to_value(expr)?),
3072 None => None,
3073 };
3074 let start_inclusive = start.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
3075 let end_inclusive = end.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
3076
3077 if tbl.is_index_unique(column) == Some(false) {
3083 if let Some(btree) = tbl.index(column) {
3084 if start_val.is_some() || end_val.is_some() {
3085 let col_idx = schema.column_index(column).ok_or_else(|| {
3086 QueryError::ColumnNotFound {
3087 table: String::new(),
3088 column: column.clone(),
3089 }
3090 })?;
3091 let rids = btree.range_rids(start_val.as_ref(), end_val.as_ref());
3092 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(rids.len());
3093 let mut cancel = CancelCheck::new();
3094 for rid in rids {
3095 cancel.tick()?;
3096 if let Some(row) = tbl.get(rid) {
3098 if !row[col_idx].is_empty()
3099 && range_matches(
3100 &row[col_idx],
3101 &start_val,
3102 start_inclusive,
3103 &end_val,
3104 end_inclusive,
3105 )
3106 {
3107 rows.push(row);
3108 }
3109 }
3110 }
3111 return Ok(QueryResult::Rows { columns, rows });
3112 }
3113 }
3114 }
3115
3116 if tbl.is_index_unique(column) == Some(true) {
3119 if let Some(btree) = tbl.index(column) {
3120 let hits: Vec<(Value, RowId)> = match (&start_val, &end_val) {
3121 (Some(s), Some(e)) => btree.range(s, e).collect(),
3122 (Some(s), None) => btree.range_from(s),
3123 (None, Some(e)) => btree.range_to(e),
3124 (None, None) => {
3125 let mut cancel = CancelCheck::new();
3126 let mut rows: Vec<Vec<Value>> = Vec::new();
3127 for (_, row) in tbl.scan() {
3128 cancel.tick()?;
3129 rows.push(row);
3130 }
3131 return Ok(QueryResult::Rows { columns, rows });
3132 }
3133 };
3134 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(hits.len());
3135 let mut cancel = CancelCheck::new();
3136 for (key, rid) in hits {
3137 cancel.tick()?;
3138 if !start_inclusive {
3139 if let Some(ref s) = start_val {
3140 if &key == s {
3141 continue;
3142 }
3143 }
3144 }
3145 if !end_inclusive {
3146 if let Some(ref e) = end_val {
3147 if &key == e {
3148 continue;
3149 }
3150 }
3151 }
3152 if let Some(row) = tbl.get(rid) {
3154 rows.push(row);
3155 }
3156 }
3157 return Ok(QueryResult::Rows { columns, rows });
3158 }
3159 }
3160
3161 let fast = FastLayout::new(schema);
3165 let synth = synthesize_range_predicate(column, start, end);
3166 if !tbl.has_overflow_rows() {
3167 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, schema) {
3168 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
3169 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3170 if compiled(data) {
3171 rows.push(decode_row(schema, data));
3172 }
3173 })?;
3174 return Ok(QueryResult::Rows { columns, rows });
3175 }
3176 }
3177
3178 let col_idx =
3179 schema
3180 .column_index(column)
3181 .ok_or_else(|| QueryError::ColumnNotFound {
3182 table: String::new(),
3183 column: column.clone(),
3184 })?;
3185 let mut cancel = CancelCheck::new();
3186 let mut rows: Vec<Vec<Value>> = Vec::new();
3187 for (_, row) in tbl.scan() {
3188 cancel.tick()?;
3189 if range_matches(
3190 &row[col_idx],
3191 &start_val,
3192 start_inclusive,
3193 &end_val,
3194 end_inclusive,
3195 ) {
3196 rows.push(row);
3197 }
3198 }
3199 Ok(QueryResult::Rows { columns, rows })
3200 }
3201 }
3202 }
3203
3204 fn create_view(&mut self, name: &str, query_text: &str) -> Result<(), QueryError> {
3209 if self.view_registry.is_view(name) {
3210 return Err(QueryError::ViewError(format!(
3211 "materialized view '{name}' already exists"
3212 )));
3213 }
3214 let result = self.execute_powql(query_text)?;
3216 let (columns, rows) = match result {
3217 QueryResult::Rows { columns, rows } => (columns, rows),
3218 _ => return Err("view source query must be a SELECT".into()),
3219 };
3220 let schema = self.derive_view_schema(name, &columns, &rows);
3222 crate::cancel::check()?;
3224 self.catalog
3225 .create_table(schema)
3226 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3227 for row in &rows {
3228 self.catalog
3229 .insert(name, row)
3230 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3231 }
3232 let depends_on = self.extract_view_deps(query_text);
3234 self.view_registry
3235 .register(ViewDef {
3236 name: name.to_string(),
3237 query: query_text.to_string(),
3238 depends_on,
3239 dirty: false,
3240 })
3241 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3242 Ok(())
3243 }
3244
3245 fn refresh_view(&mut self, name: &str) -> Result<(), QueryError> {
3248 let def = self
3249 .view_registry
3250 .get(name)
3251 .ok_or_else(|| format!("materialized view '{name}' not found"))?;
3252 let query_text = def.query.clone();
3253 let result = self.execute_powql(&query_text)?;
3255 let (_columns, rows) = match result {
3256 QueryResult::Rows { columns, rows } => (columns, rows),
3257 _ => return Err("view source query must be a SELECT".into()),
3258 };
3259 crate::cancel::check()?;
3263 self.catalog
3264 .scan_delete_matching_logged(name, |_| true)
3265 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3266 for row in &rows {
3267 self.catalog
3268 .insert(name, row)
3269 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3270 }
3271 self.view_registry.mark_clean(name);
3272 Ok(())
3273 }
3274
3275 fn drop_view(&mut self, name: &str) -> Result<(), QueryError> {
3277 if !self.view_registry.is_view(name) {
3278 return Err(QueryError::ViewError(format!(
3279 "materialized view '{name}' not found"
3280 )));
3281 }
3282 self.view_registry
3283 .unregister(name)
3284 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3285 self.catalog
3286 .drop_table(name)
3287 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3288 Ok(())
3289 }
3290
3291 fn derive_view_schema(&self, name: &str, columns: &[String], rows: &[Vec<Value>]) -> Schema {
3294 use powdb_storage::types::{ColumnDef, TypeId};
3295 let cols: Vec<ColumnDef> = columns
3296 .iter()
3297 .enumerate()
3298 .map(|(i, col_name)| {
3299 let type_id = rows
3300 .first()
3301 .and_then(|row| row.get(i))
3302 .map(|v| v.type_id())
3303 .unwrap_or(TypeId::Str);
3304 ColumnDef {
3305 name: col_name.clone(),
3306 type_id,
3307 required: false,
3308 position: i as u16,
3309 }
3310 })
3311 .collect();
3312 Schema {
3313 table_name: name.to_string(),
3314 columns: cols,
3315 }
3316 }
3317
3318 fn extract_view_deps(&self, query_text: &str) -> Vec<String> {
3321 use crate::parser::parse;
3322 match parse(query_text) {
3323 Ok(Statement::Query(q)) => {
3324 let mut deps = vec![q.source.clone()];
3325 for j in &q.joins {
3326 deps.push(j.source.clone());
3327 }
3328 deps
3329 }
3330 _ => Vec::new(),
3331 }
3332 }
3333
3334 pub(super) fn agg_single_col_fast(
3344 &self,
3345 table: &str,
3346 col: &str,
3347 function: AggFunc,
3348 predicate: Option<&Expr>,
3349 ) -> Result<Option<QueryResult>, QueryError> {
3350 if self.catalog.table_has_overflow(table) {
3354 return Ok(None);
3355 }
3356 let schema = self
3357 .catalog
3358 .schema(table)
3359 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
3360 .clone();
3361 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3362 let col_idx = match schema.column_index(col) {
3363 Some(i) => i,
3364 None => return Ok(None),
3365 };
3366 let col_type = schema.columns[col_idx].type_id;
3373 if col_type != TypeId::Int && col_type != TypeId::Float {
3374 return Ok(None);
3375 }
3376
3377 let fast = FastLayout::new(&schema);
3378 let byte_offset = match fast.fixed_offsets[col_idx] {
3383 Some(o) => o,
3384 None => return Ok(None),
3385 };
3386 let bitmap_byte = col_idx / 8;
3387 let bitmap_bit = (col_idx % 8) as u32;
3388 let body_data_offset = 2 + fast.bitmap_size + byte_offset;
3389
3390 let compiled_pred: Option<CompiledPredicate> = match predicate {
3392 Some(pred) => match compile_predicate(pred, &columns, &fast, &schema) {
3393 Some(c) => Some(c),
3394 None => return Ok(None), },
3396 None => None,
3397 };
3398
3399 let result = match col_type {
3426 TypeId::Int => match function {
3427 AggFunc::Sum | AggFunc::Avg => {
3428 let mut sum_i128: i128 = 0;
3429 let mut count: i64 = 0;
3430 agg_int_loop!(
3431 self,
3432 table,
3433 compiled_pred,
3434 bitmap_byte,
3435 bitmap_bit,
3436 body_data_offset,
3437 |v: i64| {
3438 count += 1;
3439 sum_i128 += v as i128;
3440 }
3441 );
3442 if matches!(function, AggFunc::Sum) {
3443 let clamped = sum_i128.clamp(i64::MIN as i128, i64::MAX as i128) as i64;
3444 QueryResult::Scalar(Value::Int(clamped))
3445 } else if count == 0 {
3446 QueryResult::Scalar(Value::Empty)
3447 } else {
3448 let avg = (sum_i128 as f64) / (count as f64);
3449 QueryResult::Scalar(Value::Float(avg))
3450 }
3451 }
3452 AggFunc::Min => {
3453 let mut min_v: Option<i64> = None;
3454 agg_int_loop!(
3455 self,
3456 table,
3457 compiled_pred,
3458 bitmap_byte,
3459 bitmap_bit,
3460 body_data_offset,
3461 |v: i64| {
3462 min_v = Some(match min_v {
3463 Some(m) => m.min(v),
3464 None => v,
3465 });
3466 }
3467 );
3468 QueryResult::Scalar(min_v.map(Value::Int).unwrap_or(Value::Empty))
3469 }
3470 AggFunc::Max => {
3471 let mut max_v: Option<i64> = None;
3472 agg_int_loop!(
3473 self,
3474 table,
3475 compiled_pred,
3476 bitmap_byte,
3477 bitmap_bit,
3478 body_data_offset,
3479 |v: i64| {
3480 max_v = Some(match max_v {
3481 Some(m) => m.max(v),
3482 None => v,
3483 });
3484 }
3485 );
3486 QueryResult::Scalar(max_v.map(Value::Int).unwrap_or(Value::Empty))
3487 }
3488 AggFunc::Count => {
3489 let mut count: i64 = 0;
3490 agg_int_loop!(
3491 self,
3492 table,
3493 compiled_pred,
3494 bitmap_byte,
3495 bitmap_bit,
3496 body_data_offset,
3497 |_v: i64| {
3498 count += 1;
3499 }
3500 );
3501 QueryResult::Scalar(Value::Int(count))
3502 }
3503 AggFunc::CountDistinct => {
3504 let mut seen = rustc_hash::FxHashSet::default();
3505 agg_int_loop!(
3506 self,
3507 table,
3508 compiled_pred,
3509 bitmap_byte,
3510 bitmap_bit,
3511 body_data_offset,
3512 |v: i64| {
3513 seen.insert(v);
3514 }
3515 );
3516 QueryResult::Scalar(Value::Int(seen.len() as i64))
3517 }
3518 },
3519 TypeId::Float => match function {
3520 AggFunc::Sum => {
3521 let mut sum: f64 = 0.0;
3526 agg_float_loop!(
3527 self,
3528 table,
3529 compiled_pred,
3530 bitmap_byte,
3531 bitmap_bit,
3532 body_data_offset,
3533 |v: f64| {
3534 sum += v;
3535 }
3536 );
3537 QueryResult::Scalar(Value::Float(sum))
3538 }
3539 AggFunc::Avg => {
3540 let mut sum: f64 = 0.0;
3541 let mut count: i64 = 0;
3542 agg_float_loop!(
3543 self,
3544 table,
3545 compiled_pred,
3546 bitmap_byte,
3547 bitmap_bit,
3548 body_data_offset,
3549 |v: f64| {
3550 sum += v;
3551 count += 1;
3552 }
3553 );
3554 if count == 0 {
3555 QueryResult::Scalar(Value::Empty)
3556 } else {
3557 QueryResult::Scalar(Value::Float(sum / count as f64))
3558 }
3559 }
3560 AggFunc::Min => {
3561 let mut min_v: Option<f64> = None;
3565 agg_float_loop!(
3566 self,
3567 table,
3568 compiled_pred,
3569 bitmap_byte,
3570 bitmap_bit,
3571 body_data_offset,
3572 |v: f64| {
3573 min_v = Some(match min_v {
3574 Some(m) => {
3575 if v.total_cmp(&m).is_lt() {
3576 v
3577 } else {
3578 m
3579 }
3580 }
3581 None => v,
3582 });
3583 }
3584 );
3585 QueryResult::Scalar(min_v.map(Value::Float).unwrap_or(Value::Empty))
3586 }
3587 AggFunc::Max => {
3588 let mut max_v: Option<f64> = None;
3589 agg_float_loop!(
3590 self,
3591 table,
3592 compiled_pred,
3593 bitmap_byte,
3594 bitmap_bit,
3595 body_data_offset,
3596 |v: f64| {
3597 max_v = Some(match max_v {
3598 Some(m) => {
3599 if v.total_cmp(&m).is_gt() {
3600 v
3601 } else {
3602 m
3603 }
3604 }
3605 None => v,
3606 });
3607 }
3608 );
3609 QueryResult::Scalar(max_v.map(Value::Float).unwrap_or(Value::Empty))
3610 }
3611 AggFunc::Count => {
3612 let mut count: i64 = 0;
3613 agg_float_loop!(
3614 self,
3615 table,
3616 compiled_pred,
3617 bitmap_byte,
3618 bitmap_bit,
3619 body_data_offset,
3620 |_v: f64| {
3621 count += 1;
3622 }
3623 );
3624 QueryResult::Scalar(Value::Int(count))
3625 }
3626 AggFunc::CountDistinct => {
3627 let mut seen = rustc_hash::FxHashSet::default();
3633 agg_float_loop!(
3634 self,
3635 table,
3636 compiled_pred,
3637 bitmap_byte,
3638 bitmap_bit,
3639 body_data_offset,
3640 |v: f64| {
3641 seen.insert(v.to_bits());
3642 }
3643 );
3644 QueryResult::Scalar(Value::Int(seen.len() as i64))
3645 }
3646 },
3647 _ => unreachable!("type guard above restricts to Int/Float"),
3648 };
3649 Ok(Some(result))
3650 }
3651
3652 pub(super) fn project_filter_limit_fast(
3655 &self,
3656 table: &str,
3657 fields: &[ProjectField],
3658 limit: usize,
3659 predicate: Option<&Expr>,
3660 ) -> Result<Option<QueryResult>, QueryError> {
3661 if self.catalog.table_has_overflow(table) {
3665 return Ok(None);
3666 }
3667 let schema = self
3668 .catalog
3669 .schema(table)
3670 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
3671 .clone();
3672 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3673
3674 let mut proj_indices: Vec<usize> = Vec::with_capacity(fields.len());
3677 let mut proj_columns: Vec<String> = Vec::with_capacity(fields.len());
3678 for f in fields {
3679 let name = match &f.expr {
3680 Expr::Field(n) => n.clone(),
3681 _ => return Ok(None),
3682 };
3683 let idx = match all_columns.iter().position(|c| c == &name) {
3684 Some(i) => i,
3685 None => return Ok(None),
3686 };
3687 proj_indices.push(idx);
3688 proj_columns.push(f.alias.clone().unwrap_or(name));
3689 }
3690
3691 let fast = FastLayout::new(&schema);
3692 let row_layout = RowLayout::new(&schema);
3693
3694 let compiled_pred: Option<CompiledPredicate> = match predicate {
3695 Some(pred) => match compile_predicate(pred, &all_columns, &fast, &schema) {
3696 Some(c) => Some(c),
3697 None => return Ok(None),
3698 },
3699 None => None,
3700 };
3701
3702 let mut out: Vec<Vec<Value>> = Vec::with_capacity(limit.min(1024));
3703 let mut cancel = CancelCheck::new();
3710 let mut cancel_err: Option<QueryError> = None;
3711 self.catalog
3712 .try_for_each_row_raw(table, |_rid, data| {
3713 if let Err(e) = cancel.tick() {
3714 cancel_err = Some(e);
3715 return ControlFlow::Break(());
3716 }
3717 if let Some(ref pred) = compiled_pred {
3718 if !pred(data) {
3719 return ControlFlow::Continue(());
3720 }
3721 }
3722 let row: Vec<Value> = proj_indices
3723 .iter()
3724 .map(|&ci| decode_column(&schema, &row_layout, data, ci))
3725 .collect();
3726 out.push(row);
3727 if out.len() >= limit {
3728 ControlFlow::Break(())
3729 } else {
3730 ControlFlow::Continue(())
3731 }
3732 })
3733 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3734 if let Some(e) = cancel_err {
3735 return Err(e);
3736 }
3737
3738 Ok(Some(QueryResult::Rows {
3739 columns: proj_columns,
3740 rows: out,
3741 }))
3742 }
3743
3744 pub(super) fn project_filter_sort_limit_fast(
3749 &self,
3750 table: &str,
3751 fields: &[ProjectField],
3752 sort_field: &str,
3753 descending: bool,
3754 limit: usize,
3755 predicate: Option<&Expr>,
3756 ) -> Result<Option<QueryResult>, QueryError> {
3757 if self.catalog.table_has_overflow(table) {
3760 return Ok(None);
3761 }
3762 if limit == 0 {
3763 return Ok(None);
3766 }
3767 const TOPN_PREALLOC_CAP: usize = 4096;
3773 let prealloc = limit.min(TOPN_PREALLOC_CAP);
3774 let schema = self
3775 .catalog
3776 .schema(table)
3777 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
3778 .clone();
3779 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3780
3781 let sort_idx = match schema.column_index(sort_field) {
3788 Some(i) => i,
3789 None => return Ok(None),
3790 };
3791 let sort_col_type = schema.columns[sort_idx].type_id;
3792 if sort_col_type != TypeId::Int && sort_col_type != TypeId::Float {
3793 return Ok(None);
3794 }
3795
3796 let mut proj_indices: Vec<usize> = Vec::with_capacity(fields.len());
3798 let mut proj_columns: Vec<String> = Vec::with_capacity(fields.len());
3799 for f in fields {
3800 let name = match &f.expr {
3801 Expr::Field(n) => n.clone(),
3802 _ => return Ok(None),
3803 };
3804 let idx = match all_columns.iter().position(|c| c == &name) {
3805 Some(i) => i,
3806 None => return Ok(None),
3807 };
3808 proj_indices.push(idx);
3809 proj_columns.push(f.alias.clone().unwrap_or(name));
3810 }
3811
3812 let fast = FastLayout::new(&schema);
3813 let row_layout = RowLayout::new(&schema);
3814 let sort_byte_offset = match fast.fixed_offsets[sort_idx] {
3816 Some(o) => o,
3817 None => return Ok(None),
3818 };
3819 let sort_bitmap_byte = sort_idx / 8;
3820 let sort_bitmap_bit = (sort_idx % 8) as u32;
3821 let sort_body_data_offset = 2 + fast.bitmap_size + sort_byte_offset;
3822
3823 let compiled_pred: Option<CompiledPredicate> = match predicate {
3824 Some(pred) => match compile_predicate(pred, &all_columns, &fast, &schema) {
3825 Some(c) => Some(c),
3826 None => return Ok(None),
3827 },
3828 None => None,
3829 };
3830
3831 let drained: Vec<Vec<u8>> = match sort_col_type {
3840 TypeId::Int => {
3841 let mut seq: u64 = 0;
3842 let mut heap_desc: BinaryHeap<Reverse<(i64, u64, Vec<u8>)>> =
3843 BinaryHeap::with_capacity(prealloc);
3844 let mut heap_asc: BinaryHeap<(i64, u64, Vec<u8>)> =
3845 BinaryHeap::with_capacity(prealloc);
3846 let mut null_rows: Vec<Vec<u8>> = Vec::with_capacity(prealloc);
3847
3848 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3849 if let Some(ref pred) = compiled_pred {
3850 if !pred(data) {
3851 return;
3852 }
3853 }
3854 let base = row_body_base(data);
3856 let sort_data_offset = base + sort_body_data_offset;
3857 if data.len() < sort_data_offset + 8
3858 || data.len() <= base + 2 + sort_bitmap_byte
3859 {
3860 return;
3861 }
3862 let is_null = (data[base + 2 + sort_bitmap_byte] >> sort_bitmap_bit) & 1 == 1;
3863 let id = seq;
3864 seq += 1;
3865 if is_null {
3866 if null_rows.len() < limit {
3867 null_rows.push(data.to_vec());
3868 }
3869 return;
3870 }
3871 let key = i64::from_le_bytes(
3872 data[sort_data_offset..sort_data_offset + 8]
3873 .try_into()
3874 .unwrap_or_else(|_| unreachable!()),
3875 );
3876 if descending {
3877 if heap_desc.len() < limit {
3878 heap_desc.push(Reverse((key, id, data.to_vec())));
3879 } else if let Some(Reverse((top_key, _, _))) = heap_desc.peek() {
3880 if key > *top_key {
3881 heap_desc.pop();
3882 heap_desc.push(Reverse((key, id, data.to_vec())));
3883 }
3884 }
3885 } else if heap_asc.len() < limit {
3886 heap_asc.push((key, id, data.to_vec()));
3887 } else if let Some((top_key, _, _)) = heap_asc.peek() {
3888 if key < *top_key {
3889 heap_asc.pop();
3890 heap_asc.push((key, id, data.to_vec()));
3891 }
3892 }
3893 })?;
3894
3895 let mut drained: Vec<(i64, u64, Vec<u8>)> = if descending {
3896 heap_desc.into_iter().map(|Reverse(t)| t).collect()
3897 } else {
3898 heap_asc.into_iter().collect()
3899 };
3900 if descending {
3901 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3902 b.0.cmp(&a.0).then(a.1.cmp(&b.1))
3903 })?;
3904 } else {
3905 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3906 a.0.cmp(&b.0).then(a.1.cmp(&b.1))
3907 })?;
3908 }
3909 let mut rows: Vec<Vec<u8>> = drained.into_iter().map(|(_, _, d)| d).collect();
3910 rows.extend(null_rows.into_iter().take(limit.saturating_sub(rows.len())));
3911 rows
3912 }
3913 TypeId::Float => {
3914 let mut seq: u64 = 0;
3923 let mut heap_desc: BinaryHeap<Reverse<(u64, u64, Vec<u8>)>> =
3924 BinaryHeap::with_capacity(prealloc);
3925 let mut heap_asc: BinaryHeap<(u64, u64, Vec<u8>)> =
3926 BinaryHeap::with_capacity(prealloc);
3927 let mut null_rows: Vec<Vec<u8>> = Vec::with_capacity(prealloc);
3928
3929 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3930 if let Some(ref pred) = compiled_pred {
3931 if !pred(data) {
3932 return;
3933 }
3934 }
3935 let base = row_body_base(data);
3936 let sort_data_offset = base + sort_body_data_offset;
3937 if data.len() < sort_data_offset + 8
3938 || data.len() <= base + 2 + sort_bitmap_byte
3939 {
3940 return;
3941 }
3942 let is_null = (data[base + 2 + sort_bitmap_byte] >> sort_bitmap_bit) & 1 == 1;
3943 let id = seq;
3944 seq += 1;
3945 if is_null {
3946 if null_rows.len() < limit {
3947 null_rows.push(data.to_vec());
3948 }
3949 return;
3950 }
3951 let bits = u64::from_le_bytes(
3952 data[sort_data_offset..sort_data_offset + 8]
3953 .try_into()
3954 .unwrap_or_else(|_| unreachable!()),
3955 );
3956 let key = f64_bits_to_sortable_u64(bits);
3957 if descending {
3958 if heap_desc.len() < limit {
3959 heap_desc.push(Reverse((key, id, data.to_vec())));
3960 } else if let Some(Reverse((top_key, _, _))) = heap_desc.peek() {
3961 if key > *top_key {
3962 heap_desc.pop();
3963 heap_desc.push(Reverse((key, id, data.to_vec())));
3964 }
3965 }
3966 } else if heap_asc.len() < limit {
3967 heap_asc.push((key, id, data.to_vec()));
3968 } else if let Some((top_key, _, _)) = heap_asc.peek() {
3969 if key < *top_key {
3970 heap_asc.pop();
3971 heap_asc.push((key, id, data.to_vec()));
3972 }
3973 }
3974 })?;
3975
3976 let mut drained: Vec<(u64, u64, Vec<u8>)> = if descending {
3977 heap_desc.into_iter().map(|Reverse(t)| t).collect()
3978 } else {
3979 heap_asc.into_iter().collect()
3980 };
3981 if descending {
3982 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3983 b.0.cmp(&a.0).then(a.1.cmp(&b.1))
3984 })?;
3985 } else {
3986 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3987 a.0.cmp(&b.0).then(a.1.cmp(&b.1))
3988 })?;
3989 }
3990 let mut rows: Vec<Vec<u8>> = drained.into_iter().map(|(_, _, d)| d).collect();
3991 rows.extend(null_rows.into_iter().take(limit.saturating_sub(rows.len())));
3992 rows
3993 }
3994 _ => unreachable!("type guard above restricts to Int/Float"),
3995 };
3996
3997 let mut cancel = CancelCheck::new();
3998 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(drained.len());
3999 for data in drained {
4000 cancel.tick()?;
4001 rows.push(
4002 proj_indices
4003 .iter()
4004 .map(|&ci| decode_column(&schema, &row_layout, &data, ci))
4005 .collect(),
4006 );
4007 }
4008
4009 Ok(Some(QueryResult::Rows {
4010 columns: proj_columns,
4011 rows,
4012 }))
4013 }
4014
4015 fn try_fused_scan_update(
4032 &mut self,
4033 table: &str,
4034 predicate: &Expr,
4035 resolved: &[(usize, Value)],
4036 changed_cols: &[usize],
4037 ) -> Option<Result<QueryResult, QueryError>> {
4038 if self.catalog.table_has_overflow(table) {
4044 return None;
4045 }
4046 let compiled = {
4049 let schema = self.catalog.schema(table)?;
4050 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4051 let fast = FastLayout::new(schema);
4052 compile_predicate(predicate, &columns, &fast, schema)?
4053 };
4054
4055 let fixed_patches: Option<Vec<FastPatch>> = {
4057 let tbl = self.catalog.get_table(table)?;
4058 let schema = tbl.schema();
4059 let all_fixed_nonnull = resolved
4060 .iter()
4061 .all(|(idx, val)| is_fixed_size(schema.columns[*idx].type_id) && !val.is_empty());
4062 let no_indexed = !resolved.iter().any(|(idx, _)| tbl.has_indexed_col(*idx));
4063 if all_fixed_nonnull && no_indexed {
4064 let layout = RowLayout::new(schema);
4065 let bitmap_size = layout.bitmap_size();
4066 Some(
4067 resolved
4068 .iter()
4069 .map(|(idx, val)| {
4070 let fixed_off = layout
4071 .fixed_offset(*idx)
4072 .expect("is_fixed_size already checked");
4073 let field_off = 2 + bitmap_size + fixed_off;
4074 let bytes: FixedBytes = match val {
4075 Value::Int(v) => FixedBytes::I64(v.to_le_bytes()),
4076 Value::Float(v) => FixedBytes::F64(v.to_le_bytes()),
4077 Value::Bool(v) => FixedBytes::Bool(if *v { 1 } else { 0 }),
4078 Value::DateTime(v) => FixedBytes::I64(v.to_le_bytes()),
4079 Value::Uuid(v) => FixedBytes::Uuid(*v),
4080 _ => unreachable!("all_fixed_nonnull guard"),
4081 };
4082 FastPatch {
4083 field_off,
4084 bitmap_byte_off: 2 + idx / 8,
4085 bit_mask: 1u8 << (idx % 8),
4086 bytes,
4087 }
4088 })
4089 .collect(),
4090 )
4091 } else {
4092 None
4093 }
4094 };
4095 if let Some(patches) = fixed_patches {
4096 let result = self
4097 .catalog
4098 .scan_patch_matching_logged(table, compiled, |row| {
4099 let base = row_body_base(row);
4100 for p in &patches {
4101 row[base + p.bitmap_byte_off] &= !p.bit_mask;
4102 let field_bytes = p.bytes.as_slice();
4103 row[base + p.field_off..base + p.field_off + field_bytes.len()]
4104 .copy_from_slice(field_bytes);
4105 }
4106 Some(row.len() as u16)
4107 })
4108 .map_err(|e| e.to_string());
4109 match result {
4110 Ok((count, _)) => {
4111 self.view_registry.mark_dependents_dirty(table);
4112 return Some(Ok(QueryResult::Modified(count)));
4113 }
4114 Err(e) => return Some(Err(QueryError::Execution(e))),
4115 }
4116 }
4117
4118 let var_patch: Option<(usize, Option<Vec<u8>>)> = {
4120 let tbl = self.catalog.get_table(table)?;
4121 let schema = tbl.schema();
4122 let is_single = resolved.len() == 1;
4123 let is_var = is_single && !is_fixed_size(schema.columns[resolved[0].0].type_id);
4124 let no_indexed = !resolved.iter().any(|(idx, _)| tbl.has_indexed_col(*idx));
4125 if is_single && is_var && no_indexed {
4126 let (idx, val) = &resolved[0];
4127 let bytes_opt = match val {
4128 Value::Str(s) => Some(s.as_bytes().to_vec()),
4129 Value::Bytes(b) => Some(b.clone()),
4130 Value::Empty => None,
4131 _ => return None, };
4133 Some((*idx, bytes_opt))
4134 } else {
4135 None
4136 }
4137 };
4138 if let Some((col_idx, ref new_bytes_opt)) = var_patch {
4139 let layout = {
4141 let schema = self.catalog.schema(table)?;
4142 RowLayout::new(schema)
4143 };
4144 let new_bytes_ref: Option<&[u8]> = new_bytes_opt.as_deref();
4145 let result = self
4146 .catalog
4147 .scan_patch_matching_logged(table, compiled, |row| {
4148 patch_var_column_in_place(row, &layout, col_idx, new_bytes_ref)
4149 })
4150 .map_err(|e| e.to_string());
4151 match result {
4152 Ok((mut count, fallback_rids)) => {
4153 for rid in fallback_rids {
4155 let mut row = match self.catalog.get(table, rid) {
4156 Some(r) => r,
4157 None => continue,
4158 };
4159 for (idx, val) in resolved.iter() {
4160 row[*idx] = val.clone();
4161 }
4162 if let Err(e) =
4163 self.catalog
4164 .update_hinted(table, rid, &row, Some(changed_cols))
4165 {
4166 return Some(Err(QueryError::StorageError(e.to_string())));
4167 }
4168 count += 1;
4169 }
4170 self.view_registry.mark_dependents_dirty(table);
4171 return Some(Ok(QueryResult::Modified(count)));
4172 }
4173 Err(e) => return Some(Err(QueryError::Execution(e))),
4174 }
4175 }
4176
4177 None }
4179
4180 fn index_scan_rids(&self, scan: &PlanNode) -> Result<Option<Vec<RowId>>, QueryError> {
4188 match scan {
4189 PlanNode::IndexScan { table, column, key } => {
4190 let Some(tbl) = self.catalog.get_table(table) else {
4191 return Ok(None);
4192 };
4193 if !tbl.has_index(column) {
4194 return Ok(None);
4195 }
4196 let key_value = literal_to_value(key)?;
4197 Ok(Some(tbl.index_lookup_all(column, &key_value)))
4198 }
4199 PlanNode::ExprIndexScan { table, path, key } => {
4200 let Some(index) = resolve_expression_index(&self.catalog, table, path) else {
4201 return Ok(None);
4202 };
4203 let key_value = literal_to_value(key)?;
4204 let rids = if key_value.is_empty() {
4205 self.catalog
4206 .expression_index_btree(table, index.index_id)
4207 .ok_or_else(|| {
4208 QueryError::Execution("expression index disappeared".to_string())
4209 })?
4210 .empty_rids()
4211 .to_vec()
4212 } else {
4213 self.catalog
4214 .expression_index_lookup_all(table, index.index_id, &key_value)
4215 .map_err(|error| QueryError::StorageError(error.to_string()))?
4216 };
4217 Ok(Some(rids))
4218 }
4219 PlanNode::RangeScan {
4220 table,
4221 column,
4222 start,
4223 end,
4224 } => {
4225 let Some(tbl) = self.catalog.get_table(table) else {
4226 return Ok(None);
4227 };
4228 let start_val = start
4229 .as_ref()
4230 .map(|(expr, _)| literal_to_value(expr))
4231 .transpose()?;
4232 let end_val = end
4233 .as_ref()
4234 .map(|(expr, _)| literal_to_value(expr))
4235 .transpose()?;
4236 let start_inclusive = start.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
4237 let end_inclusive = end.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
4238 match tbl.is_index_unique(column) {
4241 Some(false) => {
4242 let col_idx = tbl.schema().column_index(column).ok_or_else(|| {
4243 QueryError::ColumnNotFound {
4244 table: String::new(),
4245 column: column.clone(),
4246 }
4247 })?;
4248 let Some(btree) = tbl.index(column) else {
4249 return Ok(None);
4250 };
4251 let candidates = btree.range_rids(start_val.as_ref(), end_val.as_ref());
4255 let mut rids = Vec::with_capacity(candidates.len());
4256 let mut cancel = CancelCheck::new();
4257 for rid in candidates {
4258 cancel.tick()?;
4259 if let Some(row) = tbl.get(rid) {
4260 if !row[col_idx].is_empty()
4261 && range_matches(
4262 &row[col_idx],
4263 &start_val,
4264 start_inclusive,
4265 &end_val,
4266 end_inclusive,
4267 )
4268 {
4269 rids.push(rid);
4270 }
4271 }
4272 }
4273 Ok(Some(rids))
4274 }
4275 Some(true) => {
4276 let Some(btree) = tbl.index(column) else {
4277 return Ok(None);
4278 };
4279 let hits: Vec<(Value, RowId)> = match (&start_val, &end_val) {
4283 (Some(s), Some(e)) => btree.range(s, e).collect(),
4284 (Some(s), None) => btree.range_from(s),
4285 (None, Some(e)) => btree.range_to(e),
4286 (None, None) => return Ok(None),
4287 };
4288 let mut rids = Vec::with_capacity(hits.len());
4289 let mut cancel = CancelCheck::new();
4290 for (key, rid) in hits {
4291 cancel.tick()?;
4292 if !start_inclusive {
4293 if let Some(ref s) = start_val {
4294 if &key == s {
4295 continue;
4296 }
4297 }
4298 }
4299 if !end_inclusive {
4300 if let Some(ref e) = end_val {
4301 if &key == e {
4302 continue;
4303 }
4304 }
4305 }
4306 rids.push(rid);
4307 }
4308 Ok(Some(rids))
4309 }
4310 None => Ok(None),
4311 }
4312 }
4313 PlanNode::ExprRangeScan {
4314 table,
4315 path,
4316 start,
4317 end,
4318 } => {
4319 let Some(index) = resolve_expression_index(&self.catalog, table, path) else {
4320 return Ok(None);
4321 };
4322 let start_val = start
4323 .as_ref()
4324 .map(|(expr, _)| literal_to_value(expr))
4325 .transpose()?;
4326 let end_val = end
4327 .as_ref()
4328 .map(|(expr, _)| literal_to_value(expr))
4329 .transpose()?;
4330 let start_inclusive = start.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
4331 let end_inclusive = end.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
4332 let candidates = self
4333 .catalog
4334 .expression_index_range_rids(
4335 table,
4336 index.index_id,
4337 start_val.as_ref(),
4338 end_val.as_ref(),
4339 )
4340 .map_err(|error| QueryError::StorageError(error.to_string()))?;
4341 let schema = self
4342 .catalog
4343 .schema(table)
4344 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4345 let all_columns: Vec<String> =
4346 schema.columns.iter().map(|c| c.name.clone()).collect();
4347 let path_expr = stored_json_path_expr(path);
4348 let mut rids = Vec::with_capacity(candidates.len());
4349 let mut cancel = CancelCheck::new();
4350 for rid in candidates {
4351 cancel.tick()?;
4352 let Some(row) = self.catalog.get(table, rid) else {
4353 continue;
4354 };
4355 let value = eval_expr(&path_expr, &row, &all_columns);
4356 if value.is_empty()
4357 || !range_matches(
4358 &value,
4359 &start_val,
4360 start_inclusive,
4361 &end_val,
4362 end_inclusive,
4363 )
4364 {
4365 continue;
4366 }
4367 rids.push(rid);
4368 }
4369 Ok(Some(rids))
4370 }
4371 _ => Ok(None),
4372 }
4373 }
4374
4375 fn collect_rids_via_index_residual(
4383 &self,
4384 inner: &PlanNode,
4385 predicate: &Expr,
4386 table: &str,
4387 ) -> Result<Option<Vec<RowId>>, QueryError> {
4388 if contains_subquery(predicate) || scan_table(inner) != Some(table) {
4389 return Ok(None);
4390 }
4391 let Some(candidates) = self.index_scan_rids(inner)? else {
4392 return Ok(None);
4393 };
4394 let schema = self
4395 .catalog
4396 .schema(table)
4397 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4398 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4399 let residual_indices = predicate_column_indices_json(predicate, &all_columns);
4400 let residual_names: Vec<String> = residual_indices
4401 .iter()
4402 .map(|&index| all_columns[index].clone())
4403 .collect();
4404 let mut rids = Vec::new();
4405 let mut cancel = CancelCheck::new();
4406 for rid in candidates {
4407 cancel.tick()?;
4408 let Some(sparse) = self
4409 .catalog
4410 .get_projected(table, rid, &residual_indices)
4411 .map_err(|error| QueryError::StorageError(error.to_string()))?
4412 else {
4413 continue;
4414 };
4415 if eval_predicate(predicate, &sparse, &residual_names) {
4416 rids.push(rid);
4417 }
4418 }
4419 Ok(Some(rids))
4420 }
4421
4422 fn collect_rids_for_mutation(
4428 &mut self,
4429 input: &PlanNode,
4430 table: &str,
4431 ) -> Result<Vec<RowId>, QueryError> {
4432 if self.catalog.table_has_overflow(table) {
4440 if let Some(rids) = self.collect_rids_decoded(input, table)? {
4441 return Ok(rids);
4442 }
4443 }
4444 match input {
4445 PlanNode::SeqScan { table: t } if t == table => {
4446 let mut cancel = CancelCheck::new();
4448 let mut rids: Vec<RowId> = Vec::new();
4449 for (rid, _) in self
4450 .catalog
4451 .scan(table)
4452 .map_err(|e| QueryError::StorageError(e.to_string()))?
4453 {
4454 cancel.tick()?;
4455 rids.push(rid);
4456 }
4457 Ok(rids)
4458 }
4459 PlanNode::IndexScan {
4460 table: t,
4461 column,
4462 key,
4463 } if t == table => {
4464 let key_value = literal_to_value(key)?;
4465
4466 {
4475 let tbl = self
4476 .catalog
4477 .get_table(table)
4478 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4479 if tbl.has_index(column) {
4480 let rids = tbl.index_lookup_all(column, &key_value);
4481 return Ok(rids);
4482 }
4483 }
4484
4485 let schema = self
4490 .catalog
4491 .schema(table)
4492 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4493 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4494 let fast = FastLayout::new(schema);
4495 let synth = Expr::BinaryOp(
4496 Box::new(Expr::Field(column.clone())),
4497 BinOp::Eq,
4498 Box::new(key.clone()),
4499 );
4500 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, schema) {
4501 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4503 let mut cancel = CancelCheck::new();
4504 let mut cancel_err: Option<QueryError> = None;
4505 self.catalog
4506 .try_for_each_row_raw(table, |rid, data| {
4507 if let Err(e) = cancel.tick() {
4508 cancel_err = Some(e);
4509 return ControlFlow::Break(());
4510 }
4511 if compiled(data) {
4512 rids.push(rid);
4513 }
4514 ControlFlow::Continue(())
4515 })
4516 .map_err(|e| QueryError::StorageError(e.to_string()))?;
4517 if let Some(e) = cancel_err {
4518 return Err(e);
4519 }
4520 return Ok(rids);
4521 }
4522
4523 let col_idx =
4525 schema
4526 .column_index(column)
4527 .ok_or_else(|| QueryError::ColumnNotFound {
4528 table: String::new(),
4529 column: column.clone(),
4530 })?;
4531 let mut cancel = CancelCheck::new();
4532 let mut rids: Vec<RowId> = Vec::new();
4533 for (rid, row) in self
4534 .catalog
4535 .scan(table)
4536 .map_err(|e| QueryError::StorageError(e.to_string()))?
4537 {
4538 cancel.tick()?;
4539 if row[col_idx] == key_value {
4540 rids.push(rid);
4541 }
4542 }
4543 Ok(rids)
4544 }
4545 PlanNode::RangeScan { table: t, .. }
4546 | PlanNode::ExprIndexScan { table: t, .. }
4547 | PlanNode::ExprRangeScan { table: t, .. }
4548 if t == table =>
4549 {
4550 match self.index_scan_rids(input)? {
4554 Some(rids) => Ok(rids),
4555 None => self.generic_rid_match(input, table),
4556 }
4557 }
4558 PlanNode::Filter {
4559 input: inner,
4560 predicate,
4561 } => {
4562 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
4563 if t != table {
4564 return self.generic_rid_match(input, table);
4565 }
4566 let schema = self
4567 .catalog
4568 .schema(table)
4569 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4570 let columns: Vec<String> =
4571 schema.columns.iter().map(|c| c.name.clone()).collect();
4572 let fast = FastLayout::new(schema);
4573 let row_layout = RowLayout::new(schema);
4574
4575 let mut cancel = CancelCheck::new();
4579 let mut cancel_err: Option<QueryError> = None;
4580 if let Some(compiled) = compile_predicate(predicate, &columns, &fast, schema) {
4582 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4584 self.catalog
4585 .try_for_each_row_raw(table, |rid, data| {
4586 if let Err(e) = cancel.tick() {
4587 cancel_err = Some(e);
4588 return ControlFlow::Break(());
4589 }
4590 if compiled(data) {
4591 rids.push(rid);
4592 }
4593 ControlFlow::Continue(())
4594 })
4595 .map_err(|e| QueryError::StorageError(e.to_string()))?;
4596 if let Some(e) = cancel_err {
4597 return Err(e);
4598 }
4599 return Ok(rids);
4600 }
4601
4602 let pred_cols = predicate_column_indices_json(predicate, &columns);
4604 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4605 self.catalog
4606 .try_for_each_row_raw(table, |rid, data| {
4607 if let Err(e) = cancel.tick() {
4608 cancel_err = Some(e);
4609 return ControlFlow::Break(());
4610 }
4611 let pred_row = decode_selective(schema, &row_layout, data, &pred_cols);
4612 if eval_predicate(predicate, &pred_row, &columns) {
4613 rids.push(rid);
4614 }
4615 ControlFlow::Continue(())
4616 })
4617 .map_err(|e| QueryError::StorageError(e.to_string()))?;
4618 if let Some(e) = cancel_err {
4619 return Err(e);
4620 }
4621 return Ok(rids);
4622 }
4623 if let Some(rids) = self.collect_rids_via_index_residual(inner, predicate, table)? {
4628 return Ok(rids);
4629 }
4630 self.generic_rid_match(input, table)
4631 }
4632 _ => self.generic_rid_match(input, table),
4633 }
4634 }
4635
4636 fn collect_rids_decoded(
4643 &mut self,
4644 input: &PlanNode,
4645 table: &str,
4646 ) -> Result<Option<Vec<RowId>>, QueryError> {
4647 let pred: Option<Expr> = match input {
4649 PlanNode::SeqScan { table: t } if t == table => None,
4650 PlanNode::Filter {
4651 input: inner,
4652 predicate,
4653 } => match inner.as_ref() {
4654 PlanNode::SeqScan { table: t } if t == table => Some(predicate.clone()),
4655 _ => return Ok(None),
4656 },
4657 PlanNode::IndexScan {
4658 table: t,
4659 column,
4660 key,
4661 } if t == table => {
4662 let indexed = self
4665 .catalog
4666 .get_table(table)
4667 .map(|tb| tb.has_index(column))
4668 .unwrap_or(false);
4669 if indexed {
4670 return Ok(None);
4671 }
4672 Some(Expr::BinaryOp(
4673 Box::new(Expr::Field(column.clone())),
4674 BinOp::Eq,
4675 Box::new(key.clone()),
4676 ))
4677 }
4678 _ => return Ok(None),
4679 };
4680
4681 let columns: Vec<String> = {
4682 let schema = self
4683 .catalog
4684 .schema(table)
4685 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4686 schema.columns.iter().map(|c| c.name.clone()).collect()
4687 };
4688 let mut rids: Vec<RowId> = Vec::new();
4689 let mut cancel = CancelCheck::new();
4690 for (rid, row) in self
4691 .catalog
4692 .scan(table)
4693 .map_err(|e| QueryError::StorageError(e.to_string()))?
4694 {
4695 cancel.tick()?;
4696 let keep = match &pred {
4697 None => true,
4698 Some(p) => eval_predicate(p, &row, &columns),
4699 };
4700 if keep {
4701 rids.push(rid);
4702 }
4703 }
4704 Ok(Some(rids))
4705 }
4706
4707 fn generic_rid_match(
4711 &mut self,
4712 input: &PlanNode,
4713 table: &str,
4714 ) -> Result<Vec<RowId>, QueryError> {
4715 #[cfg(test)]
4716 GENERIC_RID_MATCH_CALLS.with(|calls| calls.set(calls.get() + 1));
4717 let result = self.execute_plan(input)?;
4718 let rows = match result {
4719 QueryResult::Rows { rows, .. } => rows,
4720 _ => return Err("mutation source must be rows".into()),
4721 };
4722 let mut matching: Vec<RowId> = Vec::new();
4723 let mut cancel = CancelCheck::new();
4724 for (rid, row) in self
4725 .catalog
4726 .scan(table)
4727 .map_err(|e| QueryError::StorageError(e.to_string()))?
4728 {
4729 cancel.tick()?;
4730 let mut matched = false;
4731 for candidate in &rows {
4732 cancel.tick()?;
4733 if candidate == &row {
4734 matched = true;
4735 break;
4736 }
4737 }
4738 if matched {
4739 matching.push(rid);
4740 }
4741 }
4742 Ok(matching)
4743 }
4744}
4745
4746pub(super) fn execute_window(
4747 result: QueryResult,
4748 windows: &[WindowDef],
4749 memory_limit: usize,
4750) -> Result<QueryResult, QueryError> {
4751 let (mut columns, mut rows) = match result {
4752 QueryResult::Rows { columns, rows } => (columns, rows),
4753 _ => return Err("window function requires row input".into()),
4754 };
4755
4756 let mut cancel = CancelCheck::new();
4757 for wdef in windows {
4758 cancel.tick()?;
4759 let part_indices: Vec<Option<usize>> = wdef
4762 .partition_by
4763 .iter()
4764 .map(|expr| resolve_direct_group_expr(expr, &columns))
4765 .collect::<Result<Vec<_>, _>>()?;
4766
4767 let ord_indices: Vec<(Option<usize>, &Expr, bool)> = wdef
4768 .order_by
4769 .iter()
4770 .map(|sk| {
4771 resolve_direct_group_expr(&sk.expr, &columns)
4772 .map(|index| (index, &sk.expr, sk.descending))
4773 })
4774 .collect::<Result<Vec<_>, _>>()?;
4775
4776 let arg_expr = wdef.args.first();
4777 let arg_col_idx = arg_expr
4778 .map(|expr| resolve_direct_group_expr(expr, &columns))
4779 .transpose()?
4780 .flatten();
4781
4782 let n = rows.len();
4786 let mut indices: Vec<usize> = (0..n).collect();
4787 cooperative_stable_sort_by(&mut indices, memory_limit, |&a, &b| {
4788 for (expr, index) in wdef.partition_by.iter().zip(&part_indices) {
4790 let av = index
4791 .map(|i| rows[a][i].clone())
4792 .unwrap_or_else(|| eval_expr(expr, &rows[a], &columns));
4793 let bv = index
4794 .map(|i| rows[b][i].clone())
4795 .unwrap_or_else(|| eval_expr(expr, &rows[b], &columns));
4796 let cmp = av.cmp(&bv);
4797 if cmp != std::cmp::Ordering::Equal {
4798 return cmp;
4799 }
4800 }
4801 for &(index, expr, desc) in &ord_indices {
4803 let av = index
4804 .map(|i| rows[a][i].clone())
4805 .unwrap_or_else(|| eval_expr(expr, &rows[a], &columns));
4806 let bv = index
4807 .map(|i| rows[b][i].clone())
4808 .unwrap_or_else(|| eval_expr(expr, &rows[b], &columns));
4809 let cmp = compare_order_values(&av, &bv, desc);
4810 if cmp != std::cmp::Ordering::Equal {
4811 return cmp;
4812 }
4813 }
4814 std::cmp::Ordering::Equal
4815 })?;
4816
4817 let whole_partition_frame = wdef.order_by.is_empty()
4825 && matches!(
4826 wdef.function,
4827 WindowFunc::Sum
4828 | WindowFunc::Avg
4829 | WindowFunc::Count
4830 | WindowFunc::Min
4831 | WindowFunc::Max
4832 );
4833 let mut partition_row_indices: Vec<usize> = Vec::new();
4836
4837 let mut win_values: Vec<Value> = vec![Value::Empty; n];
4839 let mut partition_start = 0usize;
4840 let mut running_count: i64 = 0;
4842 let mut running_int_sum: i64 = 0;
4843 let mut running_float_sum: f64 = 0.0;
4844 let mut running_saw_float = false;
4845 let mut running_min: Option<Value> = None;
4846 let mut running_max: Option<Value> = None;
4847 let mut rank_counter: i64 = 0;
4848 let mut dense_rank_counter: i64 = 0;
4849 let mut prev_order_key: Option<Vec<Value>> = None;
4850 let mut same_rank_count: i64 = 0;
4851
4852 for sorted_pos in 0..n {
4853 cancel.tick()?;
4854 let row_idx = indices[sorted_pos];
4855
4856 let new_partition = if sorted_pos == 0 {
4858 true
4859 } else {
4860 let prev_row_idx = indices[sorted_pos - 1];
4861 wdef.partition_by
4862 .iter()
4863 .zip(&part_indices)
4864 .any(|(expr, index)| {
4865 let current = index
4866 .map(|i| rows[row_idx][i].clone())
4867 .unwrap_or_else(|| eval_expr(expr, &rows[row_idx], &columns));
4868 let previous = index
4869 .map(|i| rows[prev_row_idx][i].clone())
4870 .unwrap_or_else(|| eval_expr(expr, &rows[prev_row_idx], &columns));
4871 current != previous
4872 })
4873 };
4874
4875 if new_partition {
4876 if whole_partition_frame && sorted_pos > 0 {
4880 let final_v = win_values[indices[sorted_pos - 1]].clone();
4881 for ri in partition_row_indices.drain(..) {
4882 cancel.tick()?;
4883 win_values[ri] = final_v.clone();
4884 }
4885 }
4886 partition_start = sorted_pos;
4887 running_count = 0;
4888 running_int_sum = 0;
4889 running_float_sum = 0.0;
4890 running_saw_float = false;
4891 running_min = None;
4892 running_max = None;
4893 rank_counter = 0;
4894 dense_rank_counter = 0;
4895 prev_order_key = None;
4896 same_rank_count = 0;
4897 }
4898
4899 let current_order_key: Vec<Value> = ord_indices
4901 .iter()
4902 .map(|&(index, expr, _)| {
4903 index
4904 .map(|i| rows[row_idx][i].clone())
4905 .unwrap_or_else(|| eval_expr(expr, &rows[row_idx], &columns))
4906 })
4907 .collect();
4908 let same_as_prev = prev_order_key.as_ref() == Some(¤t_order_key);
4909 let current_arg = || {
4910 arg_expr.map(|expr| {
4911 arg_col_idx
4912 .map(|index| rows[row_idx][index].clone())
4913 .unwrap_or_else(|| eval_expr(expr, &rows[row_idx], &columns))
4914 })
4915 };
4916 let count_all =
4917 arg_expr.is_none() || matches!(arg_expr, Some(Expr::Field(name)) if name == "*");
4918
4919 let value = match wdef.function {
4920 WindowFunc::RowNumber => Value::Int((sorted_pos - partition_start + 1) as i64),
4921 WindowFunc::Rank => {
4922 if same_as_prev {
4923 same_rank_count += 1;
4924 } else {
4925 rank_counter += same_rank_count + 1;
4926 same_rank_count = 0;
4927 if rank_counter == 0 {
4928 rank_counter = 1;
4929 }
4930 }
4931 Value::Int(rank_counter)
4932 }
4933 WindowFunc::DenseRank => {
4934 if !same_as_prev {
4935 dense_rank_counter += 1;
4936 }
4937 Value::Int(dense_rank_counter)
4938 }
4939 WindowFunc::Sum => {
4940 if let Some(value) = current_arg() {
4941 match value {
4942 Value::Int(v) => running_int_sum += v,
4943 Value::Float(v) => {
4944 running_float_sum += v;
4945 running_saw_float = true;
4946 }
4947 _ => {}
4948 }
4949 }
4950 if running_saw_float {
4951 Value::Float(running_float_sum + running_int_sum as f64)
4952 } else {
4953 Value::Int(running_int_sum)
4954 }
4955 }
4956 WindowFunc::Avg => {
4957 if let Some(value) = current_arg() {
4958 match value {
4959 Value::Int(v) => {
4960 running_float_sum += v as f64;
4961 running_count += 1;
4962 }
4963 Value::Float(v) => {
4964 running_float_sum += v;
4965 running_count += 1;
4966 }
4967 _ => {}
4968 }
4969 }
4970 if running_count == 0 {
4971 Value::Empty
4972 } else {
4973 Value::Float(running_float_sum / running_count as f64)
4974 }
4975 }
4976 WindowFunc::Count => {
4977 if count_all {
4978 running_count += 1;
4979 } else if let Some(value) = current_arg() {
4980 if !value.is_empty() {
4981 running_count += 1;
4982 }
4983 }
4984 Value::Int(running_count)
4985 }
4986 WindowFunc::Min => {
4987 if let Some(v) = current_arg() {
4988 if !v.is_empty() {
4989 running_min = Some(match &running_min {
4990 None => v,
4991 Some(cur) => {
4992 if v < *cur {
4993 v
4994 } else {
4995 cur.clone()
4996 }
4997 }
4998 });
4999 }
5000 }
5001 running_min.clone().unwrap_or(Value::Empty)
5002 }
5003 WindowFunc::Max => {
5004 if let Some(v) = current_arg() {
5005 if !v.is_empty() {
5006 running_max = Some(match &running_max {
5007 None => v,
5008 Some(cur) => {
5009 if v > *cur {
5010 v
5011 } else {
5012 cur.clone()
5013 }
5014 }
5015 });
5016 }
5017 }
5018 running_max.clone().unwrap_or(Value::Empty)
5019 }
5020 };
5021
5022 prev_order_key = Some(current_order_key);
5023 win_values[row_idx] = value;
5024 if whole_partition_frame {
5025 partition_row_indices.push(row_idx);
5026 }
5027 }
5028
5029 if whole_partition_frame && n > 0 {
5031 let final_v = win_values[indices[n - 1]].clone();
5032 for ri in partition_row_indices.drain(..) {
5033 cancel.tick()?;
5034 win_values[ri] = final_v.clone();
5035 }
5036 }
5037
5038 for (ri, row) in rows.iter_mut().enumerate() {
5040 cancel.tick()?;
5041 row.push(win_values[ri].clone());
5042 }
5043 columns.push(wdef.output_name.clone());
5044 }
5045
5046 Ok(QueryResult::Rows { columns, rows })
5047}
5048
5049pub(super) fn resolve_group_column(name: &str, columns: &[String]) -> Result<usize, QueryError> {
5062 if let Some(i) = columns.iter().position(|c| c == name) {
5063 return Ok(i);
5064 }
5065 if name.contains('.') {
5066 return Err(QueryError::ColumnNotFound {
5067 table: String::new(),
5068 column: name.to_string(),
5069 });
5070 }
5071 let suffix = format!(".{name}");
5072 let mut matches = columns
5073 .iter()
5074 .enumerate()
5075 .filter(|(_, c)| c.ends_with(&suffix));
5076 match matches.next() {
5077 None => Err(QueryError::ColumnNotFound {
5078 table: String::new(),
5079 column: name.to_string(),
5080 }),
5081 Some((first_idx, _)) => {
5082 let rest: Vec<&str> = matches.map(|(_, c)| c.as_str()).collect();
5083 if rest.is_empty() {
5084 Ok(first_idx)
5085 } else {
5086 let candidates: Vec<&str> = columns
5089 .iter()
5090 .filter(|c| c.ends_with(&suffix))
5091 .map(|c| c.as_str())
5092 .collect();
5093 Err(QueryError::Execution(format!(
5094 "cannot group by ambiguous column '{name}'; candidates: {}",
5095 candidates.join(", ")
5096 )))
5097 }
5098 }
5099 }
5100}
5101
5102pub(super) fn exec_group_by(
5107 columns: Vec<String>,
5108 rows: Vec<Vec<Value>>,
5109 keys: &[GroupKey],
5110 aggregates: &[GroupAgg],
5111 having: &Option<Expr>,
5112) -> Result<QueryResult, QueryError> {
5113 exec_group_by_internal(columns, rows, None, keys, aggregates, having)
5114}
5115
5116pub(super) fn exec_group_by_with_provenance(
5117 input: ProvenanceRows,
5118 keys: &[GroupKey],
5119 aggregates: &[GroupAgg],
5120 having: &Option<Expr>,
5121 memory_limit: usize,
5122) -> Result<QueryResult, QueryError> {
5123 let ProvenanceRows {
5124 columns,
5125 rows,
5126 source_aliases,
5127 provenance,
5128 } = input;
5129 exec_group_by_internal(
5130 columns,
5131 rows,
5132 Some(GroupProvenance {
5133 source_aliases,
5134 rows: provenance,
5135 memory_limit,
5136 }),
5137 keys,
5138 aggregates,
5139 having,
5140 )
5141}
5142
5143struct GroupProvenance {
5144 source_aliases: Vec<String>,
5145 rows: Vec<Vec<Option<RowId>>>,
5146 memory_limit: usize,
5147}
5148
5149fn exec_group_by_internal(
5150 columns: Vec<String>,
5151 rows: Vec<Vec<Value>>,
5152 provenance: Option<GroupProvenance>,
5153 keys: &[GroupKey],
5154 aggregates: &[GroupAgg],
5155 having: &Option<Expr>,
5156) -> Result<QueryResult, QueryError> {
5157 let key_indices: Vec<Option<usize>> = keys
5160 .iter()
5161 .map(|k| resolve_direct_group_expr(&k.expr, &columns))
5162 .collect::<Result<Vec<_>, _>>()?;
5163
5164 let agg_field_indices: Vec<Option<usize>> = aggregates
5165 .iter()
5166 .map(|a| resolve_direct_group_expr(&a.argument, &columns))
5167 .collect::<Result<Vec<_>, _>>()?;
5168 let agg_source_indices: Vec<Option<usize>> = aggregates
5169 .iter()
5170 .map(|aggregate| {
5171 aggregate
5172 .provenance_alias
5173 .as_ref()
5174 .map(|alias| {
5175 provenance
5176 .as_ref()
5177 .and_then(|provenance| {
5178 provenance
5179 .source_aliases
5180 .iter()
5181 .position(|source| source == alias)
5182 })
5183 .ok_or_else(|| {
5184 QueryError::Execution(format!(
5185 "symmetric aggregate source alias '{alias}' is not present in its input"
5186 ))
5187 })
5188 })
5189 .transpose()
5190 })
5191 .collect::<Result<Vec<_>, _>>()?;
5192
5193 let mut group_map: rustc_hash::FxHashMap<Vec<Value>, usize> = rustc_hash::FxHashMap::default();
5195 let mut groups: Vec<(Vec<Value>, Vec<usize>)> = Vec::new();
5196 let mut cancel = CancelCheck::new();
5197 for (ri, row) in rows.iter().enumerate() {
5198 cancel.tick()?;
5199 let key: Vec<Value> = keys
5200 .iter()
5201 .zip(&key_indices)
5202 .map(|(key, index)| match index {
5203 Some(index) => row[*index].clone(),
5204 None => eval_expr(&key.expr, row, &columns),
5205 })
5206 .collect();
5207 match group_map.get(&key) {
5208 Some(&idx) => groups[idx].1.push(ri),
5209 None => {
5210 let idx = groups.len();
5211 group_map.insert(key.clone(), idx);
5212 groups.push((key, vec![ri]));
5213 }
5214 }
5215 }
5216
5217 let mut out_columns: Vec<String> = keys.iter().map(|k| k.output_name()).collect();
5221 for agg in aggregates.iter() {
5222 out_columns.push(agg.output_name.clone());
5223 }
5224
5225 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(groups.len());
5227 for (key_vals, row_indices) in &groups {
5228 cancel.tick()?;
5229 let mut row = key_vals.clone();
5230 for (ai, agg) in aggregates.iter().enumerate() {
5231 let val = compute_group_aggregate(
5232 agg.function,
5233 &agg.argument,
5234 agg_field_indices[ai],
5235 GroupAggregateContext {
5236 columns: &columns,
5237 all_rows: &rows,
5238 row_indices,
5239 source_index: agg_source_indices[ai],
5240 provenance: provenance
5241 .as_ref()
5242 .map(|provenance| (provenance.rows.as_slice(), provenance.memory_limit)),
5243 },
5244 )?;
5245 row.push(val);
5246 }
5247 out_rows.push(row);
5248 }
5249
5250 if let Some(having_expr) = having {
5252 let mut filtered = Vec::with_capacity(out_rows.len());
5253 for row in out_rows {
5254 cancel.tick()?;
5255 if eval_predicate(having_expr, &row, &out_columns) {
5256 filtered.push(row);
5257 }
5258 }
5259 out_rows = filtered;
5260 }
5261
5262 Ok(QueryResult::Rows {
5263 columns: out_columns,
5264 rows: out_rows,
5265 })
5266}
5267
5268fn resolve_direct_group_expr(expr: &Expr, columns: &[String]) -> Result<Option<usize>, QueryError> {
5269 match expr {
5270 Expr::Field(name) if name == "*" => Ok(None),
5271 Expr::Field(name) => resolve_group_column(name, columns).map(Some),
5272 Expr::QualifiedField { qualifier, field } => {
5273 resolve_group_column(&format!("{qualifier}.{field}"), columns).map(Some)
5274 }
5275 _ => Ok(None),
5276 }
5277}
5278
5279pub(super) fn predicate_column_indices_json(expr: &Expr, columns: &[String]) -> Vec<usize> {
5294 let mut indices = predicate_column_indices(expr, columns);
5295 collect_json_path_base_indices(expr, columns, &mut indices);
5296 indices.sort_unstable();
5297 indices.dedup();
5298 indices
5299}
5300
5301fn collect_json_path_base_indices(expr: &Expr, columns: &[String], out: &mut Vec<usize>) {
5303 match expr {
5304 Expr::JsonPath { base, .. } => {
5305 let name = match base.as_ref() {
5306 Expr::Field(n) => n.clone(),
5307 Expr::QualifiedField { qualifier, field } => format!("{qualifier}.{field}"),
5308 other => {
5309 collect_json_path_base_indices(other, columns, out);
5310 return;
5311 }
5312 };
5313 if let Some(idx) = columns.iter().position(|c| *c == name) {
5314 out.push(idx);
5315 }
5316 }
5317 Expr::BinaryOp(l, _, r) | Expr::Coalesce(l, r) => {
5318 collect_json_path_base_indices(l, columns, out);
5319 collect_json_path_base_indices(r, columns, out);
5320 }
5321 Expr::UnaryOp(_, i) | Expr::FunctionCall(_, i, _) | Expr::Cast(i, _) => {
5322 collect_json_path_base_indices(i, columns, out);
5323 }
5324 Expr::ScalarFunc(_, args) => {
5325 for a in args {
5326 collect_json_path_base_indices(a, columns, out);
5327 }
5328 }
5329 Expr::InList { expr, list, .. } => {
5330 collect_json_path_base_indices(expr, columns, out);
5331 for item in list {
5332 collect_json_path_base_indices(item, columns, out);
5333 }
5334 }
5335 Expr::InSubquery { expr, .. } => collect_json_path_base_indices(expr, columns, out),
5336 Expr::Case { whens, else_expr } => {
5337 for (c, r) in whens {
5338 collect_json_path_base_indices(c, columns, out);
5339 collect_json_path_base_indices(r, columns, out);
5340 }
5341 if let Some(e) = else_expr {
5342 collect_json_path_base_indices(e, columns, out);
5343 }
5344 }
5345 _ => {}
5346 }
5347}
5348
5349pub(super) fn validate_json_path_types(
5360 catalog: &Catalog,
5361 plan: &PlanNode,
5362) -> Result<(), QueryError> {
5363 let mut scope: Vec<(String, TypeId)> = Vec::new();
5364 collect_scan_columns(catalog, plan, &mut scope);
5365 let mut shadowed: std::collections::HashSet<String> = std::collections::HashSet::new();
5366 collect_projected_names(plan, &mut shadowed);
5367 check_plan_json_paths(plan, &scope, &shadowed)
5368}
5369
5370fn collect_scan_columns(catalog: &Catalog, plan: &PlanNode, out: &mut Vec<(String, TypeId)>) {
5374 match plan {
5375 PlanNode::SeqScan { table }
5376 | PlanNode::IndexScan { table, .. }
5377 | PlanNode::RangeScan { table, .. } => {
5378 if let Some(schema) = catalog.schema(table) {
5379 for c in &schema.columns {
5380 out.push((c.name.clone(), c.type_id));
5381 }
5382 }
5383 }
5384 PlanNode::AliasScan { table, alias } => {
5385 if let Some(schema) = catalog.schema(table) {
5386 for c in &schema.columns {
5387 out.push((format!("{alias}.{}", c.name), c.type_id));
5388 }
5389 }
5390 }
5391 PlanNode::Filter { input, .. }
5392 | PlanNode::Project { input, .. }
5393 | PlanNode::Sort { input, .. }
5394 | PlanNode::Limit { input, .. }
5395 | PlanNode::Offset { input, .. }
5396 | PlanNode::Aggregate { input, .. }
5397 | PlanNode::Distinct { input }
5398 | PlanNode::GroupBy { input, .. }
5399 | PlanNode::Window { input, .. }
5400 | PlanNode::Update { input, .. }
5401 | PlanNode::Delete { input, .. }
5402 | PlanNode::Explain { input } => collect_scan_columns(catalog, input, out),
5403 PlanNode::NestedLoopJoin { left, right, .. } | PlanNode::Union { left, right, .. } => {
5404 collect_scan_columns(catalog, left, out);
5405 collect_scan_columns(catalog, right, out);
5406 }
5407 _ => {}
5408 }
5409}
5410
5411fn collect_projected_names(plan: &PlanNode, out: &mut std::collections::HashSet<String>) {
5414 if let PlanNode::Project { fields, .. } = plan {
5415 for f in fields {
5416 if let Some(a) = &f.alias {
5417 out.insert(a.clone());
5418 } else {
5419 match &f.expr {
5420 Expr::Field(n) => {
5421 out.insert(n.clone());
5422 }
5423 Expr::QualifiedField { qualifier, field } => {
5424 out.insert(format!("{qualifier}.{field}"));
5425 }
5426 _ => {}
5427 }
5428 }
5429 }
5430 }
5431 match plan {
5432 PlanNode::Filter { input, .. }
5433 | PlanNode::Project { input, .. }
5434 | PlanNode::Sort { input, .. }
5435 | PlanNode::Limit { input, .. }
5436 | PlanNode::Offset { input, .. }
5437 | PlanNode::Aggregate { input, .. }
5438 | PlanNode::Distinct { input }
5439 | PlanNode::GroupBy { input, .. }
5440 | PlanNode::Window { input, .. }
5441 | PlanNode::Update { input, .. }
5442 | PlanNode::Delete { input, .. }
5443 | PlanNode::Explain { input } => collect_projected_names(input, out),
5444 PlanNode::NestedLoopJoin { left, right, .. } | PlanNode::Union { left, right, .. } => {
5445 collect_projected_names(left, out);
5446 collect_projected_names(right, out);
5447 }
5448 _ => {}
5449 }
5450}
5451
5452fn resolve_scan_type(name: &str, scope: &[(String, TypeId)]) -> Option<TypeId> {
5456 let mut found: Option<TypeId> = None;
5457 for (n, t) in scope {
5458 if n == name {
5459 match found {
5460 None => found = Some(*t),
5461 Some(prev) if prev == *t => {}
5462 Some(_) => return None, }
5464 }
5465 }
5466 found
5467}
5468
5469fn json_path_base_error(
5472 base: &Expr,
5473 scope: &[(String, TypeId)],
5474 shadowed: &std::collections::HashSet<String>,
5475) -> Option<String> {
5476 let name = match base {
5477 Expr::Field(n) => n.clone(),
5478 Expr::QualifiedField { qualifier, field } => format!("{qualifier}.{field}"),
5479 _ => return None,
5482 };
5483 if shadowed.contains(&name) {
5484 return None;
5485 }
5486 match resolve_scan_type(&name, scope) {
5487 Some(TypeId::Json) | None => None,
5488 Some(other) => Some(format!(
5489 "'{}' is a {} column, not json: the '->' path operator requires a json column",
5490 name,
5491 type_id_to_name(other)
5492 )),
5493 }
5494}
5495
5496fn check_expr_json_paths(
5498 expr: &Expr,
5499 scope: &[(String, TypeId)],
5500 shadowed: &std::collections::HashSet<String>,
5501) -> Result<(), QueryError> {
5502 match expr {
5503 Expr::JsonPath { base, .. } => {
5504 if let Some(msg) = json_path_base_error(base, scope, shadowed) {
5505 return Err(QueryError::TypeError(msg));
5506 }
5507 check_expr_json_paths(base, scope, shadowed)
5508 }
5509 Expr::BinaryOp(l, _, r) | Expr::Coalesce(l, r) => {
5510 check_expr_json_paths(l, scope, shadowed)?;
5511 check_expr_json_paths(r, scope, shadowed)
5512 }
5513 Expr::UnaryOp(_, inner) | Expr::FunctionCall(_, inner, _) | Expr::Cast(inner, _) => {
5514 check_expr_json_paths(inner, scope, shadowed)
5515 }
5516 Expr::ScalarFunc(_, args) => {
5517 for a in args {
5518 check_expr_json_paths(a, scope, shadowed)?;
5519 }
5520 Ok(())
5521 }
5522 Expr::Window {
5523 args,
5524 partition_by,
5525 order_by,
5526 ..
5527 } => {
5528 for expr in args.iter().chain(partition_by) {
5529 check_expr_json_paths(expr, scope, shadowed)?;
5530 }
5531 for key in order_by {
5532 check_expr_json_paths(&key.expr, scope, shadowed)?;
5533 }
5534 Ok(())
5535 }
5536 Expr::InList { expr, list, .. } => {
5537 check_expr_json_paths(expr, scope, shadowed)?;
5538 for item in list {
5539 check_expr_json_paths(item, scope, shadowed)?;
5540 }
5541 Ok(())
5542 }
5543 Expr::Case { whens, else_expr } => {
5544 for (c, r) in whens {
5545 check_expr_json_paths(c, scope, shadowed)?;
5546 check_expr_json_paths(r, scope, shadowed)?;
5547 }
5548 if let Some(e) = else_expr {
5549 check_expr_json_paths(e, scope, shadowed)?;
5550 }
5551 Ok(())
5552 }
5553 Expr::InSubquery { expr, .. } => check_expr_json_paths(expr, scope, shadowed),
5556 _ => Ok(()),
5557 }
5558}
5559
5560fn check_plan_json_paths(
5562 plan: &PlanNode,
5563 scope: &[(String, TypeId)],
5564 shadowed: &std::collections::HashSet<String>,
5565) -> Result<(), QueryError> {
5566 match plan {
5567 PlanNode::Filter { input, predicate } => {
5568 check_expr_json_paths(predicate, scope, shadowed)?;
5569 check_plan_json_paths(input, scope, shadowed)
5570 }
5571 PlanNode::Project { input, fields } => {
5572 for f in fields {
5573 check_expr_json_paths(&f.expr, scope, shadowed)?;
5574 }
5575 check_plan_json_paths(input, scope, shadowed)
5576 }
5577 PlanNode::GroupBy {
5578 input,
5579 keys,
5580 aggregates,
5581 having,
5582 } => {
5583 for key in keys {
5584 check_expr_json_paths(&key.expr, scope, shadowed)?;
5585 }
5586 for aggregate in aggregates {
5587 check_expr_json_paths(&aggregate.argument, scope, shadowed)?;
5588 }
5589 if let Some(h) = having {
5590 check_expr_json_paths(h, scope, shadowed)?;
5591 }
5592 check_plan_json_paths(input, scope, shadowed)
5593 }
5594 PlanNode::NestedLoopJoin {
5595 left, right, on, ..
5596 } => {
5597 if let Some(on) = on {
5598 check_expr_json_paths(on, scope, shadowed)?;
5599 }
5600 check_plan_json_paths(left, scope, shadowed)?;
5601 check_plan_json_paths(right, scope, shadowed)
5602 }
5603 PlanNode::Union { left, right, .. } => {
5604 check_plan_json_paths(left, scope, shadowed)?;
5605 check_plan_json_paths(right, scope, shadowed)
5606 }
5607 PlanNode::Sort { input, keys } => {
5608 for key in keys {
5609 check_expr_json_paths(&key.expr, scope, shadowed)?;
5610 }
5611 check_plan_json_paths(input, scope, shadowed)
5612 }
5613 PlanNode::Aggregate {
5614 input, argument, ..
5615 } => {
5616 if let Some(argument) = argument {
5617 check_expr_json_paths(argument, scope, shadowed)?;
5618 }
5619 check_plan_json_paths(input, scope, shadowed)
5620 }
5621 PlanNode::Window { input, windows } => {
5622 for window in windows {
5623 for expr in window.args.iter().chain(&window.partition_by) {
5624 check_expr_json_paths(expr, scope, shadowed)?;
5625 }
5626 for key in &window.order_by {
5627 check_expr_json_paths(&key.expr, scope, shadowed)?;
5628 }
5629 }
5630 check_plan_json_paths(input, scope, shadowed)
5631 }
5632 PlanNode::Limit { input, .. }
5633 | PlanNode::Offset { input, .. }
5634 | PlanNode::Distinct { input }
5635 | PlanNode::Update { input, .. }
5636 | PlanNode::Delete { input, .. }
5637 | PlanNode::Explain { input } => check_plan_json_paths(input, scope, shadowed),
5638 _ => Ok(()),
5639 }
5640}
5641
5642pub(super) fn validate_no_stray_aggregates(plan: &PlanNode) -> Result<(), QueryError> {
5643 match plan {
5644 PlanNode::Project { input, fields } => {
5645 for f in fields {
5646 check_expr_no_aggregate(&f.expr)?;
5647 }
5648 validate_no_stray_aggregates(input)?;
5649 }
5650 PlanNode::Filter { input, predicate } => {
5651 check_expr_no_aggregate(predicate)?;
5652 validate_no_stray_aggregates(input)?;
5653 }
5654 PlanNode::GroupBy {
5655 input,
5656 keys,
5657 aggregates,
5658 having,
5659 } => {
5660 for key in keys {
5661 check_expr_no_aggregate(&key.expr)?;
5662 }
5663 for aggregate in aggregates {
5664 check_expr_no_aggregate(&aggregate.argument)?;
5665 }
5666 if let Some(h) = having {
5667 check_expr_no_aggregate(h)?;
5668 }
5669 validate_no_stray_aggregates(input)?;
5670 }
5671 PlanNode::NestedLoopJoin {
5672 left, right, on, ..
5673 } => {
5674 if let Some(on) = on {
5675 check_expr_no_aggregate(on)?;
5676 }
5677 validate_no_stray_aggregates(left)?;
5678 validate_no_stray_aggregates(right)?;
5679 }
5680 PlanNode::Union { left, right, .. } => {
5681 validate_no_stray_aggregates(left)?;
5682 validate_no_stray_aggregates(right)?;
5683 }
5684 PlanNode::Sort { input, keys } => {
5685 for key in keys {
5686 check_expr_no_aggregate(&key.expr)?;
5687 }
5688 validate_no_stray_aggregates(input)?;
5689 }
5690 PlanNode::Aggregate {
5691 input, argument, ..
5692 } => {
5693 if let Some(argument) = argument {
5694 check_expr_no_aggregate(argument)?;
5695 }
5696 validate_no_stray_aggregates(input)?;
5697 }
5698 PlanNode::Window { input, windows } => {
5699 for window in windows {
5700 for expr in window.args.iter().chain(&window.partition_by) {
5701 check_expr_no_aggregate(expr)?;
5702 }
5703 for key in &window.order_by {
5704 check_expr_no_aggregate(&key.expr)?;
5705 }
5706 }
5707 validate_no_stray_aggregates(input)?;
5708 }
5709 PlanNode::Limit { input, .. }
5710 | PlanNode::Offset { input, .. }
5711 | PlanNode::Distinct { input }
5712 | PlanNode::Update { input, .. }
5713 | PlanNode::Delete { input, .. }
5714 | PlanNode::Explain { input } => {
5715 validate_no_stray_aggregates(input)?;
5716 }
5717 _ => {}
5718 }
5719 Ok(())
5720}
5721
5722fn check_expr_no_aggregate(expr: &Expr) -> Result<(), QueryError> {
5726 match expr {
5727 Expr::FunctionCall(..) => Err(QueryError::Execution(
5728 "invalid query: aggregate function in an unsupported position".to_string(),
5729 )),
5730 Expr::BinaryOp(l, _, r) | Expr::Coalesce(l, r) => {
5731 check_expr_no_aggregate(l)?;
5732 check_expr_no_aggregate(r)
5733 }
5734 Expr::UnaryOp(_, inner) | Expr::Cast(inner, _) | Expr::JsonPath { base: inner, .. } => {
5735 check_expr_no_aggregate(inner)
5736 }
5737 Expr::ScalarFunc(_, args) => {
5738 for a in args {
5739 check_expr_no_aggregate(a)?;
5740 }
5741 Ok(())
5742 }
5743 Expr::InList { expr: e, list, .. } => {
5744 check_expr_no_aggregate(e)?;
5745 for item in list {
5746 check_expr_no_aggregate(item)?;
5747 }
5748 Ok(())
5749 }
5750 Expr::InSubquery { expr: e, .. } => check_expr_no_aggregate(e),
5751 Expr::Case { whens, else_expr } => {
5752 for (c, r) in whens {
5753 check_expr_no_aggregate(c)?;
5754 check_expr_no_aggregate(r)?;
5755 }
5756 if let Some(e) = else_expr {
5757 check_expr_no_aggregate(e)?;
5758 }
5759 Ok(())
5760 }
5761 Expr::Window {
5762 args,
5763 partition_by,
5764 order_by,
5765 ..
5766 } => {
5767 for expr in args.iter().chain(partition_by) {
5768 check_expr_no_aggregate(expr)?;
5769 }
5770 for key in order_by {
5771 check_expr_no_aggregate(&key.expr)?;
5772 }
5773 Ok(())
5774 }
5775 _ => Ok(()),
5776 }
5777}
5778
5779pub(super) fn aggregate_rows(
5783 func: AggFunc,
5784 argument: Option<&Expr>,
5785 columns: &[String],
5786 rows: &[Vec<Value>],
5787) -> Result<QueryResult, QueryError> {
5788 let mut cancel = CancelCheck::new();
5789 if func == AggFunc::Count && argument.is_none() {
5790 return Ok(QueryResult::Scalar(Value::Int(rows.len() as i64)));
5791 }
5792 let argument = argument.ok_or_else(|| {
5793 QueryError::Execution(format!(
5794 "{} requires an argument",
5795 format!("{func:?}").to_lowercase()
5796 ))
5797 })?;
5798
5799 let mut values = Vec::with_capacity(rows.len());
5800 for row in rows {
5801 cancel.tick()?;
5802 values.push(eval_expr(argument, row, columns));
5803 }
5804
5805 let value = match func {
5806 AggFunc::Count => Value::Int(values.iter().filter(|v| !v.is_empty()).count() as i64),
5807 AggFunc::CountDistinct => {
5808 let seen: std::collections::HashSet<Value> =
5809 values.into_iter().filter(|v| !v.is_empty()).collect();
5810 Value::Int(seen.len() as i64)
5811 }
5812 AggFunc::Avg => {
5813 let mut sum = 0.0;
5814 let mut count = 0_u64;
5815 for value in values {
5816 match value {
5817 Value::Int(v) => {
5818 sum += v as f64;
5819 count += 1;
5820 }
5821 Value::Float(v) => {
5822 sum += v;
5823 count += 1;
5824 }
5825 _ => {}
5826 }
5827 }
5828 if count == 0 {
5829 Value::Empty
5830 } else {
5831 Value::Float(sum / count as f64)
5832 }
5833 }
5834 AggFunc::Sum => {
5835 let mut int_sum = 0_i64;
5836 let mut float_sum = 0.0;
5837 let mut saw_float = false;
5838 for value in values {
5839 match value {
5840 Value::Int(v) => int_sum += v,
5841 Value::Float(v) => {
5842 float_sum += v;
5843 saw_float = true;
5844 }
5845 _ => {}
5846 }
5847 }
5848 if saw_float {
5849 Value::Float(float_sum + int_sum as f64)
5850 } else {
5851 Value::Int(int_sum)
5852 }
5853 }
5854 AggFunc::Min | AggFunc::Max => {
5855 let mut result: Option<Value> = None;
5856 for value in values.into_iter().filter(|v| !v.is_empty()) {
5857 let replace = match &result {
5858 None => true,
5859 Some(current) if func == AggFunc::Min => value < *current,
5860 Some(current) => value > *current,
5861 };
5862 if replace {
5863 result = Some(value);
5864 }
5865 }
5866 result.unwrap_or(Value::Empty)
5867 }
5868 };
5869 Ok(QueryResult::Scalar(value))
5870}
5871
5872const SYMMETRIC_RID_SET_ENTRY_BYTES: usize =
5873 std::mem::size_of::<RowId>() + 2 * std::mem::size_of::<usize>();
5874
5875pub(super) fn aggregate_rows_with_provenance(
5876 func: AggFunc,
5877 argument: Option<&Expr>,
5878 input: &ProvenanceRows,
5879 provenance_alias: &str,
5880 memory_limit: usize,
5881) -> Result<QueryResult, QueryError> {
5882 if matches!(func, AggFunc::Min | AggFunc::Max | AggFunc::CountDistinct) {
5883 return aggregate_rows(func, argument, &input.columns, &input.rows);
5884 }
5885 let argument = argument.ok_or_else(|| {
5886 QueryError::Execution(
5887 "symmetric aggregate requires a source-valued argument; use raw".to_string(),
5888 )
5889 })?;
5890 let source_index = input.source_index(provenance_alias).ok_or_else(|| {
5891 QueryError::Execution(format!(
5892 "symmetric aggregate source alias '{provenance_alias}' is not present in its input"
5893 ))
5894 })?;
5895 let mut seen = HashSet::new();
5896 let mut int_sum = 0_i64;
5897 let mut float_sum = 0.0_f64;
5898 let mut saw_float = false;
5899 let mut count = 0_u64;
5900 let mut cancel = CancelCheck::new();
5901 for (row, row_provenance) in input.rows.iter().zip(&input.provenance) {
5902 cancel.tick()?;
5903 let value = eval_expr(argument, row, &input.columns);
5904 if value.is_empty() {
5905 continue;
5906 }
5907 let Some(rid) = row_provenance[source_index] else {
5908 continue;
5909 };
5910 if !seen.insert(rid) {
5911 continue;
5912 }
5913 mem_budget::charge(SYMMETRIC_RID_SET_ENTRY_BYTES, memory_limit)?;
5914 match func {
5915 AggFunc::Count => count += 1,
5916 AggFunc::Sum | AggFunc::Avg => match value {
5917 Value::Int(value) => {
5918 int_sum += value;
5919 count += 1;
5920 }
5921 Value::Float(value) => {
5922 float_sum += value;
5923 saw_float = true;
5924 count += 1;
5925 }
5926 _ => {}
5927 },
5928 AggFunc::CountDistinct | AggFunc::Min | AggFunc::Max => unreachable!(),
5929 }
5930 }
5931 let value = match func {
5932 AggFunc::Count => Value::Int(count as i64),
5933 AggFunc::Sum if saw_float => Value::Float(float_sum + int_sum as f64),
5934 AggFunc::Sum => Value::Int(int_sum),
5935 AggFunc::Avg if count == 0 => Value::Empty,
5936 AggFunc::Avg => Value::Float((float_sum + int_sum as f64) / count as f64),
5937 AggFunc::CountDistinct | AggFunc::Min | AggFunc::Max => unreachable!(),
5938 };
5939 Ok(QueryResult::Scalar(value))
5940}
5941
5942pub(super) struct GroupAggregateContext<'a> {
5944 pub(super) columns: &'a [String],
5945 pub(super) all_rows: &'a [Vec<Value>],
5946 pub(super) row_indices: &'a [usize],
5947 pub(super) source_index: Option<usize>,
5948 pub(super) provenance: Option<(&'a [Vec<Option<RowId>>], usize)>,
5949}
5950
5951pub(super) fn compute_group_aggregate(
5952 func: AggFunc,
5953 argument: &Expr,
5954 direct_index: Option<usize>,
5955 context: GroupAggregateContext<'_>,
5956) -> Result<Value, QueryError> {
5957 let GroupAggregateContext {
5958 columns,
5959 all_rows,
5960 row_indices,
5961 source_index,
5962 provenance,
5963 } = context;
5964 let count_all = matches!(argument, Expr::Field(name) if name == "*");
5965 let value_at = |ri: usize| match direct_index {
5966 Some(index) => all_rows[ri][index].clone(),
5967 None => eval_expr(argument, &all_rows[ri], columns),
5968 };
5969 let mut cancel = CancelCheck::new();
5970 let mut seen_rids = HashSet::new();
5971 match func {
5972 AggFunc::Count => {
5973 if count_all {
5974 return Ok(Value::Int(row_indices.len() as i64));
5976 }
5977 let mut count = 0usize;
5978 for &ri in row_indices {
5979 cancel.tick()?;
5980 let value = value_at(ri);
5981 if !value.is_empty()
5982 && accept_symmetric_contribution(ri, source_index, provenance, &mut seen_rids)?
5983 {
5984 count += 1;
5985 }
5986 }
5987 Ok(Value::Int(count as i64))
5988 }
5989 AggFunc::CountDistinct => {
5990 let mut seen = std::collections::HashSet::new();
5991 for &ri in row_indices {
5992 cancel.tick()?;
5993 let v = value_at(ri);
5994 if !v.is_empty() {
5995 seen.insert(v);
5996 }
5997 }
5998 Ok(Value::Int(seen.len() as i64))
5999 }
6000 AggFunc::Sum => {
6001 let mut int_sum: i64 = 0;
6006 let mut float_sum: f64 = 0.0;
6007 let mut saw_float = false;
6008 for &ri in row_indices {
6009 cancel.tick()?;
6010 let value = value_at(ri);
6011 if value.is_empty()
6012 || !accept_symmetric_contribution(ri, source_index, provenance, &mut seen_rids)?
6013 {
6014 continue;
6015 }
6016 match value {
6017 Value::Int(v) => int_sum += v,
6018 Value::Float(v) => {
6019 float_sum += v;
6020 saw_float = true;
6021 }
6022 _ => {}
6023 }
6024 }
6025 if saw_float {
6026 Ok(Value::Float(float_sum + int_sum as f64))
6027 } else {
6028 Ok(Value::Int(int_sum))
6029 }
6030 }
6031 AggFunc::Avg => {
6032 let mut sum = 0.0f64;
6033 let mut count = 0usize;
6034 for &ri in row_indices {
6035 cancel.tick()?;
6036 let value = value_at(ri);
6037 if value.is_empty()
6038 || !accept_symmetric_contribution(ri, source_index, provenance, &mut seen_rids)?
6039 {
6040 continue;
6041 }
6042 match value {
6043 Value::Int(v) => {
6044 sum += v as f64;
6045 count += 1;
6046 }
6047 Value::Float(v) => {
6048 sum += v;
6049 count += 1;
6050 }
6051 _ => {}
6052 }
6053 }
6054 if count == 0 {
6055 Ok(Value::Empty)
6056 } else {
6057 Ok(Value::Float(sum / count as f64))
6058 }
6059 }
6060 AggFunc::Min | AggFunc::Max => {
6061 let mut result: Option<Value> = None;
6062 for &ri in row_indices {
6063 cancel.tick()?;
6064 let value = value_at(ri);
6065 if value.is_empty() {
6066 continue;
6067 }
6068 let replace = match &result {
6069 None => true,
6070 Some(current) if func == AggFunc::Min => value < *current,
6071 Some(current) => value > *current,
6072 };
6073 if replace {
6074 result = Some(value);
6075 }
6076 }
6077 Ok(result.unwrap_or(Value::Empty))
6078 }
6079 }
6080}
6081
6082fn accept_symmetric_contribution(
6083 row_index: usize,
6084 source_index: Option<usize>,
6085 provenance: Option<(&[Vec<Option<RowId>>], usize)>,
6086 seen: &mut HashSet<RowId>,
6087) -> Result<bool, QueryError> {
6088 let Some(source_index) = source_index else {
6089 return Ok(true);
6090 };
6091 let Some((provenance, memory_limit)) = provenance else {
6092 return Err(QueryError::Execution(
6093 "symmetric aggregate provenance is unavailable; use raw".to_string(),
6094 ));
6095 };
6096 let Some(rid) = provenance[row_index][source_index] else {
6097 return Ok(false);
6098 };
6099 if !seen.insert(rid) {
6100 return Ok(false);
6101 }
6102 mem_budget::charge(SYMMETRIC_RID_SET_ENTRY_BYTES, memory_limit)?;
6103 Ok(true)
6104}
6105
6106struct HashJoinSpec<'a> {
6107 left_key_idx: usize,
6108 right_key_idx: usize,
6109 residuals: Vec<&'a Expr>,
6110}
6111
6112struct MaterializedJoinInputs {
6113 left_columns: Vec<String>,
6114 left_rows: Vec<Vec<Value>>,
6115 right_columns: Vec<String>,
6116 right_rows: Vec<Vec<Value>>,
6117}
6118
6119fn flatten_conjunctions<'a>(expr: &'a Expr, out: &mut Vec<&'a Expr>) {
6120 match expr {
6121 Expr::BinaryOp(left, BinOp::And, right) => {
6122 flatten_conjunctions(left, out);
6123 flatten_conjunctions(right, out);
6124 }
6125 _ => out.push(expr),
6126 }
6127}
6128
6129fn try_extract_hash_join<'a>(
6133 pred: &'a Expr,
6134 left_columns: &[String],
6135 right_columns: &[String],
6136) -> Option<HashJoinSpec<'a>> {
6137 let mut conjuncts = Vec::new();
6138 flatten_conjunctions(pred, &mut conjuncts);
6139 for (key_position, conjunct) in conjuncts.iter().enumerate() {
6140 let Some((left_key_idx, right_key_idx)) =
6141 try_extract_equi_join_keys(conjunct, left_columns, right_columns)
6142 else {
6143 continue;
6144 };
6145 let residuals = conjuncts
6146 .iter()
6147 .enumerate()
6148 .filter_map(|(position, residual)| (position != key_position).then_some(*residual))
6149 .collect();
6150 return Some(HashJoinSpec {
6151 left_key_idx,
6152 right_key_idx,
6153 residuals,
6154 });
6155 }
6156 None
6157}
6158
6159pub(super) fn try_extract_equi_join_keys(
6161 pred: &Expr,
6162 left_columns: &[String],
6163 right_columns: &[String],
6164) -> Option<(usize, usize)> {
6165 let (lhs, op, rhs) = match pred {
6166 Expr::BinaryOp(l, op, r) => (l.as_ref(), *op, r.as_ref()),
6167 _ => return None,
6168 };
6169 if op != BinOp::Eq {
6170 return None;
6171 }
6172 if let (Some(li), Some(ri)) = (
6174 resolve_side_column(lhs, left_columns),
6175 resolve_side_column(rhs, right_columns),
6176 ) {
6177 return Some((li, ri));
6178 }
6179 if let (Some(li), Some(ri)) = (
6182 resolve_side_column(rhs, left_columns),
6183 resolve_side_column(lhs, right_columns),
6184 ) {
6185 return Some((li, ri));
6186 }
6187 None
6188}
6189
6190fn resolve_side_column(expr: &Expr, columns: &[String]) -> Option<usize> {
6191 match expr {
6192 Expr::QualifiedField { qualifier, field } => {
6193 let q = qualifier.as_bytes();
6198 let f = field.as_bytes();
6199 columns.iter().position(|c| {
6200 let b = c.as_bytes();
6201 b.len() == q.len() + 1 + f.len()
6202 && b[..q.len()] == *q
6203 && b[q.len()] == b'.'
6204 && b[q.len() + 1..] == *f
6205 })
6206 }
6207 Expr::Field(name) => columns.iter().position(|c| c == name),
6208 _ => None,
6209 }
6210}
6211
6212fn hash_join(
6223 inputs: MaterializedJoinInputs,
6224 left_key_idx: usize,
6225 right_key_idx: usize,
6226 kind: JoinKind,
6227 residuals: &[&Expr],
6228) -> Result<QueryResult, QueryError> {
6229 use rustc_hash::FxHashMap;
6230
6231 let MaterializedJoinInputs {
6232 left_columns,
6233 left_rows,
6234 right_columns,
6235 right_rows,
6236 } = inputs;
6237
6238 let n_left = left_columns.len();
6239 let n_right = right_columns.len();
6240 let mut columns = Vec::with_capacity(n_left + n_right);
6241 columns.extend(left_columns);
6242 columns.extend(right_columns);
6243
6244 let mut cancel = CancelCheck::new();
6247
6248 let mut build: FxHashMap<Value, Vec<usize>> =
6251 FxHashMap::with_capacity_and_hasher(right_rows.len(), Default::default());
6252 for (i, row) in right_rows.iter().enumerate() {
6253 cancel.tick()?;
6254 build.entry(row[right_key_idx].clone()).or_default().push(i);
6258 }
6259
6260 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
6263
6264 crate::cancel::check()?;
6265 for left_row in &left_rows {
6266 cancel.tick()?;
6267 let key = &left_row[left_key_idx];
6268 let candidates = build.get(key);
6269 let mut matched = false;
6270 match candidates {
6271 Some(matches) if !matches.is_empty() => {
6272 for &ri in matches {
6273 cancel.tick()?;
6274 let right_row = &right_rows[ri];
6275 let mut combined = Vec::with_capacity(n_left + n_right);
6276 combined.extend_from_slice(left_row);
6277 combined.extend_from_slice(right_row);
6278 if residuals
6279 .iter()
6280 .all(|residual| eval_predicate(residual, &combined, &columns))
6281 {
6282 rows.push(combined);
6283 check_join_limit(rows.len())?;
6284 matched = true;
6285 }
6286 }
6287 }
6288 _ => {}
6289 }
6290 if !matched && matches!(kind, JoinKind::LeftOuter) {
6291 let mut row = Vec::with_capacity(n_left + n_right);
6292 row.extend_from_slice(left_row);
6293 row.resize(n_left + n_right, Value::Empty);
6294 rows.push(row);
6295 check_join_limit(rows.len())?;
6296 }
6297 }
6298
6299 Ok(QueryResult::Rows { columns, rows })
6300}
6301
6302#[inline]
6303pub(super) fn check_nested_loop_pair_limit(
6304 left_rows: usize,
6305 right_rows: usize,
6306 pair_limit: usize,
6307) -> Result<usize, QueryError> {
6308 let candidate_pairs =
6309 left_rows
6310 .checked_mul(right_rows)
6311 .ok_or(QueryError::NestedLoopPairLimitExceeded {
6312 left_rows,
6313 right_rows,
6314 limit: pair_limit,
6315 })?;
6316 if candidate_pairs > pair_limit {
6317 return Err(QueryError::NestedLoopPairLimitExceeded {
6318 left_rows,
6319 right_rows,
6320 limit: pair_limit,
6321 });
6322 }
6323 Ok(candidate_pairs)
6324}
6325
6326pub(super) fn execute_materialized_join(
6330 left_columns: Vec<String>,
6331 left_rows: Vec<Vec<Value>>,
6332 right_columns: Vec<String>,
6333 right_rows: Vec<Vec<Value>>,
6334 on: Option<&Expr>,
6335 kind: JoinKind,
6336 pair_limit: usize,
6337) -> Result<QueryResult, QueryError> {
6338 crate::cancel::check()?;
6339 if !matches!(kind, JoinKind::Cross) {
6340 if let Some(pred) = on {
6341 if let Some(spec) = try_extract_hash_join(pred, &left_columns, &right_columns) {
6342 return hash_join(
6343 MaterializedJoinInputs {
6344 left_columns,
6345 left_rows,
6346 right_columns,
6347 right_rows,
6348 },
6349 spec.left_key_idx,
6350 spec.right_key_idx,
6351 kind,
6352 &spec.residuals,
6353 );
6354 }
6355 }
6356 }
6357
6358 check_nested_loop_pair_limit(left_rows.len(), right_rows.len(), pair_limit)?;
6359 let n_left = left_columns.len();
6360 let n_right = right_columns.len();
6361 let mut columns = Vec::with_capacity(n_left + n_right);
6362 columns.extend(left_columns);
6363 columns.extend(right_columns);
6364
6365 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
6366 let mut combined: Vec<Value> = Vec::with_capacity(n_left + n_right);
6367 let mut cancel = CancelCheck::new();
6368 for left_row in &left_rows {
6369 let mut matched = false;
6370 for right_row in &right_rows {
6371 cancel.tick()?;
6372 combined.clear();
6373 combined.extend_from_slice(left_row);
6374 combined.extend_from_slice(right_row);
6375 let keep = match kind {
6376 JoinKind::Cross => true,
6377 JoinKind::Inner | JoinKind::LeftOuter => {
6378 on.is_none_or(|pred| eval_predicate(pred, &combined, &columns))
6379 }
6380 JoinKind::RightOuter => {
6381 unreachable!("planner rewrites RightOuter to LeftOuter")
6382 }
6383 };
6384 if keep {
6385 rows.push(combined.clone());
6386 check_join_limit(rows.len())?;
6387 matched = true;
6388 }
6389 }
6390 if !matched && matches!(kind, JoinKind::LeftOuter) {
6391 let mut row = Vec::with_capacity(n_left + n_right);
6392 row.extend_from_slice(left_row);
6393 row.resize(n_left + n_right, Value::Empty);
6394 rows.push(row);
6395 check_join_limit(rows.len())?;
6396 }
6397 }
6398 Ok(QueryResult::Rows { columns, rows })
6399}
6400
6401fn execute_provenance_join(
6402 left: ProvenanceRows,
6403 right: ProvenanceRows,
6404 on: Option<&Expr>,
6405 kind: JoinKind,
6406 pair_limit: usize,
6407) -> Result<ProvenanceRows, QueryError> {
6408 let left_width = left.columns.len();
6409 let right_width = right.columns.len();
6410 let right_source_count = right.source_aliases.len();
6411 let mut columns = left.columns.clone();
6412 columns.extend(right.columns.clone());
6413 let mut source_aliases = left.source_aliases.clone();
6414 source_aliases.extend(right.source_aliases.clone());
6415 let mut rows = Vec::new();
6416 let mut provenance = Vec::new();
6417 let mut cancel = CancelCheck::new();
6418
6419 if !matches!(kind, JoinKind::Cross) {
6420 if let Some(predicate) = on {
6421 if let Some(spec) = try_extract_hash_join(predicate, &left.columns, &right.columns) {
6422 let mut build: rustc_hash::FxHashMap<Value, Vec<usize>> =
6423 rustc_hash::FxHashMap::default();
6424 for (index, row) in right.rows.iter().enumerate() {
6425 cancel.tick()?;
6426 let key = &row[spec.right_key_idx];
6427 build.entry(key.clone()).or_default().push(index);
6428 }
6429 for (left_index, left_row) in left.rows.iter().enumerate() {
6430 cancel.tick()?;
6431 let key = &left_row[spec.left_key_idx];
6432 let candidates = build.get(key);
6433 let mut matched = false;
6434 if let Some(candidates) = candidates {
6435 for &right_index in candidates {
6436 cancel.tick()?;
6437 let mut row = Vec::with_capacity(left_width + right_width);
6438 row.extend_from_slice(left_row);
6439 row.extend_from_slice(&right.rows[right_index]);
6440 if spec
6441 .residuals
6442 .iter()
6443 .all(|residual| eval_predicate(residual, &row, &columns))
6444 {
6445 let mut row_provenance = left.provenance[left_index].clone();
6446 row_provenance.extend_from_slice(&right.provenance[right_index]);
6447 rows.push(row);
6448 provenance.push(row_provenance);
6449 check_join_limit(rows.len())?;
6450 matched = true;
6451 }
6452 }
6453 }
6454 if !matched && matches!(kind, JoinKind::LeftOuter) {
6455 let mut row = left_row.clone();
6456 row.resize(left_width + right_width, Value::Empty);
6457 let mut row_provenance = left.provenance[left_index].clone();
6458 row_provenance.extend(std::iter::repeat_n(None, right_source_count));
6459 rows.push(row);
6460 provenance.push(row_provenance);
6461 check_join_limit(rows.len())?;
6462 }
6463 }
6464 return Ok(ProvenanceRows {
6465 columns,
6466 rows,
6467 source_aliases,
6468 provenance,
6469 });
6470 }
6471 }
6472 }
6473
6474 check_nested_loop_pair_limit(left.rows.len(), right.rows.len(), pair_limit)?;
6475 for (left_index, left_row) in left.rows.iter().enumerate() {
6476 let mut matched = false;
6477 for (right_index, right_row) in right.rows.iter().enumerate() {
6478 cancel.tick()?;
6479 let mut row = Vec::with_capacity(left_width + right_width);
6480 row.extend_from_slice(left_row);
6481 row.extend_from_slice(right_row);
6482 let keep = match kind {
6483 JoinKind::Cross => true,
6484 JoinKind::Inner | JoinKind::LeftOuter => {
6485 on.is_none_or(|predicate| eval_predicate(predicate, &row, &columns))
6486 }
6487 JoinKind::RightOuter => {
6488 unreachable!("planner rewrites RightOuter to LeftOuter")
6489 }
6490 };
6491 if keep {
6492 let mut row_provenance = left.provenance[left_index].clone();
6493 row_provenance.extend_from_slice(&right.provenance[right_index]);
6494 rows.push(row);
6495 provenance.push(row_provenance);
6496 check_join_limit(rows.len())?;
6497 matched = true;
6498 }
6499 }
6500 if !matched && matches!(kind, JoinKind::LeftOuter) {
6501 let mut row = left_row.clone();
6502 row.resize(left_width + right_width, Value::Empty);
6503 let mut row_provenance = left.provenance[left_index].clone();
6504 row_provenance.extend(std::iter::repeat_n(None, right_source_count));
6505 rows.push(row);
6506 provenance.push(row_provenance);
6507 check_join_limit(rows.len())?;
6508 }
6509 }
6510 Ok(ProvenanceRows {
6511 columns,
6512 rows,
6513 source_aliases,
6514 provenance,
6515 })
6516}
6517
6518fn flatten_and<'a>(expr: &'a Expr, out: &mut Vec<&'a Expr>) {
6533 match expr {
6534 Expr::BinaryOp(lhs, BinOp::And, rhs) => {
6535 flatten_and(lhs, out);
6536 flatten_and(rhs, out);
6537 }
6538 other => out.push(other),
6539 }
6540}
6541
6542fn eq_candidate_tier(catalog: &Catalog, scan: &PlanNode) -> Option<u8> {
6546 match scan {
6547 PlanNode::IndexScan { table, column, .. } => match catalog.is_index_unique(table, column) {
6548 Some(true) => Some(0),
6549 Some(false) => Some(1),
6550 None => None,
6551 },
6552 PlanNode::ExprIndexScan { table, path, .. } => {
6553 resolve_expression_index(catalog, table, path).map(|meta| u8::from(!meta.unique))
6554 }
6555 _ => None,
6556 }
6557}
6558
6559fn range_candidate_resolves(catalog: &Catalog, scan: &PlanNode) -> bool {
6561 match scan {
6562 PlanNode::RangeScan { table, column, .. } => catalog.has_index(table, column),
6563 PlanNode::ExprRangeScan { table, path, .. } => {
6564 resolve_expression_index(catalog, table, path).is_some()
6565 }
6566 _ => false,
6567 }
6568}
6569
6570fn column_type(catalog: &Catalog, table: &str, column: &str) -> Option<TypeId> {
6572 catalog
6573 .schema(table)?
6574 .find_column(column)
6575 .map(|col| col.type_id)
6576}
6577
6578fn coerce_column_index_key(col_type: TypeId, key: &Expr) -> Option<Expr> {
6592 match (key, col_type) {
6593 (Expr::Literal(Literal::Int(_)), TypeId::Int | TypeId::DateTime) => Some(key.clone()),
6597 (Expr::Literal(Literal::Float(_)), TypeId::Float) => Some(key.clone()),
6598 (Expr::Literal(Literal::String(_)), TypeId::Str) => Some(key.clone()),
6599 (Expr::Literal(Literal::Bool(_)), TypeId::Bool) => Some(key.clone()),
6600 (Expr::Literal(Literal::Int(v)), TypeId::Float) => {
6603 Some(Expr::Literal(Literal::Float(*v as f64)))
6604 }
6605 _ => None,
6608 }
6609}
6610
6611fn coerce_column_index_bound(
6615 col_type: TypeId,
6616 bound: Option<(Expr, bool)>,
6617) -> Option<Option<(Expr, bool)>> {
6618 match bound {
6619 None => Some(None),
6620 Some((expr, inclusive)) => {
6621 coerce_column_index_key(col_type, &expr).map(|expr| Some((expr, inclusive)))
6622 }
6623 }
6624}
6625
6626fn coerce_candidate_keys(catalog: &Catalog, scan: PlanNode) -> Option<PlanNode> {
6633 match scan {
6634 PlanNode::IndexScan { table, column, key } => {
6635 let col_type = column_type(catalog, &table, &column)?;
6636 let key = coerce_column_index_key(col_type, &key)?;
6637 Some(PlanNode::IndexScan { table, column, key })
6638 }
6639 PlanNode::RangeScan {
6640 table,
6641 column,
6642 start,
6643 end,
6644 } => {
6645 let col_type = column_type(catalog, &table, &column)?;
6646 let start = coerce_column_index_bound(col_type, start)?;
6647 let end = coerce_column_index_bound(col_type, end)?;
6648 Some(PlanNode::RangeScan {
6649 table,
6650 column,
6651 start,
6652 end,
6653 })
6654 }
6655 other => Some(other),
6656 }
6657}
6658
6659struct ConjunctionCandidate {
6662 plan: PlanNode,
6663 consumed: Vec<usize>,
6664 tier: u8,
6665}
6666
6667fn lower_conjunction_scan(catalog: &Catalog, table: &str, predicate: &Expr) -> Option<PlanNode> {
6679 let mut conjuncts: Vec<&Expr> = Vec::new();
6680 flatten_and(predicate, &mut conjuncts);
6681 if conjuncts.len() < 2 {
6682 return None;
6683 }
6684
6685 let mut candidates: Vec<ConjunctionCandidate> = Vec::new();
6686
6687 for (i, conjunct) in conjuncts.iter().enumerate() {
6689 if let Some(scan) = try_extract_eq_index_key(table, conjunct) {
6690 if let Some(scan) = coerce_candidate_keys(catalog, scan) {
6694 if let Some(tier) = eq_candidate_tier(catalog, &scan) {
6695 candidates.push(ConjunctionCandidate {
6696 plan: scan,
6697 consumed: vec![i],
6698 tier,
6699 });
6700 }
6701 }
6702 }
6703 }
6704
6705 let bounds: Vec<(usize, RangeBound)> = conjuncts
6710 .iter()
6711 .enumerate()
6712 .filter_map(|(i, conjunct)| extract_single_bound(conjunct).map(|bound| (i, bound)))
6713 .collect();
6714 let mut seen_targets: Vec<RangeTarget> = Vec::new();
6715 for (_, (target, _, _)) in &bounds {
6716 if !seen_targets.contains(target) {
6717 seen_targets.push(target.clone());
6718 }
6719 }
6720 for target in seen_targets {
6721 let mut lower: Option<(Expr, bool)> = None;
6722 let mut lower_idx: Option<usize> = None;
6723 let mut upper: Option<(Expr, bool)> = None;
6724 let mut upper_idx: Option<usize> = None;
6725 for (i, (candidate_target, start, end)) in &bounds {
6726 if *candidate_target != target {
6727 continue;
6728 }
6729 if lower.is_none() {
6730 if let Some(bound) = start.clone() {
6731 lower = Some(bound);
6732 lower_idx = Some(*i);
6733 }
6734 }
6735 if upper.is_none() {
6736 if let Some(bound) = end.clone() {
6737 upper = Some(bound);
6738 upper_idx = Some(*i);
6739 }
6740 }
6741 }
6742 if lower.is_none() && upper.is_none() {
6743 continue;
6744 }
6745 let scan = range_scan_for_target(table, target, lower, upper);
6746 let Some(scan) = coerce_candidate_keys(catalog, scan) else {
6750 continue;
6751 };
6752 if !range_candidate_resolves(catalog, &scan) {
6753 continue;
6754 }
6755 let mut consumed: Vec<usize> = Vec::new();
6756 if let Some(i) = lower_idx {
6757 consumed.push(i);
6758 }
6759 if let Some(i) = upper_idx {
6760 if !consumed.contains(&i) {
6761 consumed.push(i);
6762 }
6763 }
6764 candidates.push(ConjunctionCandidate {
6765 plan: scan,
6766 consumed,
6767 tier: 2,
6768 });
6769 }
6770
6771 let winner = candidates
6774 .into_iter()
6775 .min_by_key(|candidate| candidate.tier)?;
6776
6777 let mut residual: Vec<Expr> = Vec::new();
6778 for (i, conjunct) in conjuncts.iter().enumerate() {
6779 if !winner.consumed.contains(&i) {
6780 residual.push((*conjunct).clone());
6781 }
6782 }
6783 if residual.is_empty() {
6784 return Some(winner.plan);
6785 }
6786 let residual_expr = residual
6787 .into_iter()
6788 .reduce(|acc, next| Expr::BinaryOp(Box::new(acc), BinOp::And, Box::new(next)))
6789 .expect("residual is non-empty");
6790 Some(PlanNode::Filter {
6791 input: Box::new(winner.plan),
6792 predicate: residual_expr,
6793 })
6794}
6795
6796pub(super) fn lower_unindexed_scans(catalog: &Catalog, plan: &PlanNode) -> PlanNode {
6798 match plan {
6799 PlanNode::ExprIndexScan { table, path, .. }
6800 | PlanNode::ExprRangeScan { table, path, .. }
6801 | PlanNode::OrderedExprIndexScan { table, path, .. } => {
6802 if resolve_expression_index(catalog, table, path).is_some() {
6803 plan.clone()
6804 } else {
6805 expression_index_fallback(plan)
6806 .expect("expression-index branch always has a fallback")
6807 }
6808 }
6809 PlanNode::RangeScan {
6810 table,
6811 column,
6812 start,
6813 end,
6814 } => {
6815 if let Some(tbl) = catalog.get_table(table) {
6816 if tbl.has_index(column) {
6822 return plan.clone();
6823 }
6824 }
6825 let pred = synthesize_range_predicate(column, start, end);
6826 PlanNode::Filter {
6827 input: Box::new(PlanNode::SeqScan {
6828 table: table.clone(),
6829 }),
6830 predicate: pred,
6831 }
6832 }
6833 PlanNode::Filter { input, predicate } => {
6834 if let PlanNode::SeqScan { table } = input.as_ref() {
6839 if let Some(lowered) = lower_conjunction_scan(catalog, table, predicate) {
6840 return lowered;
6841 }
6842 }
6843 PlanNode::Filter {
6844 input: Box::new(lower_unindexed_scans(catalog, input)),
6845 predicate: predicate.clone(),
6846 }
6847 }
6848 PlanNode::Project { input, fields } => PlanNode::Project {
6849 input: Box::new(lower_unindexed_scans(catalog, input)),
6850 fields: fields.clone(),
6851 },
6852 PlanNode::Sort { input, keys } => PlanNode::Sort {
6853 input: Box::new(lower_unindexed_scans(catalog, input)),
6854 keys: keys.clone(),
6855 },
6856 PlanNode::Limit { input, count } => PlanNode::Limit {
6857 input: Box::new(lower_unindexed_scans(catalog, input)),
6858 count: count.clone(),
6859 },
6860 PlanNode::Offset { input, count } => PlanNode::Offset {
6861 input: Box::new(lower_unindexed_scans(catalog, input)),
6862 count: count.clone(),
6863 },
6864 PlanNode::Aggregate {
6865 input,
6866 function,
6867 argument,
6868 mode,
6869 provenance_alias,
6870 } => PlanNode::Aggregate {
6871 input: Box::new(lower_unindexed_scans(catalog, input)),
6872 function: *function,
6873 argument: argument.clone(),
6874 mode: *mode,
6875 provenance_alias: provenance_alias.clone(),
6876 },
6877 PlanNode::Distinct { input } => PlanNode::Distinct {
6878 input: Box::new(lower_unindexed_scans(catalog, input)),
6879 },
6880 PlanNode::GroupBy {
6881 input,
6882 keys,
6883 aggregates,
6884 having,
6885 } => PlanNode::GroupBy {
6886 input: Box::new(lower_unindexed_scans(catalog, input)),
6887 keys: keys.clone(),
6888 aggregates: aggregates.clone(),
6889 having: having.clone(),
6890 },
6891 PlanNode::Update {
6892 input,
6893 table,
6894 assignments,
6895 returning,
6896 } => PlanNode::Update {
6897 input: Box::new(lower_unindexed_scans(catalog, input)),
6898 table: table.clone(),
6899 assignments: assignments.clone(),
6900 returning: *returning,
6901 },
6902 PlanNode::Delete {
6903 input,
6904 table,
6905 returning,
6906 } => PlanNode::Delete {
6907 input: Box::new(lower_unindexed_scans(catalog, input)),
6908 table: table.clone(),
6909 returning: *returning,
6910 },
6911 PlanNode::Window { input, windows } => PlanNode::Window {
6912 input: Box::new(lower_unindexed_scans(catalog, input)),
6913 windows: windows.clone(),
6914 },
6915 PlanNode::Union { left, right, all } => PlanNode::Union {
6916 left: Box::new(lower_unindexed_scans(catalog, left)),
6917 right: Box::new(lower_unindexed_scans(catalog, right)),
6918 all: *all,
6919 },
6920 PlanNode::Explain { input } => PlanNode::Explain {
6921 input: Box::new(lower_unindexed_scans(catalog, input)),
6922 },
6923 PlanNode::NestedLoopJoin {
6924 left,
6925 right,
6926 on,
6927 kind,
6928 } => PlanNode::NestedLoopJoin {
6929 left: Box::new(lower_unindexed_scans(catalog, left)),
6930 right: Box::new(lower_unindexed_scans(catalog, right)),
6931 on: on.clone(),
6932 kind: *kind,
6933 },
6934 PlanNode::IndexScan { table, column, key } => {
6935 if let Some(tbl) = catalog.get_table(table) {
6936 if tbl.has_index(column) {
6937 return plan.clone();
6938 }
6939 }
6940 PlanNode::Filter {
6941 input: Box::new(PlanNode::SeqScan {
6942 table: table.clone(),
6943 }),
6944 predicate: Expr::BinaryOp(
6945 Box::new(Expr::Field(column.clone())),
6946 BinOp::Eq,
6947 Box::new(key.clone()),
6948 ),
6949 }
6950 }
6951 _ => plan.clone(),
6953 }
6954}
6955
6956fn stored_json_path_expr(path: &powdb_storage::stored_json_path::StoredJsonPathV1) -> Expr {
6957 use powdb_storage::stored_json_path::StoredJsonPathSegmentV1;
6958
6959 Expr::JsonPath {
6960 base: Box::new(Expr::Field(path.column.clone())),
6961 segments: path
6962 .segments
6963 .iter()
6964 .map(|segment| match segment {
6965 StoredJsonPathSegmentV1::Key(key) => PathSeg::Key(key.clone()),
6966 StoredJsonPathSegmentV1::Index(index) => PathSeg::Index(*index),
6967 })
6968 .collect(),
6969 }
6970}
6971
6972fn synthesize_expr_range_predicate(
6973 path: &powdb_storage::stored_json_path::StoredJsonPathV1,
6974 start: &Option<(Expr, bool)>,
6975 end: &Option<(Expr, bool)>,
6976) -> Expr {
6977 let lower = start.as_ref().map(|(expr, inclusive)| {
6978 Expr::BinaryOp(
6979 Box::new(stored_json_path_expr(path)),
6980 if *inclusive { BinOp::Gte } else { BinOp::Gt },
6981 Box::new(expr.clone()),
6982 )
6983 });
6984 let upper = end.as_ref().map(|(expr, inclusive)| {
6985 Expr::BinaryOp(
6986 Box::new(stored_json_path_expr(path)),
6987 if *inclusive { BinOp::Lte } else { BinOp::Lt },
6988 Box::new(expr.clone()),
6989 )
6990 });
6991 match (lower, upper) {
6992 (Some(lower), Some(upper)) => Expr::BinaryOp(Box::new(lower), BinOp::And, Box::new(upper)),
6993 (Some(lower), None) => lower,
6994 (None, Some(upper)) => upper,
6995 (None, None) => Expr::Literal(Literal::Bool(true)),
6996 }
6997}
6998
6999pub(super) fn synthesize_range_predicate(
7001 column: &str,
7002 start: &Option<(Expr, bool)>,
7003 end: &Option<(Expr, bool)>,
7004) -> Expr {
7005 let lower = start.as_ref().map(|(expr, inclusive)| {
7006 let op = if *inclusive { BinOp::Gte } else { BinOp::Gt };
7007 Expr::BinaryOp(
7008 Box::new(Expr::Field(column.to_string())),
7009 op,
7010 Box::new(expr.clone()),
7011 )
7012 });
7013 let upper = end.as_ref().map(|(expr, inclusive)| {
7014 let op = if *inclusive { BinOp::Lte } else { BinOp::Lt };
7015 Expr::BinaryOp(
7016 Box::new(Expr::Field(column.to_string())),
7017 op,
7018 Box::new(expr.clone()),
7019 )
7020 });
7021 match (lower, upper) {
7022 (Some(l), Some(u)) => Expr::BinaryOp(Box::new(l), BinOp::And, Box::new(u)),
7023 (Some(l), None) => l,
7024 (None, Some(u)) => u,
7025 (None, None) => Expr::Literal(Literal::Bool(true)),
7026 }
7027}
7028
7029fn scan_table(scan: &PlanNode) -> Option<&str> {
7034 match scan {
7035 PlanNode::IndexScan { table, .. }
7036 | PlanNode::RangeScan { table, .. }
7037 | PlanNode::ExprIndexScan { table, .. }
7038 | PlanNode::ExprRangeScan { table, .. } => Some(table),
7039 _ => None,
7040 }
7041}
7042
7043pub(super) fn range_matches(
7044 val: &Value,
7045 start: &Option<Value>,
7046 start_inc: bool,
7047 end: &Option<Value>,
7048 end_inc: bool,
7049) -> bool {
7050 if let Some(ref s) = start {
7051 if start_inc {
7052 if val < s {
7053 return false;
7054 }
7055 } else if val <= s {
7056 return false;
7057 }
7058 }
7059 if let Some(ref e) = end {
7060 if end_inc {
7061 if val > e {
7062 return false;
7063 }
7064 } else if val >= e {
7065 return false;
7066 }
7067 }
7068 true
7069}
7070
7071fn collect_plan_qualifiers(plan: &PlanNode, qualifiers: &mut HashSet<String>) {
7072 match plan {
7073 PlanNode::SeqScan { table }
7074 | PlanNode::IndexScan { table, .. }
7075 | PlanNode::RangeScan { table, .. }
7076 | PlanNode::ExprIndexScan { table, .. }
7077 | PlanNode::ExprRangeScan { table, .. }
7078 | PlanNode::OrderedExprIndexScan { table, .. } => {
7079 qualifiers.insert(table.clone());
7080 }
7081 PlanNode::AliasScan { alias, .. } => {
7082 qualifiers.insert(alias.clone());
7083 }
7084 PlanNode::Filter { input, .. }
7085 | PlanNode::Project { input, .. }
7086 | PlanNode::Sort { input, .. }
7087 | PlanNode::Limit { input, .. }
7088 | PlanNode::Offset { input, .. }
7089 | PlanNode::Aggregate { input, .. }
7090 | PlanNode::Distinct { input }
7091 | PlanNode::GroupBy { input, .. }
7092 | PlanNode::Update { input, .. }
7093 | PlanNode::Delete { input, .. }
7094 | PlanNode::Window { input, .. }
7095 | PlanNode::Explain { input } => collect_plan_qualifiers(input, qualifiers),
7096 PlanNode::NestedLoopJoin { left, right, .. } | PlanNode::Union { left, right, .. } => {
7097 collect_plan_qualifiers(left, qualifiers);
7098 collect_plan_qualifiers(right, qualifiers);
7099 }
7100 _ => {}
7101 }
7102}
7103
7104fn qualified_ref(expr: &Expr) -> Option<&str> {
7105 match expr {
7106 Expr::QualifiedField { qualifier, .. } => Some(qualifier),
7107 _ => None,
7108 }
7109}
7110
7111fn explain_join_strategy(
7112 left: &PlanNode,
7113 right: &PlanNode,
7114 on: Option<&Expr>,
7115 kind: JoinKind,
7116) -> &'static str {
7117 if matches!(kind, JoinKind::Cross) {
7118 return "nested-loop-bounded";
7119 }
7120 let Some(predicate) = on else {
7121 return "nested-loop-bounded";
7122 };
7123 let mut conjunctions = Vec::new();
7124 flatten_conjunctions(predicate, &mut conjunctions);
7125 let mut left_qualifiers = HashSet::new();
7126 let mut right_qualifiers = HashSet::new();
7127 collect_plan_qualifiers(left, &mut left_qualifiers);
7128 collect_plan_qualifiers(right, &mut right_qualifiers);
7129
7130 let has_cross_side_equi = conjunctions.iter().any(|expr| {
7131 let Expr::BinaryOp(lhs, BinOp::Eq, rhs) = expr else {
7132 return false;
7133 };
7134 let (Some(lhs_q), Some(rhs_q)) = (qualified_ref(lhs), qualified_ref(rhs)) else {
7135 return false;
7136 };
7137 (left_qualifiers.contains(lhs_q) && right_qualifiers.contains(rhs_q))
7138 || (left_qualifiers.contains(rhs_q) && right_qualifiers.contains(lhs_q))
7139 });
7140 if has_cross_side_equi {
7141 if conjunctions.len() > 1 {
7142 "hash+residual"
7143 } else {
7144 "hash"
7145 }
7146 } else {
7147 "nested-loop-bounded"
7148 }
7149}
7150
7151pub(super) fn format_plan_tree(catalog: &Catalog, plan: &PlanNode, depth: usize) -> String {
7154 let indent = " ".repeat(depth);
7155 match plan {
7156 PlanNode::SeqScan { table } => format!("{indent}SeqScan table={table}"),
7157 PlanNode::AliasScan { table, alias } => {
7158 format!("{indent}AliasScan table={table} alias={alias}")
7159 }
7160 PlanNode::IndexScan { table, column, key } => {
7161 format!("{indent}IndexScan table={table} column={column} key={key:?}")
7162 }
7163 PlanNode::RangeScan {
7164 table,
7165 column,
7166 start,
7167 end,
7168 } => {
7169 let s = match start {
7170 Some((expr, inc)) => {
7171 let op = if *inc { ">=" } else { ">" };
7172 format!("{op}{expr:?}")
7173 }
7174 None => "unbounded".to_string(),
7175 };
7176 let e = match end {
7177 Some((expr, inc)) => {
7178 let op = if *inc { "<=" } else { "<" };
7179 format!("{op}{expr:?}")
7180 }
7181 None => "unbounded".to_string(),
7182 };
7183 format!("{indent}RangeScan table={table} column={column} [{s}, {e}]")
7184 }
7185 PlanNode::ExprIndexScan { table, path, key } => {
7186 let index_id = resolve_expression_index(catalog, table, path)
7187 .map(|metadata| metadata.index_id.to_string())
7188 .unwrap_or_else(|| "unresolved".to_string());
7189 format!(
7190 "{indent}ExprIndexScan table={table} path={} index_id={index_id} key={key:?}",
7191 path.canonical_text()
7192 )
7193 }
7194 PlanNode::ExprRangeScan {
7195 table,
7196 path,
7197 start,
7198 end,
7199 } => {
7200 let index_id = resolve_expression_index(catalog, table, path)
7201 .map(|metadata| metadata.index_id.to_string())
7202 .unwrap_or_else(|| "unresolved".to_string());
7203 format!(
7204 "{indent}ExprRangeScan table={table} path={} index_id={index_id} start={start:?} end={end:?}",
7205 path.canonical_text()
7206 )
7207 }
7208 PlanNode::OrderedExprIndexScan {
7209 table,
7210 path,
7211 descending,
7212 limit,
7213 offset,
7214 } => {
7215 let index_id = resolve_expression_index(catalog, table, path)
7216 .map(|metadata| metadata.index_id.to_string())
7217 .unwrap_or_else(|| "unresolved".to_string());
7218 format!(
7219 "{indent}OrderedExprIndexScan table={table} path={} index_id={index_id} descending={descending} limit={limit:?} offset={offset:?}",
7220 path.canonical_text()
7221 )
7222 }
7223 PlanNode::Filter { input, predicate } => {
7224 let child = format_plan_tree(catalog, input, depth + 1);
7225 format!("{indent}Filter predicate={predicate:?}\n{child}")
7226 }
7227 PlanNode::Project { input, fields } => {
7228 let names: Vec<String> = fields
7229 .iter()
7230 .map(|f| match &f.alias {
7231 Some(a) => format!("{a}: {:?}", f.expr),
7232 None => format!("{:?}", f.expr),
7233 })
7234 .collect();
7235 let child = format_plan_tree(catalog, input, depth + 1);
7236 format!("{indent}Project fields=[{}]\n{child}", names.join(", "))
7237 }
7238 PlanNode::Sort { input, keys } => {
7239 let ks: Vec<String> = keys
7240 .iter()
7241 .map(|k| {
7242 let expr = expression_output_name(&k.expr);
7243 if k.descending {
7244 format!("{expr} desc")
7245 } else {
7246 expr
7247 }
7248 })
7249 .collect();
7250 let child = format_plan_tree(catalog, input, depth + 1);
7251 format!("{indent}Sort keys=[{}]\n{child}", ks.join(", "))
7252 }
7253 PlanNode::Limit { input, count } => {
7254 let child = format_plan_tree(catalog, input, depth + 1);
7255 format!("{indent}Limit count={count:?}\n{child}")
7256 }
7257 PlanNode::Offset { input, count } => {
7258 let child = format_plan_tree(catalog, input, depth + 1);
7259 format!("{indent}Offset count={count:?}\n{child}")
7260 }
7261 PlanNode::Aggregate {
7262 input,
7263 function,
7264 argument,
7265 mode,
7266 provenance_alias: _,
7267 } => {
7268 let argument = argument
7269 .as_ref()
7270 .map(expression_output_name)
7271 .unwrap_or_else(|| "*".to_string());
7272 let child = format_plan_tree(catalog, input, depth + 1);
7273 format!("{indent}Aggregate fn={function:?} mode={mode:?} argument={argument}\n{child}")
7274 }
7275 PlanNode::NestedLoopJoin {
7276 left,
7277 right,
7278 on,
7279 kind,
7280 } => {
7281 let left_child = format_plan_tree(catalog, left, depth + 1);
7282 let right_child = format_plan_tree(catalog, right, depth + 1);
7283 let on_str = match on {
7284 Some(pred) => format!("{pred:?}"),
7285 None => "none".to_string(),
7286 };
7287 let strategy = explain_join_strategy(left, right, on.as_ref(), *kind);
7288 format!(
7289 "{indent}NestedLoopJoin kind={kind:?} strategy={strategy} on={on_str}\n{left_child}\n{right_child}"
7290 )
7291 }
7292 PlanNode::Distinct { input } => {
7293 let child = format_plan_tree(catalog, input, depth + 1);
7294 format!("{indent}Distinct\n{child}")
7295 }
7296 PlanNode::GroupBy {
7297 input,
7298 keys,
7299 aggregates,
7300 having,
7301 } => {
7302 let agg_strs: Vec<String> = aggregates
7303 .iter()
7304 .map(|a| {
7305 format!(
7306 "{:?}({}) mode={:?} as {}",
7307 a.function,
7308 expression_output_name(&a.argument),
7309 a.mode,
7310 a.output_name
7311 )
7312 })
7313 .collect();
7314 let having_str = match having {
7315 Some(h) => format!(" having={h:?}"),
7316 None => String::new(),
7317 };
7318 let key_strs: Vec<String> = keys.iter().map(|k| k.output_name()).collect();
7319 let child = format_plan_tree(catalog, input, depth + 1);
7320 format!(
7321 "{indent}GroupBy keys=[{}] aggs=[{}]{having_str}\n{child}",
7322 key_strs.join(", "),
7323 agg_strs.join(", "),
7324 )
7325 }
7326 PlanNode::Insert { table, rows, .. } => {
7327 let cols: Vec<&str> = rows
7328 .first()
7329 .map(|r| r.iter().map(|a| a.field.as_str()).collect())
7330 .unwrap_or_default();
7331 format!(
7332 "{indent}Insert table={table} rows={} cols=[{}]",
7333 rows.len(),
7334 cols.join(", ")
7335 )
7336 }
7337 PlanNode::Upsert {
7338 table,
7339 key_column,
7340 assignments,
7341 on_conflict,
7342 } => {
7343 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
7344 let conflict_cols: Vec<&str> = on_conflict.iter().map(|a| a.field.as_str()).collect();
7345 if conflict_cols.is_empty() {
7346 format!(
7347 "{indent}Upsert table={table} key={key_column} cols=[{}]",
7348 cols.join(", ")
7349 )
7350 } else {
7351 format!(
7352 "{indent}Upsert table={table} key={key_column} cols=[{}] on_conflict=[{}]",
7353 cols.join(", "),
7354 conflict_cols.join(", ")
7355 )
7356 }
7357 }
7358 PlanNode::Update {
7359 input,
7360 table,
7361 assignments,
7362 returning,
7363 } => {
7364 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
7365 let child = format_plan_tree(catalog, input, depth + 1);
7366 let ret = if *returning { " returning" } else { "" };
7367 format!(
7368 "{indent}Update table={table} set=[{}]{ret}\n{child}",
7369 cols.join(", ")
7370 )
7371 }
7372 PlanNode::Delete {
7373 input,
7374 table,
7375 returning,
7376 } => {
7377 let child = format_plan_tree(catalog, input, depth + 1);
7378 let ret = if *returning { " returning" } else { "" };
7379 format!("{indent}Delete table={table}{ret}\n{child}")
7380 }
7381 PlanNode::CreateTable { name, fields, .. } => {
7382 let fs: Vec<String> = fields
7383 .iter()
7384 .map(|f| {
7385 let mut mods = String::new();
7386 if f.required {
7387 mods.push_str(" required");
7388 }
7389 if f.unique {
7390 mods.push_str(" unique");
7391 }
7392 format!("{}: {}{mods}", f.name, f.type_name)
7393 })
7394 .collect();
7395 format!("{indent}CreateTable name={name} fields=[{}]", fs.join(", "))
7396 }
7397 PlanNode::AlterTable { table, action } => {
7398 format!("{indent}AlterTable table={table} action={action:?}")
7399 }
7400 PlanNode::DropTable { name, .. } => format!("{indent}DropTable name={name}"),
7401 PlanNode::CreateView { name, .. } => format!("{indent}CreateView name={name}"),
7402 PlanNode::RefreshView { name } => format!("{indent}RefreshView name={name}"),
7403 PlanNode::DropView { name, .. } => format!("{indent}DropView name={name}"),
7404 PlanNode::ListTypes => format!("{indent}ListTypes"),
7405 PlanNode::Describe { table } => format!("{indent}Describe table={table}"),
7406 PlanNode::Window { input, windows } => {
7407 let ws: Vec<String> = windows
7408 .iter()
7409 .map(|w| format!("{:?} as {}", w.function, w.output_name))
7410 .collect();
7411 let child = format_plan_tree(catalog, input, depth + 1);
7412 format!("{indent}Window fns=[{}]\n{child}", ws.join(", "))
7413 }
7414 PlanNode::Union { left, right, all } => {
7415 let kind = if *all { "UNION ALL" } else { "UNION" };
7416 let left_child = format_plan_tree(catalog, left, depth + 1);
7417 let right_child = format_plan_tree(catalog, right, depth + 1);
7418 format!("{indent}{kind}\n{left_child}\n{right_child}")
7419 }
7420 PlanNode::Explain { input } => {
7421 let child = format_plan_tree(catalog, input, depth + 1);
7422 format!("{indent}Explain\n{child}")
7423 }
7424 PlanNode::Begin => format!("{indent}Begin"),
7425 PlanNode::Commit => format!("{indent}Commit"),
7426 PlanNode::Rollback => format!("{indent}Rollback"),
7427 }
7428}