1use crate::ast::*;
4use crate::cancel::CancelCheck;
5use crate::plan::*;
6use crate::result::{QueryError, QueryResult};
7use powdb_storage::catalog::{Catalog, ExpressionIndexMeta, IndexOrderDirection};
8use powdb_storage::row::{decode_column, decode_row, patch_var_column_in_place, RowLayout};
9use powdb_storage::stored_json_path::StoredJsonPathV1;
10use powdb_storage::types::*;
11use std::cmp::Reverse;
12use std::collections::{BinaryHeap, HashSet};
13use std::ops::ControlFlow;
14
15use super::compiled::*;
16use super::eval::*;
17use super::row_body_base;
18use super::{check_join_limit, mem_budget, Engine, MAX_SORT_ROWS};
19use powdb_storage::view::ViewDef;
20
21const CANCELLABLE_SORT_RUN: usize = 2_048;
25
26pub(super) fn compare_order_values(
32 left: &Value,
33 right: &Value,
34 descending: bool,
35) -> std::cmp::Ordering {
36 use std::cmp::Ordering;
37
38 match (left, right) {
39 (Value::Empty, Value::Empty) => Ordering::Equal,
40 (Value::Empty, _) => Ordering::Greater,
41 (_, Value::Empty) => Ordering::Less,
42 _ if descending => left.cmp(right).reverse(),
43 _ => left.cmp(right),
44 }
45}
46
47pub(super) fn cooperative_stable_sort_by<T, F>(
56 values: &mut [T],
57 memory_limit: usize,
58 compare: F,
59) -> Result<(), QueryError>
60where
61 F: Fn(&T, &T) -> std::cmp::Ordering,
62{
63 crate::cancel::check()?;
64 let len = values.len();
65 if len < 2 {
66 return Ok(());
67 }
68
69 let scratch_bytes = len
70 .saturating_mul(std::mem::size_of::<usize>())
71 .saturating_mul(2);
72 mem_budget::charge(scratch_bytes, memory_limit)?;
73
74 let mut order: Vec<usize> = (0..len).collect();
75 let mut scratch = vec![0usize; len];
76
77 for run in order.chunks_mut(CANCELLABLE_SORT_RUN) {
78 crate::cancel::check()?;
79 run.sort_by(|&a, &b| compare(&values[a], &values[b]));
80 crate::cancel::check()?;
81 }
82
83 let mut cancel = CancelCheck::new();
84 let mut width = CANCELLABLE_SORT_RUN;
85 while width < len {
86 let step = width.saturating_mul(2);
87 let mut start = 0usize;
88 while start < len {
89 let mid = start.saturating_add(width).min(len);
90 let end = start.saturating_add(step).min(len);
91 let (mut left, mut right, mut out) = (start, mid, start);
92
93 while left < mid && right < end {
94 cancel.tick()?;
95 if compare(&values[order[left]], &values[order[right]])
96 != std::cmp::Ordering::Greater
97 {
98 scratch[out] = order[left];
99 left += 1;
100 } else {
101 scratch[out] = order[right];
102 right += 1;
103 }
104 out += 1;
105 }
106 while left < mid {
107 cancel.tick()?;
108 scratch[out] = order[left];
109 left += 1;
110 out += 1;
111 }
112 while right < end {
113 cancel.tick()?;
114 scratch[out] = order[right];
115 right += 1;
116 out += 1;
117 }
118 start = start.saturating_add(step);
119 }
120 std::mem::swap(&mut order, &mut scratch);
121 width = step;
122 }
123
124 for (new_position, &old_position) in order.iter().enumerate() {
127 cancel.tick()?;
128 scratch[old_position] = new_position;
129 }
130 drop(order);
131 for position in 0..len {
132 while scratch[position] != position {
133 cancel.tick()?;
134 let destination = scratch[position];
135 values.swap(position, destination);
136 scratch.swap(position, destination);
137 }
138 }
139 Ok(())
140}
141
142pub(super) fn for_each_row_raw_cancellable(
145 catalog: &Catalog,
146 table: &str,
147 mut f: impl FnMut(RowId, &[u8]),
148) -> Result<(), QueryError> {
149 if !crate::cancel::has_active_install() {
155 return catalog
156 .for_each_row_raw(table, f)
157 .map_err(|err| QueryError::StorageError(err.to_string()));
158 }
159
160 let mut cancel = CancelCheck::new();
161 let mut cancel_err: Option<QueryError> = None;
162 catalog
163 .try_for_each_row_raw(table, |rid, data| {
164 if let Err(err) = cancel.tick() {
165 cancel_err = Some(err);
166 return ControlFlow::Break(());
167 }
168 f(rid, data);
169 ControlFlow::Continue(())
170 })
171 .map_err(|err| QueryError::StorageError(err.to_string()))?;
172 match cancel_err {
173 Some(err) => Err(err),
174 None => Ok(()),
175 }
176}
177
178fn resolve_expression_index(
179 catalog: &Catalog,
180 table: &str,
181 path: &StoredJsonPathV1,
182) -> Option<ExpressionIndexMeta> {
183 catalog
184 .expression_index_metadata(table)?
185 .into_iter()
186 .find(|metadata| metadata.canonical_version == 1 && metadata.json_path == *path)
187}
188
189fn expression_index_fallback(plan: &PlanNode) -> Option<PlanNode> {
190 match plan {
191 PlanNode::ExprIndexScan { table, path, key } => Some(PlanNode::Filter {
192 input: Box::new(PlanNode::SeqScan {
193 table: table.clone(),
194 }),
195 predicate: Expr::BinaryOp(
196 Box::new(stored_json_path_expr(path)),
197 BinOp::Eq,
198 Box::new(key.clone()),
199 ),
200 }),
201 PlanNode::ExprRangeScan {
202 table,
203 path,
204 start,
205 end,
206 } => Some(PlanNode::Filter {
207 input: Box::new(PlanNode::SeqScan {
208 table: table.clone(),
209 }),
210 predicate: synthesize_expr_range_predicate(path, start, end),
211 }),
212 PlanNode::OrderedExprIndexScan {
213 table,
214 path,
215 descending,
216 limit,
217 offset,
218 } => {
219 let sorted = PlanNode::Sort {
220 input: Box::new(PlanNode::SeqScan {
221 table: table.clone(),
222 }),
223 keys: vec![SortKey {
224 expr: stored_json_path_expr(path),
225 descending: *descending,
226 }],
227 };
228 let sliced = match offset {
229 Some(count) => PlanNode::Offset {
230 input: Box::new(sorted),
231 count: count.clone(),
232 },
233 None => sorted,
234 };
235 Some(PlanNode::Limit {
236 input: Box::new(sliced),
237 count: limit.clone(),
238 })
239 }
240 _ => None,
241 }
242}
243
244#[derive(Debug)]
245pub(super) struct ProvenanceRows {
246 pub(super) columns: Vec<String>,
247 pub(super) rows: Vec<Vec<Value>>,
248 source_aliases: Vec<String>,
249 provenance: Vec<Vec<Option<RowId>>>,
250}
251
252impl ProvenanceRows {
253 fn source_index(&self, alias: &str) -> Option<usize> {
254 self.source_aliases
255 .iter()
256 .position(|source| source == alias)
257 }
258}
259
260impl Engine {
261 pub(super) fn execute_expression_index_plan(
262 &self,
263 plan: &PlanNode,
264 projected_fields: Option<&[ProjectField]>,
265 ) -> Result<Option<QueryResult>, QueryError> {
266 let (table, path) = match plan {
267 PlanNode::ExprIndexScan { table, path, .. }
268 | PlanNode::ExprRangeScan { table, path, .. }
269 | PlanNode::OrderedExprIndexScan { table, path, .. } => (table, path),
270 _ => return Ok(None),
271 };
272 let Some(index) = resolve_expression_index(&self.catalog, table, path) else {
273 return Ok(None);
274 };
275 let schema = self
276 .catalog
277 .schema(table)
278 .ok_or_else(|| QueryError::TableNotFound(table.clone()))?
279 .clone();
280 let all_columns: Vec<String> = schema
281 .columns
282 .iter()
283 .map(|column| column.name.clone())
284 .collect();
285
286 let projection = match projected_fields {
287 Some(fields) => {
288 if !fields
289 .iter()
290 .all(|field| matches!(field.expr, Expr::Field(_)))
291 {
292 return Ok(None);
293 }
294 let mut indices = Vec::with_capacity(fields.len());
295 let mut columns = Vec::with_capacity(fields.len());
296 for field in fields {
297 let Expr::Field(name) = &field.expr else {
298 unreachable!("plain-field projection checked above")
299 };
300 let index =
301 schema
302 .column_index(name)
303 .ok_or_else(|| QueryError::ColumnNotFound {
304 table: table.clone(),
305 column: name.clone(),
306 })?;
307 indices.push(index);
308 columns.push(field.alias.clone().unwrap_or_else(|| name.clone()));
309 }
310 Some((indices, columns))
311 }
312 None => None,
313 };
314
315 let (rids, range) = match plan {
316 PlanNode::ExprIndexScan { key, .. } => {
317 let key = literal_to_value(key)?;
318 let rids = if key.is_empty() {
319 self.catalog
320 .expression_index_btree(table, index.index_id)
321 .ok_or_else(|| {
322 QueryError::Execution("expression index disappeared".to_string())
323 })?
324 .empty_rids()
325 .to_vec()
326 } else {
327 self.catalog
328 .expression_index_lookup_all(table, index.index_id, &key)
329 .map_err(|error| QueryError::StorageError(error.to_string()))?
330 };
331 (rids, None)
332 }
333 PlanNode::ExprRangeScan { start, end, .. } => {
334 let start_value = start
335 .as_ref()
336 .map(|(expr, _)| literal_to_value(expr))
337 .transpose()?;
338 let end_value = end
339 .as_ref()
340 .map(|(expr, _)| literal_to_value(expr))
341 .transpose()?;
342 let rids = self
343 .catalog
344 .expression_index_range_rids(
345 table,
346 index.index_id,
347 start_value.as_ref(),
348 end_value.as_ref(),
349 )
350 .map_err(|error| QueryError::StorageError(error.to_string()))?;
351 (
352 rids,
353 Some((
354 start_value,
355 start.as_ref().is_none_or(|(_, inclusive)| *inclusive),
356 end_value,
357 end.as_ref().is_none_or(|(_, inclusive)| *inclusive),
358 )),
359 )
360 }
361 PlanNode::OrderedExprIndexScan {
362 descending,
363 limit,
364 offset,
365 ..
366 } => {
367 let Expr::Literal(Literal::Int(limit)) = limit else {
368 return Err(QueryError::Execution(
369 "expression-index limit must be a non-negative integer".to_string(),
370 ));
371 };
372 let offset = match offset {
373 Some(Expr::Literal(Literal::Int(offset))) if *offset >= 0 => *offset as usize,
374 None => 0,
375 _ => {
376 return Err(QueryError::Execution(
377 "expression-index offset must be a non-negative integer".to_string(),
378 ));
379 }
380 };
381 if *limit < 0 {
382 return Err(QueryError::Execution(
383 "expression-index limit must be a non-negative integer".to_string(),
384 ));
385 }
386 let rids = self
387 .catalog
388 .expression_index_ordered_rids_bounded(
389 table,
390 index.index_id,
391 if *descending {
392 IndexOrderDirection::Desc
393 } else {
394 IndexOrderDirection::Asc
395 },
396 offset,
397 *limit as usize,
398 )
399 .map_err(|error| QueryError::StorageError(error.to_string()))?;
400 (rids, None)
401 }
402 _ => unreachable!("expression-index plan checked above"),
403 };
404
405 let root_index =
406 schema
407 .column_index(&path.column)
408 .ok_or_else(|| QueryError::ColumnNotFound {
409 table: table.clone(),
410 column: path.column.clone(),
411 })?;
412 let path_expr = stored_json_path_expr(path);
413 let mut rows = Vec::with_capacity(rids.len());
414 let mut cancel = CancelCheck::new();
415 for rid in rids {
416 cancel.tick()?;
417 match &projection {
418 Some((projected_indices, _)) => {
419 let mut fetch_indices = projected_indices.clone();
420 let root_position = fetch_indices.iter().position(|index| *index == root_index);
421 let root_position = match root_position {
422 Some(position) => position,
423 None => {
424 fetch_indices.push(root_index);
425 fetch_indices.len() - 1
426 }
427 };
428 let Some(mut fetched) = self
429 .catalog
430 .get_projected(table, rid, &fetch_indices)
431 .map_err(|error| QueryError::StorageError(error.to_string()))?
432 else {
433 continue;
434 };
435 if let Some((start, start_inclusive, end, end_inclusive)) = &range {
436 let value = eval_expr(
437 &path_expr,
438 std::slice::from_ref(&fetched[root_position]),
439 std::slice::from_ref(&path.column),
440 );
441 if value.is_empty()
442 || !range_matches(&value, start, *start_inclusive, end, *end_inclusive)
443 {
444 continue;
445 }
446 }
447 fetched.truncate(projected_indices.len());
448 rows.push(fetched);
449 }
450 None => {
451 let Some(row) = self.catalog.get(table, rid) else {
452 continue;
453 };
454 if let Some((start, start_inclusive, end, end_inclusive)) = &range {
455 let value = eval_expr(&path_expr, &row, &all_columns);
456 if value.is_empty()
457 || !range_matches(&value, start, *start_inclusive, end, *end_inclusive)
458 {
459 continue;
460 }
461 }
462 rows.push(row);
463 }
464 }
465 }
466
467 let columns = projection
468 .map(|(_, columns)| columns)
469 .unwrap_or(all_columns);
470 Ok(Some(QueryResult::Rows { columns, rows }))
471 }
472
473 fn charge_provenance(&self, rows: &ProvenanceRows) -> Result<(), QueryError> {
474 let aliases =
475 rows.source_aliases
476 .iter()
477 .fold(std::mem::size_of::<Vec<String>>(), |total, alias| {
478 total
479 .saturating_add(std::mem::size_of::<String>())
480 .saturating_add(alias.capacity())
481 });
482 let per_row = std::mem::size_of::<Vec<Option<RowId>>>().saturating_add(
483 rows.source_aliases
484 .len()
485 .saturating_mul(std::mem::size_of::<Option<RowId>>()),
486 );
487 mem_budget::charge(
488 aliases.saturating_add(rows.provenance.len().saturating_mul(per_row)),
489 self.query_memory_limit(),
490 )
491 }
492
493 fn provenance_scan(
494 &self,
495 table: &str,
496 alias: &str,
497 qualify_columns: bool,
498 ) -> Result<ProvenanceRows, QueryError> {
499 let schema = self
500 .catalog
501 .schema(table)
502 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
503 .clone();
504 let columns = schema
505 .columns
506 .iter()
507 .map(|column| {
508 if qualify_columns {
509 format!("{alias}.{}", column.name)
510 } else {
511 column.name.clone()
512 }
513 })
514 .collect();
515 let mut rows = Vec::new();
516 let mut provenance = Vec::new();
517 let mut cancel = CancelCheck::new();
518 for (rid, row) in self
519 .catalog
520 .scan(table)
521 .map_err(|error| QueryError::StorageError(error.to_string()))?
522 {
523 cancel.tick()?;
524 rows.push(row);
525 provenance.push(vec![Some(rid)]);
526 }
527 let result = ProvenanceRows {
528 columns,
529 rows,
530 source_aliases: vec![alias.to_string()],
531 provenance,
532 };
533 Ok(result)
534 }
535
536 pub(super) fn materialize_rows_with_provenance(
537 &self,
538 plan: &PlanNode,
539 ) -> Result<ProvenanceRows, QueryError> {
540 let result = match plan {
541 PlanNode::SeqScan { table } => self.provenance_scan(table, table, false)?,
542 PlanNode::AliasScan { table, alias } => self.provenance_scan(table, alias, true)?,
543 PlanNode::IndexScan { table, column, key } => {
544 let fallback = PlanNode::Filter {
545 input: Box::new(PlanNode::SeqScan {
546 table: table.clone(),
547 }),
548 predicate: Expr::BinaryOp(
549 Box::new(Expr::Field(column.clone())),
550 BinOp::Eq,
551 Box::new(key.clone()),
552 ),
553 };
554 self.materialize_rows_with_provenance(&fallback)?
555 }
556 PlanNode::RangeScan {
557 table,
558 column,
559 start,
560 end,
561 } => {
562 let fallback = PlanNode::Filter {
563 input: Box::new(PlanNode::SeqScan {
564 table: table.clone(),
565 }),
566 predicate: synthesize_range_predicate(column, start, end),
567 };
568 self.materialize_rows_with_provenance(&fallback)?
569 }
570 PlanNode::ExprIndexScan { .. }
571 | PlanNode::ExprRangeScan { .. }
572 | PlanNode::OrderedExprIndexScan { .. } => {
573 let fallback = expression_index_fallback(plan)
574 .expect("expression-index branch always has a fallback");
575 self.materialize_rows_with_provenance(&fallback)?
576 }
577 PlanNode::Filter { input, predicate } => {
578 if contains_subquery(predicate) {
579 return Err(QueryError::Execution(
580 "symmetric aggregation over a subquery filter is not supported; use raw"
581 .to_string(),
582 ));
583 }
584 let input = self.materialize_rows_with_provenance(input)?;
585 let mut rows = Vec::new();
586 let mut provenance = Vec::new();
587 let mut cancel = CancelCheck::new();
588 for (row, row_provenance) in input.rows.into_iter().zip(input.provenance) {
589 cancel.tick()?;
590 if eval_predicate(predicate, &row, &input.columns) {
591 rows.push(row);
592 provenance.push(row_provenance);
593 }
594 }
595 ProvenanceRows {
596 columns: input.columns,
597 rows,
598 source_aliases: input.source_aliases,
599 provenance,
600 }
601 }
602 PlanNode::Project { input, fields } => {
603 let input = self.materialize_rows_with_provenance(input)?;
604 let columns = fields
605 .iter()
606 .map(|field| {
607 field.alias.clone().unwrap_or_else(|| match &field.expr {
608 Expr::Field(name) => name.clone(),
609 Expr::QualifiedField { qualifier, field } => {
610 format!("{qualifier}.{field}")
611 }
612 _ => expression_output_name(&field.expr),
613 })
614 })
615 .collect();
616 let mut rows = Vec::with_capacity(input.rows.len());
617 let mut cancel = CancelCheck::new();
618 for row in &input.rows {
619 cancel.tick()?;
620 rows.push(
621 fields
622 .iter()
623 .map(|field| eval_expr(&field.expr, row, &input.columns))
624 .collect(),
625 );
626 }
627 ProvenanceRows {
628 columns,
629 rows,
630 source_aliases: input.source_aliases,
631 provenance: input.provenance,
632 }
633 }
634 PlanNode::Sort { input, keys } => {
635 let input = self.materialize_rows_with_provenance(input)?;
636 if input.rows.len() > MAX_SORT_ROWS {
637 return Err(QueryError::SortLimitExceeded);
638 }
639 self.charge_rows(&input.rows)?;
640 let mut paired: Vec<_> = input.rows.into_iter().zip(input.provenance).collect();
641 cooperative_stable_sort_by(
642 &mut paired,
643 self.query_memory_limit(),
644 |(left, _), (right, _)| {
645 for key in keys {
646 let left_value = eval_expr(&key.expr, left, &input.columns);
647 let right_value = eval_expr(&key.expr, right, &input.columns);
648 let comparison =
649 compare_order_values(&left_value, &right_value, key.descending);
650 if comparison != std::cmp::Ordering::Equal {
651 return comparison;
652 }
653 }
654 std::cmp::Ordering::Equal
655 },
656 )?;
657 let (rows, provenance) = paired.into_iter().unzip();
658 ProvenanceRows {
659 columns: input.columns,
660 rows,
661 source_aliases: input.source_aliases,
662 provenance,
663 }
664 }
665 PlanNode::Limit { input, count } | PlanNode::Offset { input, count } => {
666 let input_rows = self.materialize_rows_with_provenance(input)?;
667 let Expr::Literal(Literal::Int(count)) = count else {
668 return Err(QueryError::Execution(
669 "limit/offset must be an integer literal".to_string(),
670 ));
671 };
672 let count = *count as usize;
673 let is_limit = matches!(plan, PlanNode::Limit { .. });
674 let iterator = input_rows.rows.into_iter().zip(input_rows.provenance);
675 let (rows, provenance) = if is_limit {
676 iterator.take(count).unzip()
677 } else {
678 iterator.skip(count).unzip()
679 };
680 ProvenanceRows {
681 columns: input_rows.columns,
682 rows,
683 source_aliases: input_rows.source_aliases,
684 provenance,
685 }
686 }
687 PlanNode::Distinct { input } => {
688 let input = self.materialize_rows_with_provenance(input)?;
689 let mut seen = HashSet::new();
690 let mut rows = Vec::new();
691 let mut provenance = Vec::new();
692 let mut cancel = CancelCheck::new();
693 for (row, row_provenance) in input.rows.into_iter().zip(input.provenance) {
694 cancel.tick()?;
695 if seen.insert(row.clone()) {
696 rows.push(row);
697 provenance.push(row_provenance);
698 }
699 }
700 ProvenanceRows {
701 columns: input.columns,
702 rows,
703 source_aliases: input.source_aliases,
704 provenance,
705 }
706 }
707 PlanNode::Union { left, right, all } => {
708 let mut left_rows = self.materialize_rows_with_provenance(left)?;
709 let right_rows = self.materialize_rows_with_provenance(right)?;
710 if left_rows.columns.len() != right_rows.columns.len() {
711 return Err(QueryError::Execution(
712 "union sides must have the same number of columns".to_string(),
713 ));
714 }
715 if left_rows.source_aliases != right_rows.source_aliases {
716 return Err(QueryError::Execution(
717 "symmetric aggregation over union requires matching source aliases; use raw"
718 .to_string(),
719 ));
720 }
721 left_rows.rows.extend(right_rows.rows);
722 left_rows.provenance.extend(right_rows.provenance);
723 if !all {
724 let mut seen = HashSet::new();
725 let mut rows = Vec::new();
726 let mut provenance = Vec::new();
727 for (row, row_provenance) in
728 left_rows.rows.into_iter().zip(left_rows.provenance)
729 {
730 if seen.insert(row.clone()) {
731 rows.push(row);
732 provenance.push(row_provenance);
733 }
734 }
735 left_rows.rows = rows;
736 left_rows.provenance = provenance;
737 }
738 left_rows
739 }
740 PlanNode::NestedLoopJoin {
741 left,
742 right,
743 on,
744 kind,
745 } => {
746 let left = self.materialize_rows_with_provenance(left)?;
747 let right = self.materialize_rows_with_provenance(right)?;
748 execute_provenance_join(
749 left,
750 right,
751 on.as_ref(),
752 *kind,
753 self.nested_loop_pair_limit,
754 )?
755 }
756 _ => {
757 return Err(QueryError::Execution(
758 "symmetric aggregation input shape is not supported; use raw".to_string(),
759 ));
760 }
761 };
762 self.charge_provenance(&result)?;
763 Ok(result)
764 }
765
766 pub(super) fn introspect_list_types(&self) -> Result<QueryResult, QueryError> {
769 let rows: Vec<Vec<Value>> = self
770 .catalog
771 .list_tables()
772 .iter()
773 .map(|name| {
774 let cols = self
775 .catalog
776 .schema(name)
777 .map(|s| s.columns.len())
778 .unwrap_or(0) as i64;
779 vec![Value::Str((*name).to_string()), Value::Int(cols)]
780 })
781 .collect();
782 Ok(QueryResult::Rows {
783 columns: vec!["name".to_string(), "columns".to_string()],
784 rows,
785 })
786 }
787
788 pub(super) fn introspect_describe(&self, table: &str) -> Result<QueryResult, QueryError> {
791 let schema = self
792 .catalog
793 .schema(table)
794 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
795 let rows: Vec<Vec<Value>> = schema
796 .columns
797 .iter()
798 .map(|c| {
799 let index = if self.catalog.has_index(table, &c.name) {
800 match self.catalog.is_index_unique(table, &c.name) {
801 Some(true) => "unique",
802 _ => "index",
803 }
804 } else {
805 ""
806 };
807 vec![
808 Value::Str(c.name.clone()),
809 Value::Str(type_id_to_name(c.type_id).to_string()),
810 Value::Bool(!c.required),
811 Value::Str(index.to_string()),
812 ]
813 })
814 .collect();
815 Ok(QueryResult::Rows {
816 columns: vec![
817 "column".to_string(),
818 "type".to_string(),
819 "nullable".to_string(),
820 "index".to_string(),
821 ],
822 rows,
823 })
824 }
825
826 pub fn execute_plan(&mut self, plan: &PlanNode) -> Result<QueryResult, QueryError> {
827 validate_no_stray_aggregates(plan)?;
833 validate_json_path_types(&self.catalog, plan)?;
834 match plan {
835 PlanNode::ExprIndexScan { .. }
836 | PlanNode::ExprRangeScan { .. }
837 | PlanNode::OrderedExprIndexScan { .. } => {
838 if let Some(result) = self.execute_expression_index_plan(plan, None)? {
839 return Ok(result);
840 }
841 let fallback = expression_index_fallback(plan)
842 .expect("expression-index branch always has a fallback");
843 self.execute_plan(&fallback)
844 }
845 PlanNode::SeqScan { table } => {
846 if self.view_registry.is_dirty(table) {
848 self.refresh_view(table)?;
849 }
850 let schema = self
851 .catalog
852 .schema(table)
853 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
854 .clone();
855 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
856 let mut cancel = CancelCheck::new();
859 let mut rows: Vec<Vec<Value>> = Vec::new();
860 for (_, row) in self
861 .catalog
862 .scan(table)
863 .map_err(|e| QueryError::StorageError(e.to_string()))?
864 {
865 cancel.tick()?;
866 rows.push(row);
867 }
868 Ok(QueryResult::Rows { columns, rows })
869 }
870
871 PlanNode::Filter { input, predicate } => {
872 let materialized;
876 let predicate = if contains_subquery(predicate) {
877 materialized = self.materialize_subqueries(predicate)?;
878 &materialized
879 } else {
880 predicate
881 };
882
883 if contains_subquery(predicate) {
885 let result = self.execute_plan(input)?;
886 return match result {
887 QueryResult::Rows { columns, rows } => {
888 let mut filtered = Vec::new();
889 let mut cancel = CancelCheck::new();
892 for row in rows {
893 cancel.tick()?;
894 let row_pred =
895 self.materialize_correlated_for_row(predicate, &row, &columns)?;
896 if eval_predicate(&row_pred, &row, &columns) {
897 filtered.push(row);
898 }
899 }
900 Ok(QueryResult::Rows {
901 columns,
902 rows: filtered,
903 })
904 }
905 _ => Err("filter requires row input".into()),
906 };
907 }
908
909 if let PlanNode::SeqScan { table } = input.as_ref() {
917 if !self.catalog.table_has_overflow(table) {
918 if self.view_registry.is_dirty(table) {
920 self.refresh_view(table)?;
921 }
922 let schema = self
923 .catalog
924 .schema(table)
925 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
926 .clone();
927 let columns: Vec<String> =
928 schema.columns.iter().map(|c| c.name.clone()).collect();
929 let fast = FastLayout::new(&schema);
930 let row_layout = RowLayout::new(&schema);
931 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
935
936 let mut cancel = CancelCheck::new();
943 let mut cancel_err: Option<QueryError> = None;
944 if let Some(compiled) =
945 compile_predicate(predicate, &columns, &fast, &schema)
946 {
947 self.catalog
948 .try_for_each_row_raw(table, |_rid, data| {
949 if let Err(e) = cancel.tick() {
950 cancel_err = Some(e);
951 return ControlFlow::Break(());
952 }
953 if compiled(data) {
954 rows.push(decode_row(&schema, data));
955 }
956 ControlFlow::Continue(())
957 })
958 .map_err(|e| QueryError::StorageError(e.to_string()))?;
959 } else {
960 let pred_cols = predicate_column_indices_json(predicate, &columns);
961 self.catalog
962 .try_for_each_row_raw(table, |_rid, data| {
963 if let Err(e) = cancel.tick() {
964 cancel_err = Some(e);
965 return ControlFlow::Break(());
966 }
967 let pred_row =
968 decode_selective(&schema, &row_layout, data, &pred_cols);
969 if eval_predicate(predicate, &pred_row, &columns) {
970 rows.push(decode_row(&schema, data));
971 }
972 ControlFlow::Continue(())
973 })
974 .map_err(|e| QueryError::StorageError(e.to_string()))?;
975 }
976 if let Some(e) = cancel_err {
977 return Err(e);
978 }
979
980 return Ok(QueryResult::Rows { columns, rows });
981 }
982 }
983
984 let result = self.execute_plan(input)?;
986 match result {
987 QueryResult::Rows { columns, rows } => {
988 let mut cancel = CancelCheck::new();
989 let mut filtered: Vec<Vec<Value>> = Vec::new();
990 for row in rows {
991 cancel.tick()?;
992 if eval_predicate(predicate, &row, &columns) {
993 filtered.push(row);
994 }
995 }
996 Ok(QueryResult::Rows {
997 columns,
998 rows: filtered,
999 })
1000 }
1001 _ => Err("filter requires row input".into()),
1002 }
1003 }
1004
1005 PlanNode::Project { input, fields } => {
1006 if matches!(
1007 input.as_ref(),
1008 PlanNode::ExprIndexScan { .. }
1009 | PlanNode::ExprRangeScan { .. }
1010 | PlanNode::OrderedExprIndexScan { .. }
1011 ) {
1012 if let Some(result) = self.execute_expression_index_plan(input, Some(fields))? {
1013 return Ok(result);
1014 }
1015 }
1016 if let PlanNode::IndexScan { table, column, key } = input.as_ref() {
1019 let schema = self
1020 .catalog
1021 .schema(table)
1022 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
1023 .clone();
1024 let all_columns: Vec<String> =
1025 schema.columns.iter().map(|c| c.name.clone()).collect();
1026 let key_value = literal_to_value(key)?;
1027 let tbl = self
1028 .catalog
1029 .get_table(table)
1030 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1031
1032 let proj_columns: Vec<String> = fields
1033 .iter()
1034 .map(|f| {
1035 f.alias.clone().unwrap_or_else(|| match &f.expr {
1036 Expr::Field(name) => name.clone(),
1037 _ => "?".into(),
1038 })
1039 })
1040 .collect();
1041
1042 let proj_indices: Vec<usize> = fields
1044 .iter()
1045 .filter_map(|f| {
1046 if let Expr::Field(name) = &f.expr {
1047 all_columns.iter().position(|c| c == name)
1048 } else {
1049 None
1050 }
1051 })
1052 .collect();
1053
1054 let all_plain_fields = fields.iter().all(|f| matches!(f.expr, Expr::Field(_)));
1059 if tbl.has_index(column) && all_plain_fields {
1060 let rids = tbl.index_lookup_all(column, &key_value);
1061 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(rids.len());
1062 let mut cancel = CancelCheck::new();
1063 for rid in rids {
1064 cancel.tick()?;
1065 if let Some(full) = tbl.get(rid) {
1071 let row: Vec<Value> =
1072 proj_indices.iter().map(|&ci| full[ci].clone()).collect();
1073 rows.push(row);
1074 }
1075 }
1076 return Ok(QueryResult::Rows {
1077 columns: proj_columns,
1078 rows,
1079 });
1080 }
1081 }
1082
1083 if let PlanNode::Limit {
1088 input: inner,
1089 count: limit_expr,
1090 } = input.as_ref()
1091 {
1092 if let PlanNode::Sort {
1093 input: sort_input,
1094 keys,
1095 } = inner.as_ref()
1096 {
1097 if keys.len() == 1 {
1099 if let Expr::Field(sort_field) = &keys[0].expr {
1100 let descending = keys[0].descending;
1101 let limit = match limit_expr {
1102 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
1103 _ => usize::MAX,
1104 };
1105 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
1106 match sort_input.as_ref() {
1107 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
1108 PlanNode::Filter {
1109 input: fi,
1110 predicate,
1111 } => {
1112 if let PlanNode::SeqScan { table } = fi.as_ref() {
1113 (Some(table.as_str()), Some(predicate))
1114 } else {
1115 (None, None)
1116 }
1117 }
1118 _ => (None, None),
1119 };
1120 if let Some(table) = table_opt {
1121 if let Some(result) = self.project_filter_sort_limit_fast(
1122 table, fields, sort_field, descending, limit, pred_opt,
1123 )? {
1124 return Ok(result);
1125 }
1126 }
1127 }
1128 }
1129 }
1130 if let PlanNode::Filter {
1133 input: fi,
1134 predicate,
1135 } = inner.as_ref()
1136 {
1137 if let PlanNode::SeqScan { table } = fi.as_ref() {
1138 let limit = match limit_expr {
1139 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
1140 _ => usize::MAX,
1141 };
1142 if let Some(result) = self.project_filter_limit_fast(
1143 table,
1144 fields,
1145 limit,
1146 Some(predicate),
1147 )? {
1148 return Ok(result);
1149 }
1150 }
1151 }
1152 if let PlanNode::SeqScan { table } = inner.as_ref() {
1154 let limit = match limit_expr {
1155 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
1156 _ => usize::MAX,
1157 };
1158 if let Some(result) =
1159 self.project_filter_limit_fast(table, fields, limit, None)?
1160 {
1161 return Ok(result);
1162 }
1163 }
1164 }
1165
1166 if let PlanNode::Filter {
1177 input: fi,
1178 predicate,
1179 } = input.as_ref()
1180 {
1181 if let PlanNode::SeqScan { table } = fi.as_ref() {
1182 if let Some(result) = self.project_filter_limit_fast(
1183 table,
1184 fields,
1185 usize::MAX,
1186 Some(predicate),
1187 )? {
1188 return Ok(result);
1189 }
1190 }
1191 }
1192
1193 if let PlanNode::SeqScan { table } = input.as_ref() {
1197 if let Some(result) =
1198 self.project_filter_limit_fast(table, fields, usize::MAX, None)?
1199 {
1200 return Ok(result);
1201 }
1202 }
1203
1204 let result = self.execute_plan(input)?;
1205 match result {
1206 QueryResult::Rows { columns, rows } => {
1207 let proj_columns: Vec<String> = fields
1208 .iter()
1209 .map(|f| {
1210 f.alias.clone().unwrap_or_else(|| match &f.expr {
1211 Expr::Field(name) => name.clone(),
1212 Expr::QualifiedField { qualifier, field } => {
1216 format!("{qualifier}.{field}")
1217 }
1218 _ => "?".into(),
1219 })
1220 })
1221 .collect();
1222 let mut cancel = CancelCheck::new();
1223 let mut proj_rows: Vec<Vec<Value>> = Vec::with_capacity(rows.len());
1224 for row in &rows {
1225 cancel.tick()?;
1226 proj_rows.push(
1227 fields
1228 .iter()
1229 .map(|f| eval_expr(&f.expr, row, &columns))
1230 .collect(),
1231 );
1232 }
1233 Ok(QueryResult::Rows {
1234 columns: proj_columns,
1235 rows: proj_rows,
1236 })
1237 }
1238 _ => Err("project requires row input".into()),
1239 }
1240 }
1241
1242 PlanNode::Sort { input, keys } => {
1243 let result = self.execute_plan(input)?;
1244 match result {
1245 QueryResult::Rows { columns, mut rows } => {
1246 if rows.len() > MAX_SORT_ROWS {
1250 return Err(QueryError::SortLimitExceeded);
1251 }
1252 self.charge_rows(&rows)?;
1253 let key_specs: Vec<(Option<usize>, &Expr, bool)> = keys
1254 .iter()
1255 .map(|k| {
1256 let stored_name = match &k.expr {
1257 Expr::Field(name) => Some(name.clone()),
1258 Expr::QualifiedField { qualifier, field } => {
1259 Some(format!("{qualifier}.{field}"))
1260 }
1261 _ => None,
1262 };
1263 let index = stored_name
1264 .as_ref()
1265 .and_then(|name| columns.iter().position(|c| c == name));
1266 if let Some(name) = stored_name {
1267 if index.is_none() {
1268 return Err(QueryError::ColumnNotFound {
1269 table: String::new(),
1270 column: name,
1271 });
1272 }
1273 }
1274 Ok((index, &k.expr, k.descending))
1275 })
1276 .collect::<Result<_, QueryError>>()?;
1277 cooperative_stable_sort_by(&mut rows, self.query_memory_limit, |a, b| {
1278 for &(col_idx, expr, descending) in &key_specs {
1279 let (left_value, right_value) = match col_idx {
1280 Some(index) => (&a[index], &b[index]),
1281 None => {
1282 let left = eval_expr(expr, a, &columns);
1283 let right = eval_expr(expr, b, &columns);
1284 let cmp = compare_order_values(&left, &right, descending);
1285 if cmp != std::cmp::Ordering::Equal {
1286 return cmp;
1287 }
1288 continue;
1289 }
1290 };
1291 let cmp = compare_order_values(left_value, right_value, descending);
1292 if cmp != std::cmp::Ordering::Equal {
1293 return cmp;
1294 }
1295 }
1296 std::cmp::Ordering::Equal
1297 })?;
1298 Ok(QueryResult::Rows { columns, rows })
1299 }
1300 _ => Err("sort requires row input".into()),
1301 }
1302 }
1303
1304 PlanNode::Limit { input, count } => {
1305 let result = self.execute_plan(input)?;
1306 let n = match count {
1307 Expr::Literal(Literal::Int(v)) => *v as usize,
1308 _ => return Err("limit must be integer literal".into()),
1309 };
1310 match result {
1311 QueryResult::Rows { columns, rows } => {
1312 let mut cancel = CancelCheck::new();
1313 let mut limited = Vec::with_capacity(n.min(rows.len()));
1314 for row in rows.into_iter().take(n) {
1315 cancel.tick()?;
1316 limited.push(row);
1317 }
1318 Ok(QueryResult::Rows {
1319 columns,
1320 rows: limited,
1321 })
1322 }
1323 _ => Err("limit requires row input".into()),
1324 }
1325 }
1326
1327 PlanNode::Offset { input, count } => {
1328 let result = self.execute_plan(input)?;
1329 let n = match count {
1330 Expr::Literal(Literal::Int(v)) => *v as usize,
1331 _ => return Err("offset must be integer literal".into()),
1332 };
1333 match result {
1334 QueryResult::Rows { columns, rows } => {
1335 let mut cancel = CancelCheck::new();
1336 let mut offset = Vec::with_capacity(rows.len().saturating_sub(n));
1337 for (index, row) in rows.into_iter().enumerate() {
1338 cancel.tick()?;
1339 if index >= n {
1340 offset.push(row);
1341 }
1342 }
1343 Ok(QueryResult::Rows {
1344 columns,
1345 rows: offset,
1346 })
1347 }
1348 _ => Err("offset requires row input".into()),
1349 }
1350 }
1351
1352 PlanNode::Aggregate {
1353 input,
1354 function,
1355 argument,
1356 mode: _,
1357 provenance_alias,
1358 } => {
1359 if let Some(provenance_alias) = provenance_alias {
1360 let input = self.materialize_rows_with_provenance(input)?;
1361 self.charge_rows(&input.rows)?;
1362 return aggregate_rows_with_provenance(
1363 *function,
1364 argument.as_ref(),
1365 &input,
1366 provenance_alias,
1367 self.query_memory_limit(),
1368 );
1369 }
1370 if *function == AggFunc::Count {
1372 if let PlanNode::SeqScan { table } = input.as_ref() {
1376 if !self.catalog.table_has_overflow(table) {
1377 if self.view_registry.is_dirty(table) {
1381 self.refresh_view(table)?;
1382 }
1383 let mut count: i64 = 0;
1384 for_each_row_raw_cancellable(&self.catalog, table, |_rid, _data| {
1385 count += 1;
1386 })?;
1387 return Ok(QueryResult::Scalar(Value::Int(count)));
1388 }
1389 }
1390 if let PlanNode::Filter {
1398 input: inner,
1399 predicate,
1400 } = input.as_ref()
1401 {
1402 if let PlanNode::SeqScan { table } = inner.as_ref() {
1403 if self.view_registry.is_dirty(table) {
1404 self.refresh_view(table)?;
1405 }
1406 }
1407 if let (PlanNode::SeqScan { table }, false) =
1408 (inner.as_ref(), contains_subquery(predicate))
1409 {
1410 if !self.catalog.table_has_overflow(table) {
1411 let schema = self
1412 .catalog
1413 .schema(table)
1414 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
1415 .clone();
1416 let columns: Vec<String> =
1417 schema.columns.iter().map(|c| c.name.clone()).collect();
1418 let fast = FastLayout::new(&schema);
1419 let row_layout = RowLayout::new(&schema);
1420
1421 if let Some(compiled) =
1424 compile_predicate(predicate, &columns, &fast, &schema)
1425 {
1426 let mut count: i64 = 0;
1427 for_each_row_raw_cancellable(
1428 &self.catalog,
1429 table,
1430 |_rid, data| {
1431 if compiled(data) {
1432 count += 1;
1433 }
1434 },
1435 )?;
1436 return Ok(QueryResult::Scalar(Value::Int(count)));
1437 }
1438
1439 let pred_cols = predicate_column_indices_json(predicate, &columns);
1441 let mut count: i64 = 0;
1442 for_each_row_raw_cancellable(
1443 &self.catalog,
1444 table,
1445 |_rid, data| {
1446 let pred_row = decode_selective(
1447 &schema,
1448 &row_layout,
1449 data,
1450 &pred_cols,
1451 );
1452 if eval_predicate(predicate, &pred_row, &columns) {
1453 count += 1;
1454 }
1455 },
1456 )?;
1457
1458 return Ok(QueryResult::Scalar(Value::Int(count)));
1459 }
1460 }
1461 }
1462 }
1463
1464 if matches!(
1468 function,
1469 AggFunc::Sum
1470 | AggFunc::Avg
1471 | AggFunc::Min
1472 | AggFunc::Max
1473 | AggFunc::CountDistinct
1474 ) {
1475 if let Some(Expr::Field(col)) = argument.as_ref() {
1476 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
1478 match input.as_ref() {
1479 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
1480 PlanNode::Filter {
1481 input: inner,
1482 predicate,
1483 } => {
1484 if let PlanNode::SeqScan { table } = inner.as_ref() {
1485 (Some(table.as_str()), Some(predicate))
1486 } else {
1487 (None, None)
1488 }
1489 }
1490 _ => (None, None),
1491 };
1492 if let Some(table) = table_opt {
1493 if let Some(result) =
1494 self.agg_single_col_fast(table, col, *function, pred_opt)?
1495 {
1496 return Ok(result);
1497 }
1498 }
1499 }
1500 }
1501
1502 let result = self.execute_plan(input)?;
1507 match result {
1508 QueryResult::Rows { columns, rows } => {
1509 aggregate_rows(*function, argument.as_ref(), &columns, &rows)
1510 }
1511 _ => Err("aggregate requires row input".into()),
1512 }
1513 }
1514
1515 PlanNode::Insert {
1516 table,
1517 rows,
1518 returning,
1519 } => {
1520 let mut returning_columns: Vec<String> = Vec::new();
1525 let all_values: Vec<Vec<Value>> = {
1526 let schema = self
1527 .catalog
1528 .schema(table)
1529 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1530 if *returning {
1531 returning_columns = schema.columns.iter().map(|c| c.name.clone()).collect();
1532 }
1533 let defaults = self.catalog.column_defaults(table).unwrap_or(&[]);
1534 let auto = self.catalog.auto_columns(table).unwrap_or(&[]);
1535 let mut all = Vec::with_capacity(rows.len());
1536 for assignments in rows {
1537 let mut values = vec![Value::Empty; schema.columns.len()];
1538 for a in assignments {
1539 let idx = schema.column_index(&a.field).ok_or_else(|| {
1540 QueryError::ColumnNotFound {
1541 table: String::new(),
1542 column: a.field.clone(),
1543 }
1544 })?;
1545 let raw = literal_to_value(&a.value)?;
1546 values[idx] = coerce_value(raw, &schema.columns[idx])?;
1547 }
1548 for (i, slot) in values.iter_mut().enumerate() {
1552 if slot.is_empty() {
1553 if let Some(Some(d)) = defaults.get(i) {
1554 *slot = d.clone();
1555 }
1556 }
1557 }
1558 for col in &schema.columns {
1559 let pos = col.position as usize;
1560 let is_auto = auto.get(pos).copied().unwrap_or(false);
1563 if col.required && !is_auto && matches!(values[pos], Value::Empty) {
1564 return Err(QueryError::Execution(format!(
1565 "column '{}' is required but no value was provided",
1566 col.name
1567 )));
1568 }
1569 }
1570 all.push(values);
1571 }
1572 all
1573 };
1574 let mut all_values = all_values;
1579 for values in all_values.iter_mut() {
1580 self.catalog.assign_auto_columns(table, values);
1581 }
1582 self.charge_rows(&all_values)?;
1588 let n = all_values.len() as u64;
1589 for values in &all_values {
1590 self.catalog
1591 .insert(table, values)
1592 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1593 }
1594 self.view_registry.mark_dependents_dirty(table);
1595 if *returning {
1596 Ok(QueryResult::Rows {
1597 columns: returning_columns,
1598 rows: all_values,
1599 })
1600 } else {
1601 Ok(QueryResult::Modified(n))
1602 }
1603 }
1604
1605 PlanNode::Upsert {
1606 table,
1607 key_column,
1608 assignments,
1609 on_conflict,
1610 } => {
1611 let (values, key_idx) = {
1612 let schema = self
1613 .catalog
1614 .schema(table)
1615 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1616 let mut values = vec![Value::Empty; schema.columns.len()];
1617 for a in assignments {
1618 let idx = schema.column_index(&a.field).ok_or_else(|| {
1619 QueryError::ColumnNotFound {
1620 table: String::new(),
1621 column: a.field.clone(),
1622 }
1623 })?;
1624 let raw = literal_to_value(&a.value)?;
1625 values[idx] = coerce_value(raw, &schema.columns[idx])?;
1626 }
1627 let defaults = self.catalog.column_defaults(table).unwrap_or(&[]);
1630 for (i, slot) in values.iter_mut().enumerate() {
1631 if slot.is_empty() {
1632 if let Some(Some(d)) = defaults.get(i) {
1633 *slot = d.clone();
1634 }
1635 }
1636 }
1637 for col in &schema.columns {
1638 if col.required && matches!(values[col.position as usize], Value::Empty) {
1639 return Err(QueryError::Execution(format!(
1640 "column '{}' is required but no value was provided",
1641 col.name
1642 )));
1643 }
1644 }
1645 let key_idx = schema
1646 .column_index(key_column)
1647 .ok_or_else(|| format!("key column '{key_column}' not found"))?;
1648 (values, key_idx)
1649 };
1650
1651 if self.catalog.is_index_unique(table, key_column) != Some(true) {
1655 return Err(QueryError::Execution(format!(
1656 "upsert on .{key_column} requires a unique column (declare it with \
1657 `unique {key_column}: <type>` or `alter {table} add unique .{key_column}`)"
1658 )));
1659 }
1660
1661 let key_value = values[key_idx].clone();
1662
1663 let existing = {
1665 let tbl = self
1666 .catalog
1667 .get_table(table)
1668 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1669 let rids = tbl.index_lookup_all(key_column, &key_value);
1672 rids.into_iter()
1675 .next()
1676 .and_then(|rid| tbl.get(rid).map(|row| (rid, row)))
1677 };
1678
1679 if let Some((rid, mut existing_row)) = existing {
1680 let update_assignments = if on_conflict.is_empty() {
1682 assignments
1683 } else {
1684 on_conflict
1685 };
1686 let changed_cols: Vec<usize> = {
1687 let schema = self
1688 .catalog
1689 .schema(table)
1690 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1691 let mut indices = Vec::new();
1692 for a in update_assignments {
1693 let idx = schema.column_index(&a.field).ok_or_else(|| {
1694 QueryError::ColumnNotFound {
1695 table: String::new(),
1696 column: a.field.clone(),
1697 }
1698 })?;
1699 if idx != key_idx {
1700 existing_row[idx] =
1705 coerce_value(literal_to_value(&a.value)?, &schema.columns[idx])
1706 .map_err(QueryError::TypeError)?;
1707 indices.push(idx);
1708 }
1709 }
1710 indices
1711 };
1712 self.catalog
1713 .update_hinted(table, rid, &existing_row, Some(&changed_cols))
1714 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1715 self.view_registry.mark_dependents_dirty(table);
1716 Ok(QueryResult::Modified(1))
1717 } else {
1718 self.catalog
1720 .insert(table, &values)
1721 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1722 self.view_registry.mark_dependents_dirty(table);
1723 Ok(QueryResult::Modified(1))
1724 }
1725 }
1726
1727 PlanNode::Update {
1728 input,
1729 table,
1730 assignments,
1731 returning,
1732 } => {
1733 let (col_indices, literal_vals, target_cols): (
1739 Vec<usize>,
1740 Option<Vec<Value>>,
1741 Vec<ColumnDef>,
1742 ) = {
1743 let schema_ref = self
1744 .catalog
1745 .schema(table)
1746 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1747 let indices: Vec<usize> = assignments
1748 .iter()
1749 .map(|a| {
1750 schema_ref.column_index(&a.field).ok_or_else(|| {
1751 QueryError::ColumnNotFound {
1752 table: String::new(),
1753 column: a.field.clone(),
1754 }
1755 })
1756 })
1757 .collect::<Result<_, _>>()?;
1758 let target_cols: Vec<ColumnDef> = indices
1762 .iter()
1763 .map(|&idx| schema_ref.columns[idx].clone())
1764 .collect();
1765 let raw_vals: Result<Vec<Value>, _> = assignments
1769 .iter()
1770 .map(|a| literal_to_value(&a.value))
1771 .collect();
1772 let coerced = match raw_vals {
1781 Ok(raws) => {
1782 let mut out = Vec::with_capacity(raws.len());
1783 for (raw, &idx) in raws.into_iter().zip(indices.iter()) {
1784 out.push(
1785 coerce_value(raw, &schema_ref.columns[idx])
1786 .map_err(QueryError::TypeError)?,
1787 );
1788 }
1789 Some(out)
1790 }
1791 Err(_) => None,
1792 };
1793 (indices, coerced, target_cols)
1794 };
1795 let resolved_assignments: Option<Vec<(usize, Value)>> =
1796 literal_vals.map(|vals| col_indices.iter().copied().zip(vals).collect());
1797
1798 let changed_cols: Vec<usize> = col_indices.clone();
1801
1802 if *returning {
1809 let columns: Vec<String> = {
1810 let schema_ref = self
1811 .catalog
1812 .schema(table)
1813 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1814 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
1815 };
1816 let matching_rids = self.collect_rids_for_mutation(input, table)?;
1817 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(matching_rids.len());
1818 crate::cancel::check()?;
1825 for rid in matching_rids {
1826 let mut row = match self.catalog.get(table, rid) {
1827 Some(r) => r,
1828 None => continue,
1829 };
1830 match &resolved_assignments {
1831 Some(resolved) => {
1833 for (idx, val) in resolved.iter() {
1834 row[*idx] = val.clone();
1835 }
1836 }
1837 None => {
1843 for (i, asgn) in assignments.iter().enumerate() {
1844 let val = eval_expr(&asgn.value, &row, &columns);
1845 row[col_indices[i]] = coerce_value(val, &target_cols[i])
1846 .map_err(QueryError::TypeError)?;
1847 }
1848 }
1849 }
1850 self.catalog
1851 .update_hinted(table, rid, &row, Some(&changed_cols))
1852 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1853 out_rows.push(row);
1854 }
1855 self.view_registry.mark_dependents_dirty(table);
1856 return Ok(QueryResult::Rows {
1857 columns,
1858 rows: out_rows,
1859 });
1860 }
1861
1862 if let Some(ref resolved_assignments) = resolved_assignments {
1869 if let PlanNode::Filter {
1870 input: inner,
1871 predicate,
1872 } = input.as_ref()
1873 {
1874 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
1875 if t == table {
1876 crate::cancel::check()?;
1882 let fused_result = self.try_fused_scan_update(
1883 table,
1884 predicate,
1885 resolved_assignments,
1886 &changed_cols,
1887 );
1888 if let Some(result) = fused_result {
1889 return result;
1890 }
1891 }
1892 }
1893 }
1894 }
1895
1896 let matching_rids = self.collect_rids_for_mutation(input, table)?;
1898 crate::cancel::check()?;
1901
1902 if let Some(ref resolved_assignments) = resolved_assignments {
1904 let fast_patch: Option<Vec<FastPatch>> = {
1910 let tbl = self
1911 .catalog
1912 .get_table(table)
1913 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
1914 let schema = tbl.schema();
1915 let all_fixed_nonnull = !tbl.has_overflow_rows()
1919 && resolved_assignments.iter().all(|(idx, val)| {
1920 is_fixed_size(schema.columns[*idx].type_id) && !val.is_empty()
1921 });
1922 let no_indexed = !resolved_assignments
1923 .iter()
1924 .any(|(idx, _)| tbl.has_indexed_col(*idx));
1925
1926 if all_fixed_nonnull && no_indexed {
1927 let layout = RowLayout::new(schema);
1928 let bitmap_size = layout.bitmap_size();
1929 let patches: Vec<FastPatch> = resolved_assignments
1930 .iter()
1931 .map(|(idx, val)| {
1932 let fixed_off = layout
1933 .fixed_offset(*idx)
1934 .expect("is_fixed_size already checked");
1935 let field_off = 2 + bitmap_size + fixed_off;
1936 let bytes: FixedBytes = match val {
1937 Value::Int(v) => FixedBytes::I64(v.to_le_bytes()),
1938 Value::Float(v) => FixedBytes::F64(v.to_le_bytes()),
1939 Value::Bool(v) => FixedBytes::Bool(if *v { 1 } else { 0 }),
1940 Value::DateTime(v) => FixedBytes::I64(v.to_le_bytes()),
1941 Value::Uuid(v) => FixedBytes::Uuid(*v),
1942 _ => unreachable!("all_fixed_nonnull guard lied"),
1943 };
1944 FastPatch {
1945 field_off,
1946 bitmap_byte_off: 2 + idx / 8,
1947 bit_mask: 1u8 << (idx % 8),
1948 bytes,
1949 }
1950 })
1951 .collect();
1952 Some(patches)
1953 } else {
1954 None
1955 }
1956 };
1957
1958 if let Some(patches) = fast_patch {
1959 let mut count = 0u64;
1960 let mut fallback_rids: Vec<RowId> = Vec::new();
1961 for rid in &matching_rids {
1962 let ok = self
1977 .catalog
1978 .update_row_bytes_logged(table, *rid, |row| {
1979 let base = row_body_base(row);
1980 for p in &patches {
1981 row[base + p.bitmap_byte_off] &= !p.bit_mask;
1982 let field_bytes = p.bytes.as_slice();
1983 row[base + p.field_off
1984 ..base + p.field_off + field_bytes.len()]
1985 .copy_from_slice(field_bytes);
1986 }
1987 })
1988 .map_err(|e| QueryError::StorageError(e.to_string()))?;
1989 if ok {
1990 count += 1;
1991 } else {
1992 fallback_rids.push(*rid);
1993 }
1994 }
1995 for rid in fallback_rids {
1996 let mut row = match self.catalog.get(table, rid) {
1997 Some(r) => r,
1998 None => continue,
1999 };
2000 for (idx, val) in resolved_assignments.iter() {
2001 row[*idx] = val.clone();
2002 }
2003 self.catalog
2004 .update_hinted(table, rid, &row, Some(&changed_cols))
2005 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2006 count += 1;
2007 }
2008 self.view_registry.mark_dependents_dirty(table);
2009 return Ok(QueryResult::Modified(count));
2010 }
2011
2012 let var_fast: Option<(usize, Option<Vec<u8>>)> = {
2014 let tbl = self
2015 .catalog
2016 .get_table(table)
2017 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2018 let schema = tbl.schema();
2019 let is_single = resolved_assignments.len() == 1 && !tbl.has_overflow_rows();
2023 let is_var_col = is_single
2024 && !is_fixed_size(schema.columns[resolved_assignments[0].0].type_id);
2025 let no_indexed = !resolved_assignments
2026 .iter()
2027 .any(|(idx, _)| tbl.has_indexed_col(*idx));
2028
2029 if is_single && is_var_col && no_indexed {
2030 let (idx, val) = &resolved_assignments[0];
2031 let bytes_opt: Option<Vec<u8>> = match val {
2032 Value::Str(s) => Some(s.as_bytes().to_vec()),
2033 Value::Bytes(b) => Some(b.clone()),
2034 Value::Json(b) => Some(b.to_vec()),
2038 Value::Empty => None,
2039 _ => {
2040 return Err(QueryError::TypeError(format!(
2041 "cannot assign non-var value to var column '{}'",
2042 schema.columns[*idx].name
2043 )))
2044 }
2045 };
2046 Some((*idx, bytes_opt))
2047 } else {
2048 None
2049 }
2050 };
2051
2052 if let Some((col_idx, new_bytes_opt)) = var_fast {
2053 let new_bytes_ref: Option<&[u8]> = new_bytes_opt.as_deref();
2054 let mut count = 0u64;
2055 let mut fallback_rids: Vec<RowId> = Vec::new();
2056 for rid in &matching_rids {
2057 let ok = self
2063 .catalog
2064 .patch_var_col_logged(table, *rid, col_idx, new_bytes_ref)
2065 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2066 if ok {
2067 count += 1;
2068 } else {
2069 fallback_rids.push(*rid);
2070 }
2071 }
2072 for rid in fallback_rids {
2073 let mut row = match self.catalog.get(table, rid) {
2074 Some(r) => r,
2075 None => continue,
2076 };
2077 for (idx, val) in resolved_assignments.iter() {
2078 row[*idx] = val.clone();
2079 }
2080 self.catalog
2081 .update_hinted(table, rid, &row, Some(&changed_cols))
2082 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2083 count += 1;
2084 }
2085 self.view_registry.mark_dependents_dirty(table);
2086 return Ok(QueryResult::Modified(count));
2087 }
2088
2089 let mut count = 0u64;
2091 for rid in matching_rids {
2092 let mut row = match self.catalog.get(table, rid) {
2093 Some(r) => r,
2094 None => continue,
2095 };
2096 for (idx, val) in resolved_assignments.iter() {
2097 row[*idx] = val.clone();
2098 }
2099 self.catalog
2100 .update_hinted(table, rid, &row, Some(&changed_cols))
2101 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2102 count += 1;
2103 }
2104 self.view_registry.mark_dependents_dirty(table);
2105 return Ok(QueryResult::Modified(count));
2106 } let col_names: Vec<String> = {
2112 let schema_ref = self
2113 .catalog
2114 .schema(table)
2115 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2116 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
2117 };
2118 let mut count = 0u64;
2119 for rid in matching_rids {
2120 let mut row = match self.catalog.get(table, rid) {
2121 Some(r) => r,
2122 None => continue,
2123 };
2124 for (i, asgn) in assignments.iter().enumerate() {
2125 let val = eval_expr(&asgn.value, &row, &col_names);
2126 row[col_indices[i]] =
2132 coerce_value(val, &target_cols[i]).map_err(QueryError::TypeError)?;
2133 }
2134 self.catalog
2135 .update_hinted(table, rid, &row, Some(&changed_cols))
2136 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2137 count += 1;
2138 }
2139 self.view_registry.mark_dependents_dirty(table);
2140 Ok(QueryResult::Modified(count))
2141 }
2142
2143 PlanNode::Delete {
2144 input,
2145 table,
2146 returning,
2147 } => {
2148 if *returning {
2155 let columns: Vec<String> = {
2156 let schema_ref = self
2157 .catalog
2158 .schema(table)
2159 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2160 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
2161 };
2162 let matching_rids = self.collect_rids_for_mutation(input, table)?;
2163 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(matching_rids.len());
2164 let mut cancel = CancelCheck::new();
2168 for rid in &matching_rids {
2169 cancel.tick()?;
2170 if let Some(row) = self.catalog.get(table, *rid) {
2171 out_rows.push(row);
2172 }
2173 }
2174 crate::cancel::check()?;
2175 self.catalog
2176 .delete_many(table, &matching_rids)
2177 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2178 self.view_registry.mark_dependents_dirty(table);
2179 return Ok(QueryResult::Rows {
2180 columns,
2181 rows: out_rows,
2182 });
2183 }
2184
2185 let delete_overflow = self.catalog.table_has_overflow(table);
2209 if let PlanNode::Filter {
2210 input: inner,
2211 predicate,
2212 } = input.as_ref()
2213 {
2214 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
2215 if t == table && !delete_overflow {
2216 let schema = self
2217 .catalog
2218 .schema(table)
2219 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2220 let columns: Vec<String> =
2221 schema.columns.iter().map(|c| c.name.clone()).collect();
2222 let fast = FastLayout::new(schema);
2223 if let Some(compiled) =
2224 compile_predicate(predicate, &columns, &fast, schema)
2225 {
2226 crate::cancel::check()?;
2232 let count = self
2233 .catalog
2234 .scan_delete_matching_logged(table, |data| compiled(data))
2235 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2236 self.view_registry.mark_dependents_dirty(table);
2237 return Ok(QueryResult::Modified(count));
2238 }
2239 }
2240 }
2241 } else if let PlanNode::SeqScan { table: t } = input.as_ref() {
2242 if t == table && !delete_overflow {
2243 crate::cancel::check()?;
2247 let count = self
2248 .catalog
2249 .scan_delete_matching_logged(table, |_| true)
2250 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2251 self.view_registry.mark_dependents_dirty(table);
2252 return Ok(QueryResult::Modified(count));
2253 }
2254 }
2255
2256 let matching_rids = self.collect_rids_for_mutation(input, table)?;
2257 crate::cancel::check()?;
2258 let count = self
2259 .catalog
2260 .delete_many(table, &matching_rids)
2261 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2262 self.view_registry.mark_dependents_dirty(table);
2263 Ok(QueryResult::Modified(count))
2264 }
2265
2266 PlanNode::AliasScan { table, alias } => {
2267 let schema = self
2277 .catalog
2278 .schema(table)
2279 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
2280 .clone();
2281 let columns: Vec<String> = schema
2282 .columns
2283 .iter()
2284 .map(|c| format!("{alias}.{}", c.name))
2285 .collect();
2286 let mut cancel = CancelCheck::new();
2287 let mut rows: Vec<Vec<Value>> = Vec::new();
2288 for (_, row) in self
2289 .catalog
2290 .scan(table)
2291 .map_err(|e| QueryError::StorageError(e.to_string()))?
2292 {
2293 cancel.tick()?;
2294 rows.push(row);
2295 }
2296 Ok(QueryResult::Rows { columns, rows })
2297 }
2298
2299 PlanNode::NestedLoopJoin {
2300 left,
2301 right,
2302 on,
2303 kind,
2304 } => {
2305 let left_result = self.execute_plan(left)?;
2316 let right_result = self.execute_plan(right)?;
2317 let (left_columns, left_rows) = match left_result {
2318 QueryResult::Rows { columns, rows } => (columns, rows),
2319 _ => return Err("join left side must produce rows".into()),
2320 };
2321 let (right_columns, right_rows) = match right_result {
2322 QueryResult::Rows { columns, rows } => (columns, rows),
2323 _ => return Err("join right side must produce rows".into()),
2324 };
2325
2326 self.charge_rows(&left_rows)?;
2330 self.charge_rows(&right_rows)?;
2331
2332 execute_materialized_join(
2333 left_columns,
2334 left_rows,
2335 right_columns,
2336 right_rows,
2337 on.as_ref(),
2338 *kind,
2339 self.nested_loop_pair_limit,
2340 )
2341 }
2342
2343 PlanNode::Distinct { input } => {
2344 let result = self.execute_plan(input)?;
2345 match result {
2346 QueryResult::Rows { columns, rows } => {
2347 let mut seen = std::collections::HashSet::new();
2348 let mut unique_rows = Vec::new();
2349 let mut cancel = CancelCheck::new();
2350 for row in rows {
2351 cancel.tick()?;
2352 if seen.insert(row.clone()) {
2353 unique_rows.push(row);
2354 }
2355 }
2356 Ok(QueryResult::Rows {
2357 columns,
2358 rows: unique_rows,
2359 })
2360 }
2361 other => Ok(other),
2362 }
2363 }
2364
2365 PlanNode::GroupBy {
2366 input,
2367 keys,
2368 aggregates,
2369 having,
2370 } => {
2371 if aggregates
2372 .iter()
2373 .any(|aggregate| aggregate.provenance_alias.is_some())
2374 {
2375 let input = self.materialize_rows_with_provenance(input)?;
2376 self.charge_rows(&input.rows)?;
2377 return exec_group_by_with_provenance(
2378 input,
2379 keys,
2380 aggregates,
2381 having,
2382 self.query_memory_limit(),
2383 );
2384 }
2385 let result = self.execute_plan(input)?;
2386 match result {
2387 QueryResult::Rows { columns, rows } => {
2388 self.charge_rows(&rows)?;
2391 exec_group_by(columns, rows, keys, aggregates, having)
2392 }
2393 _ => Err("group by requires row input".into()),
2394 }
2395 }
2396
2397 PlanNode::CreateTable {
2398 name,
2399 fields,
2400 if_not_exists,
2401 } => {
2402 if self.catalog.schema(name).is_some() {
2406 if *if_not_exists {
2407 return Ok(QueryResult::Executed {
2408 message: format!("type '{name}' already exists (skipped)"),
2409 });
2410 }
2411 return Err(QueryError::Execution(format!(
2414 "cannot create type '{name}': it already exists"
2415 )));
2416 }
2417 let columns: Vec<ColumnDef> = fields
2418 .iter()
2419 .enumerate()
2420 .map(|(i, f)| -> Result<ColumnDef, QueryError> {
2421 Ok(ColumnDef {
2422 name: f.name.clone(),
2423 type_id: type_name_to_id(&f.type_name)
2424 .map_err(QueryError::TypeError)?,
2425 required: f.required,
2426 position: i as u16,
2427 })
2428 })
2429 .collect::<Result<Vec<_>, _>>()?;
2430 let mut defaults: Vec<Option<Value>> = vec![None; columns.len()];
2434 let mut auto_cols: Vec<bool> = vec![false; columns.len()];
2435 for (i, f) in fields.iter().enumerate() {
2436 if let Some(lit) = &f.default {
2437 let raw = literal_value_from(lit);
2438 defaults[i] = Some(coerce_value(raw, &columns[i])?);
2439 }
2440 if f.auto {
2441 if columns[i].type_id != TypeId::Int {
2445 return Err(QueryError::TypeError(format!(
2446 "auto column '{}' must be of type int",
2447 f.name
2448 )));
2449 }
2450 if f.default.is_some() {
2451 return Err(QueryError::TypeError(format!(
2452 "auto column '{}' cannot also declare a default",
2453 f.name
2454 )));
2455 }
2456 auto_cols[i] = true;
2457 }
2458 }
2459 let schema = Schema {
2460 table_name: name.clone(),
2461 columns,
2462 };
2463 self.catalog
2464 .create_table_full(schema, defaults, auto_cols)
2465 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2466 for f in fields.iter().filter(|f| f.unique) {
2469 self.catalog
2470 .create_index_unique(name, &f.name, true)
2471 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2472 }
2473 Ok(QueryResult::Created(name.clone()))
2474 }
2475
2476 PlanNode::AlterTable { table, action } => match action {
2477 AlterAction::AddColumn {
2478 name,
2479 type_name,
2480 required,
2481 } => {
2482 let position = self
2483 .catalog
2484 .schema(table)
2485 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
2486 .columns
2487 .len() as u16;
2488 let col = ColumnDef {
2489 name: name.clone(),
2490 type_id: type_name_to_id(type_name).map_err(QueryError::TypeError)?,
2491 required: *required,
2492 position,
2493 };
2494 self.catalog
2495 .alter_table_add_column(table, col)
2496 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2497 Ok(QueryResult::Executed {
2498 message: format!("column '{name}' added to '{table}'"),
2499 })
2500 }
2501 AlterAction::DropColumn { name, if_exists } => {
2502 if *if_exists {
2505 let present = self
2506 .catalog
2507 .schema(table)
2508 .map(|s| s.column_index(name).is_some())
2509 .unwrap_or(false);
2510 if !present {
2511 return Ok(QueryResult::Executed {
2512 message: format!(
2513 "column '{name}' does not exist on '{table}' (skipped)"
2514 ),
2515 });
2516 }
2517 }
2518 self.catalog
2519 .alter_table_drop_column(table, name)
2520 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2521 Ok(QueryResult::Executed {
2522 message: format!("column '{name}' dropped from '{table}'"),
2523 })
2524 }
2525 AlterAction::AddIndex {
2526 target,
2527 if_not_exists: _,
2528 } => {
2529 let IndexTarget::Column(column) = target else {
2530 let IndexTarget::JsonPath(path) = target else {
2531 unreachable!("index target variants are exhaustive")
2532 };
2533 if let Some(existing) = resolve_expression_index(&self.catalog, table, path)
2534 {
2535 return Ok(QueryResult::Executed {
2536 message: format!(
2537 "expression index {} on '{}' already exists (skipped)",
2538 existing.index_id, table
2539 ),
2540 });
2541 }
2542 crate::cancel::check()?;
2543 let index_id = self
2544 .catalog
2545 .create_expression_index_metadata(
2546 table,
2547 1,
2548 path.canonical_text(),
2549 path.clone(),
2550 false,
2551 )
2552 .map_err(|error| QueryError::StorageError(error.to_string()))?;
2553 return Ok(QueryResult::Executed {
2554 message: format!("expression index {index_id} on '{}' created", table),
2555 });
2556 };
2557 crate::cancel::check()?;
2561 self.catalog
2562 .create_index(table, column)
2563 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2564 Ok(QueryResult::Executed {
2565 message: format!("index on '{table}.{column}' created"),
2566 })
2567 }
2568 AlterAction::AddUnique {
2569 target,
2570 if_not_exists,
2571 } => {
2572 let IndexTarget::Column(column) = target else {
2573 let IndexTarget::JsonPath(path) = target else {
2574 unreachable!("index target variants are exhaustive")
2575 };
2576 if let Some(existing) = resolve_expression_index(&self.catalog, table, path)
2577 {
2578 if *if_not_exists {
2579 return Ok(QueryResult::Executed {
2580 message: format!(
2581 "expression index {} on '{}' already exists (skipped)",
2582 existing.index_id, table
2583 ),
2584 });
2585 }
2586 return Err(QueryError::Execution(format!(
2587 "cannot add unique expression index on {}: path already indexed",
2588 table
2589 )));
2590 }
2591 crate::cancel::check()?;
2592 let index_id = self
2593 .catalog
2594 .create_expression_index_metadata(
2595 table,
2596 1,
2597 path.canonical_text(),
2598 path.clone(),
2599 true,
2600 )
2601 .map_err(|error| QueryError::StorageError(error.to_string()))?;
2602 return Ok(QueryResult::Executed {
2603 message: format!(
2604 "unique expression index {index_id} on '{}' created",
2605 table
2606 ),
2607 });
2608 };
2609 if self.catalog.has_index(table, column) {
2612 if *if_not_exists {
2613 return Ok(QueryResult::Executed {
2614 message: format!(
2615 "index on '{table}.{column}' already exists (skipped)"
2616 ),
2617 });
2618 }
2619 return Err(QueryError::Execution(format!(
2622 "cannot add unique on {table}.{column}: column already indexed"
2623 )));
2624 }
2625 {
2628 let tbl = self
2629 .catalog
2630 .get_table(table)
2631 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2632 let col_idx = tbl.schema().column_index(column).ok_or_else(|| {
2633 QueryError::ColumnNotFound {
2634 table: table.to_string(),
2635 column: column.clone(),
2636 }
2637 })?;
2638 let mut seen = std::collections::HashSet::new();
2639 let mut cancel = CancelCheck::new();
2640 for (_, row) in tbl.scan() {
2641 cancel.tick()?;
2642 let v = &row[col_idx];
2643 if v.is_empty() {
2644 continue;
2645 }
2646 if !seen.insert(v.clone()) {
2647 return Err(QueryError::Execution(format!(
2648 "cannot add unique on {table}.{column}: \
2649 duplicate value {v:?} exists"
2650 )));
2651 }
2652 }
2653 }
2654 crate::cancel::check()?;
2655 self.catalog
2656 .create_index_unique(table, column, true)
2657 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2658 Ok(QueryResult::Executed {
2659 message: format!("unique index on '{table}.{column}' created"),
2660 })
2661 }
2662 AlterAction::DropIndex { target, if_exists } => {
2663 let IndexTarget::JsonPath(path) = target else {
2664 return Err(QueryError::Execution(
2665 "dropping stored-column indexes is not supported".to_string(),
2666 ));
2667 };
2668 let Some(existing) = resolve_expression_index(&self.catalog, table, path)
2669 else {
2670 if *if_exists {
2671 return Ok(QueryResult::Executed {
2672 message: format!(
2673 "expression index on '{}' does not exist (skipped)",
2674 table
2675 ),
2676 });
2677 }
2678 return Err(QueryError::Execution(format!(
2679 "expression index on '{}' does not exist",
2680 table
2681 )));
2682 };
2683 crate::cancel::check()?;
2684 self.catalog
2685 .drop_expression_index(table, existing.index_id)
2686 .map_err(|error| QueryError::StorageError(error.to_string()))?;
2687 Ok(QueryResult::Executed {
2688 message: format!(
2689 "expression index {} on '{}' dropped",
2690 existing.index_id, table
2691 ),
2692 })
2693 }
2694 },
2695
2696 PlanNode::DropTable { name, if_exists } => {
2697 if *if_exists && self.catalog.schema(name).is_none() {
2698 return Ok(QueryResult::Executed {
2699 message: format!("type '{name}' does not exist (skipped)"),
2700 });
2701 }
2702 self.catalog
2703 .drop_table(name)
2704 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2705 Ok(QueryResult::Executed {
2706 message: format!("table '{name}' dropped"),
2707 })
2708 }
2709
2710 PlanNode::ListTypes => self.introspect_list_types(),
2711
2712 PlanNode::Describe { table } => self.introspect_describe(table),
2713
2714 PlanNode::CreateView { name, query_text } => {
2715 self.create_view(name, query_text)?;
2716 Ok(QueryResult::Executed {
2717 message: format!("materialized view '{name}' created"),
2718 })
2719 }
2720
2721 PlanNode::RefreshView { name } => {
2722 self.refresh_view(name)?;
2723 Ok(QueryResult::Executed {
2724 message: format!("materialized view '{name}' refreshed"),
2725 })
2726 }
2727
2728 PlanNode::DropView { name, if_exists } => {
2729 if *if_exists && !self.view_registry.is_view(name) {
2730 return Ok(QueryResult::Executed {
2731 message: format!("view '{name}' does not exist (skipped)"),
2732 });
2733 }
2734 self.drop_view(name)?;
2735 Ok(QueryResult::Executed {
2736 message: format!("materialized view '{name}' dropped"),
2737 })
2738 }
2739
2740 PlanNode::Window { input, windows } => {
2741 let result = self.execute_plan(input)?;
2742 execute_window(result, windows, self.query_memory_limit)
2743 }
2744
2745 PlanNode::Union { left, right, all } => {
2746 let left_result = self.execute_plan(left)?;
2747 let right_result = self.execute_plan(right)?;
2748 let (left_cols, left_rows) = match left_result {
2749 QueryResult::Rows { columns, rows } => (columns, rows),
2750 _ => return Err("UNION requires query results on left side".into()),
2751 };
2752 let (_, right_rows) = match right_result {
2753 QueryResult::Rows { columns, rows } => (columns, rows),
2754 _ => return Err("UNION requires query results on right side".into()),
2755 };
2756 let mut combined = left_rows;
2757 let mut cancel = CancelCheck::new();
2758 if *all {
2759 for row in right_rows {
2761 cancel.tick()?;
2762 combined.push(row);
2763 }
2764 } else {
2765 let mut seen = std::collections::HashSet::new();
2768 for row in &combined {
2769 cancel.tick()?;
2770 seen.insert(row.clone());
2771 }
2772 for row in right_rows {
2773 cancel.tick()?;
2774 if seen.insert(row.clone()) {
2775 combined.push(row);
2776 }
2777 }
2778 }
2779 Ok(QueryResult::Rows {
2780 columns: left_cols,
2781 rows: combined,
2782 })
2783 }
2784
2785 PlanNode::Explain { input } => {
2786 let text = format_plan_tree(&self.catalog, input, 0);
2787 Ok(QueryResult::Rows {
2788 columns: vec!["plan".to_string()],
2789 rows: text
2790 .lines()
2791 .map(|line| vec![Value::Str(line.to_string())])
2792 .collect(),
2793 })
2794 }
2795
2796 PlanNode::Begin => {
2797 if self.in_transaction {
2798 return Err(QueryError::Execution(
2799 "already in a transaction (nested transactions not supported)".into(),
2800 ));
2801 }
2802 self.catalog
2803 .begin_transaction()
2804 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2805 self.in_transaction = true;
2806 Ok(QueryResult::Executed {
2807 message: "transaction started".to_string(),
2808 })
2809 }
2810
2811 PlanNode::Commit => {
2812 if !self.in_transaction {
2813 return Err(QueryError::Execution(
2814 "no active transaction to commit".into(),
2815 ));
2816 }
2817 self.catalog
2818 .commit_transaction()
2819 .map_err(|e| QueryError::StorageError(e.to_string()))?;
2820 self.in_transaction = false;
2821 Ok(QueryResult::Executed {
2822 message: "transaction committed".to_string(),
2823 })
2824 }
2825
2826 PlanNode::Rollback => {
2827 if !self.in_transaction {
2828 return Err(QueryError::Execution(
2829 "no active transaction to roll back".into(),
2830 ));
2831 }
2832 self.rollback_transaction_preserving_wal_archive()
2833 }
2834
2835 PlanNode::IndexScan { table, column, key } => {
2836 let key_value = literal_to_value(key)?;
2837 let tbl = self
2838 .catalog
2839 .get_table(table)
2840 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2841 let columns: Vec<String> = tbl
2842 .schema()
2843 .columns
2844 .iter()
2845 .map(|c| c.name.clone())
2846 .collect();
2847
2848 if tbl.has_index(column) {
2852 let rids = tbl.index_lookup_all(column, &key_value);
2853 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(rids.len());
2854 let mut cancel = CancelCheck::new();
2855 for rid in rids {
2856 cancel.tick()?;
2857 if let Some(row) = tbl.get(rid) {
2861 rows.push(row);
2862 }
2863 }
2864 return Ok(QueryResult::Rows { columns, rows });
2865 }
2866
2867 let schema = tbl.schema();
2875 let fast = FastLayout::new(schema);
2876 let synth_pred = Expr::BinaryOp(
2877 Box::new(Expr::Field(column.clone())),
2878 BinOp::Eq,
2879 Box::new(key.clone()),
2880 );
2881 if !tbl.has_overflow_rows() {
2884 if let Some(compiled) = compile_predicate(&synth_pred, &columns, &fast, schema)
2885 {
2886 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
2888 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
2889 if compiled(data) {
2890 rows.push(decode_row(schema, data));
2891 }
2892 })?;
2893 return Ok(QueryResult::Rows { columns, rows });
2894 }
2895 }
2896
2897 let col_idx =
2899 schema
2900 .column_index(column)
2901 .ok_or_else(|| QueryError::ColumnNotFound {
2902 table: String::new(),
2903 column: column.clone(),
2904 })?;
2905 let mut cancel = CancelCheck::new();
2906 let mut rows: Vec<Vec<Value>> = Vec::new();
2907 for (_, row) in tbl.scan() {
2908 cancel.tick()?;
2909 if row[col_idx] == key_value {
2910 rows.push(row);
2911 }
2912 }
2913 Ok(QueryResult::Rows { columns, rows })
2914 }
2915
2916 PlanNode::RangeScan {
2917 table,
2918 column,
2919 start,
2920 end,
2921 } => {
2922 let tbl = self
2923 .catalog
2924 .get_table(table)
2925 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
2926 let columns: Vec<String> = tbl
2927 .schema()
2928 .columns
2929 .iter()
2930 .map(|c| c.name.clone())
2931 .collect();
2932 let schema = tbl.schema();
2933
2934 let start_val = match start {
2935 Some((expr, _)) => Some(literal_to_value(expr)?),
2936 None => None,
2937 };
2938 let end_val = match end {
2939 Some((expr, _)) => Some(literal_to_value(expr)?),
2940 None => None,
2941 };
2942 let start_inclusive = start.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
2943 let end_inclusive = end.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
2944
2945 if tbl.is_index_unique(column) == Some(false) {
2951 if let Some(btree) = tbl.index(column) {
2952 if start_val.is_some() || end_val.is_some() {
2953 let col_idx = schema.column_index(column).ok_or_else(|| {
2954 QueryError::ColumnNotFound {
2955 table: String::new(),
2956 column: column.clone(),
2957 }
2958 })?;
2959 let rids = btree.range_rids(start_val.as_ref(), end_val.as_ref());
2960 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(rids.len());
2961 let mut cancel = CancelCheck::new();
2962 for rid in rids {
2963 cancel.tick()?;
2964 if let Some(row) = tbl.get(rid) {
2966 if !row[col_idx].is_empty()
2967 && range_matches(
2968 &row[col_idx],
2969 &start_val,
2970 start_inclusive,
2971 &end_val,
2972 end_inclusive,
2973 )
2974 {
2975 rows.push(row);
2976 }
2977 }
2978 }
2979 return Ok(QueryResult::Rows { columns, rows });
2980 }
2981 }
2982 }
2983
2984 if tbl.is_index_unique(column) == Some(true) {
2987 if let Some(btree) = tbl.index(column) {
2988 let hits: Vec<(Value, RowId)> = match (&start_val, &end_val) {
2989 (Some(s), Some(e)) => btree.range(s, e).collect(),
2990 (Some(s), None) => btree.range_from(s),
2991 (None, Some(e)) => btree.range_to(e),
2992 (None, None) => {
2993 let mut cancel = CancelCheck::new();
2994 let mut rows: Vec<Vec<Value>> = Vec::new();
2995 for (_, row) in tbl.scan() {
2996 cancel.tick()?;
2997 rows.push(row);
2998 }
2999 return Ok(QueryResult::Rows { columns, rows });
3000 }
3001 };
3002 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(hits.len());
3003 let mut cancel = CancelCheck::new();
3004 for (key, rid) in hits {
3005 cancel.tick()?;
3006 if !start_inclusive {
3007 if let Some(ref s) = start_val {
3008 if &key == s {
3009 continue;
3010 }
3011 }
3012 }
3013 if !end_inclusive {
3014 if let Some(ref e) = end_val {
3015 if &key == e {
3016 continue;
3017 }
3018 }
3019 }
3020 if let Some(row) = tbl.get(rid) {
3022 rows.push(row);
3023 }
3024 }
3025 return Ok(QueryResult::Rows { columns, rows });
3026 }
3027 }
3028
3029 let fast = FastLayout::new(schema);
3033 let synth = synthesize_range_predicate(column, start, end);
3034 if !tbl.has_overflow_rows() {
3035 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, schema) {
3036 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
3037 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3038 if compiled(data) {
3039 rows.push(decode_row(schema, data));
3040 }
3041 })?;
3042 return Ok(QueryResult::Rows { columns, rows });
3043 }
3044 }
3045
3046 let col_idx =
3047 schema
3048 .column_index(column)
3049 .ok_or_else(|| QueryError::ColumnNotFound {
3050 table: String::new(),
3051 column: column.clone(),
3052 })?;
3053 let mut cancel = CancelCheck::new();
3054 let mut rows: Vec<Vec<Value>> = Vec::new();
3055 for (_, row) in tbl.scan() {
3056 cancel.tick()?;
3057 if range_matches(
3058 &row[col_idx],
3059 &start_val,
3060 start_inclusive,
3061 &end_val,
3062 end_inclusive,
3063 ) {
3064 rows.push(row);
3065 }
3066 }
3067 Ok(QueryResult::Rows { columns, rows })
3068 }
3069 }
3070 }
3071
3072 fn create_view(&mut self, name: &str, query_text: &str) -> Result<(), QueryError> {
3077 if self.view_registry.is_view(name) {
3078 return Err(QueryError::ViewError(format!(
3079 "materialized view '{name}' already exists"
3080 )));
3081 }
3082 let result = self.execute_powql(query_text)?;
3084 let (columns, rows) = match result {
3085 QueryResult::Rows { columns, rows } => (columns, rows),
3086 _ => return Err("view source query must be a SELECT".into()),
3087 };
3088 let schema = self.derive_view_schema(name, &columns, &rows);
3090 crate::cancel::check()?;
3092 self.catalog
3093 .create_table(schema)
3094 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3095 for row in &rows {
3096 self.catalog
3097 .insert(name, row)
3098 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3099 }
3100 let depends_on = self.extract_view_deps(query_text);
3102 self.view_registry
3103 .register(ViewDef {
3104 name: name.to_string(),
3105 query: query_text.to_string(),
3106 depends_on,
3107 dirty: false,
3108 })
3109 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3110 Ok(())
3111 }
3112
3113 fn refresh_view(&mut self, name: &str) -> Result<(), QueryError> {
3116 let def = self
3117 .view_registry
3118 .get(name)
3119 .ok_or_else(|| format!("materialized view '{name}' not found"))?;
3120 let query_text = def.query.clone();
3121 let result = self.execute_powql(&query_text)?;
3123 let (_columns, rows) = match result {
3124 QueryResult::Rows { columns, rows } => (columns, rows),
3125 _ => return Err("view source query must be a SELECT".into()),
3126 };
3127 crate::cancel::check()?;
3131 self.catalog
3132 .scan_delete_matching_logged(name, |_| true)
3133 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3134 for row in &rows {
3135 self.catalog
3136 .insert(name, row)
3137 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3138 }
3139 self.view_registry.mark_clean(name);
3140 Ok(())
3141 }
3142
3143 fn drop_view(&mut self, name: &str) -> Result<(), QueryError> {
3145 if !self.view_registry.is_view(name) {
3146 return Err(QueryError::ViewError(format!(
3147 "materialized view '{name}' not found"
3148 )));
3149 }
3150 self.view_registry
3151 .unregister(name)
3152 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3153 self.catalog
3154 .drop_table(name)
3155 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3156 Ok(())
3157 }
3158
3159 fn derive_view_schema(&self, name: &str, columns: &[String], rows: &[Vec<Value>]) -> Schema {
3162 use powdb_storage::types::{ColumnDef, TypeId};
3163 let cols: Vec<ColumnDef> = columns
3164 .iter()
3165 .enumerate()
3166 .map(|(i, col_name)| {
3167 let type_id = rows
3168 .first()
3169 .and_then(|row| row.get(i))
3170 .map(|v| v.type_id())
3171 .unwrap_or(TypeId::Str);
3172 ColumnDef {
3173 name: col_name.clone(),
3174 type_id,
3175 required: false,
3176 position: i as u16,
3177 }
3178 })
3179 .collect();
3180 Schema {
3181 table_name: name.to_string(),
3182 columns: cols,
3183 }
3184 }
3185
3186 fn extract_view_deps(&self, query_text: &str) -> Vec<String> {
3189 use crate::parser::parse;
3190 match parse(query_text) {
3191 Ok(Statement::Query(q)) => {
3192 let mut deps = vec![q.source.clone()];
3193 for j in &q.joins {
3194 deps.push(j.source.clone());
3195 }
3196 deps
3197 }
3198 _ => Vec::new(),
3199 }
3200 }
3201
3202 pub(super) fn agg_single_col_fast(
3212 &self,
3213 table: &str,
3214 col: &str,
3215 function: AggFunc,
3216 predicate: Option<&Expr>,
3217 ) -> Result<Option<QueryResult>, QueryError> {
3218 if self.catalog.table_has_overflow(table) {
3222 return Ok(None);
3223 }
3224 let schema = self
3225 .catalog
3226 .schema(table)
3227 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
3228 .clone();
3229 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3230 let col_idx = match schema.column_index(col) {
3231 Some(i) => i,
3232 None => return Ok(None),
3233 };
3234 let col_type = schema.columns[col_idx].type_id;
3241 if col_type != TypeId::Int && col_type != TypeId::Float {
3242 return Ok(None);
3243 }
3244
3245 let fast = FastLayout::new(&schema);
3246 let byte_offset = match fast.fixed_offsets[col_idx] {
3251 Some(o) => o,
3252 None => return Ok(None),
3253 };
3254 let bitmap_byte = col_idx / 8;
3255 let bitmap_bit = (col_idx % 8) as u32;
3256 let body_data_offset = 2 + fast.bitmap_size + byte_offset;
3257
3258 let compiled_pred: Option<CompiledPredicate> = match predicate {
3260 Some(pred) => match compile_predicate(pred, &columns, &fast, &schema) {
3261 Some(c) => Some(c),
3262 None => return Ok(None), },
3264 None => None,
3265 };
3266
3267 let result = match col_type {
3294 TypeId::Int => match function {
3295 AggFunc::Sum | AggFunc::Avg => {
3296 let mut sum_i128: i128 = 0;
3297 let mut count: i64 = 0;
3298 agg_int_loop!(
3299 self,
3300 table,
3301 compiled_pred,
3302 bitmap_byte,
3303 bitmap_bit,
3304 body_data_offset,
3305 |v: i64| {
3306 count += 1;
3307 sum_i128 += v as i128;
3308 }
3309 );
3310 if matches!(function, AggFunc::Sum) {
3311 let clamped = sum_i128.clamp(i64::MIN as i128, i64::MAX as i128) as i64;
3312 QueryResult::Scalar(Value::Int(clamped))
3313 } else if count == 0 {
3314 QueryResult::Scalar(Value::Empty)
3315 } else {
3316 let avg = (sum_i128 as f64) / (count as f64);
3317 QueryResult::Scalar(Value::Float(avg))
3318 }
3319 }
3320 AggFunc::Min => {
3321 let mut min_v: Option<i64> = None;
3322 agg_int_loop!(
3323 self,
3324 table,
3325 compiled_pred,
3326 bitmap_byte,
3327 bitmap_bit,
3328 body_data_offset,
3329 |v: i64| {
3330 min_v = Some(match min_v {
3331 Some(m) => m.min(v),
3332 None => v,
3333 });
3334 }
3335 );
3336 QueryResult::Scalar(min_v.map(Value::Int).unwrap_or(Value::Empty))
3337 }
3338 AggFunc::Max => {
3339 let mut max_v: Option<i64> = None;
3340 agg_int_loop!(
3341 self,
3342 table,
3343 compiled_pred,
3344 bitmap_byte,
3345 bitmap_bit,
3346 body_data_offset,
3347 |v: i64| {
3348 max_v = Some(match max_v {
3349 Some(m) => m.max(v),
3350 None => v,
3351 });
3352 }
3353 );
3354 QueryResult::Scalar(max_v.map(Value::Int).unwrap_or(Value::Empty))
3355 }
3356 AggFunc::Count => {
3357 let mut count: i64 = 0;
3358 agg_int_loop!(
3359 self,
3360 table,
3361 compiled_pred,
3362 bitmap_byte,
3363 bitmap_bit,
3364 body_data_offset,
3365 |_v: i64| {
3366 count += 1;
3367 }
3368 );
3369 QueryResult::Scalar(Value::Int(count))
3370 }
3371 AggFunc::CountDistinct => {
3372 let mut seen = rustc_hash::FxHashSet::default();
3373 agg_int_loop!(
3374 self,
3375 table,
3376 compiled_pred,
3377 bitmap_byte,
3378 bitmap_bit,
3379 body_data_offset,
3380 |v: i64| {
3381 seen.insert(v);
3382 }
3383 );
3384 QueryResult::Scalar(Value::Int(seen.len() as i64))
3385 }
3386 },
3387 TypeId::Float => match function {
3388 AggFunc::Sum => {
3389 let mut sum: f64 = 0.0;
3394 agg_float_loop!(
3395 self,
3396 table,
3397 compiled_pred,
3398 bitmap_byte,
3399 bitmap_bit,
3400 body_data_offset,
3401 |v: f64| {
3402 sum += v;
3403 }
3404 );
3405 QueryResult::Scalar(Value::Float(sum))
3406 }
3407 AggFunc::Avg => {
3408 let mut sum: f64 = 0.0;
3409 let mut count: i64 = 0;
3410 agg_float_loop!(
3411 self,
3412 table,
3413 compiled_pred,
3414 bitmap_byte,
3415 bitmap_bit,
3416 body_data_offset,
3417 |v: f64| {
3418 sum += v;
3419 count += 1;
3420 }
3421 );
3422 if count == 0 {
3423 QueryResult::Scalar(Value::Empty)
3424 } else {
3425 QueryResult::Scalar(Value::Float(sum / count as f64))
3426 }
3427 }
3428 AggFunc::Min => {
3429 let mut min_v: Option<f64> = None;
3433 agg_float_loop!(
3434 self,
3435 table,
3436 compiled_pred,
3437 bitmap_byte,
3438 bitmap_bit,
3439 body_data_offset,
3440 |v: f64| {
3441 min_v = Some(match min_v {
3442 Some(m) => {
3443 if v.total_cmp(&m).is_lt() {
3444 v
3445 } else {
3446 m
3447 }
3448 }
3449 None => v,
3450 });
3451 }
3452 );
3453 QueryResult::Scalar(min_v.map(Value::Float).unwrap_or(Value::Empty))
3454 }
3455 AggFunc::Max => {
3456 let mut max_v: Option<f64> = None;
3457 agg_float_loop!(
3458 self,
3459 table,
3460 compiled_pred,
3461 bitmap_byte,
3462 bitmap_bit,
3463 body_data_offset,
3464 |v: f64| {
3465 max_v = Some(match max_v {
3466 Some(m) => {
3467 if v.total_cmp(&m).is_gt() {
3468 v
3469 } else {
3470 m
3471 }
3472 }
3473 None => v,
3474 });
3475 }
3476 );
3477 QueryResult::Scalar(max_v.map(Value::Float).unwrap_or(Value::Empty))
3478 }
3479 AggFunc::Count => {
3480 let mut count: i64 = 0;
3481 agg_float_loop!(
3482 self,
3483 table,
3484 compiled_pred,
3485 bitmap_byte,
3486 bitmap_bit,
3487 body_data_offset,
3488 |_v: f64| {
3489 count += 1;
3490 }
3491 );
3492 QueryResult::Scalar(Value::Int(count))
3493 }
3494 AggFunc::CountDistinct => {
3495 let mut seen = rustc_hash::FxHashSet::default();
3501 agg_float_loop!(
3502 self,
3503 table,
3504 compiled_pred,
3505 bitmap_byte,
3506 bitmap_bit,
3507 body_data_offset,
3508 |v: f64| {
3509 seen.insert(v.to_bits());
3510 }
3511 );
3512 QueryResult::Scalar(Value::Int(seen.len() as i64))
3513 }
3514 },
3515 _ => unreachable!("type guard above restricts to Int/Float"),
3516 };
3517 Ok(Some(result))
3518 }
3519
3520 pub(super) fn project_filter_limit_fast(
3523 &self,
3524 table: &str,
3525 fields: &[ProjectField],
3526 limit: usize,
3527 predicate: Option<&Expr>,
3528 ) -> Result<Option<QueryResult>, QueryError> {
3529 if self.catalog.table_has_overflow(table) {
3533 return Ok(None);
3534 }
3535 let schema = self
3536 .catalog
3537 .schema(table)
3538 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
3539 .clone();
3540 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3541
3542 let mut proj_indices: Vec<usize> = Vec::with_capacity(fields.len());
3545 let mut proj_columns: Vec<String> = Vec::with_capacity(fields.len());
3546 for f in fields {
3547 let name = match &f.expr {
3548 Expr::Field(n) => n.clone(),
3549 _ => return Ok(None),
3550 };
3551 let idx = match all_columns.iter().position(|c| c == &name) {
3552 Some(i) => i,
3553 None => return Ok(None),
3554 };
3555 proj_indices.push(idx);
3556 proj_columns.push(f.alias.clone().unwrap_or(name));
3557 }
3558
3559 let fast = FastLayout::new(&schema);
3560 let row_layout = RowLayout::new(&schema);
3561
3562 let compiled_pred: Option<CompiledPredicate> = match predicate {
3563 Some(pred) => match compile_predicate(pred, &all_columns, &fast, &schema) {
3564 Some(c) => Some(c),
3565 None => return Ok(None),
3566 },
3567 None => None,
3568 };
3569
3570 let mut out: Vec<Vec<Value>> = Vec::with_capacity(limit.min(1024));
3571 let mut cancel = CancelCheck::new();
3578 let mut cancel_err: Option<QueryError> = None;
3579 self.catalog
3580 .try_for_each_row_raw(table, |_rid, data| {
3581 if let Err(e) = cancel.tick() {
3582 cancel_err = Some(e);
3583 return ControlFlow::Break(());
3584 }
3585 if let Some(ref pred) = compiled_pred {
3586 if !pred(data) {
3587 return ControlFlow::Continue(());
3588 }
3589 }
3590 let row: Vec<Value> = proj_indices
3591 .iter()
3592 .map(|&ci| decode_column(&schema, &row_layout, data, ci))
3593 .collect();
3594 out.push(row);
3595 if out.len() >= limit {
3596 ControlFlow::Break(())
3597 } else {
3598 ControlFlow::Continue(())
3599 }
3600 })
3601 .map_err(|e| QueryError::StorageError(e.to_string()))?;
3602 if let Some(e) = cancel_err {
3603 return Err(e);
3604 }
3605
3606 Ok(Some(QueryResult::Rows {
3607 columns: proj_columns,
3608 rows: out,
3609 }))
3610 }
3611
3612 pub(super) fn project_filter_sort_limit_fast(
3617 &self,
3618 table: &str,
3619 fields: &[ProjectField],
3620 sort_field: &str,
3621 descending: bool,
3622 limit: usize,
3623 predicate: Option<&Expr>,
3624 ) -> Result<Option<QueryResult>, QueryError> {
3625 if self.catalog.table_has_overflow(table) {
3628 return Ok(None);
3629 }
3630 if limit == 0 {
3631 return Ok(None);
3634 }
3635 const TOPN_PREALLOC_CAP: usize = 4096;
3641 let prealloc = limit.min(TOPN_PREALLOC_CAP);
3642 let schema = self
3643 .catalog
3644 .schema(table)
3645 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?
3646 .clone();
3647 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3648
3649 let sort_idx = match schema.column_index(sort_field) {
3656 Some(i) => i,
3657 None => return Ok(None),
3658 };
3659 let sort_col_type = schema.columns[sort_idx].type_id;
3660 if sort_col_type != TypeId::Int && sort_col_type != TypeId::Float {
3661 return Ok(None);
3662 }
3663
3664 let mut proj_indices: Vec<usize> = Vec::with_capacity(fields.len());
3666 let mut proj_columns: Vec<String> = Vec::with_capacity(fields.len());
3667 for f in fields {
3668 let name = match &f.expr {
3669 Expr::Field(n) => n.clone(),
3670 _ => return Ok(None),
3671 };
3672 let idx = match all_columns.iter().position(|c| c == &name) {
3673 Some(i) => i,
3674 None => return Ok(None),
3675 };
3676 proj_indices.push(idx);
3677 proj_columns.push(f.alias.clone().unwrap_or(name));
3678 }
3679
3680 let fast = FastLayout::new(&schema);
3681 let row_layout = RowLayout::new(&schema);
3682 let sort_byte_offset = match fast.fixed_offsets[sort_idx] {
3684 Some(o) => o,
3685 None => return Ok(None),
3686 };
3687 let sort_bitmap_byte = sort_idx / 8;
3688 let sort_bitmap_bit = (sort_idx % 8) as u32;
3689 let sort_body_data_offset = 2 + fast.bitmap_size + sort_byte_offset;
3690
3691 let compiled_pred: Option<CompiledPredicate> = match predicate {
3692 Some(pred) => match compile_predicate(pred, &all_columns, &fast, &schema) {
3693 Some(c) => Some(c),
3694 None => return Ok(None),
3695 },
3696 None => None,
3697 };
3698
3699 let drained: Vec<Vec<u8>> = match sort_col_type {
3708 TypeId::Int => {
3709 let mut seq: u64 = 0;
3710 let mut heap_desc: BinaryHeap<Reverse<(i64, u64, Vec<u8>)>> =
3711 BinaryHeap::with_capacity(prealloc);
3712 let mut heap_asc: BinaryHeap<(i64, u64, Vec<u8>)> =
3713 BinaryHeap::with_capacity(prealloc);
3714 let mut null_rows: Vec<Vec<u8>> = Vec::with_capacity(prealloc);
3715
3716 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3717 if let Some(ref pred) = compiled_pred {
3718 if !pred(data) {
3719 return;
3720 }
3721 }
3722 let base = row_body_base(data);
3724 let sort_data_offset = base + sort_body_data_offset;
3725 if data.len() < sort_data_offset + 8
3726 || data.len() <= base + 2 + sort_bitmap_byte
3727 {
3728 return;
3729 }
3730 let is_null = (data[base + 2 + sort_bitmap_byte] >> sort_bitmap_bit) & 1 == 1;
3731 let id = seq;
3732 seq += 1;
3733 if is_null {
3734 if null_rows.len() < limit {
3735 null_rows.push(data.to_vec());
3736 }
3737 return;
3738 }
3739 let key = i64::from_le_bytes(
3740 data[sort_data_offset..sort_data_offset + 8]
3741 .try_into()
3742 .unwrap_or_else(|_| unreachable!()),
3743 );
3744 if descending {
3745 if heap_desc.len() < limit {
3746 heap_desc.push(Reverse((key, id, data.to_vec())));
3747 } else if let Some(Reverse((top_key, _, _))) = heap_desc.peek() {
3748 if key > *top_key {
3749 heap_desc.pop();
3750 heap_desc.push(Reverse((key, id, data.to_vec())));
3751 }
3752 }
3753 } else if heap_asc.len() < limit {
3754 heap_asc.push((key, id, data.to_vec()));
3755 } else if let Some((top_key, _, _)) = heap_asc.peek() {
3756 if key < *top_key {
3757 heap_asc.pop();
3758 heap_asc.push((key, id, data.to_vec()));
3759 }
3760 }
3761 })?;
3762
3763 let mut drained: Vec<(i64, u64, Vec<u8>)> = if descending {
3764 heap_desc.into_iter().map(|Reverse(t)| t).collect()
3765 } else {
3766 heap_asc.into_iter().collect()
3767 };
3768 if descending {
3769 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3770 b.0.cmp(&a.0).then(a.1.cmp(&b.1))
3771 })?;
3772 } else {
3773 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3774 a.0.cmp(&b.0).then(a.1.cmp(&b.1))
3775 })?;
3776 }
3777 let mut rows: Vec<Vec<u8>> = drained.into_iter().map(|(_, _, d)| d).collect();
3778 rows.extend(null_rows.into_iter().take(limit.saturating_sub(rows.len())));
3779 rows
3780 }
3781 TypeId::Float => {
3782 let mut seq: u64 = 0;
3791 let mut heap_desc: BinaryHeap<Reverse<(u64, u64, Vec<u8>)>> =
3792 BinaryHeap::with_capacity(prealloc);
3793 let mut heap_asc: BinaryHeap<(u64, u64, Vec<u8>)> =
3794 BinaryHeap::with_capacity(prealloc);
3795 let mut null_rows: Vec<Vec<u8>> = Vec::with_capacity(prealloc);
3796
3797 for_each_row_raw_cancellable(&self.catalog, table, |_rid, data| {
3798 if let Some(ref pred) = compiled_pred {
3799 if !pred(data) {
3800 return;
3801 }
3802 }
3803 let base = row_body_base(data);
3804 let sort_data_offset = base + sort_body_data_offset;
3805 if data.len() < sort_data_offset + 8
3806 || data.len() <= base + 2 + sort_bitmap_byte
3807 {
3808 return;
3809 }
3810 let is_null = (data[base + 2 + sort_bitmap_byte] >> sort_bitmap_bit) & 1 == 1;
3811 let id = seq;
3812 seq += 1;
3813 if is_null {
3814 if null_rows.len() < limit {
3815 null_rows.push(data.to_vec());
3816 }
3817 return;
3818 }
3819 let bits = u64::from_le_bytes(
3820 data[sort_data_offset..sort_data_offset + 8]
3821 .try_into()
3822 .unwrap_or_else(|_| unreachable!()),
3823 );
3824 let key = f64_bits_to_sortable_u64(bits);
3825 if descending {
3826 if heap_desc.len() < limit {
3827 heap_desc.push(Reverse((key, id, data.to_vec())));
3828 } else if let Some(Reverse((top_key, _, _))) = heap_desc.peek() {
3829 if key > *top_key {
3830 heap_desc.pop();
3831 heap_desc.push(Reverse((key, id, data.to_vec())));
3832 }
3833 }
3834 } else if heap_asc.len() < limit {
3835 heap_asc.push((key, id, data.to_vec()));
3836 } else if let Some((top_key, _, _)) = heap_asc.peek() {
3837 if key < *top_key {
3838 heap_asc.pop();
3839 heap_asc.push((key, id, data.to_vec()));
3840 }
3841 }
3842 })?;
3843
3844 let mut drained: Vec<(u64, u64, Vec<u8>)> = if descending {
3845 heap_desc.into_iter().map(|Reverse(t)| t).collect()
3846 } else {
3847 heap_asc.into_iter().collect()
3848 };
3849 if descending {
3850 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3851 b.0.cmp(&a.0).then(a.1.cmp(&b.1))
3852 })?;
3853 } else {
3854 cooperative_stable_sort_by(&mut drained, self.query_memory_limit, |a, b| {
3855 a.0.cmp(&b.0).then(a.1.cmp(&b.1))
3856 })?;
3857 }
3858 let mut rows: Vec<Vec<u8>> = drained.into_iter().map(|(_, _, d)| d).collect();
3859 rows.extend(null_rows.into_iter().take(limit.saturating_sub(rows.len())));
3860 rows
3861 }
3862 _ => unreachable!("type guard above restricts to Int/Float"),
3863 };
3864
3865 let mut cancel = CancelCheck::new();
3866 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(drained.len());
3867 for data in drained {
3868 cancel.tick()?;
3869 rows.push(
3870 proj_indices
3871 .iter()
3872 .map(|&ci| decode_column(&schema, &row_layout, &data, ci))
3873 .collect(),
3874 );
3875 }
3876
3877 Ok(Some(QueryResult::Rows {
3878 columns: proj_columns,
3879 rows,
3880 }))
3881 }
3882
3883 fn try_fused_scan_update(
3900 &mut self,
3901 table: &str,
3902 predicate: &Expr,
3903 resolved: &[(usize, Value)],
3904 changed_cols: &[usize],
3905 ) -> Option<Result<QueryResult, QueryError>> {
3906 if self.catalog.table_has_overflow(table) {
3912 return None;
3913 }
3914 let compiled = {
3917 let schema = self.catalog.schema(table)?;
3918 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3919 let fast = FastLayout::new(schema);
3920 compile_predicate(predicate, &columns, &fast, schema)?
3921 };
3922
3923 let fixed_patches: Option<Vec<FastPatch>> = {
3925 let tbl = self.catalog.get_table(table)?;
3926 let schema = tbl.schema();
3927 let all_fixed_nonnull = resolved
3928 .iter()
3929 .all(|(idx, val)| is_fixed_size(schema.columns[*idx].type_id) && !val.is_empty());
3930 let no_indexed = !resolved.iter().any(|(idx, _)| tbl.has_indexed_col(*idx));
3931 if all_fixed_nonnull && no_indexed {
3932 let layout = RowLayout::new(schema);
3933 let bitmap_size = layout.bitmap_size();
3934 Some(
3935 resolved
3936 .iter()
3937 .map(|(idx, val)| {
3938 let fixed_off = layout
3939 .fixed_offset(*idx)
3940 .expect("is_fixed_size already checked");
3941 let field_off = 2 + bitmap_size + fixed_off;
3942 let bytes: FixedBytes = match val {
3943 Value::Int(v) => FixedBytes::I64(v.to_le_bytes()),
3944 Value::Float(v) => FixedBytes::F64(v.to_le_bytes()),
3945 Value::Bool(v) => FixedBytes::Bool(if *v { 1 } else { 0 }),
3946 Value::DateTime(v) => FixedBytes::I64(v.to_le_bytes()),
3947 Value::Uuid(v) => FixedBytes::Uuid(*v),
3948 _ => unreachable!("all_fixed_nonnull guard"),
3949 };
3950 FastPatch {
3951 field_off,
3952 bitmap_byte_off: 2 + idx / 8,
3953 bit_mask: 1u8 << (idx % 8),
3954 bytes,
3955 }
3956 })
3957 .collect(),
3958 )
3959 } else {
3960 None
3961 }
3962 };
3963 if let Some(patches) = fixed_patches {
3964 let result = self
3965 .catalog
3966 .scan_patch_matching_logged(table, compiled, |row| {
3967 let base = row_body_base(row);
3968 for p in &patches {
3969 row[base + p.bitmap_byte_off] &= !p.bit_mask;
3970 let field_bytes = p.bytes.as_slice();
3971 row[base + p.field_off..base + p.field_off + field_bytes.len()]
3972 .copy_from_slice(field_bytes);
3973 }
3974 Some(row.len() as u16)
3975 })
3976 .map_err(|e| e.to_string());
3977 match result {
3978 Ok((count, _)) => {
3979 self.view_registry.mark_dependents_dirty(table);
3980 return Some(Ok(QueryResult::Modified(count)));
3981 }
3982 Err(e) => return Some(Err(QueryError::Execution(e))),
3983 }
3984 }
3985
3986 let var_patch: Option<(usize, Option<Vec<u8>>)> = {
3988 let tbl = self.catalog.get_table(table)?;
3989 let schema = tbl.schema();
3990 let is_single = resolved.len() == 1;
3991 let is_var = is_single && !is_fixed_size(schema.columns[resolved[0].0].type_id);
3992 let no_indexed = !resolved.iter().any(|(idx, _)| tbl.has_indexed_col(*idx));
3993 if is_single && is_var && no_indexed {
3994 let (idx, val) = &resolved[0];
3995 let bytes_opt = match val {
3996 Value::Str(s) => Some(s.as_bytes().to_vec()),
3997 Value::Bytes(b) => Some(b.clone()),
3998 Value::Empty => None,
3999 _ => return None, };
4001 Some((*idx, bytes_opt))
4002 } else {
4003 None
4004 }
4005 };
4006 if let Some((col_idx, ref new_bytes_opt)) = var_patch {
4007 let layout = {
4009 let schema = self.catalog.schema(table)?;
4010 RowLayout::new(schema)
4011 };
4012 let new_bytes_ref: Option<&[u8]> = new_bytes_opt.as_deref();
4013 let result = self
4014 .catalog
4015 .scan_patch_matching_logged(table, compiled, |row| {
4016 patch_var_column_in_place(row, &layout, col_idx, new_bytes_ref)
4017 })
4018 .map_err(|e| e.to_string());
4019 match result {
4020 Ok((mut count, fallback_rids)) => {
4021 for rid in fallback_rids {
4023 let mut row = match self.catalog.get(table, rid) {
4024 Some(r) => r,
4025 None => continue,
4026 };
4027 for (idx, val) in resolved.iter() {
4028 row[*idx] = val.clone();
4029 }
4030 if let Err(e) =
4031 self.catalog
4032 .update_hinted(table, rid, &row, Some(changed_cols))
4033 {
4034 return Some(Err(QueryError::StorageError(e.to_string())));
4035 }
4036 count += 1;
4037 }
4038 self.view_registry.mark_dependents_dirty(table);
4039 return Some(Ok(QueryResult::Modified(count)));
4040 }
4041 Err(e) => return Some(Err(QueryError::Execution(e))),
4042 }
4043 }
4044
4045 None }
4047
4048 fn collect_rids_for_mutation(
4054 &mut self,
4055 input: &PlanNode,
4056 table: &str,
4057 ) -> Result<Vec<RowId>, QueryError> {
4058 if self.catalog.table_has_overflow(table) {
4066 if let Some(rids) = self.collect_rids_decoded(input, table)? {
4067 return Ok(rids);
4068 }
4069 }
4070 match input {
4071 PlanNode::SeqScan { table: t } if t == table => {
4072 let mut cancel = CancelCheck::new();
4074 let mut rids: Vec<RowId> = Vec::new();
4075 for (rid, _) in self
4076 .catalog
4077 .scan(table)
4078 .map_err(|e| QueryError::StorageError(e.to_string()))?
4079 {
4080 cancel.tick()?;
4081 rids.push(rid);
4082 }
4083 Ok(rids)
4084 }
4085 PlanNode::IndexScan {
4086 table: t,
4087 column,
4088 key,
4089 } if t == table => {
4090 let key_value = literal_to_value(key)?;
4091
4092 {
4101 let tbl = self
4102 .catalog
4103 .get_table(table)
4104 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4105 if tbl.has_index(column) {
4106 let rids = tbl.index_lookup_all(column, &key_value);
4107 return Ok(rids);
4108 }
4109 }
4110
4111 let schema = self
4116 .catalog
4117 .schema(table)
4118 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4119 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4120 let fast = FastLayout::new(schema);
4121 let synth = Expr::BinaryOp(
4122 Box::new(Expr::Field(column.clone())),
4123 BinOp::Eq,
4124 Box::new(key.clone()),
4125 );
4126 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, schema) {
4127 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4129 let mut cancel = CancelCheck::new();
4130 let mut cancel_err: Option<QueryError> = None;
4131 self.catalog
4132 .try_for_each_row_raw(table, |rid, data| {
4133 if let Err(e) = cancel.tick() {
4134 cancel_err = Some(e);
4135 return ControlFlow::Break(());
4136 }
4137 if compiled(data) {
4138 rids.push(rid);
4139 }
4140 ControlFlow::Continue(())
4141 })
4142 .map_err(|e| QueryError::StorageError(e.to_string()))?;
4143 if let Some(e) = cancel_err {
4144 return Err(e);
4145 }
4146 return Ok(rids);
4147 }
4148
4149 let col_idx =
4151 schema
4152 .column_index(column)
4153 .ok_or_else(|| QueryError::ColumnNotFound {
4154 table: String::new(),
4155 column: column.clone(),
4156 })?;
4157 let mut cancel = CancelCheck::new();
4158 let mut rids: Vec<RowId> = Vec::new();
4159 for (rid, row) in self
4160 .catalog
4161 .scan(table)
4162 .map_err(|e| QueryError::StorageError(e.to_string()))?
4163 {
4164 cancel.tick()?;
4165 if row[col_idx] == key_value {
4166 rids.push(rid);
4167 }
4168 }
4169 Ok(rids)
4170 }
4171 PlanNode::Filter {
4172 input: inner,
4173 predicate,
4174 } => {
4175 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
4176 if t != table {
4177 return self.generic_rid_match(input, table);
4178 }
4179 let schema = self
4180 .catalog
4181 .schema(table)
4182 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4183 let columns: Vec<String> =
4184 schema.columns.iter().map(|c| c.name.clone()).collect();
4185 let fast = FastLayout::new(schema);
4186 let row_layout = RowLayout::new(schema);
4187
4188 let mut cancel = CancelCheck::new();
4192 let mut cancel_err: Option<QueryError> = None;
4193 if let Some(compiled) = compile_predicate(predicate, &columns, &fast, schema) {
4195 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4197 self.catalog
4198 .try_for_each_row_raw(table, |rid, data| {
4199 if let Err(e) = cancel.tick() {
4200 cancel_err = Some(e);
4201 return ControlFlow::Break(());
4202 }
4203 if compiled(data) {
4204 rids.push(rid);
4205 }
4206 ControlFlow::Continue(())
4207 })
4208 .map_err(|e| QueryError::StorageError(e.to_string()))?;
4209 if let Some(e) = cancel_err {
4210 return Err(e);
4211 }
4212 return Ok(rids);
4213 }
4214
4215 let pred_cols = predicate_column_indices_json(predicate, &columns);
4217 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4218 self.catalog
4219 .try_for_each_row_raw(table, |rid, data| {
4220 if let Err(e) = cancel.tick() {
4221 cancel_err = Some(e);
4222 return ControlFlow::Break(());
4223 }
4224 let pred_row = decode_selective(schema, &row_layout, data, &pred_cols);
4225 if eval_predicate(predicate, &pred_row, &columns) {
4226 rids.push(rid);
4227 }
4228 ControlFlow::Continue(())
4229 })
4230 .map_err(|e| QueryError::StorageError(e.to_string()))?;
4231 if let Some(e) = cancel_err {
4232 return Err(e);
4233 }
4234 return Ok(rids);
4235 }
4236 self.generic_rid_match(input, table)
4237 }
4238 _ => self.generic_rid_match(input, table),
4239 }
4240 }
4241
4242 fn collect_rids_decoded(
4249 &mut self,
4250 input: &PlanNode,
4251 table: &str,
4252 ) -> Result<Option<Vec<RowId>>, QueryError> {
4253 let pred: Option<Expr> = match input {
4255 PlanNode::SeqScan { table: t } if t == table => None,
4256 PlanNode::Filter {
4257 input: inner,
4258 predicate,
4259 } => match inner.as_ref() {
4260 PlanNode::SeqScan { table: t } if t == table => Some(predicate.clone()),
4261 _ => return Ok(None),
4262 },
4263 PlanNode::IndexScan {
4264 table: t,
4265 column,
4266 key,
4267 } if t == table => {
4268 let indexed = self
4271 .catalog
4272 .get_table(table)
4273 .map(|tb| tb.has_index(column))
4274 .unwrap_or(false);
4275 if indexed {
4276 return Ok(None);
4277 }
4278 Some(Expr::BinaryOp(
4279 Box::new(Expr::Field(column.clone())),
4280 BinOp::Eq,
4281 Box::new(key.clone()),
4282 ))
4283 }
4284 _ => return Ok(None),
4285 };
4286
4287 let columns: Vec<String> = {
4288 let schema = self
4289 .catalog
4290 .schema(table)
4291 .ok_or_else(|| QueryError::TableNotFound(table.to_string()))?;
4292 schema.columns.iter().map(|c| c.name.clone()).collect()
4293 };
4294 let mut rids: Vec<RowId> = Vec::new();
4295 let mut cancel = CancelCheck::new();
4296 for (rid, row) in self
4297 .catalog
4298 .scan(table)
4299 .map_err(|e| QueryError::StorageError(e.to_string()))?
4300 {
4301 cancel.tick()?;
4302 let keep = match &pred {
4303 None => true,
4304 Some(p) => eval_predicate(p, &row, &columns),
4305 };
4306 if keep {
4307 rids.push(rid);
4308 }
4309 }
4310 Ok(Some(rids))
4311 }
4312
4313 fn generic_rid_match(
4317 &mut self,
4318 input: &PlanNode,
4319 table: &str,
4320 ) -> Result<Vec<RowId>, QueryError> {
4321 let result = self.execute_plan(input)?;
4322 let rows = match result {
4323 QueryResult::Rows { rows, .. } => rows,
4324 _ => return Err("mutation source must be rows".into()),
4325 };
4326 let mut matching: Vec<RowId> = Vec::new();
4327 let mut cancel = CancelCheck::new();
4328 for (rid, row) in self
4329 .catalog
4330 .scan(table)
4331 .map_err(|e| QueryError::StorageError(e.to_string()))?
4332 {
4333 cancel.tick()?;
4334 let mut matched = false;
4335 for candidate in &rows {
4336 cancel.tick()?;
4337 if candidate == &row {
4338 matched = true;
4339 break;
4340 }
4341 }
4342 if matched {
4343 matching.push(rid);
4344 }
4345 }
4346 Ok(matching)
4347 }
4348}
4349
4350pub(super) fn execute_window(
4351 result: QueryResult,
4352 windows: &[WindowDef],
4353 memory_limit: usize,
4354) -> Result<QueryResult, QueryError> {
4355 let (mut columns, mut rows) = match result {
4356 QueryResult::Rows { columns, rows } => (columns, rows),
4357 _ => return Err("window function requires row input".into()),
4358 };
4359
4360 let mut cancel = CancelCheck::new();
4361 for wdef in windows {
4362 cancel.tick()?;
4363 let part_indices: Vec<Option<usize>> = wdef
4366 .partition_by
4367 .iter()
4368 .map(|expr| resolve_direct_group_expr(expr, &columns))
4369 .collect::<Result<Vec<_>, _>>()?;
4370
4371 let ord_indices: Vec<(Option<usize>, &Expr, bool)> = wdef
4372 .order_by
4373 .iter()
4374 .map(|sk| {
4375 resolve_direct_group_expr(&sk.expr, &columns)
4376 .map(|index| (index, &sk.expr, sk.descending))
4377 })
4378 .collect::<Result<Vec<_>, _>>()?;
4379
4380 let arg_expr = wdef.args.first();
4381 let arg_col_idx = arg_expr
4382 .map(|expr| resolve_direct_group_expr(expr, &columns))
4383 .transpose()?
4384 .flatten();
4385
4386 let n = rows.len();
4390 let mut indices: Vec<usize> = (0..n).collect();
4391 cooperative_stable_sort_by(&mut indices, memory_limit, |&a, &b| {
4392 for (expr, index) in wdef.partition_by.iter().zip(&part_indices) {
4394 let av = index
4395 .map(|i| rows[a][i].clone())
4396 .unwrap_or_else(|| eval_expr(expr, &rows[a], &columns));
4397 let bv = index
4398 .map(|i| rows[b][i].clone())
4399 .unwrap_or_else(|| eval_expr(expr, &rows[b], &columns));
4400 let cmp = av.cmp(&bv);
4401 if cmp != std::cmp::Ordering::Equal {
4402 return cmp;
4403 }
4404 }
4405 for &(index, expr, desc) in &ord_indices {
4407 let av = index
4408 .map(|i| rows[a][i].clone())
4409 .unwrap_or_else(|| eval_expr(expr, &rows[a], &columns));
4410 let bv = index
4411 .map(|i| rows[b][i].clone())
4412 .unwrap_or_else(|| eval_expr(expr, &rows[b], &columns));
4413 let cmp = compare_order_values(&av, &bv, desc);
4414 if cmp != std::cmp::Ordering::Equal {
4415 return cmp;
4416 }
4417 }
4418 std::cmp::Ordering::Equal
4419 })?;
4420
4421 let whole_partition_frame = wdef.order_by.is_empty()
4429 && matches!(
4430 wdef.function,
4431 WindowFunc::Sum
4432 | WindowFunc::Avg
4433 | WindowFunc::Count
4434 | WindowFunc::Min
4435 | WindowFunc::Max
4436 );
4437 let mut partition_row_indices: Vec<usize> = Vec::new();
4440
4441 let mut win_values: Vec<Value> = vec![Value::Empty; n];
4443 let mut partition_start = 0usize;
4444 let mut running_count: i64 = 0;
4446 let mut running_int_sum: i64 = 0;
4447 let mut running_float_sum: f64 = 0.0;
4448 let mut running_saw_float = false;
4449 let mut running_min: Option<Value> = None;
4450 let mut running_max: Option<Value> = None;
4451 let mut rank_counter: i64 = 0;
4452 let mut dense_rank_counter: i64 = 0;
4453 let mut prev_order_key: Option<Vec<Value>> = None;
4454 let mut same_rank_count: i64 = 0;
4455
4456 for sorted_pos in 0..n {
4457 cancel.tick()?;
4458 let row_idx = indices[sorted_pos];
4459
4460 let new_partition = if sorted_pos == 0 {
4462 true
4463 } else {
4464 let prev_row_idx = indices[sorted_pos - 1];
4465 wdef.partition_by
4466 .iter()
4467 .zip(&part_indices)
4468 .any(|(expr, index)| {
4469 let current = index
4470 .map(|i| rows[row_idx][i].clone())
4471 .unwrap_or_else(|| eval_expr(expr, &rows[row_idx], &columns));
4472 let previous = index
4473 .map(|i| rows[prev_row_idx][i].clone())
4474 .unwrap_or_else(|| eval_expr(expr, &rows[prev_row_idx], &columns));
4475 current != previous
4476 })
4477 };
4478
4479 if new_partition {
4480 if whole_partition_frame && sorted_pos > 0 {
4484 let final_v = win_values[indices[sorted_pos - 1]].clone();
4485 for ri in partition_row_indices.drain(..) {
4486 cancel.tick()?;
4487 win_values[ri] = final_v.clone();
4488 }
4489 }
4490 partition_start = sorted_pos;
4491 running_count = 0;
4492 running_int_sum = 0;
4493 running_float_sum = 0.0;
4494 running_saw_float = false;
4495 running_min = None;
4496 running_max = None;
4497 rank_counter = 0;
4498 dense_rank_counter = 0;
4499 prev_order_key = None;
4500 same_rank_count = 0;
4501 }
4502
4503 let current_order_key: Vec<Value> = ord_indices
4505 .iter()
4506 .map(|&(index, expr, _)| {
4507 index
4508 .map(|i| rows[row_idx][i].clone())
4509 .unwrap_or_else(|| eval_expr(expr, &rows[row_idx], &columns))
4510 })
4511 .collect();
4512 let same_as_prev = prev_order_key.as_ref() == Some(¤t_order_key);
4513 let current_arg = || {
4514 arg_expr.map(|expr| {
4515 arg_col_idx
4516 .map(|index| rows[row_idx][index].clone())
4517 .unwrap_or_else(|| eval_expr(expr, &rows[row_idx], &columns))
4518 })
4519 };
4520 let count_all =
4521 arg_expr.is_none() || matches!(arg_expr, Some(Expr::Field(name)) if name == "*");
4522
4523 let value = match wdef.function {
4524 WindowFunc::RowNumber => Value::Int((sorted_pos - partition_start + 1) as i64),
4525 WindowFunc::Rank => {
4526 if same_as_prev {
4527 same_rank_count += 1;
4528 } else {
4529 rank_counter += same_rank_count + 1;
4530 same_rank_count = 0;
4531 if rank_counter == 0 {
4532 rank_counter = 1;
4533 }
4534 }
4535 Value::Int(rank_counter)
4536 }
4537 WindowFunc::DenseRank => {
4538 if !same_as_prev {
4539 dense_rank_counter += 1;
4540 }
4541 Value::Int(dense_rank_counter)
4542 }
4543 WindowFunc::Sum => {
4544 if let Some(value) = current_arg() {
4545 match value {
4546 Value::Int(v) => running_int_sum += v,
4547 Value::Float(v) => {
4548 running_float_sum += v;
4549 running_saw_float = true;
4550 }
4551 _ => {}
4552 }
4553 }
4554 if running_saw_float {
4555 Value::Float(running_float_sum + running_int_sum as f64)
4556 } else {
4557 Value::Int(running_int_sum)
4558 }
4559 }
4560 WindowFunc::Avg => {
4561 if let Some(value) = current_arg() {
4562 match value {
4563 Value::Int(v) => {
4564 running_float_sum += v as f64;
4565 running_count += 1;
4566 }
4567 Value::Float(v) => {
4568 running_float_sum += v;
4569 running_count += 1;
4570 }
4571 _ => {}
4572 }
4573 }
4574 if running_count == 0 {
4575 Value::Empty
4576 } else {
4577 Value::Float(running_float_sum / running_count as f64)
4578 }
4579 }
4580 WindowFunc::Count => {
4581 if count_all {
4582 running_count += 1;
4583 } else if let Some(value) = current_arg() {
4584 if !value.is_empty() {
4585 running_count += 1;
4586 }
4587 }
4588 Value::Int(running_count)
4589 }
4590 WindowFunc::Min => {
4591 if let Some(v) = current_arg() {
4592 if !v.is_empty() {
4593 running_min = Some(match &running_min {
4594 None => v,
4595 Some(cur) => {
4596 if v < *cur {
4597 v
4598 } else {
4599 cur.clone()
4600 }
4601 }
4602 });
4603 }
4604 }
4605 running_min.clone().unwrap_or(Value::Empty)
4606 }
4607 WindowFunc::Max => {
4608 if let Some(v) = current_arg() {
4609 if !v.is_empty() {
4610 running_max = Some(match &running_max {
4611 None => v,
4612 Some(cur) => {
4613 if v > *cur {
4614 v
4615 } else {
4616 cur.clone()
4617 }
4618 }
4619 });
4620 }
4621 }
4622 running_max.clone().unwrap_or(Value::Empty)
4623 }
4624 };
4625
4626 prev_order_key = Some(current_order_key);
4627 win_values[row_idx] = value;
4628 if whole_partition_frame {
4629 partition_row_indices.push(row_idx);
4630 }
4631 }
4632
4633 if whole_partition_frame && n > 0 {
4635 let final_v = win_values[indices[n - 1]].clone();
4636 for ri in partition_row_indices.drain(..) {
4637 cancel.tick()?;
4638 win_values[ri] = final_v.clone();
4639 }
4640 }
4641
4642 for (ri, row) in rows.iter_mut().enumerate() {
4644 cancel.tick()?;
4645 row.push(win_values[ri].clone());
4646 }
4647 columns.push(wdef.output_name.clone());
4648 }
4649
4650 Ok(QueryResult::Rows { columns, rows })
4651}
4652
4653pub(super) fn resolve_group_column(name: &str, columns: &[String]) -> Result<usize, QueryError> {
4666 if let Some(i) = columns.iter().position(|c| c == name) {
4667 return Ok(i);
4668 }
4669 if name.contains('.') {
4670 return Err(QueryError::ColumnNotFound {
4671 table: String::new(),
4672 column: name.to_string(),
4673 });
4674 }
4675 let suffix = format!(".{name}");
4676 let mut matches = columns
4677 .iter()
4678 .enumerate()
4679 .filter(|(_, c)| c.ends_with(&suffix));
4680 match matches.next() {
4681 None => Err(QueryError::ColumnNotFound {
4682 table: String::new(),
4683 column: name.to_string(),
4684 }),
4685 Some((first_idx, _)) => {
4686 let rest: Vec<&str> = matches.map(|(_, c)| c.as_str()).collect();
4687 if rest.is_empty() {
4688 Ok(first_idx)
4689 } else {
4690 let candidates: Vec<&str> = columns
4693 .iter()
4694 .filter(|c| c.ends_with(&suffix))
4695 .map(|c| c.as_str())
4696 .collect();
4697 Err(QueryError::Execution(format!(
4698 "cannot group by ambiguous column '{name}'; candidates: {}",
4699 candidates.join(", ")
4700 )))
4701 }
4702 }
4703 }
4704}
4705
4706pub(super) fn exec_group_by(
4711 columns: Vec<String>,
4712 rows: Vec<Vec<Value>>,
4713 keys: &[GroupKey],
4714 aggregates: &[GroupAgg],
4715 having: &Option<Expr>,
4716) -> Result<QueryResult, QueryError> {
4717 exec_group_by_internal(columns, rows, None, keys, aggregates, having)
4718}
4719
4720pub(super) fn exec_group_by_with_provenance(
4721 input: ProvenanceRows,
4722 keys: &[GroupKey],
4723 aggregates: &[GroupAgg],
4724 having: &Option<Expr>,
4725 memory_limit: usize,
4726) -> Result<QueryResult, QueryError> {
4727 let ProvenanceRows {
4728 columns,
4729 rows,
4730 source_aliases,
4731 provenance,
4732 } = input;
4733 exec_group_by_internal(
4734 columns,
4735 rows,
4736 Some(GroupProvenance {
4737 source_aliases,
4738 rows: provenance,
4739 memory_limit,
4740 }),
4741 keys,
4742 aggregates,
4743 having,
4744 )
4745}
4746
4747struct GroupProvenance {
4748 source_aliases: Vec<String>,
4749 rows: Vec<Vec<Option<RowId>>>,
4750 memory_limit: usize,
4751}
4752
4753fn exec_group_by_internal(
4754 columns: Vec<String>,
4755 rows: Vec<Vec<Value>>,
4756 provenance: Option<GroupProvenance>,
4757 keys: &[GroupKey],
4758 aggregates: &[GroupAgg],
4759 having: &Option<Expr>,
4760) -> Result<QueryResult, QueryError> {
4761 let key_indices: Vec<Option<usize>> = keys
4764 .iter()
4765 .map(|k| resolve_direct_group_expr(&k.expr, &columns))
4766 .collect::<Result<Vec<_>, _>>()?;
4767
4768 let agg_field_indices: Vec<Option<usize>> = aggregates
4769 .iter()
4770 .map(|a| resolve_direct_group_expr(&a.argument, &columns))
4771 .collect::<Result<Vec<_>, _>>()?;
4772 let agg_source_indices: Vec<Option<usize>> = aggregates
4773 .iter()
4774 .map(|aggregate| {
4775 aggregate
4776 .provenance_alias
4777 .as_ref()
4778 .map(|alias| {
4779 provenance
4780 .as_ref()
4781 .and_then(|provenance| {
4782 provenance
4783 .source_aliases
4784 .iter()
4785 .position(|source| source == alias)
4786 })
4787 .ok_or_else(|| {
4788 QueryError::Execution(format!(
4789 "symmetric aggregate source alias '{alias}' is not present in its input"
4790 ))
4791 })
4792 })
4793 .transpose()
4794 })
4795 .collect::<Result<Vec<_>, _>>()?;
4796
4797 let mut group_map: rustc_hash::FxHashMap<Vec<Value>, usize> = rustc_hash::FxHashMap::default();
4799 let mut groups: Vec<(Vec<Value>, Vec<usize>)> = Vec::new();
4800 let mut cancel = CancelCheck::new();
4801 for (ri, row) in rows.iter().enumerate() {
4802 cancel.tick()?;
4803 let key: Vec<Value> = keys
4804 .iter()
4805 .zip(&key_indices)
4806 .map(|(key, index)| match index {
4807 Some(index) => row[*index].clone(),
4808 None => eval_expr(&key.expr, row, &columns),
4809 })
4810 .collect();
4811 match group_map.get(&key) {
4812 Some(&idx) => groups[idx].1.push(ri),
4813 None => {
4814 let idx = groups.len();
4815 group_map.insert(key.clone(), idx);
4816 groups.push((key, vec![ri]));
4817 }
4818 }
4819 }
4820
4821 let mut out_columns: Vec<String> = keys.iter().map(|k| k.output_name()).collect();
4825 for agg in aggregates.iter() {
4826 out_columns.push(agg.output_name.clone());
4827 }
4828
4829 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(groups.len());
4831 for (key_vals, row_indices) in &groups {
4832 cancel.tick()?;
4833 let mut row = key_vals.clone();
4834 for (ai, agg) in aggregates.iter().enumerate() {
4835 let val = compute_group_aggregate(
4836 agg.function,
4837 &agg.argument,
4838 agg_field_indices[ai],
4839 GroupAggregateContext {
4840 columns: &columns,
4841 all_rows: &rows,
4842 row_indices,
4843 source_index: agg_source_indices[ai],
4844 provenance: provenance
4845 .as_ref()
4846 .map(|provenance| (provenance.rows.as_slice(), provenance.memory_limit)),
4847 },
4848 )?;
4849 row.push(val);
4850 }
4851 out_rows.push(row);
4852 }
4853
4854 if let Some(having_expr) = having {
4856 let mut filtered = Vec::with_capacity(out_rows.len());
4857 for row in out_rows {
4858 cancel.tick()?;
4859 if eval_predicate(having_expr, &row, &out_columns) {
4860 filtered.push(row);
4861 }
4862 }
4863 out_rows = filtered;
4864 }
4865
4866 Ok(QueryResult::Rows {
4867 columns: out_columns,
4868 rows: out_rows,
4869 })
4870}
4871
4872fn resolve_direct_group_expr(expr: &Expr, columns: &[String]) -> Result<Option<usize>, QueryError> {
4873 match expr {
4874 Expr::Field(name) if name == "*" => Ok(None),
4875 Expr::Field(name) => resolve_group_column(name, columns).map(Some),
4876 Expr::QualifiedField { qualifier, field } => {
4877 resolve_group_column(&format!("{qualifier}.{field}"), columns).map(Some)
4878 }
4879 _ => Ok(None),
4880 }
4881}
4882
4883pub(super) fn predicate_column_indices_json(expr: &Expr, columns: &[String]) -> Vec<usize> {
4898 let mut indices = predicate_column_indices(expr, columns);
4899 collect_json_path_base_indices(expr, columns, &mut indices);
4900 indices.sort_unstable();
4901 indices.dedup();
4902 indices
4903}
4904
4905fn collect_json_path_base_indices(expr: &Expr, columns: &[String], out: &mut Vec<usize>) {
4907 match expr {
4908 Expr::JsonPath { base, .. } => {
4909 let name = match base.as_ref() {
4910 Expr::Field(n) => n.clone(),
4911 Expr::QualifiedField { qualifier, field } => format!("{qualifier}.{field}"),
4912 other => {
4913 collect_json_path_base_indices(other, columns, out);
4914 return;
4915 }
4916 };
4917 if let Some(idx) = columns.iter().position(|c| *c == name) {
4918 out.push(idx);
4919 }
4920 }
4921 Expr::BinaryOp(l, _, r) | Expr::Coalesce(l, r) => {
4922 collect_json_path_base_indices(l, columns, out);
4923 collect_json_path_base_indices(r, columns, out);
4924 }
4925 Expr::UnaryOp(_, i) | Expr::FunctionCall(_, i, _) | Expr::Cast(i, _) => {
4926 collect_json_path_base_indices(i, columns, out);
4927 }
4928 Expr::ScalarFunc(_, args) => {
4929 for a in args {
4930 collect_json_path_base_indices(a, columns, out);
4931 }
4932 }
4933 Expr::InList { expr, list, .. } => {
4934 collect_json_path_base_indices(expr, columns, out);
4935 for item in list {
4936 collect_json_path_base_indices(item, columns, out);
4937 }
4938 }
4939 Expr::InSubquery { expr, .. } => collect_json_path_base_indices(expr, columns, out),
4940 Expr::Case { whens, else_expr } => {
4941 for (c, r) in whens {
4942 collect_json_path_base_indices(c, columns, out);
4943 collect_json_path_base_indices(r, columns, out);
4944 }
4945 if let Some(e) = else_expr {
4946 collect_json_path_base_indices(e, columns, out);
4947 }
4948 }
4949 _ => {}
4950 }
4951}
4952
4953pub(super) fn validate_json_path_types(
4964 catalog: &Catalog,
4965 plan: &PlanNode,
4966) -> Result<(), QueryError> {
4967 let mut scope: Vec<(String, TypeId)> = Vec::new();
4968 collect_scan_columns(catalog, plan, &mut scope);
4969 let mut shadowed: std::collections::HashSet<String> = std::collections::HashSet::new();
4970 collect_projected_names(plan, &mut shadowed);
4971 check_plan_json_paths(plan, &scope, &shadowed)
4972}
4973
4974fn collect_scan_columns(catalog: &Catalog, plan: &PlanNode, out: &mut Vec<(String, TypeId)>) {
4978 match plan {
4979 PlanNode::SeqScan { table }
4980 | PlanNode::IndexScan { table, .. }
4981 | PlanNode::RangeScan { table, .. } => {
4982 if let Some(schema) = catalog.schema(table) {
4983 for c in &schema.columns {
4984 out.push((c.name.clone(), c.type_id));
4985 }
4986 }
4987 }
4988 PlanNode::AliasScan { table, alias } => {
4989 if let Some(schema) = catalog.schema(table) {
4990 for c in &schema.columns {
4991 out.push((format!("{alias}.{}", c.name), c.type_id));
4992 }
4993 }
4994 }
4995 PlanNode::Filter { input, .. }
4996 | PlanNode::Project { input, .. }
4997 | PlanNode::Sort { input, .. }
4998 | PlanNode::Limit { input, .. }
4999 | PlanNode::Offset { input, .. }
5000 | PlanNode::Aggregate { input, .. }
5001 | PlanNode::Distinct { input }
5002 | PlanNode::GroupBy { input, .. }
5003 | PlanNode::Window { input, .. }
5004 | PlanNode::Update { input, .. }
5005 | PlanNode::Delete { input, .. }
5006 | PlanNode::Explain { input } => collect_scan_columns(catalog, input, out),
5007 PlanNode::NestedLoopJoin { left, right, .. } | PlanNode::Union { left, right, .. } => {
5008 collect_scan_columns(catalog, left, out);
5009 collect_scan_columns(catalog, right, out);
5010 }
5011 _ => {}
5012 }
5013}
5014
5015fn collect_projected_names(plan: &PlanNode, out: &mut std::collections::HashSet<String>) {
5018 if let PlanNode::Project { fields, .. } = plan {
5019 for f in fields {
5020 if let Some(a) = &f.alias {
5021 out.insert(a.clone());
5022 } else {
5023 match &f.expr {
5024 Expr::Field(n) => {
5025 out.insert(n.clone());
5026 }
5027 Expr::QualifiedField { qualifier, field } => {
5028 out.insert(format!("{qualifier}.{field}"));
5029 }
5030 _ => {}
5031 }
5032 }
5033 }
5034 }
5035 match plan {
5036 PlanNode::Filter { input, .. }
5037 | PlanNode::Project { input, .. }
5038 | PlanNode::Sort { input, .. }
5039 | PlanNode::Limit { input, .. }
5040 | PlanNode::Offset { input, .. }
5041 | PlanNode::Aggregate { input, .. }
5042 | PlanNode::Distinct { input }
5043 | PlanNode::GroupBy { input, .. }
5044 | PlanNode::Window { input, .. }
5045 | PlanNode::Update { input, .. }
5046 | PlanNode::Delete { input, .. }
5047 | PlanNode::Explain { input } => collect_projected_names(input, out),
5048 PlanNode::NestedLoopJoin { left, right, .. } | PlanNode::Union { left, right, .. } => {
5049 collect_projected_names(left, out);
5050 collect_projected_names(right, out);
5051 }
5052 _ => {}
5053 }
5054}
5055
5056fn resolve_scan_type(name: &str, scope: &[(String, TypeId)]) -> Option<TypeId> {
5060 let mut found: Option<TypeId> = None;
5061 for (n, t) in scope {
5062 if n == name {
5063 match found {
5064 None => found = Some(*t),
5065 Some(prev) if prev == *t => {}
5066 Some(_) => return None, }
5068 }
5069 }
5070 found
5071}
5072
5073fn json_path_base_error(
5076 base: &Expr,
5077 scope: &[(String, TypeId)],
5078 shadowed: &std::collections::HashSet<String>,
5079) -> Option<String> {
5080 let name = match base {
5081 Expr::Field(n) => n.clone(),
5082 Expr::QualifiedField { qualifier, field } => format!("{qualifier}.{field}"),
5083 _ => return None,
5086 };
5087 if shadowed.contains(&name) {
5088 return None;
5089 }
5090 match resolve_scan_type(&name, scope) {
5091 Some(TypeId::Json) | None => None,
5092 Some(other) => Some(format!(
5093 "'{}' is a {} column, not json: the '->' path operator requires a json column",
5094 name,
5095 type_id_to_name(other)
5096 )),
5097 }
5098}
5099
5100fn check_expr_json_paths(
5102 expr: &Expr,
5103 scope: &[(String, TypeId)],
5104 shadowed: &std::collections::HashSet<String>,
5105) -> Result<(), QueryError> {
5106 match expr {
5107 Expr::JsonPath { base, .. } => {
5108 if let Some(msg) = json_path_base_error(base, scope, shadowed) {
5109 return Err(QueryError::TypeError(msg));
5110 }
5111 check_expr_json_paths(base, scope, shadowed)
5112 }
5113 Expr::BinaryOp(l, _, r) | Expr::Coalesce(l, r) => {
5114 check_expr_json_paths(l, scope, shadowed)?;
5115 check_expr_json_paths(r, scope, shadowed)
5116 }
5117 Expr::UnaryOp(_, inner) | Expr::FunctionCall(_, inner, _) | Expr::Cast(inner, _) => {
5118 check_expr_json_paths(inner, scope, shadowed)
5119 }
5120 Expr::ScalarFunc(_, args) => {
5121 for a in args {
5122 check_expr_json_paths(a, scope, shadowed)?;
5123 }
5124 Ok(())
5125 }
5126 Expr::Window {
5127 args,
5128 partition_by,
5129 order_by,
5130 ..
5131 } => {
5132 for expr in args.iter().chain(partition_by) {
5133 check_expr_json_paths(expr, scope, shadowed)?;
5134 }
5135 for key in order_by {
5136 check_expr_json_paths(&key.expr, scope, shadowed)?;
5137 }
5138 Ok(())
5139 }
5140 Expr::InList { expr, list, .. } => {
5141 check_expr_json_paths(expr, scope, shadowed)?;
5142 for item in list {
5143 check_expr_json_paths(item, scope, shadowed)?;
5144 }
5145 Ok(())
5146 }
5147 Expr::Case { whens, else_expr } => {
5148 for (c, r) in whens {
5149 check_expr_json_paths(c, scope, shadowed)?;
5150 check_expr_json_paths(r, scope, shadowed)?;
5151 }
5152 if let Some(e) = else_expr {
5153 check_expr_json_paths(e, scope, shadowed)?;
5154 }
5155 Ok(())
5156 }
5157 Expr::InSubquery { expr, .. } => check_expr_json_paths(expr, scope, shadowed),
5160 _ => Ok(()),
5161 }
5162}
5163
5164fn check_plan_json_paths(
5166 plan: &PlanNode,
5167 scope: &[(String, TypeId)],
5168 shadowed: &std::collections::HashSet<String>,
5169) -> Result<(), QueryError> {
5170 match plan {
5171 PlanNode::Filter { input, predicate } => {
5172 check_expr_json_paths(predicate, scope, shadowed)?;
5173 check_plan_json_paths(input, scope, shadowed)
5174 }
5175 PlanNode::Project { input, fields } => {
5176 for f in fields {
5177 check_expr_json_paths(&f.expr, scope, shadowed)?;
5178 }
5179 check_plan_json_paths(input, scope, shadowed)
5180 }
5181 PlanNode::GroupBy {
5182 input,
5183 keys,
5184 aggregates,
5185 having,
5186 } => {
5187 for key in keys {
5188 check_expr_json_paths(&key.expr, scope, shadowed)?;
5189 }
5190 for aggregate in aggregates {
5191 check_expr_json_paths(&aggregate.argument, scope, shadowed)?;
5192 }
5193 if let Some(h) = having {
5194 check_expr_json_paths(h, scope, shadowed)?;
5195 }
5196 check_plan_json_paths(input, scope, shadowed)
5197 }
5198 PlanNode::NestedLoopJoin {
5199 left, right, on, ..
5200 } => {
5201 if let Some(on) = on {
5202 check_expr_json_paths(on, scope, shadowed)?;
5203 }
5204 check_plan_json_paths(left, scope, shadowed)?;
5205 check_plan_json_paths(right, scope, shadowed)
5206 }
5207 PlanNode::Union { left, right, .. } => {
5208 check_plan_json_paths(left, scope, shadowed)?;
5209 check_plan_json_paths(right, scope, shadowed)
5210 }
5211 PlanNode::Sort { input, keys } => {
5212 for key in keys {
5213 check_expr_json_paths(&key.expr, scope, shadowed)?;
5214 }
5215 check_plan_json_paths(input, scope, shadowed)
5216 }
5217 PlanNode::Aggregate {
5218 input, argument, ..
5219 } => {
5220 if let Some(argument) = argument {
5221 check_expr_json_paths(argument, scope, shadowed)?;
5222 }
5223 check_plan_json_paths(input, scope, shadowed)
5224 }
5225 PlanNode::Window { input, windows } => {
5226 for window in windows {
5227 for expr in window.args.iter().chain(&window.partition_by) {
5228 check_expr_json_paths(expr, scope, shadowed)?;
5229 }
5230 for key in &window.order_by {
5231 check_expr_json_paths(&key.expr, scope, shadowed)?;
5232 }
5233 }
5234 check_plan_json_paths(input, scope, shadowed)
5235 }
5236 PlanNode::Limit { input, .. }
5237 | PlanNode::Offset { input, .. }
5238 | PlanNode::Distinct { input }
5239 | PlanNode::Update { input, .. }
5240 | PlanNode::Delete { input, .. }
5241 | PlanNode::Explain { input } => check_plan_json_paths(input, scope, shadowed),
5242 _ => Ok(()),
5243 }
5244}
5245
5246pub(super) fn validate_no_stray_aggregates(plan: &PlanNode) -> Result<(), QueryError> {
5247 match plan {
5248 PlanNode::Project { input, fields } => {
5249 for f in fields {
5250 check_expr_no_aggregate(&f.expr)?;
5251 }
5252 validate_no_stray_aggregates(input)?;
5253 }
5254 PlanNode::Filter { input, predicate } => {
5255 check_expr_no_aggregate(predicate)?;
5256 validate_no_stray_aggregates(input)?;
5257 }
5258 PlanNode::GroupBy {
5259 input,
5260 keys,
5261 aggregates,
5262 having,
5263 } => {
5264 for key in keys {
5265 check_expr_no_aggregate(&key.expr)?;
5266 }
5267 for aggregate in aggregates {
5268 check_expr_no_aggregate(&aggregate.argument)?;
5269 }
5270 if let Some(h) = having {
5271 check_expr_no_aggregate(h)?;
5272 }
5273 validate_no_stray_aggregates(input)?;
5274 }
5275 PlanNode::NestedLoopJoin {
5276 left, right, on, ..
5277 } => {
5278 if let Some(on) = on {
5279 check_expr_no_aggregate(on)?;
5280 }
5281 validate_no_stray_aggregates(left)?;
5282 validate_no_stray_aggregates(right)?;
5283 }
5284 PlanNode::Union { left, right, .. } => {
5285 validate_no_stray_aggregates(left)?;
5286 validate_no_stray_aggregates(right)?;
5287 }
5288 PlanNode::Sort { input, keys } => {
5289 for key in keys {
5290 check_expr_no_aggregate(&key.expr)?;
5291 }
5292 validate_no_stray_aggregates(input)?;
5293 }
5294 PlanNode::Aggregate {
5295 input, argument, ..
5296 } => {
5297 if let Some(argument) = argument {
5298 check_expr_no_aggregate(argument)?;
5299 }
5300 validate_no_stray_aggregates(input)?;
5301 }
5302 PlanNode::Window { input, windows } => {
5303 for window in windows {
5304 for expr in window.args.iter().chain(&window.partition_by) {
5305 check_expr_no_aggregate(expr)?;
5306 }
5307 for key in &window.order_by {
5308 check_expr_no_aggregate(&key.expr)?;
5309 }
5310 }
5311 validate_no_stray_aggregates(input)?;
5312 }
5313 PlanNode::Limit { input, .. }
5314 | PlanNode::Offset { input, .. }
5315 | PlanNode::Distinct { input }
5316 | PlanNode::Update { input, .. }
5317 | PlanNode::Delete { input, .. }
5318 | PlanNode::Explain { input } => {
5319 validate_no_stray_aggregates(input)?;
5320 }
5321 _ => {}
5322 }
5323 Ok(())
5324}
5325
5326fn check_expr_no_aggregate(expr: &Expr) -> Result<(), QueryError> {
5330 match expr {
5331 Expr::FunctionCall(..) => Err(QueryError::Execution(
5332 "invalid query: aggregate function in an unsupported position".to_string(),
5333 )),
5334 Expr::BinaryOp(l, _, r) | Expr::Coalesce(l, r) => {
5335 check_expr_no_aggregate(l)?;
5336 check_expr_no_aggregate(r)
5337 }
5338 Expr::UnaryOp(_, inner) | Expr::Cast(inner, _) | Expr::JsonPath { base: inner, .. } => {
5339 check_expr_no_aggregate(inner)
5340 }
5341 Expr::ScalarFunc(_, args) => {
5342 for a in args {
5343 check_expr_no_aggregate(a)?;
5344 }
5345 Ok(())
5346 }
5347 Expr::InList { expr: e, list, .. } => {
5348 check_expr_no_aggregate(e)?;
5349 for item in list {
5350 check_expr_no_aggregate(item)?;
5351 }
5352 Ok(())
5353 }
5354 Expr::InSubquery { expr: e, .. } => check_expr_no_aggregate(e),
5355 Expr::Case { whens, else_expr } => {
5356 for (c, r) in whens {
5357 check_expr_no_aggregate(c)?;
5358 check_expr_no_aggregate(r)?;
5359 }
5360 if let Some(e) = else_expr {
5361 check_expr_no_aggregate(e)?;
5362 }
5363 Ok(())
5364 }
5365 Expr::Window {
5366 args,
5367 partition_by,
5368 order_by,
5369 ..
5370 } => {
5371 for expr in args.iter().chain(partition_by) {
5372 check_expr_no_aggregate(expr)?;
5373 }
5374 for key in order_by {
5375 check_expr_no_aggregate(&key.expr)?;
5376 }
5377 Ok(())
5378 }
5379 _ => Ok(()),
5380 }
5381}
5382
5383pub(super) fn aggregate_rows(
5387 func: AggFunc,
5388 argument: Option<&Expr>,
5389 columns: &[String],
5390 rows: &[Vec<Value>],
5391) -> Result<QueryResult, QueryError> {
5392 let mut cancel = CancelCheck::new();
5393 if func == AggFunc::Count && argument.is_none() {
5394 return Ok(QueryResult::Scalar(Value::Int(rows.len() as i64)));
5395 }
5396 let argument = argument.ok_or_else(|| {
5397 QueryError::Execution(format!(
5398 "{} requires an argument",
5399 format!("{func:?}").to_lowercase()
5400 ))
5401 })?;
5402
5403 let mut values = Vec::with_capacity(rows.len());
5404 for row in rows {
5405 cancel.tick()?;
5406 values.push(eval_expr(argument, row, columns));
5407 }
5408
5409 let value = match func {
5410 AggFunc::Count => Value::Int(values.iter().filter(|v| !v.is_empty()).count() as i64),
5411 AggFunc::CountDistinct => {
5412 let seen: std::collections::HashSet<Value> =
5413 values.into_iter().filter(|v| !v.is_empty()).collect();
5414 Value::Int(seen.len() as i64)
5415 }
5416 AggFunc::Avg => {
5417 let mut sum = 0.0;
5418 let mut count = 0_u64;
5419 for value in values {
5420 match value {
5421 Value::Int(v) => {
5422 sum += v as f64;
5423 count += 1;
5424 }
5425 Value::Float(v) => {
5426 sum += v;
5427 count += 1;
5428 }
5429 _ => {}
5430 }
5431 }
5432 if count == 0 {
5433 Value::Empty
5434 } else {
5435 Value::Float(sum / count as f64)
5436 }
5437 }
5438 AggFunc::Sum => {
5439 let mut int_sum = 0_i64;
5440 let mut float_sum = 0.0;
5441 let mut saw_float = false;
5442 for value in values {
5443 match value {
5444 Value::Int(v) => int_sum += v,
5445 Value::Float(v) => {
5446 float_sum += v;
5447 saw_float = true;
5448 }
5449 _ => {}
5450 }
5451 }
5452 if saw_float {
5453 Value::Float(float_sum + int_sum as f64)
5454 } else {
5455 Value::Int(int_sum)
5456 }
5457 }
5458 AggFunc::Min | AggFunc::Max => {
5459 let mut result: Option<Value> = None;
5460 for value in values.into_iter().filter(|v| !v.is_empty()) {
5461 let replace = match &result {
5462 None => true,
5463 Some(current) if func == AggFunc::Min => value < *current,
5464 Some(current) => value > *current,
5465 };
5466 if replace {
5467 result = Some(value);
5468 }
5469 }
5470 result.unwrap_or(Value::Empty)
5471 }
5472 };
5473 Ok(QueryResult::Scalar(value))
5474}
5475
5476const SYMMETRIC_RID_SET_ENTRY_BYTES: usize =
5477 std::mem::size_of::<RowId>() + 2 * std::mem::size_of::<usize>();
5478
5479pub(super) fn aggregate_rows_with_provenance(
5480 func: AggFunc,
5481 argument: Option<&Expr>,
5482 input: &ProvenanceRows,
5483 provenance_alias: &str,
5484 memory_limit: usize,
5485) -> Result<QueryResult, QueryError> {
5486 if matches!(func, AggFunc::Min | AggFunc::Max | AggFunc::CountDistinct) {
5487 return aggregate_rows(func, argument, &input.columns, &input.rows);
5488 }
5489 let argument = argument.ok_or_else(|| {
5490 QueryError::Execution(
5491 "symmetric aggregate requires a source-valued argument; use raw".to_string(),
5492 )
5493 })?;
5494 let source_index = input.source_index(provenance_alias).ok_or_else(|| {
5495 QueryError::Execution(format!(
5496 "symmetric aggregate source alias '{provenance_alias}' is not present in its input"
5497 ))
5498 })?;
5499 let mut seen = HashSet::new();
5500 let mut int_sum = 0_i64;
5501 let mut float_sum = 0.0_f64;
5502 let mut saw_float = false;
5503 let mut count = 0_u64;
5504 let mut cancel = CancelCheck::new();
5505 for (row, row_provenance) in input.rows.iter().zip(&input.provenance) {
5506 cancel.tick()?;
5507 let value = eval_expr(argument, row, &input.columns);
5508 if value.is_empty() {
5509 continue;
5510 }
5511 let Some(rid) = row_provenance[source_index] else {
5512 continue;
5513 };
5514 if !seen.insert(rid) {
5515 continue;
5516 }
5517 mem_budget::charge(SYMMETRIC_RID_SET_ENTRY_BYTES, memory_limit)?;
5518 match func {
5519 AggFunc::Count => count += 1,
5520 AggFunc::Sum | AggFunc::Avg => match value {
5521 Value::Int(value) => {
5522 int_sum += value;
5523 count += 1;
5524 }
5525 Value::Float(value) => {
5526 float_sum += value;
5527 saw_float = true;
5528 count += 1;
5529 }
5530 _ => {}
5531 },
5532 AggFunc::CountDistinct | AggFunc::Min | AggFunc::Max => unreachable!(),
5533 }
5534 }
5535 let value = match func {
5536 AggFunc::Count => Value::Int(count as i64),
5537 AggFunc::Sum if saw_float => Value::Float(float_sum + int_sum as f64),
5538 AggFunc::Sum => Value::Int(int_sum),
5539 AggFunc::Avg if count == 0 => Value::Empty,
5540 AggFunc::Avg => Value::Float((float_sum + int_sum as f64) / count as f64),
5541 AggFunc::CountDistinct | AggFunc::Min | AggFunc::Max => unreachable!(),
5542 };
5543 Ok(QueryResult::Scalar(value))
5544}
5545
5546pub(super) struct GroupAggregateContext<'a> {
5548 pub(super) columns: &'a [String],
5549 pub(super) all_rows: &'a [Vec<Value>],
5550 pub(super) row_indices: &'a [usize],
5551 pub(super) source_index: Option<usize>,
5552 pub(super) provenance: Option<(&'a [Vec<Option<RowId>>], usize)>,
5553}
5554
5555pub(super) fn compute_group_aggregate(
5556 func: AggFunc,
5557 argument: &Expr,
5558 direct_index: Option<usize>,
5559 context: GroupAggregateContext<'_>,
5560) -> Result<Value, QueryError> {
5561 let GroupAggregateContext {
5562 columns,
5563 all_rows,
5564 row_indices,
5565 source_index,
5566 provenance,
5567 } = context;
5568 let count_all = matches!(argument, Expr::Field(name) if name == "*");
5569 let value_at = |ri: usize| match direct_index {
5570 Some(index) => all_rows[ri][index].clone(),
5571 None => eval_expr(argument, &all_rows[ri], columns),
5572 };
5573 let mut cancel = CancelCheck::new();
5574 let mut seen_rids = HashSet::new();
5575 match func {
5576 AggFunc::Count => {
5577 if count_all {
5578 return Ok(Value::Int(row_indices.len() as i64));
5580 }
5581 let mut count = 0usize;
5582 for &ri in row_indices {
5583 cancel.tick()?;
5584 let value = value_at(ri);
5585 if !value.is_empty()
5586 && accept_symmetric_contribution(ri, source_index, provenance, &mut seen_rids)?
5587 {
5588 count += 1;
5589 }
5590 }
5591 Ok(Value::Int(count as i64))
5592 }
5593 AggFunc::CountDistinct => {
5594 let mut seen = std::collections::HashSet::new();
5595 for &ri in row_indices {
5596 cancel.tick()?;
5597 let v = value_at(ri);
5598 if !v.is_empty() {
5599 seen.insert(v);
5600 }
5601 }
5602 Ok(Value::Int(seen.len() as i64))
5603 }
5604 AggFunc::Sum => {
5605 let mut int_sum: i64 = 0;
5610 let mut float_sum: f64 = 0.0;
5611 let mut saw_float = false;
5612 for &ri in row_indices {
5613 cancel.tick()?;
5614 let value = value_at(ri);
5615 if value.is_empty()
5616 || !accept_symmetric_contribution(ri, source_index, provenance, &mut seen_rids)?
5617 {
5618 continue;
5619 }
5620 match value {
5621 Value::Int(v) => int_sum += v,
5622 Value::Float(v) => {
5623 float_sum += v;
5624 saw_float = true;
5625 }
5626 _ => {}
5627 }
5628 }
5629 if saw_float {
5630 Ok(Value::Float(float_sum + int_sum as f64))
5631 } else {
5632 Ok(Value::Int(int_sum))
5633 }
5634 }
5635 AggFunc::Avg => {
5636 let mut sum = 0.0f64;
5637 let mut count = 0usize;
5638 for &ri in row_indices {
5639 cancel.tick()?;
5640 let value = value_at(ri);
5641 if value.is_empty()
5642 || !accept_symmetric_contribution(ri, source_index, provenance, &mut seen_rids)?
5643 {
5644 continue;
5645 }
5646 match value {
5647 Value::Int(v) => {
5648 sum += v as f64;
5649 count += 1;
5650 }
5651 Value::Float(v) => {
5652 sum += v;
5653 count += 1;
5654 }
5655 _ => {}
5656 }
5657 }
5658 if count == 0 {
5659 Ok(Value::Empty)
5660 } else {
5661 Ok(Value::Float(sum / count as f64))
5662 }
5663 }
5664 AggFunc::Min | AggFunc::Max => {
5665 let mut result: Option<Value> = None;
5666 for &ri in row_indices {
5667 cancel.tick()?;
5668 let value = value_at(ri);
5669 if value.is_empty() {
5670 continue;
5671 }
5672 let replace = match &result {
5673 None => true,
5674 Some(current) if func == AggFunc::Min => value < *current,
5675 Some(current) => value > *current,
5676 };
5677 if replace {
5678 result = Some(value);
5679 }
5680 }
5681 Ok(result.unwrap_or(Value::Empty))
5682 }
5683 }
5684}
5685
5686fn accept_symmetric_contribution(
5687 row_index: usize,
5688 source_index: Option<usize>,
5689 provenance: Option<(&[Vec<Option<RowId>>], usize)>,
5690 seen: &mut HashSet<RowId>,
5691) -> Result<bool, QueryError> {
5692 let Some(source_index) = source_index else {
5693 return Ok(true);
5694 };
5695 let Some((provenance, memory_limit)) = provenance else {
5696 return Err(QueryError::Execution(
5697 "symmetric aggregate provenance is unavailable; use raw".to_string(),
5698 ));
5699 };
5700 let Some(rid) = provenance[row_index][source_index] else {
5701 return Ok(false);
5702 };
5703 if !seen.insert(rid) {
5704 return Ok(false);
5705 }
5706 mem_budget::charge(SYMMETRIC_RID_SET_ENTRY_BYTES, memory_limit)?;
5707 Ok(true)
5708}
5709
5710struct HashJoinSpec<'a> {
5711 left_key_idx: usize,
5712 right_key_idx: usize,
5713 residuals: Vec<&'a Expr>,
5714}
5715
5716struct MaterializedJoinInputs {
5717 left_columns: Vec<String>,
5718 left_rows: Vec<Vec<Value>>,
5719 right_columns: Vec<String>,
5720 right_rows: Vec<Vec<Value>>,
5721}
5722
5723fn flatten_conjunctions<'a>(expr: &'a Expr, out: &mut Vec<&'a Expr>) {
5724 match expr {
5725 Expr::BinaryOp(left, BinOp::And, right) => {
5726 flatten_conjunctions(left, out);
5727 flatten_conjunctions(right, out);
5728 }
5729 _ => out.push(expr),
5730 }
5731}
5732
5733fn try_extract_hash_join<'a>(
5737 pred: &'a Expr,
5738 left_columns: &[String],
5739 right_columns: &[String],
5740) -> Option<HashJoinSpec<'a>> {
5741 let mut conjuncts = Vec::new();
5742 flatten_conjunctions(pred, &mut conjuncts);
5743 for (key_position, conjunct) in conjuncts.iter().enumerate() {
5744 let Some((left_key_idx, right_key_idx)) =
5745 try_extract_equi_join_keys(conjunct, left_columns, right_columns)
5746 else {
5747 continue;
5748 };
5749 let residuals = conjuncts
5750 .iter()
5751 .enumerate()
5752 .filter_map(|(position, residual)| (position != key_position).then_some(*residual))
5753 .collect();
5754 return Some(HashJoinSpec {
5755 left_key_idx,
5756 right_key_idx,
5757 residuals,
5758 });
5759 }
5760 None
5761}
5762
5763pub(super) fn try_extract_equi_join_keys(
5765 pred: &Expr,
5766 left_columns: &[String],
5767 right_columns: &[String],
5768) -> Option<(usize, usize)> {
5769 let (lhs, op, rhs) = match pred {
5770 Expr::BinaryOp(l, op, r) => (l.as_ref(), *op, r.as_ref()),
5771 _ => return None,
5772 };
5773 if op != BinOp::Eq {
5774 return None;
5775 }
5776 if let (Some(li), Some(ri)) = (
5778 resolve_side_column(lhs, left_columns),
5779 resolve_side_column(rhs, right_columns),
5780 ) {
5781 return Some((li, ri));
5782 }
5783 if let (Some(li), Some(ri)) = (
5786 resolve_side_column(rhs, left_columns),
5787 resolve_side_column(lhs, right_columns),
5788 ) {
5789 return Some((li, ri));
5790 }
5791 None
5792}
5793
5794fn resolve_side_column(expr: &Expr, columns: &[String]) -> Option<usize> {
5795 match expr {
5796 Expr::QualifiedField { qualifier, field } => {
5797 let q = qualifier.as_bytes();
5802 let f = field.as_bytes();
5803 columns.iter().position(|c| {
5804 let b = c.as_bytes();
5805 b.len() == q.len() + 1 + f.len()
5806 && b[..q.len()] == *q
5807 && b[q.len()] == b'.'
5808 && b[q.len() + 1..] == *f
5809 })
5810 }
5811 Expr::Field(name) => columns.iter().position(|c| c == name),
5812 _ => None,
5813 }
5814}
5815
5816fn hash_join(
5827 inputs: MaterializedJoinInputs,
5828 left_key_idx: usize,
5829 right_key_idx: usize,
5830 kind: JoinKind,
5831 residuals: &[&Expr],
5832) -> Result<QueryResult, QueryError> {
5833 use rustc_hash::FxHashMap;
5834
5835 let MaterializedJoinInputs {
5836 left_columns,
5837 left_rows,
5838 right_columns,
5839 right_rows,
5840 } = inputs;
5841
5842 let n_left = left_columns.len();
5843 let n_right = right_columns.len();
5844 let mut columns = Vec::with_capacity(n_left + n_right);
5845 columns.extend(left_columns);
5846 columns.extend(right_columns);
5847
5848 let mut cancel = CancelCheck::new();
5851
5852 let mut build: FxHashMap<Value, Vec<usize>> =
5855 FxHashMap::with_capacity_and_hasher(right_rows.len(), Default::default());
5856 for (i, row) in right_rows.iter().enumerate() {
5857 cancel.tick()?;
5858 build.entry(row[right_key_idx].clone()).or_default().push(i);
5862 }
5863
5864 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
5867
5868 crate::cancel::check()?;
5869 for left_row in &left_rows {
5870 cancel.tick()?;
5871 let key = &left_row[left_key_idx];
5872 let candidates = build.get(key);
5873 let mut matched = false;
5874 match candidates {
5875 Some(matches) if !matches.is_empty() => {
5876 for &ri in matches {
5877 cancel.tick()?;
5878 let right_row = &right_rows[ri];
5879 let mut combined = Vec::with_capacity(n_left + n_right);
5880 combined.extend_from_slice(left_row);
5881 combined.extend_from_slice(right_row);
5882 if residuals
5883 .iter()
5884 .all(|residual| eval_predicate(residual, &combined, &columns))
5885 {
5886 rows.push(combined);
5887 check_join_limit(rows.len())?;
5888 matched = true;
5889 }
5890 }
5891 }
5892 _ => {}
5893 }
5894 if !matched && matches!(kind, JoinKind::LeftOuter) {
5895 let mut row = Vec::with_capacity(n_left + n_right);
5896 row.extend_from_slice(left_row);
5897 row.resize(n_left + n_right, Value::Empty);
5898 rows.push(row);
5899 check_join_limit(rows.len())?;
5900 }
5901 }
5902
5903 Ok(QueryResult::Rows { columns, rows })
5904}
5905
5906#[inline]
5907pub(super) fn check_nested_loop_pair_limit(
5908 left_rows: usize,
5909 right_rows: usize,
5910 pair_limit: usize,
5911) -> Result<usize, QueryError> {
5912 let candidate_pairs =
5913 left_rows
5914 .checked_mul(right_rows)
5915 .ok_or(QueryError::NestedLoopPairLimitExceeded {
5916 left_rows,
5917 right_rows,
5918 limit: pair_limit,
5919 })?;
5920 if candidate_pairs > pair_limit {
5921 return Err(QueryError::NestedLoopPairLimitExceeded {
5922 left_rows,
5923 right_rows,
5924 limit: pair_limit,
5925 });
5926 }
5927 Ok(candidate_pairs)
5928}
5929
5930pub(super) fn execute_materialized_join(
5934 left_columns: Vec<String>,
5935 left_rows: Vec<Vec<Value>>,
5936 right_columns: Vec<String>,
5937 right_rows: Vec<Vec<Value>>,
5938 on: Option<&Expr>,
5939 kind: JoinKind,
5940 pair_limit: usize,
5941) -> Result<QueryResult, QueryError> {
5942 crate::cancel::check()?;
5943 if !matches!(kind, JoinKind::Cross) {
5944 if let Some(pred) = on {
5945 if let Some(spec) = try_extract_hash_join(pred, &left_columns, &right_columns) {
5946 return hash_join(
5947 MaterializedJoinInputs {
5948 left_columns,
5949 left_rows,
5950 right_columns,
5951 right_rows,
5952 },
5953 spec.left_key_idx,
5954 spec.right_key_idx,
5955 kind,
5956 &spec.residuals,
5957 );
5958 }
5959 }
5960 }
5961
5962 check_nested_loop_pair_limit(left_rows.len(), right_rows.len(), pair_limit)?;
5963 let n_left = left_columns.len();
5964 let n_right = right_columns.len();
5965 let mut columns = Vec::with_capacity(n_left + n_right);
5966 columns.extend(left_columns);
5967 columns.extend(right_columns);
5968
5969 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
5970 let mut combined: Vec<Value> = Vec::with_capacity(n_left + n_right);
5971 let mut cancel = CancelCheck::new();
5972 for left_row in &left_rows {
5973 let mut matched = false;
5974 for right_row in &right_rows {
5975 cancel.tick()?;
5976 combined.clear();
5977 combined.extend_from_slice(left_row);
5978 combined.extend_from_slice(right_row);
5979 let keep = match kind {
5980 JoinKind::Cross => true,
5981 JoinKind::Inner | JoinKind::LeftOuter => {
5982 on.is_none_or(|pred| eval_predicate(pred, &combined, &columns))
5983 }
5984 JoinKind::RightOuter => {
5985 unreachable!("planner rewrites RightOuter to LeftOuter")
5986 }
5987 };
5988 if keep {
5989 rows.push(combined.clone());
5990 check_join_limit(rows.len())?;
5991 matched = true;
5992 }
5993 }
5994 if !matched && matches!(kind, JoinKind::LeftOuter) {
5995 let mut row = Vec::with_capacity(n_left + n_right);
5996 row.extend_from_slice(left_row);
5997 row.resize(n_left + n_right, Value::Empty);
5998 rows.push(row);
5999 check_join_limit(rows.len())?;
6000 }
6001 }
6002 Ok(QueryResult::Rows { columns, rows })
6003}
6004
6005fn execute_provenance_join(
6006 left: ProvenanceRows,
6007 right: ProvenanceRows,
6008 on: Option<&Expr>,
6009 kind: JoinKind,
6010 pair_limit: usize,
6011) -> Result<ProvenanceRows, QueryError> {
6012 let left_width = left.columns.len();
6013 let right_width = right.columns.len();
6014 let right_source_count = right.source_aliases.len();
6015 let mut columns = left.columns.clone();
6016 columns.extend(right.columns.clone());
6017 let mut source_aliases = left.source_aliases.clone();
6018 source_aliases.extend(right.source_aliases.clone());
6019 let mut rows = Vec::new();
6020 let mut provenance = Vec::new();
6021 let mut cancel = CancelCheck::new();
6022
6023 if !matches!(kind, JoinKind::Cross) {
6024 if let Some(predicate) = on {
6025 if let Some(spec) = try_extract_hash_join(predicate, &left.columns, &right.columns) {
6026 let mut build: rustc_hash::FxHashMap<Value, Vec<usize>> =
6027 rustc_hash::FxHashMap::default();
6028 for (index, row) in right.rows.iter().enumerate() {
6029 cancel.tick()?;
6030 let key = &row[spec.right_key_idx];
6031 build.entry(key.clone()).or_default().push(index);
6032 }
6033 for (left_index, left_row) in left.rows.iter().enumerate() {
6034 cancel.tick()?;
6035 let key = &left_row[spec.left_key_idx];
6036 let candidates = build.get(key);
6037 let mut matched = false;
6038 if let Some(candidates) = candidates {
6039 for &right_index in candidates {
6040 cancel.tick()?;
6041 let mut row = Vec::with_capacity(left_width + right_width);
6042 row.extend_from_slice(left_row);
6043 row.extend_from_slice(&right.rows[right_index]);
6044 if spec
6045 .residuals
6046 .iter()
6047 .all(|residual| eval_predicate(residual, &row, &columns))
6048 {
6049 let mut row_provenance = left.provenance[left_index].clone();
6050 row_provenance.extend_from_slice(&right.provenance[right_index]);
6051 rows.push(row);
6052 provenance.push(row_provenance);
6053 check_join_limit(rows.len())?;
6054 matched = true;
6055 }
6056 }
6057 }
6058 if !matched && matches!(kind, JoinKind::LeftOuter) {
6059 let mut row = left_row.clone();
6060 row.resize(left_width + right_width, Value::Empty);
6061 let mut row_provenance = left.provenance[left_index].clone();
6062 row_provenance.extend(std::iter::repeat_n(None, right_source_count));
6063 rows.push(row);
6064 provenance.push(row_provenance);
6065 check_join_limit(rows.len())?;
6066 }
6067 }
6068 return Ok(ProvenanceRows {
6069 columns,
6070 rows,
6071 source_aliases,
6072 provenance,
6073 });
6074 }
6075 }
6076 }
6077
6078 check_nested_loop_pair_limit(left.rows.len(), right.rows.len(), pair_limit)?;
6079 for (left_index, left_row) in left.rows.iter().enumerate() {
6080 let mut matched = false;
6081 for (right_index, right_row) in right.rows.iter().enumerate() {
6082 cancel.tick()?;
6083 let mut row = Vec::with_capacity(left_width + right_width);
6084 row.extend_from_slice(left_row);
6085 row.extend_from_slice(right_row);
6086 let keep = match kind {
6087 JoinKind::Cross => true,
6088 JoinKind::Inner | JoinKind::LeftOuter => {
6089 on.is_none_or(|predicate| eval_predicate(predicate, &row, &columns))
6090 }
6091 JoinKind::RightOuter => {
6092 unreachable!("planner rewrites RightOuter to LeftOuter")
6093 }
6094 };
6095 if keep {
6096 let mut row_provenance = left.provenance[left_index].clone();
6097 row_provenance.extend_from_slice(&right.provenance[right_index]);
6098 rows.push(row);
6099 provenance.push(row_provenance);
6100 check_join_limit(rows.len())?;
6101 matched = true;
6102 }
6103 }
6104 if !matched && matches!(kind, JoinKind::LeftOuter) {
6105 let mut row = left_row.clone();
6106 row.resize(left_width + right_width, Value::Empty);
6107 let mut row_provenance = left.provenance[left_index].clone();
6108 row_provenance.extend(std::iter::repeat_n(None, right_source_count));
6109 rows.push(row);
6110 provenance.push(row_provenance);
6111 check_join_limit(rows.len())?;
6112 }
6113 }
6114 Ok(ProvenanceRows {
6115 columns,
6116 rows,
6117 source_aliases,
6118 provenance,
6119 })
6120}
6121
6122pub(super) fn lower_unindexed_scans(catalog: &Catalog, plan: &PlanNode) -> PlanNode {
6136 match plan {
6137 PlanNode::ExprIndexScan { table, path, .. }
6138 | PlanNode::ExprRangeScan { table, path, .. }
6139 | PlanNode::OrderedExprIndexScan { table, path, .. } => {
6140 if resolve_expression_index(catalog, table, path).is_some() {
6141 plan.clone()
6142 } else {
6143 expression_index_fallback(plan)
6144 .expect("expression-index branch always has a fallback")
6145 }
6146 }
6147 PlanNode::RangeScan {
6148 table,
6149 column,
6150 start,
6151 end,
6152 } => {
6153 if let Some(tbl) = catalog.get_table(table) {
6154 if tbl.has_index(column) {
6160 return plan.clone();
6161 }
6162 }
6163 let pred = synthesize_range_predicate(column, start, end);
6164 PlanNode::Filter {
6165 input: Box::new(PlanNode::SeqScan {
6166 table: table.clone(),
6167 }),
6168 predicate: pred,
6169 }
6170 }
6171 PlanNode::Filter { input, predicate } => PlanNode::Filter {
6172 input: Box::new(lower_unindexed_scans(catalog, input)),
6173 predicate: predicate.clone(),
6174 },
6175 PlanNode::Project { input, fields } => PlanNode::Project {
6176 input: Box::new(lower_unindexed_scans(catalog, input)),
6177 fields: fields.clone(),
6178 },
6179 PlanNode::Sort { input, keys } => PlanNode::Sort {
6180 input: Box::new(lower_unindexed_scans(catalog, input)),
6181 keys: keys.clone(),
6182 },
6183 PlanNode::Limit { input, count } => PlanNode::Limit {
6184 input: Box::new(lower_unindexed_scans(catalog, input)),
6185 count: count.clone(),
6186 },
6187 PlanNode::Offset { input, count } => PlanNode::Offset {
6188 input: Box::new(lower_unindexed_scans(catalog, input)),
6189 count: count.clone(),
6190 },
6191 PlanNode::Aggregate {
6192 input,
6193 function,
6194 argument,
6195 mode,
6196 provenance_alias,
6197 } => PlanNode::Aggregate {
6198 input: Box::new(lower_unindexed_scans(catalog, input)),
6199 function: *function,
6200 argument: argument.clone(),
6201 mode: *mode,
6202 provenance_alias: provenance_alias.clone(),
6203 },
6204 PlanNode::Distinct { input } => PlanNode::Distinct {
6205 input: Box::new(lower_unindexed_scans(catalog, input)),
6206 },
6207 PlanNode::GroupBy {
6208 input,
6209 keys,
6210 aggregates,
6211 having,
6212 } => PlanNode::GroupBy {
6213 input: Box::new(lower_unindexed_scans(catalog, input)),
6214 keys: keys.clone(),
6215 aggregates: aggregates.clone(),
6216 having: having.clone(),
6217 },
6218 PlanNode::Update {
6219 input,
6220 table,
6221 assignments,
6222 returning,
6223 } => PlanNode::Update {
6224 input: Box::new(lower_unindexed_scans(catalog, input)),
6225 table: table.clone(),
6226 assignments: assignments.clone(),
6227 returning: *returning,
6228 },
6229 PlanNode::Delete {
6230 input,
6231 table,
6232 returning,
6233 } => PlanNode::Delete {
6234 input: Box::new(lower_unindexed_scans(catalog, input)),
6235 table: table.clone(),
6236 returning: *returning,
6237 },
6238 PlanNode::Window { input, windows } => PlanNode::Window {
6239 input: Box::new(lower_unindexed_scans(catalog, input)),
6240 windows: windows.clone(),
6241 },
6242 PlanNode::Union { left, right, all } => PlanNode::Union {
6243 left: Box::new(lower_unindexed_scans(catalog, left)),
6244 right: Box::new(lower_unindexed_scans(catalog, right)),
6245 all: *all,
6246 },
6247 PlanNode::Explain { input } => PlanNode::Explain {
6248 input: Box::new(lower_unindexed_scans(catalog, input)),
6249 },
6250 PlanNode::NestedLoopJoin {
6251 left,
6252 right,
6253 on,
6254 kind,
6255 } => PlanNode::NestedLoopJoin {
6256 left: Box::new(lower_unindexed_scans(catalog, left)),
6257 right: Box::new(lower_unindexed_scans(catalog, right)),
6258 on: on.clone(),
6259 kind: *kind,
6260 },
6261 PlanNode::IndexScan { table, column, key } => {
6262 if let Some(tbl) = catalog.get_table(table) {
6263 if tbl.has_index(column) {
6264 return plan.clone();
6265 }
6266 }
6267 PlanNode::Filter {
6268 input: Box::new(PlanNode::SeqScan {
6269 table: table.clone(),
6270 }),
6271 predicate: Expr::BinaryOp(
6272 Box::new(Expr::Field(column.clone())),
6273 BinOp::Eq,
6274 Box::new(key.clone()),
6275 ),
6276 }
6277 }
6278 _ => plan.clone(),
6280 }
6281}
6282
6283fn stored_json_path_expr(path: &powdb_storage::stored_json_path::StoredJsonPathV1) -> Expr {
6284 use powdb_storage::stored_json_path::StoredJsonPathSegmentV1;
6285
6286 Expr::JsonPath {
6287 base: Box::new(Expr::Field(path.column.clone())),
6288 segments: path
6289 .segments
6290 .iter()
6291 .map(|segment| match segment {
6292 StoredJsonPathSegmentV1::Key(key) => PathSeg::Key(key.clone()),
6293 StoredJsonPathSegmentV1::Index(index) => PathSeg::Index(*index),
6294 })
6295 .collect(),
6296 }
6297}
6298
6299fn synthesize_expr_range_predicate(
6300 path: &powdb_storage::stored_json_path::StoredJsonPathV1,
6301 start: &Option<(Expr, bool)>,
6302 end: &Option<(Expr, bool)>,
6303) -> Expr {
6304 let lower = start.as_ref().map(|(expr, inclusive)| {
6305 Expr::BinaryOp(
6306 Box::new(stored_json_path_expr(path)),
6307 if *inclusive { BinOp::Gte } else { BinOp::Gt },
6308 Box::new(expr.clone()),
6309 )
6310 });
6311 let upper = end.as_ref().map(|(expr, inclusive)| {
6312 Expr::BinaryOp(
6313 Box::new(stored_json_path_expr(path)),
6314 if *inclusive { BinOp::Lte } else { BinOp::Lt },
6315 Box::new(expr.clone()),
6316 )
6317 });
6318 match (lower, upper) {
6319 (Some(lower), Some(upper)) => Expr::BinaryOp(Box::new(lower), BinOp::And, Box::new(upper)),
6320 (Some(lower), None) => lower,
6321 (None, Some(upper)) => upper,
6322 (None, None) => Expr::Literal(Literal::Bool(true)),
6323 }
6324}
6325
6326pub(super) fn synthesize_range_predicate(
6328 column: &str,
6329 start: &Option<(Expr, bool)>,
6330 end: &Option<(Expr, bool)>,
6331) -> Expr {
6332 let lower = start.as_ref().map(|(expr, inclusive)| {
6333 let op = if *inclusive { BinOp::Gte } else { BinOp::Gt };
6334 Expr::BinaryOp(
6335 Box::new(Expr::Field(column.to_string())),
6336 op,
6337 Box::new(expr.clone()),
6338 )
6339 });
6340 let upper = end.as_ref().map(|(expr, inclusive)| {
6341 let op = if *inclusive { BinOp::Lte } else { BinOp::Lt };
6342 Expr::BinaryOp(
6343 Box::new(Expr::Field(column.to_string())),
6344 op,
6345 Box::new(expr.clone()),
6346 )
6347 });
6348 match (lower, upper) {
6349 (Some(l), Some(u)) => Expr::BinaryOp(Box::new(l), BinOp::And, Box::new(u)),
6350 (Some(l), None) => l,
6351 (None, Some(u)) => u,
6352 (None, None) => Expr::Literal(Literal::Bool(true)),
6353 }
6354}
6355
6356pub(super) fn range_matches(
6358 val: &Value,
6359 start: &Option<Value>,
6360 start_inc: bool,
6361 end: &Option<Value>,
6362 end_inc: bool,
6363) -> bool {
6364 if let Some(ref s) = start {
6365 if start_inc {
6366 if val < s {
6367 return false;
6368 }
6369 } else if val <= s {
6370 return false;
6371 }
6372 }
6373 if let Some(ref e) = end {
6374 if end_inc {
6375 if val > e {
6376 return false;
6377 }
6378 } else if val >= e {
6379 return false;
6380 }
6381 }
6382 true
6383}
6384
6385fn collect_plan_qualifiers(plan: &PlanNode, qualifiers: &mut HashSet<String>) {
6386 match plan {
6387 PlanNode::SeqScan { table }
6388 | PlanNode::IndexScan { table, .. }
6389 | PlanNode::RangeScan { table, .. }
6390 | PlanNode::ExprIndexScan { table, .. }
6391 | PlanNode::ExprRangeScan { table, .. }
6392 | PlanNode::OrderedExprIndexScan { table, .. } => {
6393 qualifiers.insert(table.clone());
6394 }
6395 PlanNode::AliasScan { alias, .. } => {
6396 qualifiers.insert(alias.clone());
6397 }
6398 PlanNode::Filter { input, .. }
6399 | PlanNode::Project { input, .. }
6400 | PlanNode::Sort { input, .. }
6401 | PlanNode::Limit { input, .. }
6402 | PlanNode::Offset { input, .. }
6403 | PlanNode::Aggregate { input, .. }
6404 | PlanNode::Distinct { input }
6405 | PlanNode::GroupBy { input, .. }
6406 | PlanNode::Update { input, .. }
6407 | PlanNode::Delete { input, .. }
6408 | PlanNode::Window { input, .. }
6409 | PlanNode::Explain { input } => collect_plan_qualifiers(input, qualifiers),
6410 PlanNode::NestedLoopJoin { left, right, .. } | PlanNode::Union { left, right, .. } => {
6411 collect_plan_qualifiers(left, qualifiers);
6412 collect_plan_qualifiers(right, qualifiers);
6413 }
6414 _ => {}
6415 }
6416}
6417
6418fn qualified_ref(expr: &Expr) -> Option<&str> {
6419 match expr {
6420 Expr::QualifiedField { qualifier, .. } => Some(qualifier),
6421 _ => None,
6422 }
6423}
6424
6425fn explain_join_strategy(
6426 left: &PlanNode,
6427 right: &PlanNode,
6428 on: Option<&Expr>,
6429 kind: JoinKind,
6430) -> &'static str {
6431 if matches!(kind, JoinKind::Cross) {
6432 return "nested-loop-bounded";
6433 }
6434 let Some(predicate) = on else {
6435 return "nested-loop-bounded";
6436 };
6437 let mut conjunctions = Vec::new();
6438 flatten_conjunctions(predicate, &mut conjunctions);
6439 let mut left_qualifiers = HashSet::new();
6440 let mut right_qualifiers = HashSet::new();
6441 collect_plan_qualifiers(left, &mut left_qualifiers);
6442 collect_plan_qualifiers(right, &mut right_qualifiers);
6443
6444 let has_cross_side_equi = conjunctions.iter().any(|expr| {
6445 let Expr::BinaryOp(lhs, BinOp::Eq, rhs) = expr else {
6446 return false;
6447 };
6448 let (Some(lhs_q), Some(rhs_q)) = (qualified_ref(lhs), qualified_ref(rhs)) else {
6449 return false;
6450 };
6451 (left_qualifiers.contains(lhs_q) && right_qualifiers.contains(rhs_q))
6452 || (left_qualifiers.contains(rhs_q) && right_qualifiers.contains(lhs_q))
6453 });
6454 if has_cross_side_equi {
6455 if conjunctions.len() > 1 {
6456 "hash+residual"
6457 } else {
6458 "hash"
6459 }
6460 } else {
6461 "nested-loop-bounded"
6462 }
6463}
6464
6465pub(super) fn format_plan_tree(catalog: &Catalog, plan: &PlanNode, depth: usize) -> String {
6468 let indent = " ".repeat(depth);
6469 match plan {
6470 PlanNode::SeqScan { table } => format!("{indent}SeqScan table={table}"),
6471 PlanNode::AliasScan { table, alias } => {
6472 format!("{indent}AliasScan table={table} alias={alias}")
6473 }
6474 PlanNode::IndexScan { table, column, key } => {
6475 format!("{indent}IndexScan table={table} column={column} key={key:?}")
6476 }
6477 PlanNode::RangeScan {
6478 table,
6479 column,
6480 start,
6481 end,
6482 } => {
6483 let s = match start {
6484 Some((expr, inc)) => {
6485 let op = if *inc { ">=" } else { ">" };
6486 format!("{op}{expr:?}")
6487 }
6488 None => "unbounded".to_string(),
6489 };
6490 let e = match end {
6491 Some((expr, inc)) => {
6492 let op = if *inc { "<=" } else { "<" };
6493 format!("{op}{expr:?}")
6494 }
6495 None => "unbounded".to_string(),
6496 };
6497 format!("{indent}RangeScan table={table} column={column} [{s}, {e}]")
6498 }
6499 PlanNode::ExprIndexScan { table, path, key } => {
6500 let index_id = resolve_expression_index(catalog, table, path)
6501 .map(|metadata| metadata.index_id.to_string())
6502 .unwrap_or_else(|| "unresolved".to_string());
6503 format!(
6504 "{indent}ExprIndexScan table={table} path={} index_id={index_id} key={key:?}",
6505 path.canonical_text()
6506 )
6507 }
6508 PlanNode::ExprRangeScan {
6509 table,
6510 path,
6511 start,
6512 end,
6513 } => {
6514 let index_id = resolve_expression_index(catalog, table, path)
6515 .map(|metadata| metadata.index_id.to_string())
6516 .unwrap_or_else(|| "unresolved".to_string());
6517 format!(
6518 "{indent}ExprRangeScan table={table} path={} index_id={index_id} start={start:?} end={end:?}",
6519 path.canonical_text()
6520 )
6521 }
6522 PlanNode::OrderedExprIndexScan {
6523 table,
6524 path,
6525 descending,
6526 limit,
6527 offset,
6528 } => {
6529 let index_id = resolve_expression_index(catalog, table, path)
6530 .map(|metadata| metadata.index_id.to_string())
6531 .unwrap_or_else(|| "unresolved".to_string());
6532 format!(
6533 "{indent}OrderedExprIndexScan table={table} path={} index_id={index_id} descending={descending} limit={limit:?} offset={offset:?}",
6534 path.canonical_text()
6535 )
6536 }
6537 PlanNode::Filter { input, predicate } => {
6538 let child = format_plan_tree(catalog, input, depth + 1);
6539 format!("{indent}Filter predicate={predicate:?}\n{child}")
6540 }
6541 PlanNode::Project { input, fields } => {
6542 let names: Vec<String> = fields
6543 .iter()
6544 .map(|f| match &f.alias {
6545 Some(a) => format!("{a}: {:?}", f.expr),
6546 None => format!("{:?}", f.expr),
6547 })
6548 .collect();
6549 let child = format_plan_tree(catalog, input, depth + 1);
6550 format!("{indent}Project fields=[{}]\n{child}", names.join(", "))
6551 }
6552 PlanNode::Sort { input, keys } => {
6553 let ks: Vec<String> = keys
6554 .iter()
6555 .map(|k| {
6556 let expr = expression_output_name(&k.expr);
6557 if k.descending {
6558 format!("{expr} desc")
6559 } else {
6560 expr
6561 }
6562 })
6563 .collect();
6564 let child = format_plan_tree(catalog, input, depth + 1);
6565 format!("{indent}Sort keys=[{}]\n{child}", ks.join(", "))
6566 }
6567 PlanNode::Limit { input, count } => {
6568 let child = format_plan_tree(catalog, input, depth + 1);
6569 format!("{indent}Limit count={count:?}\n{child}")
6570 }
6571 PlanNode::Offset { input, count } => {
6572 let child = format_plan_tree(catalog, input, depth + 1);
6573 format!("{indent}Offset count={count:?}\n{child}")
6574 }
6575 PlanNode::Aggregate {
6576 input,
6577 function,
6578 argument,
6579 mode,
6580 provenance_alias: _,
6581 } => {
6582 let argument = argument
6583 .as_ref()
6584 .map(expression_output_name)
6585 .unwrap_or_else(|| "*".to_string());
6586 let child = format_plan_tree(catalog, input, depth + 1);
6587 format!("{indent}Aggregate fn={function:?} mode={mode:?} argument={argument}\n{child}")
6588 }
6589 PlanNode::NestedLoopJoin {
6590 left,
6591 right,
6592 on,
6593 kind,
6594 } => {
6595 let left_child = format_plan_tree(catalog, left, depth + 1);
6596 let right_child = format_plan_tree(catalog, right, depth + 1);
6597 let on_str = match on {
6598 Some(pred) => format!("{pred:?}"),
6599 None => "none".to_string(),
6600 };
6601 let strategy = explain_join_strategy(left, right, on.as_ref(), *kind);
6602 format!(
6603 "{indent}NestedLoopJoin kind={kind:?} strategy={strategy} on={on_str}\n{left_child}\n{right_child}"
6604 )
6605 }
6606 PlanNode::Distinct { input } => {
6607 let child = format_plan_tree(catalog, input, depth + 1);
6608 format!("{indent}Distinct\n{child}")
6609 }
6610 PlanNode::GroupBy {
6611 input,
6612 keys,
6613 aggregates,
6614 having,
6615 } => {
6616 let agg_strs: Vec<String> = aggregates
6617 .iter()
6618 .map(|a| {
6619 format!(
6620 "{:?}({}) mode={:?} as {}",
6621 a.function,
6622 expression_output_name(&a.argument),
6623 a.mode,
6624 a.output_name
6625 )
6626 })
6627 .collect();
6628 let having_str = match having {
6629 Some(h) => format!(" having={h:?}"),
6630 None => String::new(),
6631 };
6632 let key_strs: Vec<String> = keys.iter().map(|k| k.output_name()).collect();
6633 let child = format_plan_tree(catalog, input, depth + 1);
6634 format!(
6635 "{indent}GroupBy keys=[{}] aggs=[{}]{having_str}\n{child}",
6636 key_strs.join(", "),
6637 agg_strs.join(", "),
6638 )
6639 }
6640 PlanNode::Insert { table, rows, .. } => {
6641 let cols: Vec<&str> = rows
6642 .first()
6643 .map(|r| r.iter().map(|a| a.field.as_str()).collect())
6644 .unwrap_or_default();
6645 format!(
6646 "{indent}Insert table={table} rows={} cols=[{}]",
6647 rows.len(),
6648 cols.join(", ")
6649 )
6650 }
6651 PlanNode::Upsert {
6652 table,
6653 key_column,
6654 assignments,
6655 on_conflict,
6656 } => {
6657 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
6658 let conflict_cols: Vec<&str> = on_conflict.iter().map(|a| a.field.as_str()).collect();
6659 if conflict_cols.is_empty() {
6660 format!(
6661 "{indent}Upsert table={table} key={key_column} cols=[{}]",
6662 cols.join(", ")
6663 )
6664 } else {
6665 format!(
6666 "{indent}Upsert table={table} key={key_column} cols=[{}] on_conflict=[{}]",
6667 cols.join(", "),
6668 conflict_cols.join(", ")
6669 )
6670 }
6671 }
6672 PlanNode::Update {
6673 input,
6674 table,
6675 assignments,
6676 returning,
6677 } => {
6678 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
6679 let child = format_plan_tree(catalog, input, depth + 1);
6680 let ret = if *returning { " returning" } else { "" };
6681 format!(
6682 "{indent}Update table={table} set=[{}]{ret}\n{child}",
6683 cols.join(", ")
6684 )
6685 }
6686 PlanNode::Delete {
6687 input,
6688 table,
6689 returning,
6690 } => {
6691 let child = format_plan_tree(catalog, input, depth + 1);
6692 let ret = if *returning { " returning" } else { "" };
6693 format!("{indent}Delete table={table}{ret}\n{child}")
6694 }
6695 PlanNode::CreateTable { name, fields, .. } => {
6696 let fs: Vec<String> = fields
6697 .iter()
6698 .map(|f| {
6699 let mut mods = String::new();
6700 if f.required {
6701 mods.push_str(" required");
6702 }
6703 if f.unique {
6704 mods.push_str(" unique");
6705 }
6706 format!("{}: {}{mods}", f.name, f.type_name)
6707 })
6708 .collect();
6709 format!("{indent}CreateTable name={name} fields=[{}]", fs.join(", "))
6710 }
6711 PlanNode::AlterTable { table, action } => {
6712 format!("{indent}AlterTable table={table} action={action:?}")
6713 }
6714 PlanNode::DropTable { name, .. } => format!("{indent}DropTable name={name}"),
6715 PlanNode::CreateView { name, .. } => format!("{indent}CreateView name={name}"),
6716 PlanNode::RefreshView { name } => format!("{indent}RefreshView name={name}"),
6717 PlanNode::DropView { name, .. } => format!("{indent}DropView name={name}"),
6718 PlanNode::ListTypes => format!("{indent}ListTypes"),
6719 PlanNode::Describe { table } => format!("{indent}Describe table={table}"),
6720 PlanNode::Window { input, windows } => {
6721 let ws: Vec<String> = windows
6722 .iter()
6723 .map(|w| format!("{:?} as {}", w.function, w.output_name))
6724 .collect();
6725 let child = format_plan_tree(catalog, input, depth + 1);
6726 format!("{indent}Window fns=[{}]\n{child}", ws.join(", "))
6727 }
6728 PlanNode::Union { left, right, all } => {
6729 let kind = if *all { "UNION ALL" } else { "UNION" };
6730 let left_child = format_plan_tree(catalog, left, depth + 1);
6731 let right_child = format_plan_tree(catalog, right, depth + 1);
6732 format!("{indent}{kind}\n{left_child}\n{right_child}")
6733 }
6734 PlanNode::Explain { input } => {
6735 let child = format_plan_tree(catalog, input, depth + 1);
6736 format!("{indent}Explain\n{child}")
6737 }
6738 PlanNode::Begin => format!("{indent}Begin"),
6739 PlanNode::Commit => format!("{indent}Commit"),
6740 PlanNode::Rollback => format!("{indent}Rollback"),
6741 }
6742}