1use crate::ast::*;
2use crate::canonicalize::canonicalize;
3use crate::plan::*;
4use crate::plan_cache::PlanCache;
5use crate::planner;
6use crate::result::QueryResult;
7use powdb_storage::catalog::Catalog;
8use powdb_storage::row::{decode_column, decode_row, patch_var_column_in_place, RowLayout};
9use powdb_storage::types::*;
10use powdb_storage::view::{ViewDef, ViewRegistry};
11use std::cmp::Reverse;
12use std::collections::BinaryHeap;
13use std::io;
14use std::path::Path;
15use std::sync::Mutex;
16use std::time::Instant;
17use tracing::{error, info, Level};
18
19pub const READONLY_NEEDS_WRITE: &str = "__POWDB_READONLY_NEEDS_WRITE__";
27
28const PLAN_CACHE_CAPACITY: usize = 256;
32
33const MAX_JOIN_ROWS: usize = 1_000_000;
37
38const MAX_SORT_ROWS: usize = 10_000_000;
42
43#[inline]
44fn check_join_limit(row_count: usize) -> Result<(), String> {
45 if row_count > MAX_JOIN_ROWS {
46 return Err(format!("join result exceeds {} row limit", MAX_JOIN_ROWS));
47 }
48 Ok(())
49}
50
51macro_rules! agg_int_loop {
78 (
79 $self:expr, $table:expr, $pred:expr,
80 $bmp_byte:expr, $bmp_bit:expr, $off:expr,
81 |$v:ident : i64| $body:block
82 ) => {{
83 let bmp_byte = $bmp_byte;
84 let bmp_bit = $bmp_bit;
85 let off = $off;
86 if let Some(pred) = &$pred {
87 $self
88 .catalog
89 .for_each_row_raw($table, |_rid, data| {
90 if !pred(data) {
91 return;
92 }
93 let bmp = unsafe { *data.get_unchecked(2 + bmp_byte) };
95 if (bmp >> bmp_bit) & 1 == 1 {
96 return;
97 }
98 let $v: i64 =
99 unsafe { i64::from_le_bytes(*(data.as_ptr().add(off) as *const [u8; 8])) };
100 $body
101 })
102 .map_err(|e| e.to_string())?;
103 } else {
104 $self
105 .catalog
106 .for_each_row_raw($table, |_rid, data| {
107 let bmp = unsafe { *data.get_unchecked(2 + bmp_byte) };
109 if (bmp >> bmp_bit) & 1 == 1 {
110 return;
111 }
112 let $v: i64 =
113 unsafe { i64::from_le_bytes(*(data.as_ptr().add(off) as *const [u8; 8])) };
114 $body
115 })
116 .map_err(|e| e.to_string())?;
117 }
118 }};
119}
120
121macro_rules! agg_float_loop {
122 (
123 $self:expr, $table:expr, $pred:expr,
124 $bmp_byte:expr, $bmp_bit:expr, $off:expr,
125 |$v:ident : f64| $body:block
126 ) => {{
127 let bmp_byte = $bmp_byte;
128 let bmp_bit = $bmp_bit;
129 let off = $off;
130 if let Some(pred) = &$pred {
131 $self
132 .catalog
133 .for_each_row_raw($table, |_rid, data| {
134 if !pred(data) {
135 return;
136 }
137 let bmp = unsafe { *data.get_unchecked(2 + bmp_byte) };
139 if (bmp >> bmp_bit) & 1 == 1 {
140 return;
141 }
142 let $v: f64 =
143 unsafe { f64::from_le_bytes(*(data.as_ptr().add(off) as *const [u8; 8])) };
144 $body
145 })
146 .map_err(|e| e.to_string())?;
147 } else {
148 $self
149 .catalog
150 .for_each_row_raw($table, |_rid, data| {
151 let bmp = unsafe { *data.get_unchecked(2 + bmp_byte) };
153 if (bmp >> bmp_bit) & 1 == 1 {
154 return;
155 }
156 let $v: f64 =
157 unsafe { f64::from_le_bytes(*(data.as_ptr().add(off) as *const [u8; 8])) };
158 $body
159 })
160 .map_err(|e| e.to_string())?;
161 }
162 }};
163}
164
165pub fn is_read_only_statement(stmt: &Statement) -> bool {
171 match stmt {
172 Statement::Query(_) => true,
173 Statement::Union(u) => is_read_only_statement(&u.left) && is_read_only_statement(&u.right),
174 Statement::Insert(_)
175 | Statement::Upsert(_)
176 | Statement::UpdateQuery(_)
177 | Statement::DeleteQuery(_)
178 | Statement::CreateType(_)
179 | Statement::AlterTable(_)
180 | Statement::DropTable(_)
181 | Statement::CreateView(_)
182 | Statement::RefreshView(_)
183 | Statement::DropView(_) => false,
184 Statement::Explain(inner) => is_read_only_statement(inner),
185 }
186}
187
188pub struct Engine {
189 catalog: Catalog,
190 plan_cache: Mutex<PlanCache>,
200 insert_values_scratch: Vec<Value>,
205 view_registry: ViewRegistry,
209}
210
211#[derive(Clone)]
230pub struct PreparedQuery {
231 plan_template: PlanNode,
232 pub param_count: usize,
235 insert_fast: Option<InsertFast>,
247 update_pk_fast: Option<UpdatePkFast>,
255}
256
257#[derive(Clone)]
262struct InsertFast {
263 table_slot: usize,
269 col_indices: Vec<usize>,
272 n_cols: usize,
276}
277
278#[derive(Clone)]
282struct UpdatePkFast {
283 table_slot: usize,
288 key_col: String,
294 field_off: usize,
297 bitmap_byte_off: usize,
300 bit_mask: u8,
302 target_type: TypeId,
305 key_literal_idx: usize,
309 value_literal_idx: usize,
311}
312
313impl Engine {
314 pub fn new(data_dir: &Path) -> io::Result<Self> {
315 std::fs::create_dir_all(data_dir)?;
316 let catalog = match Catalog::open(data_dir) {
319 Ok(c) => {
320 info!(data_dir = %data_dir.display(), "engine reopened existing database");
321 c
322 }
323 Err(e) if e.kind() == io::ErrorKind::NotFound => {
324 info!(data_dir = %data_dir.display(), "engine initialized fresh database");
325 Catalog::create(data_dir)?
326 }
327 Err(e) => return Err(e),
328 };
329 let view_registry =
330 ViewRegistry::open(data_dir).unwrap_or_else(|_| ViewRegistry::new(data_dir));
331 Ok(Engine {
332 catalog,
333 plan_cache: Mutex::new(PlanCache::new(PLAN_CACHE_CAPACITY)),
334 insert_values_scratch: Vec::new(),
335 view_registry,
336 })
337 }
338
339 pub fn execute_powql(&mut self, input: &str) -> Result<QueryResult, String> {
357 if !tracing::enabled!(Level::INFO) {
359 if let Ok((hash, literals)) = canonicalize(input) {
362 let cached = self
363 .plan_cache
364 .lock()
365 .map_err(|e| format!("plan cache lock poisoned: {e}"))?
366 .get_with_substitution(hash, &literals);
367 if let Some(plan) = cached {
368 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
369 let result = self.execute_plan(&plan);
370 self.catalog.sync_wal().map_err(|e| e.to_string())?;
376 return result;
377 }
378 return match planner::plan(input) {
380 Ok(plan) => {
381 self.plan_cache
382 .lock()
383 .map_err(|e| format!("plan cache lock poisoned: {e}"))?
384 .insert(hash, plan.clone());
385 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
386 let result = self.execute_plan(&plan);
387 self.catalog.sync_wal().map_err(|e| e.to_string())?;
388 result
389 }
390 Err(e) => Err(e.to_string()),
391 };
392 }
393 return match planner::plan(input) {
396 Ok(plan) => {
397 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
398 let result = self.execute_plan(&plan);
399 self.catalog.sync_wal().map_err(|e| e.to_string())?;
400 result
401 }
402 Err(e) => Err(e.to_string()),
403 };
404 }
405
406 let total_start = Instant::now();
408 let plan_start = Instant::now();
409 let plan = planner::plan(input).map_err(|e| {
410 error!(query = %input, error = %e.to_string(), "query plan failed");
411 e.to_string()
412 })?;
413 let plan_us = plan_start.elapsed().as_micros();
414
415 let exec_start = Instant::now();
416 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
417 let result = self.execute_plan(&plan);
418 let _ = self.catalog.sync_wal();
420 let exec_us = exec_start.elapsed().as_micros();
421
422 let total_us = total_start.elapsed().as_micros();
423 match &result {
424 Ok(r) => {
425 info!(
426 query = %input,
427 plan_us = plan_us,
428 exec_us = exec_us,
429 total_us = total_us,
430 rows = r.row_count(),
431 "query ok"
432 );
433 }
434 Err(e) => {
435 error!(
436 query = %input,
437 plan_us = plan_us,
438 exec_us = exec_us,
439 error = %e,
440 "query failed"
441 );
442 }
443 }
444 result
445 }
446
447 pub fn plan_cache_stats(&self) -> (u64, u64, usize) {
449 let cache = self.plan_cache.lock().unwrap();
450 (cache.hits, cache.misses, cache.len())
451 }
452
453 pub fn execute_powql_readonly(&self, input: &str) -> Result<QueryResult, String> {
469 let stmt = crate::parser::parse(input).map_err(|e| e.to_string())?;
473 if !is_read_only_statement(&stmt) {
474 return Err(READONLY_NEEDS_WRITE.to_string());
475 }
476
477 if let Ok((hash, literals)) = canonicalize(input) {
481 let cached = self
482 .plan_cache
483 .lock()
484 .map_err(|e| format!("plan cache lock poisoned: {e}"))?
485 .get_with_substitution(hash, &literals);
486 if let Some(plan) = cached {
487 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
488 return self.execute_plan_readonly(&plan);
489 }
490 let plan = crate::planner::plan_statement(stmt).map_err(|e| e.to_string())?;
493 self.plan_cache
494 .lock()
495 .map_err(|e| format!("plan cache lock poisoned: {e}"))?
496 .insert(hash, plan.clone());
497 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
498 return self.execute_plan_readonly(&plan);
499 }
500 let plan = crate::planner::plan_statement(stmt).map_err(|e| e.to_string())?;
503 let plan = lower_unindexed_range_scans(&self.catalog, &plan);
504 self.execute_plan_readonly(&plan)
505 }
506
507 fn execute_plan_readonly(&self, plan: &PlanNode) -> Result<QueryResult, String> {
518 match plan {
519 PlanNode::SeqScan { table } => {
520 if self.view_registry.is_dirty(table) {
523 return Err(READONLY_NEEDS_WRITE.to_string());
524 }
525 let schema = self
526 .catalog
527 .schema(table)
528 .ok_or_else(|| format!("table '{table}' not found"))?
529 .clone();
530 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
531 let rows: Vec<Vec<Value>> = self
532 .catalog
533 .scan(table)
534 .map_err(|e| e.to_string())?
535 .map(|(_, row)| row)
536 .collect();
537 Ok(QueryResult::Rows { columns, rows })
538 }
539
540 PlanNode::AliasScan { table, alias } => {
541 let schema = self
542 .catalog
543 .schema(table)
544 .ok_or_else(|| format!("table '{table}' not found"))?
545 .clone();
546 let columns: Vec<String> = schema
547 .columns
548 .iter()
549 .map(|c| format!("{alias}.{}", c.name))
550 .collect();
551 let rows: Vec<Vec<Value>> = self
552 .catalog
553 .scan(table)
554 .map_err(|e| e.to_string())?
555 .map(|(_, row)| row)
556 .collect();
557 Ok(QueryResult::Rows { columns, rows })
558 }
559
560 PlanNode::IndexScan { table, column, key } => {
561 let schema = self
562 .catalog
563 .schema(table)
564 .ok_or_else(|| format!("table '{table}' not found"))?
565 .clone();
566 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
567 let key_value = literal_to_value(key)?;
568 let tbl = self
569 .catalog
570 .get_table(table)
571 .ok_or_else(|| format!("table '{table}' not found"))?;
572
573 if let Some(btree) = tbl.index(column) {
574 let hit = match &key_value {
575 Value::Int(k) => btree.lookup_int(*k),
576 other => btree.lookup(other),
577 };
578 let rows = match hit {
579 Some(rid) => match tbl.heap.get(rid) {
580 Some(data) => vec![decode_row(&tbl.schema, &data)],
581 None => Vec::new(),
582 },
583 None => Vec::new(),
584 };
585 return Ok(QueryResult::Rows { columns, rows });
586 }
587
588 let fast = FastLayout::new(&schema);
590 let synth_pred = Expr::BinaryOp(
591 Box::new(Expr::Field(column.clone())),
592 BinOp::Eq,
593 Box::new(key.clone()),
594 );
595 if let Some(compiled) = compile_predicate(&synth_pred, &columns, &fast, &schema) {
596 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
597 self.catalog
598 .for_each_row_raw(table, |_rid, data| {
599 if compiled(data) {
600 rows.push(decode_row(&schema, data));
601 }
602 })
603 .map_err(|e| e.to_string())?;
604 return Ok(QueryResult::Rows { columns, rows });
605 }
606
607 let col_idx = schema
609 .column_index(column)
610 .ok_or_else(|| format!("column '{column}' not found"))?;
611 let rows: Vec<Vec<Value>> = tbl
612 .scan()
613 .filter_map(|(_, row)| {
614 if row[col_idx] == key_value {
615 Some(row)
616 } else {
617 None
618 }
619 })
620 .collect();
621 Ok(QueryResult::Rows { columns, rows })
622 }
623
624 PlanNode::RangeScan {
625 table,
626 column,
627 start,
628 end,
629 } => {
630 let tbl = self
631 .catalog
632 .get_table(table)
633 .ok_or_else(|| format!("table '{table}' not found"))?;
634 let columns: Vec<String> =
635 tbl.schema.columns.iter().map(|c| c.name.clone()).collect();
636 let schema = tbl.schema.clone();
637
638 let start_val = match start {
639 Some((expr, _)) => Some(literal_to_value(expr)?),
640 None => None,
641 };
642 let end_val = match end {
643 Some((expr, _)) => Some(literal_to_value(expr)?),
644 None => None,
645 };
646 let start_inclusive = start.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
647 let end_inclusive = end.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
648
649 if let Some(btree) = tbl.index(column) {
650 let hits: Vec<(Value, RowId)> = match (&start_val, &end_val) {
651 (Some(s), Some(e)) => btree.range(s, e).collect(),
652 (Some(s), None) => btree.range_from(s),
653 (None, Some(e)) => btree.range_to(e),
654 (None, None) => {
655 let rows: Vec<Vec<Value>> = tbl.scan().map(|(_, row)| row).collect();
657 return Ok(QueryResult::Rows { columns, rows });
658 }
659 };
660 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(hits.len());
661 for (key, rid) in hits {
662 if !start_inclusive {
664 if let Some(ref s) = start_val {
665 if &key == s {
666 continue;
667 }
668 }
669 }
670 if !end_inclusive {
671 if let Some(ref e) = end_val {
672 if &key == e {
673 continue;
674 }
675 }
676 }
677 if let Some(data) = tbl.heap.get(rid) {
678 rows.push(decode_row(&schema, &data));
679 }
680 }
681 return Ok(QueryResult::Rows { columns, rows });
682 }
683
684 let fast = FastLayout::new(&schema);
686 let synth = synthesize_range_predicate(column, start, end);
687 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, &schema) {
688 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
689 self.catalog
690 .for_each_row_raw(table, |_rid, data| {
691 if compiled(data) {
692 rows.push(decode_row(&schema, data));
693 }
694 })
695 .map_err(|e| e.to_string())?;
696 return Ok(QueryResult::Rows { columns, rows });
697 }
698
699 let col_idx = schema
701 .column_index(column)
702 .ok_or_else(|| format!("column '{column}' not found"))?;
703 let rows: Vec<Vec<Value>> = tbl
704 .scan()
705 .filter(|(_, row)| {
706 range_matches(
707 &row[col_idx],
708 &start_val,
709 start_inclusive,
710 &end_val,
711 end_inclusive,
712 )
713 })
714 .map(|(_, row)| row)
715 .collect();
716 Ok(QueryResult::Rows { columns, rows })
717 }
718
719 PlanNode::Filter { input, predicate } => {
720 let materialized;
725 let predicate = if contains_subquery(predicate) {
726 materialized = self.materialize_subqueries_readonly(predicate)?;
727 &materialized
728 } else {
729 predicate
730 };
731
732 if contains_subquery(predicate) {
734 let result = self.execute_plan_readonly(input)?;
735 return match result {
736 QueryResult::Rows { columns, rows } => {
737 let mut filtered = Vec::new();
738 for row in rows {
739 let row_pred = self.materialize_correlated_for_row_readonly(
740 predicate, &row, &columns,
741 )?;
742 if eval_predicate(&row_pred, &row, &columns) {
743 filtered.push(row);
744 }
745 }
746 Ok(QueryResult::Rows {
747 columns,
748 rows: filtered,
749 })
750 }
751 _ => Err("filter requires row input".into()),
752 };
753 }
754
755 if let PlanNode::SeqScan { table } = input.as_ref() {
757 if self.view_registry.is_dirty(table) {
758 return Err(READONLY_NEEDS_WRITE.to_string());
759 }
760 let schema = self
761 .catalog
762 .schema(table)
763 .ok_or_else(|| format!("table '{table}' not found"))?
764 .clone();
765 let columns: Vec<String> =
766 schema.columns.iter().map(|c| c.name.clone()).collect();
767 let fast = FastLayout::new(&schema);
768 let row_layout = RowLayout::new(&schema);
769 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
770
771 if let Some(compiled) = compile_predicate(predicate, &columns, &fast, &schema) {
772 self.catalog
773 .for_each_row_raw(table, |_rid, data| {
774 if compiled(data) {
775 rows.push(decode_row(&schema, data));
776 }
777 })
778 .map_err(|e| e.to_string())?;
779 } else {
780 let pred_cols = predicate_column_indices(predicate, &columns);
781 self.catalog
782 .for_each_row_raw(table, |_rid, data| {
783 let pred_row =
784 decode_selective(&schema, &row_layout, data, &pred_cols);
785 if eval_predicate(predicate, &pred_row, &columns) {
786 rows.push(decode_row(&schema, data));
787 }
788 })
789 .map_err(|e| e.to_string())?;
790 }
791
792 return Ok(QueryResult::Rows { columns, rows });
793 }
794
795 let result = self.execute_plan_readonly(input)?;
797 match result {
798 QueryResult::Rows { columns, rows } => {
799 let filtered: Vec<Vec<Value>> = rows
800 .into_iter()
801 .filter(|row| eval_predicate(predicate, row, &columns))
802 .collect();
803 Ok(QueryResult::Rows {
804 columns,
805 rows: filtered,
806 })
807 }
808 _ => Err("filter requires row input".into()),
809 }
810 }
811
812 PlanNode::Project { input, fields } => {
813 if let PlanNode::IndexScan { table, column, key } = input.as_ref() {
816 let key_value = literal_to_value(key)?;
817 let tbl = self
818 .catalog
819 .get_table(table)
820 .ok_or_else(|| format!("table '{table}' not found"))?;
821 let schema = &tbl.schema;
822 let layout = tbl.row_layout();
823
824 let proj_columns: Vec<String> = fields
825 .iter()
826 .map(|f| {
827 f.alias.clone().unwrap_or_else(|| match &f.expr {
828 Expr::Field(name) => name.clone(),
829 _ => "?".into(),
830 })
831 })
832 .collect();
833
834 let proj_indices: Vec<usize> = fields
835 .iter()
836 .filter_map(|f| {
837 if let Expr::Field(name) = &f.expr {
838 schema.column_index(name)
839 } else {
840 None
841 }
842 })
843 .collect();
844
845 if let Some(btree) = tbl.index(column) {
846 let lookup_result = match &key_value {
847 Value::Int(k) => btree.lookup_int(*k),
848 other => btree.lookup(other),
849 };
850 let rows = match lookup_result {
851 Some(rid) => match tbl.heap.get(rid) {
852 Some(data) => {
853 let row: Vec<Value> = proj_indices
854 .iter()
855 .map(|&ci| decode_column(schema, layout, &data, ci))
856 .collect();
857 vec![row]
858 }
859 None => Vec::new(),
860 },
861 None => Vec::new(),
862 };
863 return Ok(QueryResult::Rows {
864 columns: proj_columns,
865 rows,
866 });
867 }
868 }
869
870 if let PlanNode::Limit {
872 input: inner,
873 count: limit_expr,
874 } = input.as_ref()
875 {
876 if let PlanNode::Sort {
877 input: sort_input,
878 keys,
879 } = inner.as_ref()
880 {
881 if keys.len() == 1 {
882 let sort_field = &keys[0].field;
883 let descending = keys[0].descending;
884 let limit = match limit_expr {
885 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
886 _ => usize::MAX,
887 };
888 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
889 match sort_input.as_ref() {
890 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
891 PlanNode::Filter {
892 input: fi,
893 predicate,
894 } => {
895 if let PlanNode::SeqScan { table } = fi.as_ref() {
896 (Some(table.as_str()), Some(predicate))
897 } else {
898 (None, None)
899 }
900 }
901 _ => (None, None),
902 };
903 if let Some(table) = table_opt {
904 if let Some(result) = self.project_filter_sort_limit_fast(
905 table, fields, sort_field, descending, limit, pred_opt,
906 )? {
907 return Ok(result);
908 }
909 }
910 }
911 }
912 if let PlanNode::Filter {
913 input: fi,
914 predicate,
915 } = inner.as_ref()
916 {
917 if let PlanNode::SeqScan { table } = fi.as_ref() {
918 let limit = match limit_expr {
919 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
920 _ => usize::MAX,
921 };
922 if let Some(result) = self.project_filter_limit_fast(
923 table,
924 fields,
925 limit,
926 Some(predicate),
927 )? {
928 return Ok(result);
929 }
930 }
931 }
932 if let PlanNode::SeqScan { table } = inner.as_ref() {
933 let limit = match limit_expr {
934 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
935 _ => usize::MAX,
936 };
937 if let Some(result) =
938 self.project_filter_limit_fast(table, fields, limit, None)?
939 {
940 return Ok(result);
941 }
942 }
943 }
944
945 if let PlanNode::Filter {
947 input: fi,
948 predicate,
949 } = input.as_ref()
950 {
951 if let PlanNode::SeqScan { table } = fi.as_ref() {
952 if let Some(result) = self.project_filter_limit_fast(
953 table,
954 fields,
955 usize::MAX,
956 Some(predicate),
957 )? {
958 return Ok(result);
959 }
960 }
961 }
962
963 if let PlanNode::SeqScan { table } = input.as_ref() {
965 if let Some(result) =
966 self.project_filter_limit_fast(table, fields, usize::MAX, None)?
967 {
968 return Ok(result);
969 }
970 }
971
972 let result = self.execute_plan_readonly(input)?;
974 match result {
975 QueryResult::Rows { columns, rows } => {
976 let proj_columns: Vec<String> = fields
977 .iter()
978 .map(|f| {
979 f.alias.clone().unwrap_or_else(|| match &f.expr {
980 Expr::Field(name) => name.clone(),
981 Expr::QualifiedField { qualifier, field } => {
982 format!("{qualifier}.{field}")
983 }
984 _ => "?".into(),
985 })
986 })
987 .collect();
988 let proj_rows: Vec<Vec<Value>> = rows
989 .iter()
990 .map(|row| {
991 fields
992 .iter()
993 .map(|f| eval_expr(&f.expr, row, &columns))
994 .collect()
995 })
996 .collect();
997 Ok(QueryResult::Rows {
998 columns: proj_columns,
999 rows: proj_rows,
1000 })
1001 }
1002 _ => Err("project requires row input".into()),
1003 }
1004 }
1005
1006 PlanNode::Sort { input, keys } => {
1007 let result = self.execute_plan_readonly(input)?;
1008 match result {
1009 QueryResult::Rows { columns, mut rows } => {
1010 if rows.len() > MAX_SORT_ROWS {
1011 return Err(format!(
1012 "sort input exceeds {} row limit — add a LIMIT clause",
1013 MAX_SORT_ROWS
1014 ));
1015 }
1016 let key_indices: Vec<(usize, bool)> = keys
1017 .iter()
1018 .map(|k| {
1019 columns
1020 .iter()
1021 .position(|c| c == &k.field)
1022 .map(|idx| (idx, k.descending))
1023 .ok_or_else(|| format!("column '{}' not found", k.field))
1024 })
1025 .collect::<Result<_, String>>()?;
1026 rows.sort_by(|a, b| {
1027 for &(col_idx, descending) in &key_indices {
1028 let cmp = a[col_idx].cmp(&b[col_idx]);
1029 let cmp = if descending { cmp.reverse() } else { cmp };
1030 if cmp != std::cmp::Ordering::Equal {
1031 return cmp;
1032 }
1033 }
1034 std::cmp::Ordering::Equal
1035 });
1036 Ok(QueryResult::Rows { columns, rows })
1037 }
1038 _ => Err("sort requires row input".into()),
1039 }
1040 }
1041
1042 PlanNode::Limit { input, count } => {
1043 let result = self.execute_plan_readonly(input)?;
1044 let n = match count {
1045 Expr::Literal(Literal::Int(v)) => *v as usize,
1046 _ => return Err("limit must be integer literal".into()),
1047 };
1048 match result {
1049 QueryResult::Rows { columns, rows } => Ok(QueryResult::Rows {
1050 columns,
1051 rows: rows.into_iter().take(n).collect(),
1052 }),
1053 _ => Err("limit requires row input".into()),
1054 }
1055 }
1056
1057 PlanNode::Offset { input, count } => {
1058 let result = self.execute_plan_readonly(input)?;
1059 let n = match count {
1060 Expr::Literal(Literal::Int(v)) => *v as usize,
1061 _ => return Err("offset must be integer literal".into()),
1062 };
1063 match result {
1064 QueryResult::Rows { columns, rows } => Ok(QueryResult::Rows {
1065 columns,
1066 rows: rows.into_iter().skip(n).collect(),
1067 }),
1068 _ => Err("offset requires row input".into()),
1069 }
1070 }
1071
1072 PlanNode::Aggregate {
1073 input,
1074 function,
1075 field,
1076 } => {
1077 if *function == AggFunc::Count {
1079 if let PlanNode::SeqScan { table } = input.as_ref() {
1080 let mut count: i64 = 0;
1081 self.catalog
1082 .for_each_row_raw(table, |_rid, _data| {
1083 count += 1;
1084 })
1085 .map_err(|e| e.to_string())?;
1086 return Ok(QueryResult::Scalar(Value::Int(count)));
1087 }
1088 if let PlanNode::Filter {
1089 input: inner,
1090 predicate,
1091 } = input.as_ref()
1092 {
1093 if let PlanNode::SeqScan { table } = inner.as_ref() {
1094 let schema = self
1095 .catalog
1096 .schema(table)
1097 .ok_or_else(|| format!("table '{table}' not found"))?
1098 .clone();
1099 let columns: Vec<String> =
1100 schema.columns.iter().map(|c| c.name.clone()).collect();
1101 let fast = FastLayout::new(&schema);
1102 let row_layout = RowLayout::new(&schema);
1103
1104 if let Some(compiled) =
1105 compile_predicate(predicate, &columns, &fast, &schema)
1106 {
1107 let mut count: i64 = 0;
1108 self.catalog
1109 .for_each_row_raw(table, |_rid, data| {
1110 if compiled(data) {
1111 count += 1;
1112 }
1113 })
1114 .map_err(|e| e.to_string())?;
1115 return Ok(QueryResult::Scalar(Value::Int(count)));
1116 }
1117
1118 let pred_cols = predicate_column_indices(predicate, &columns);
1119 let mut count: i64 = 0;
1120 self.catalog
1121 .for_each_row_raw(table, |_rid, data| {
1122 let pred_row =
1123 decode_selective(&schema, &row_layout, data, &pred_cols);
1124 if eval_predicate(predicate, &pred_row, &columns) {
1125 count += 1;
1126 }
1127 })
1128 .map_err(|e| e.to_string())?;
1129 return Ok(QueryResult::Scalar(Value::Int(count)));
1130 }
1131 }
1132 }
1133
1134 if matches!(
1136 function,
1137 AggFunc::Sum
1138 | AggFunc::Avg
1139 | AggFunc::Min
1140 | AggFunc::Max
1141 | AggFunc::CountDistinct
1142 ) {
1143 if let Some(col) = field.as_ref() {
1144 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
1145 match input.as_ref() {
1146 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
1147 PlanNode::Filter {
1148 input: inner,
1149 predicate,
1150 } => {
1151 if let PlanNode::SeqScan { table } = inner.as_ref() {
1152 (Some(table.as_str()), Some(predicate))
1153 } else {
1154 (None, None)
1155 }
1156 }
1157 _ => (None, None),
1158 };
1159 if let Some(table) = table_opt {
1160 if let Some(result) =
1161 self.agg_single_col_fast(table, col, *function, pred_opt)?
1162 {
1163 return Ok(result);
1164 }
1165 }
1166 }
1167 }
1168
1169 let result = self.execute_plan_readonly(input)?;
1171 match result {
1172 QueryResult::Rows { columns, rows } => match function {
1173 AggFunc::Count => Ok(QueryResult::Scalar(Value::Int(rows.len() as i64))),
1174 AggFunc::CountDistinct => {
1175 let col = field.as_ref().ok_or("count distinct requires field")?;
1176 let idx = columns
1177 .iter()
1178 .position(|c| c == col)
1179 .ok_or("col not found")?;
1180 let mut seen = std::collections::HashSet::new();
1181 for row in &rows {
1182 let v = &row[idx];
1183 if !v.is_empty() {
1184 seen.insert(v.clone());
1185 }
1186 }
1187 Ok(QueryResult::Scalar(Value::Int(seen.len() as i64)))
1188 }
1189 AggFunc::Avg => {
1190 let col = field.as_ref().ok_or("avg requires field")?;
1191 let idx = columns
1192 .iter()
1193 .position(|c| c == col)
1194 .ok_or("col not found")?;
1195 let sum: f64 = rows
1196 .iter()
1197 .filter_map(|r| match &r[idx] {
1198 Value::Int(v) => Some(*v as f64),
1199 Value::Float(v) => Some(*v),
1200 _ => None,
1201 })
1202 .sum();
1203 let count = rows.len() as f64;
1204 Ok(QueryResult::Scalar(Value::Float(sum / count)))
1205 }
1206 AggFunc::Sum => {
1207 let col = field.as_ref().ok_or("sum requires field")?;
1208 let idx = columns
1209 .iter()
1210 .position(|c| c == col)
1211 .ok_or("col not found")?;
1212 let mut int_sum: i64 = 0;
1213 let mut float_sum: f64 = 0.0;
1214 let mut saw_float = false;
1215 for r in &rows {
1216 match &r[idx] {
1217 Value::Int(v) => int_sum += *v,
1218 Value::Float(v) => {
1219 float_sum += *v;
1220 saw_float = true;
1221 }
1222 _ => {}
1223 }
1224 }
1225 let result = if saw_float {
1226 Value::Float(float_sum + int_sum as f64)
1227 } else {
1228 Value::Int(int_sum)
1229 };
1230 Ok(QueryResult::Scalar(result))
1231 }
1232 AggFunc::Min | AggFunc::Max => {
1233 let col = field.as_ref().ok_or("min/max requires field")?;
1234 let idx = columns
1235 .iter()
1236 .position(|c| c == col)
1237 .ok_or("col not found")?;
1238 let vals: Vec<&Value> = rows.iter().map(|r| &r[idx]).collect();
1239 let result = if *function == AggFunc::Min {
1240 vals.into_iter().min().cloned()
1241 } else {
1242 vals.into_iter().max().cloned()
1243 };
1244 Ok(QueryResult::Scalar(result.unwrap_or(Value::Empty)))
1245 }
1246 },
1247 _ => Err("aggregate requires row input".into()),
1248 }
1249 }
1250
1251 PlanNode::Distinct { input } => {
1252 let result = self.execute_plan_readonly(input)?;
1253 match result {
1254 QueryResult::Rows { columns, rows } => {
1255 let mut seen = std::collections::HashSet::new();
1256 let mut unique_rows = Vec::new();
1257 for row in rows {
1258 if seen.insert(row.clone()) {
1259 unique_rows.push(row);
1260 }
1261 }
1262 Ok(QueryResult::Rows {
1263 columns,
1264 rows: unique_rows,
1265 })
1266 }
1267 other => Ok(other),
1268 }
1269 }
1270
1271 PlanNode::GroupBy {
1272 input,
1273 keys,
1274 aggregates,
1275 having,
1276 } => {
1277 let result = self.execute_plan_readonly(input)?;
1278 match result {
1279 QueryResult::Rows { columns, rows } => {
1280 let key_indices: Vec<usize> = keys
1281 .iter()
1282 .map(|k| {
1283 columns
1284 .iter()
1285 .position(|c| c == k)
1286 .ok_or_else(|| format!("group-by column '{k}' not found"))
1287 })
1288 .collect::<Result<Vec<_>, _>>()?;
1289
1290 let agg_field_indices: Vec<usize> = aggregates
1291 .iter()
1292 .map(|a| {
1293 if a.field == "*" {
1294 Ok(usize::MAX)
1295 } else {
1296 columns.iter().position(|c| c == &a.field).ok_or_else(|| {
1297 format!("aggregate column '{}' not found", a.field)
1298 })
1299 }
1300 })
1301 .collect::<Result<Vec<_>, _>>()?;
1302
1303 let mut group_map: rustc_hash::FxHashMap<Vec<Value>, usize> =
1304 rustc_hash::FxHashMap::default();
1305 let mut groups: Vec<(Vec<Value>, Vec<usize>)> = Vec::new();
1306 for (ri, row) in rows.iter().enumerate() {
1307 let key: Vec<Value> =
1308 key_indices.iter().map(|&i| row[i].clone()).collect();
1309 match group_map.get(&key) {
1310 Some(&idx) => groups[idx].1.push(ri),
1311 None => {
1312 let idx = groups.len();
1313 group_map.insert(key.clone(), idx);
1314 groups.push((key, vec![ri]));
1315 }
1316 }
1317 }
1318
1319 let mut out_columns: Vec<String> = keys.clone();
1320 for agg in aggregates.iter() {
1321 out_columns.push(agg.output_name.clone());
1322 }
1323
1324 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(groups.len());
1325 for (key_vals, row_indices) in &groups {
1326 let mut row = key_vals.clone();
1327 for (ai, agg) in aggregates.iter().enumerate() {
1328 let col_idx = agg_field_indices[ai];
1329 let val = compute_group_aggregate(
1330 agg.function,
1331 &rows,
1332 row_indices,
1333 col_idx,
1334 );
1335 row.push(val);
1336 }
1337 out_rows.push(row);
1338 }
1339
1340 if let Some(having_expr) = having {
1341 out_rows.retain(|row| eval_predicate(having_expr, row, &out_columns));
1342 }
1343
1344 Ok(QueryResult::Rows {
1345 columns: out_columns,
1346 rows: out_rows,
1347 })
1348 }
1349 _ => Err("group by requires row input".into()),
1350 }
1351 }
1352
1353 PlanNode::NestedLoopJoin {
1354 left,
1355 right,
1356 on,
1357 kind,
1358 } => {
1359 let left_result = self.execute_plan_readonly(left)?;
1360 let right_result = self.execute_plan_readonly(right)?;
1361 let (left_columns, left_rows) = match left_result {
1362 QueryResult::Rows { columns, rows } => (columns, rows),
1363 _ => return Err("join left side must produce rows".into()),
1364 };
1365 let (right_columns, right_rows) = match right_result {
1366 QueryResult::Rows { columns, rows } => (columns, rows),
1367 _ => return Err("join right side must produce rows".into()),
1368 };
1369
1370 if !matches!(kind, JoinKind::Cross) {
1371 if let Some(pred) = on {
1372 if let Some((l_idx, r_idx)) =
1373 try_extract_equi_join_keys(pred, &left_columns, &right_columns)
1374 {
1375 let result = hash_join(
1376 left_columns,
1377 left_rows,
1378 right_columns,
1379 right_rows,
1380 l_idx,
1381 r_idx,
1382 *kind,
1383 );
1384 if let QueryResult::Rows { ref rows, .. } = result {
1385 check_join_limit(rows.len())?;
1386 }
1387 return Ok(result);
1388 }
1389 }
1390 }
1391
1392 let n_left = left_columns.len();
1393 let n_right = right_columns.len();
1394 let mut columns = Vec::with_capacity(n_left + n_right);
1395 columns.extend(left_columns);
1396 columns.extend(right_columns);
1397
1398 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
1399 let mut combined: Vec<Value> = Vec::with_capacity(n_left + n_right);
1400
1401 for left_row in &left_rows {
1402 let mut matched = false;
1403 for right_row in &right_rows {
1404 combined.clear();
1405 combined.extend_from_slice(left_row);
1406 combined.extend_from_slice(right_row);
1407 let keep = match kind {
1408 JoinKind::Cross => true,
1409 JoinKind::Inner | JoinKind::LeftOuter => match on {
1410 Some(pred) => eval_predicate(pred, &combined, &columns),
1411 None => true,
1412 },
1413 JoinKind::RightOuter => {
1414 unreachable!("planner rewrites RightOuter to LeftOuter")
1415 }
1416 };
1417 if keep {
1418 rows.push(combined.clone());
1419 check_join_limit(rows.len())?;
1420 matched = true;
1421 }
1422 }
1423 if !matched && matches!(kind, JoinKind::LeftOuter) {
1424 let mut row = Vec::with_capacity(n_left + n_right);
1425 row.extend_from_slice(left_row);
1426 row.resize(n_left + n_right, Value::Empty);
1427 rows.push(row);
1428 check_join_limit(rows.len())?;
1429 }
1430 }
1431
1432 Ok(QueryResult::Rows { columns, rows })
1433 }
1434
1435 PlanNode::Window { input, windows } => {
1436 let result = self.execute_plan_readonly(input)?;
1437 execute_window(result, windows)
1438 }
1439
1440 PlanNode::Union { left, right, all } => {
1441 let left_result = self.execute_plan_readonly(left)?;
1442 let right_result = self.execute_plan_readonly(right)?;
1443 let (left_cols, left_rows) = match left_result {
1444 QueryResult::Rows { columns, rows } => (columns, rows),
1445 _ => return Err("UNION requires query results on left side".into()),
1446 };
1447 let (_, right_rows) = match right_result {
1448 QueryResult::Rows { columns, rows } => (columns, rows),
1449 _ => return Err("UNION requires query results on right side".into()),
1450 };
1451 let mut combined = left_rows;
1452 if *all {
1453 combined.extend(right_rows);
1454 } else {
1455 let mut seen = std::collections::HashSet::new();
1456 for row in &combined {
1457 seen.insert(row.clone());
1458 }
1459 for row in right_rows {
1460 if seen.insert(row.clone()) {
1461 combined.push(row);
1462 }
1463 }
1464 }
1465 Ok(QueryResult::Rows {
1466 columns: left_cols,
1467 rows: combined,
1468 })
1469 }
1470
1471 PlanNode::Explain { input } => {
1472 let text = format_plan_tree(input, 0);
1473 Ok(QueryResult::Rows {
1474 columns: vec!["plan".to_string()],
1475 rows: text
1476 .lines()
1477 .map(|line| vec![Value::Str(line.to_string())])
1478 .collect(),
1479 })
1480 }
1481
1482 PlanNode::Insert { .. }
1484 | PlanNode::Update { .. }
1485 | PlanNode::Delete { .. }
1486 | PlanNode::Upsert { .. }
1487 | PlanNode::CreateTable { .. }
1488 | PlanNode::AlterTable { .. }
1489 | PlanNode::DropTable { .. }
1490 | PlanNode::CreateView { .. }
1491 | PlanNode::RefreshView { .. }
1492 | PlanNode::DropView { .. } => Err(READONLY_NEEDS_WRITE.to_string()),
1493 }
1494 }
1495
1496 fn materialize_subqueries_readonly(&self, expr: &Expr) -> Result<Expr, String> {
1503 match expr {
1504 Expr::InSubquery {
1505 expr: inner,
1506 subquery,
1507 negated,
1508 } => {
1509 if is_correlated_subquery(subquery, &self.catalog) {
1510 let inner = self.materialize_subqueries_readonly(inner)?;
1513 return Ok(Expr::InSubquery {
1514 expr: Box::new(inner),
1515 subquery: subquery.clone(),
1516 negated: *negated,
1517 });
1518 }
1519 let inner = self.materialize_subqueries_readonly(inner)?;
1520 let sub_plan = crate::planner::plan_statement(Statement::Query(*subquery.clone()))
1521 .map_err(|e| e.to_string())?;
1522 let result = self.execute_plan_readonly(&sub_plan)?;
1523 let values = match result {
1524 QueryResult::Rows { rows, .. } => rows
1525 .into_iter()
1526 .filter_map(|mut row| {
1527 if row.is_empty() {
1528 None
1529 } else {
1530 Some(value_to_expr(row.swap_remove(0)))
1531 }
1532 })
1533 .collect(),
1534 _ => Vec::new(),
1535 };
1536 Ok(Expr::InList {
1537 expr: Box::new(inner),
1538 list: values,
1539 negated: *negated,
1540 })
1541 }
1542 Expr::ExistsSubquery { subquery, negated } => {
1543 if is_correlated_subquery(subquery, &self.catalog) {
1544 return Ok(expr.clone());
1545 }
1546 let sub_plan = crate::planner::plan_statement(Statement::Query(*subquery.clone()))
1547 .map_err(|e| e.to_string())?;
1548 let result = self.execute_plan_readonly(&sub_plan)?;
1549 let has_rows = match result {
1550 QueryResult::Rows { rows, .. } => !rows.is_empty(),
1551 _ => false,
1552 };
1553 let truth = if *negated { !has_rows } else { has_rows };
1554 Ok(Expr::Literal(Literal::Bool(truth)))
1555 }
1556 Expr::BinaryOp(l, op, r) => {
1557 let l = self.materialize_subqueries_readonly(l)?;
1558 let r = self.materialize_subqueries_readonly(r)?;
1559 Ok(Expr::BinaryOp(Box::new(l), *op, Box::new(r)))
1560 }
1561 Expr::UnaryOp(op, inner) => {
1562 let inner = self.materialize_subqueries_readonly(inner)?;
1563 Ok(Expr::UnaryOp(*op, Box::new(inner)))
1564 }
1565 Expr::Case { whens, else_expr } => {
1566 let whens = whens
1567 .iter()
1568 .map(|(c, r)| {
1569 let c = self.materialize_subqueries_readonly(c)?;
1570 let r = self.materialize_subqueries_readonly(r)?;
1571 Ok((Box::new(c), Box::new(r)))
1572 })
1573 .collect::<Result<Vec<_>, String>>()?;
1574 let else_expr = match else_expr {
1575 Some(e) => Some(Box::new(self.materialize_subqueries_readonly(e)?)),
1576 None => None,
1577 };
1578 Ok(Expr::Case { whens, else_expr })
1579 }
1580 other => Ok(other.clone()),
1581 }
1582 }
1583
1584 fn materialize_correlated_for_row_readonly(
1589 &self,
1590 expr: &Expr,
1591 outer_row: &[Value],
1592 outer_columns: &[String],
1593 ) -> Result<Expr, String> {
1594 match expr {
1595 Expr::InSubquery {
1596 expr: inner,
1597 subquery,
1598 negated,
1599 } => {
1600 let inner =
1601 self.materialize_correlated_for_row_readonly(inner, outer_row, outer_columns)?;
1602 let mut sub = *subquery.clone();
1603 if let Some(ref filter) = sub.filter {
1604 sub.filter = Some(substitute_outer_refs(
1605 filter,
1606 &sub.source,
1607 &self.catalog,
1608 outer_row,
1609 outer_columns,
1610 ));
1611 }
1612 let sub_plan = crate::planner::plan_statement(Statement::Query(sub))
1613 .map_err(|e| e.to_string())?;
1614 let result = self.execute_plan_readonly(&sub_plan)?;
1615 let values = match result {
1616 QueryResult::Rows { rows, .. } => rows
1617 .into_iter()
1618 .filter_map(|mut row| {
1619 if row.is_empty() {
1620 None
1621 } else {
1622 Some(value_to_expr(row.swap_remove(0)))
1623 }
1624 })
1625 .collect(),
1626 _ => Vec::new(),
1627 };
1628 Ok(Expr::InList {
1629 expr: Box::new(inner),
1630 list: values,
1631 negated: *negated,
1632 })
1633 }
1634 Expr::ExistsSubquery { subquery, negated } => {
1635 let mut sub = *subquery.clone();
1636 if let Some(ref filter) = sub.filter {
1637 sub.filter = Some(substitute_outer_refs(
1638 filter,
1639 &sub.source,
1640 &self.catalog,
1641 outer_row,
1642 outer_columns,
1643 ));
1644 }
1645 let sub_plan = crate::planner::plan_statement(Statement::Query(sub))
1646 .map_err(|e| e.to_string())?;
1647 let result = self.execute_plan_readonly(&sub_plan)?;
1648 let has_rows = match result {
1649 QueryResult::Rows { rows, .. } => !rows.is_empty(),
1650 _ => false,
1651 };
1652 let truth = if *negated { !has_rows } else { has_rows };
1653 Ok(Expr::Literal(Literal::Bool(truth)))
1654 }
1655 Expr::BinaryOp(l, op, r) => {
1656 let l =
1657 self.materialize_correlated_for_row_readonly(l, outer_row, outer_columns)?;
1658 let r =
1659 self.materialize_correlated_for_row_readonly(r, outer_row, outer_columns)?;
1660 Ok(Expr::BinaryOp(Box::new(l), *op, Box::new(r)))
1661 }
1662 Expr::UnaryOp(op, inner) => {
1663 let inner =
1664 self.materialize_correlated_for_row_readonly(inner, outer_row, outer_columns)?;
1665 Ok(Expr::UnaryOp(*op, Box::new(inner)))
1666 }
1667 other => Ok(other.clone()),
1668 }
1669 }
1670
1671 pub fn prepare(&mut self, query: &str) -> Result<PreparedQuery, String> {
1693 let plan = planner::plan(query).map_err(|e| e.to_string())?;
1694 let param_count = crate::plan_cache::count_literal_slots(&plan);
1695
1696 let insert_fast = match &plan {
1705 PlanNode::Insert { table, assignments }
1706 if assignments
1707 .iter()
1708 .all(|a| matches!(a.value, Expr::Literal(_)))
1709 && param_count == assignments.len() =>
1710 {
1711 let table_slot = self
1712 .catalog
1713 .table_slot(table)
1714 .ok_or_else(|| format!("table '{table}' not found"))?;
1715 let schema = &self.catalog.table_by_slot(table_slot).schema;
1716 let n_cols = schema.columns.len();
1717 let indices: Result<Vec<usize>, String> = assignments
1718 .iter()
1719 .map(|a| {
1720 schema
1721 .column_index(&a.field)
1722 .ok_or_else(|| format!("column '{}' not found", a.field))
1723 })
1724 .collect();
1725 Some(InsertFast {
1726 table_slot,
1727 col_indices: indices?,
1728 n_cols,
1729 })
1730 }
1731 _ => None,
1732 };
1733
1734 let update_pk_fast = Self::try_build_update_pk_fast(&self.catalog, &plan);
1748
1749 Ok(PreparedQuery {
1750 plan_template: plan,
1751 param_count,
1752 insert_fast,
1753 update_pk_fast,
1754 })
1755 }
1756
1757 fn try_build_update_pk_fast(catalog: &Catalog, plan: &PlanNode) -> Option<UpdatePkFast> {
1762 let (table, input, assignments) = match plan {
1764 PlanNode::Update {
1765 table,
1766 input,
1767 assignments,
1768 } => (table, input.as_ref(), assignments),
1769 _ => return None,
1770 };
1771 if assignments.len() != 1 {
1774 return None;
1775 }
1776 let assn = &assignments[0];
1777 if !matches!(assn.value, Expr::Literal(_)) {
1779 return None;
1780 }
1781 let (key_col, key_table) = match input {
1783 PlanNode::IndexScan {
1784 table: t,
1785 column,
1786 key: Expr::Literal(_),
1787 } => (column.clone(), t.clone()),
1788 _ => return None,
1789 };
1790 if &key_table != table {
1791 return None;
1792 }
1793
1794 let table_slot = catalog.table_slot(table)?;
1797 let tbl = catalog.table_by_slot(table_slot);
1798 let schema = &tbl.schema;
1799
1800 if !tbl.has_index(&key_col) {
1803 return None;
1804 }
1805
1806 let target_col_idx = schema.column_index(&assn.field)?;
1809 let target_type = schema.columns[target_col_idx].type_id;
1810 if !is_fixed_size(target_type) {
1811 return None;
1812 }
1813 if tbl.has_indexed_col(target_col_idx) {
1814 return None;
1815 }
1816
1817 let layout = tbl.row_layout();
1819 let fixed_off = layout.fixed_offset(target_col_idx)?;
1820 let bitmap_size = layout.bitmap_size();
1821 let field_off = 2 + bitmap_size + fixed_off;
1822 let bitmap_byte_off = 2 + target_col_idx / 8;
1823 let bit_mask = 1u8 << (target_col_idx % 8);
1824
1825 Some(UpdatePkFast {
1830 table_slot,
1831 key_col,
1832 field_off,
1833 bitmap_byte_off,
1834 bit_mask,
1835 target_type,
1836 key_literal_idx: 0,
1837 value_literal_idx: 1,
1838 })
1839 }
1840
1841 pub fn execute_prepared(
1849 &mut self,
1850 prep: &PreparedQuery,
1851 literals: &[Literal],
1852 ) -> Result<QueryResult, String> {
1853 if literals.len() != prep.param_count {
1854 return Err(format!(
1855 "prepared query expects {} literal(s), got {}",
1856 prep.param_count,
1857 literals.len(),
1858 ));
1859 }
1860
1861 if let Some(fast) = &prep.update_pk_fast {
1868 if let Some(result) = self.try_execute_update_pk_fast(fast, literals)? {
1869 if let PlanNode::Update { table, .. } = &prep.plan_template {
1871 self.view_registry.mark_dependents_dirty(table);
1872 }
1873 self.catalog.sync_wal().map_err(|e| e.to_string())?;
1878 return Ok(result);
1879 }
1880 }
1881
1882 if let Some(fast) = &prep.insert_fast {
1899 let mut values = std::mem::take(&mut self.insert_values_scratch);
1900 values.clear();
1901 values.resize(fast.n_cols, Value::Empty);
1902 for (pos, lit) in literals.iter().enumerate() {
1903 values[fast.col_indices[pos]] = literal_value_from(lit);
1904 }
1905 let tbl = self.catalog.table_by_slot_mut(fast.table_slot);
1908 let res = tbl.insert(&values).map_err(|e| e.to_string());
1909 values.clear();
1913 self.insert_values_scratch = values;
1914 res?;
1915 if let PlanNode::Insert { table, .. } = &prep.plan_template {
1917 self.view_registry.mark_dependents_dirty(table);
1918 }
1919 self.catalog.sync_wal().map_err(|e| e.to_string())?;
1921 return Ok(QueryResult::Modified(1));
1922 }
1923
1924 let mut plan = prep.plan_template.clone();
1925 let mut idx = 0usize;
1926 crate::plan_cache::substitute_plan(&mut plan, literals, &mut idx);
1927 debug_assert_eq!(idx, literals.len());
1928 let result = self.execute_plan(&plan);
1929 self.catalog.sync_wal().map_err(|e| e.to_string())?;
1932 result
1933 }
1934
1935 #[inline]
1949 fn try_execute_update_pk_fast(
1950 &mut self,
1951 fast: &UpdatePkFast,
1952 literals: &[Literal],
1953 ) -> Result<Option<QueryResult>, String> {
1954 let key_int = match &literals[fast.key_literal_idx] {
1959 Literal::Int(v) => *v,
1960 _ => return Ok(None),
1961 };
1962
1963 let bytes: FixedBytes = match (fast.target_type, &literals[fast.value_literal_idx]) {
1966 (TypeId::Int, Literal::Int(v)) => FixedBytes::I64(v.to_le_bytes()),
1967 (TypeId::DateTime, Literal::Int(v)) => FixedBytes::I64(v.to_le_bytes()),
1968 (TypeId::Float, Literal::Float(v)) => FixedBytes::F64(v.to_le_bytes()),
1969 (TypeId::Bool, Literal::Bool(v)) => FixedBytes::Bool(if *v { 1 } else { 0 }),
1970 _ => return Ok(None),
1973 };
1974
1975 let tbl = self.catalog.table_by_slot_mut(fast.table_slot);
1986 let Some(btree) = tbl.index(&fast.key_col) else {
1987 return Ok(None);
1989 };
1990 let Some(rid) = btree.lookup_int(key_int) else {
1991 return Ok(Some(QueryResult::Modified(0)));
1992 };
1993
1994 let fast_table_slot = fast.table_slot;
1995 let bitmap_byte_off = fast.bitmap_byte_off;
1996 let bit_mask = fast.bit_mask;
1997 let field_off = fast.field_off;
1998 let ok = self
1999 .catalog
2000 .update_row_bytes_logged_by_slot(fast_table_slot, rid, |row| {
2001 row[bitmap_byte_off] &= !bit_mask;
2004 let field_bytes = bytes.as_slice();
2005 row[field_off..field_off + field_bytes.len()].copy_from_slice(field_bytes);
2006 })
2007 .map_err(|e| e.to_string())?;
2008
2009 Ok(Some(QueryResult::Modified(if ok { 1 } else { 0 })))
2010 }
2011
2012 pub fn execute_prepared_take(
2026 &mut self,
2027 prep: &PreparedQuery,
2028 literals: &mut [Literal],
2029 ) -> Result<QueryResult, String> {
2030 if literals.len() != prep.param_count {
2031 return Err(format!(
2032 "prepared query expects {} literal(s), got {}",
2033 prep.param_count,
2034 literals.len(),
2035 ));
2036 }
2037
2038 if let Some(fast) = &prep.insert_fast {
2039 let mut values = std::mem::take(&mut self.insert_values_scratch);
2040 values.clear();
2041 values.resize(fast.n_cols, Value::Empty);
2042 for (pos, lit) in literals.iter_mut().enumerate() {
2043 values[fast.col_indices[pos]] = literal_value_take(lit);
2044 }
2045 let tbl = self.catalog.table_by_slot_mut(fast.table_slot);
2049 let res = tbl.insert(&values).map_err(|e| e.to_string());
2050 values.clear();
2051 self.insert_values_scratch = values;
2052 res?;
2053 self.catalog.sync_wal().map_err(|e| e.to_string())?;
2055 return Ok(QueryResult::Modified(1));
2056 }
2057
2058 self.execute_prepared(prep, literals)
2063 }
2064
2065 fn materialize_subqueries(&mut self, expr: &Expr) -> Result<Expr, String> {
2071 match expr {
2072 Expr::InSubquery {
2073 expr: inner,
2074 subquery,
2075 negated,
2076 } => {
2077 if is_correlated_subquery(subquery, &self.catalog) {
2078 let inner = self.materialize_subqueries(inner)?;
2079 return Ok(Expr::InSubquery {
2080 expr: Box::new(inner),
2081 subquery: subquery.clone(),
2082 negated: *negated,
2083 });
2084 }
2085 let inner = self.materialize_subqueries(inner)?;
2086 let sub_plan = crate::planner::plan_statement(Statement::Query(*subquery.clone()))
2088 .map_err(|e| e.to_string())?;
2089 let result = self.execute_plan(&sub_plan)?;
2090 let values = match result {
2091 QueryResult::Rows { rows, .. } => rows
2092 .into_iter()
2093 .filter_map(|mut row| {
2094 if row.is_empty() {
2095 None
2096 } else {
2097 Some(value_to_expr(row.swap_remove(0)))
2098 }
2099 })
2100 .collect(),
2101 _ => Vec::new(),
2102 };
2103 Ok(Expr::InList {
2104 expr: Box::new(inner),
2105 list: values,
2106 negated: *negated,
2107 })
2108 }
2109 Expr::ExistsSubquery { subquery, negated } => {
2110 if is_correlated_subquery(subquery, &self.catalog) {
2111 return Ok(expr.clone());
2112 }
2113 let sub_plan = crate::planner::plan_statement(Statement::Query(*subquery.clone()))
2116 .map_err(|e| e.to_string())?;
2117 let result = self.execute_plan(&sub_plan)?;
2118 let has_rows = match result {
2119 QueryResult::Rows { rows, .. } => !rows.is_empty(),
2120 _ => false,
2121 };
2122 let truth = if *negated { !has_rows } else { has_rows };
2123 Ok(Expr::Literal(Literal::Bool(truth)))
2124 }
2125 Expr::BinaryOp(l, op, r) => {
2126 let l = self.materialize_subqueries(l)?;
2127 let r = self.materialize_subqueries(r)?;
2128 Ok(Expr::BinaryOp(Box::new(l), *op, Box::new(r)))
2129 }
2130 Expr::UnaryOp(op, inner) => {
2131 let inner = self.materialize_subqueries(inner)?;
2132 Ok(Expr::UnaryOp(*op, Box::new(inner)))
2133 }
2134 Expr::Case { whens, else_expr } => {
2135 let whens = whens
2136 .iter()
2137 .map(|(c, r)| {
2138 let c = self.materialize_subqueries(c)?;
2139 let r = self.materialize_subqueries(r)?;
2140 Ok((Box::new(c), Box::new(r)))
2141 })
2142 .collect::<Result<Vec<_>, String>>()?;
2143 let else_expr = match else_expr {
2144 Some(e) => Some(Box::new(self.materialize_subqueries(e)?)),
2145 None => None,
2146 };
2147 Ok(Expr::Case { whens, else_expr })
2148 }
2149 other => Ok(other.clone()),
2151 }
2152 }
2153
2154 fn materialize_correlated_for_row(
2156 &mut self,
2157 expr: &Expr,
2158 outer_row: &[Value],
2159 outer_columns: &[String],
2160 ) -> Result<Expr, String> {
2161 match expr {
2162 Expr::InSubquery {
2163 expr: inner,
2164 subquery,
2165 negated,
2166 } => {
2167 let inner = self.materialize_correlated_for_row(inner, outer_row, outer_columns)?;
2168 let mut sub = *subquery.clone();
2169 if let Some(ref filter) = sub.filter {
2170 sub.filter = Some(substitute_outer_refs(
2171 filter,
2172 &sub.source,
2173 &self.catalog,
2174 outer_row,
2175 outer_columns,
2176 ));
2177 }
2178 let sub_plan = crate::planner::plan_statement(Statement::Query(sub))
2179 .map_err(|e| e.to_string())?;
2180 let result = self.execute_plan(&sub_plan)?;
2181 let values = match result {
2182 QueryResult::Rows { rows, .. } => rows
2183 .into_iter()
2184 .filter_map(|mut row| {
2185 if row.is_empty() {
2186 None
2187 } else {
2188 Some(value_to_expr(row.swap_remove(0)))
2189 }
2190 })
2191 .collect(),
2192 _ => Vec::new(),
2193 };
2194 Ok(Expr::InList {
2195 expr: Box::new(inner),
2196 list: values,
2197 negated: *negated,
2198 })
2199 }
2200 Expr::ExistsSubquery { subquery, negated } => {
2201 let mut sub = *subquery.clone();
2202 if let Some(ref filter) = sub.filter {
2203 sub.filter = Some(substitute_outer_refs(
2204 filter,
2205 &sub.source,
2206 &self.catalog,
2207 outer_row,
2208 outer_columns,
2209 ));
2210 }
2211 let sub_plan = crate::planner::plan_statement(Statement::Query(sub))
2212 .map_err(|e| e.to_string())?;
2213 let result = self.execute_plan(&sub_plan)?;
2214 let has_rows = match result {
2215 QueryResult::Rows { rows, .. } => !rows.is_empty(),
2216 _ => false,
2217 };
2218 let truth = if *negated { !has_rows } else { has_rows };
2219 Ok(Expr::Literal(Literal::Bool(truth)))
2220 }
2221 Expr::BinaryOp(l, op, r) => {
2222 let l = self.materialize_correlated_for_row(l, outer_row, outer_columns)?;
2223 let r = self.materialize_correlated_for_row(r, outer_row, outer_columns)?;
2224 Ok(Expr::BinaryOp(Box::new(l), *op, Box::new(r)))
2225 }
2226 Expr::UnaryOp(op, inner) => {
2227 let inner = self.materialize_correlated_for_row(inner, outer_row, outer_columns)?;
2228 Ok(Expr::UnaryOp(*op, Box::new(inner)))
2229 }
2230 other => Ok(other.clone()),
2231 }
2232 }
2233
2234 pub fn execute_plan(&mut self, plan: &PlanNode) -> Result<QueryResult, String> {
2235 match plan {
2236 PlanNode::SeqScan { table } => {
2237 if self.view_registry.is_dirty(table) {
2239 self.refresh_view(table)?;
2240 }
2241 let schema = self
2242 .catalog
2243 .schema(table)
2244 .ok_or_else(|| format!("table '{table}' not found"))?
2245 .clone();
2246 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
2247 let rows: Vec<Vec<Value>> = self
2248 .catalog
2249 .scan(table)
2250 .map_err(|e| e.to_string())?
2251 .map(|(_, row)| row)
2252 .collect();
2253 Ok(QueryResult::Rows { columns, rows })
2254 }
2255
2256 PlanNode::Filter { input, predicate } => {
2257 let materialized;
2261 let predicate = if contains_subquery(predicate) {
2262 materialized = self.materialize_subqueries(predicate)?;
2263 &materialized
2264 } else {
2265 predicate
2266 };
2267
2268 if contains_subquery(predicate) {
2270 let result = self.execute_plan(input)?;
2271 return match result {
2272 QueryResult::Rows { columns, rows } => {
2273 let mut filtered = Vec::new();
2274 for row in rows {
2275 let row_pred =
2276 self.materialize_correlated_for_row(predicate, &row, &columns)?;
2277 if eval_predicate(&row_pred, &row, &columns) {
2278 filtered.push(row);
2279 }
2280 }
2281 Ok(QueryResult::Rows {
2282 columns,
2283 rows: filtered,
2284 })
2285 }
2286 _ => Err("filter requires row input".into()),
2287 };
2288 }
2289
2290 if let PlanNode::SeqScan { table } = input.as_ref() {
2295 if self.view_registry.is_dirty(table) {
2297 self.refresh_view(table)?;
2298 }
2299 let schema = self
2300 .catalog
2301 .schema(table)
2302 .ok_or_else(|| format!("table '{table}' not found"))?
2303 .clone();
2304 let columns: Vec<String> =
2305 schema.columns.iter().map(|c| c.name.clone()).collect();
2306 let fast = FastLayout::new(&schema);
2307 let row_layout = RowLayout::new(&schema);
2308 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
2312
2313 if let Some(compiled) = compile_predicate(predicate, &columns, &fast, &schema) {
2316 self.catalog
2317 .for_each_row_raw(table, |_rid, data| {
2318 if compiled(data) {
2319 rows.push(decode_row(&schema, data));
2320 }
2321 })
2322 .map_err(|e| e.to_string())?;
2323 } else {
2324 let pred_cols = predicate_column_indices(predicate, &columns);
2325 self.catalog
2326 .for_each_row_raw(table, |_rid, data| {
2327 let pred_row =
2328 decode_selective(&schema, &row_layout, data, &pred_cols);
2329 if eval_predicate(predicate, &pred_row, &columns) {
2330 rows.push(decode_row(&schema, data));
2331 }
2332 })
2333 .map_err(|e| e.to_string())?;
2334 }
2335
2336 return Ok(QueryResult::Rows { columns, rows });
2337 }
2338
2339 let result = self.execute_plan(input)?;
2341 match result {
2342 QueryResult::Rows { columns, rows } => {
2343 let filtered: Vec<Vec<Value>> = rows
2344 .into_iter()
2345 .filter(|row| eval_predicate(predicate, row, &columns))
2346 .collect();
2347 Ok(QueryResult::Rows {
2348 columns,
2349 rows: filtered,
2350 })
2351 }
2352 _ => Err("filter requires row input".into()),
2353 }
2354 }
2355
2356 PlanNode::Project { input, fields } => {
2357 if let PlanNode::IndexScan { table, column, key } = input.as_ref() {
2360 let schema = self
2361 .catalog
2362 .schema(table)
2363 .ok_or_else(|| format!("table '{table}' not found"))?
2364 .clone();
2365 let all_columns: Vec<String> =
2366 schema.columns.iter().map(|c| c.name.clone()).collect();
2367 let key_value = literal_to_value(key)?;
2368 let tbl = self
2369 .catalog
2370 .get_table(table)
2371 .ok_or_else(|| format!("table '{table}' not found"))?;
2372
2373 let proj_columns: Vec<String> = fields
2374 .iter()
2375 .map(|f| {
2376 f.alias.clone().unwrap_or_else(|| match &f.expr {
2377 Expr::Field(name) => name.clone(),
2378 _ => "?".into(),
2379 })
2380 })
2381 .collect();
2382
2383 let proj_indices: Vec<usize> = fields
2385 .iter()
2386 .filter_map(|f| {
2387 if let Expr::Field(name) = &f.expr {
2388 all_columns.iter().position(|c| c == name)
2389 } else {
2390 None
2391 }
2392 })
2393 .collect();
2394
2395 if let Some(btree) = tbl.index(column) {
2396 let layout = RowLayout::new(&schema);
2397 let lookup_result = match &key_value {
2401 Value::Int(k) => btree.lookup_int(*k),
2402 other => btree.lookup(other),
2403 };
2404 let rows = match lookup_result {
2405 Some(rid) => match tbl.heap.get(rid) {
2406 Some(data) => {
2407 let row: Vec<Value> = proj_indices
2408 .iter()
2409 .map(|&ci| decode_column(&schema, &layout, &data, ci))
2410 .collect();
2411 vec![row]
2412 }
2413 None => Vec::new(),
2414 },
2415 None => Vec::new(),
2416 };
2417 return Ok(QueryResult::Rows {
2418 columns: proj_columns,
2419 rows,
2420 });
2421 }
2422 }
2423
2424 if let PlanNode::Limit {
2429 input: inner,
2430 count: limit_expr,
2431 } = input.as_ref()
2432 {
2433 if let PlanNode::Sort {
2434 input: sort_input,
2435 keys,
2436 } = inner.as_ref()
2437 {
2438 if keys.len() == 1 {
2440 let sort_field = &keys[0].field;
2441 let descending = keys[0].descending;
2442 let limit = match limit_expr {
2443 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
2444 _ => usize::MAX,
2445 };
2446 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
2447 match sort_input.as_ref() {
2448 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
2449 PlanNode::Filter {
2450 input: fi,
2451 predicate,
2452 } => {
2453 if let PlanNode::SeqScan { table } = fi.as_ref() {
2454 (Some(table.as_str()), Some(predicate))
2455 } else {
2456 (None, None)
2457 }
2458 }
2459 _ => (None, None),
2460 };
2461 if let Some(table) = table_opt {
2462 if let Some(result) = self.project_filter_sort_limit_fast(
2463 table, fields, sort_field, descending, limit, pred_opt,
2464 )? {
2465 return Ok(result);
2466 }
2467 }
2468 }
2469 }
2470 if let PlanNode::Filter {
2473 input: fi,
2474 predicate,
2475 } = inner.as_ref()
2476 {
2477 if let PlanNode::SeqScan { table } = fi.as_ref() {
2478 let limit = match limit_expr {
2479 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
2480 _ => usize::MAX,
2481 };
2482 if let Some(result) = self.project_filter_limit_fast(
2483 table,
2484 fields,
2485 limit,
2486 Some(predicate),
2487 )? {
2488 return Ok(result);
2489 }
2490 }
2491 }
2492 if let PlanNode::SeqScan { table } = inner.as_ref() {
2494 let limit = match limit_expr {
2495 Expr::Literal(Literal::Int(v)) if *v >= 0 => *v as usize,
2496 _ => usize::MAX,
2497 };
2498 if let Some(result) =
2499 self.project_filter_limit_fast(table, fields, limit, None)?
2500 {
2501 return Ok(result);
2502 }
2503 }
2504 }
2505
2506 if let PlanNode::Filter {
2517 input: fi,
2518 predicate,
2519 } = input.as_ref()
2520 {
2521 if let PlanNode::SeqScan { table } = fi.as_ref() {
2522 if let Some(result) = self.project_filter_limit_fast(
2523 table,
2524 fields,
2525 usize::MAX,
2526 Some(predicate),
2527 )? {
2528 return Ok(result);
2529 }
2530 }
2531 }
2532
2533 if let PlanNode::SeqScan { table } = input.as_ref() {
2537 if let Some(result) =
2538 self.project_filter_limit_fast(table, fields, usize::MAX, None)?
2539 {
2540 return Ok(result);
2541 }
2542 }
2543
2544 let result = self.execute_plan(input)?;
2545 match result {
2546 QueryResult::Rows { columns, rows } => {
2547 let proj_columns: Vec<String> = fields
2548 .iter()
2549 .map(|f| {
2550 f.alias.clone().unwrap_or_else(|| match &f.expr {
2551 Expr::Field(name) => name.clone(),
2552 Expr::QualifiedField { qualifier, field } => {
2556 format!("{qualifier}.{field}")
2557 }
2558 _ => "?".into(),
2559 })
2560 })
2561 .collect();
2562 let proj_rows: Vec<Vec<Value>> = rows
2563 .iter()
2564 .map(|row| {
2565 fields
2566 .iter()
2567 .map(|f| eval_expr(&f.expr, row, &columns))
2568 .collect()
2569 })
2570 .collect();
2571 Ok(QueryResult::Rows {
2572 columns: proj_columns,
2573 rows: proj_rows,
2574 })
2575 }
2576 _ => Err("project requires row input".into()),
2577 }
2578 }
2579
2580 PlanNode::Sort { input, keys } => {
2581 let result = self.execute_plan(input)?;
2582 match result {
2583 QueryResult::Rows { columns, mut rows } => {
2584 if rows.len() > MAX_SORT_ROWS {
2585 return Err(format!(
2586 "sort input exceeds {} row limit — add a LIMIT clause",
2587 MAX_SORT_ROWS
2588 ));
2589 }
2590 let key_indices: Vec<(usize, bool)> = keys
2591 .iter()
2592 .map(|k| {
2593 columns
2594 .iter()
2595 .position(|c| c == &k.field)
2596 .map(|idx| (idx, k.descending))
2597 .ok_or_else(|| format!("column '{}' not found", k.field))
2598 })
2599 .collect::<Result<_, String>>()?;
2600 rows.sort_by(|a, b| {
2601 for &(col_idx, descending) in &key_indices {
2602 let cmp = a[col_idx].cmp(&b[col_idx]);
2603 let cmp = if descending { cmp.reverse() } else { cmp };
2604 if cmp != std::cmp::Ordering::Equal {
2605 return cmp;
2606 }
2607 }
2608 std::cmp::Ordering::Equal
2609 });
2610 Ok(QueryResult::Rows { columns, rows })
2611 }
2612 _ => Err("sort requires row input".into()),
2613 }
2614 }
2615
2616 PlanNode::Limit { input, count } => {
2617 let result = self.execute_plan(input)?;
2618 let n = match count {
2619 Expr::Literal(Literal::Int(v)) => *v as usize,
2620 _ => return Err("limit must be integer literal".into()),
2621 };
2622 match result {
2623 QueryResult::Rows { columns, rows } => Ok(QueryResult::Rows {
2624 columns,
2625 rows: rows.into_iter().take(n).collect(),
2626 }),
2627 _ => Err("limit requires row input".into()),
2628 }
2629 }
2630
2631 PlanNode::Offset { input, count } => {
2632 let result = self.execute_plan(input)?;
2633 let n = match count {
2634 Expr::Literal(Literal::Int(v)) => *v as usize,
2635 _ => return Err("offset must be integer literal".into()),
2636 };
2637 match result {
2638 QueryResult::Rows { columns, rows } => Ok(QueryResult::Rows {
2639 columns,
2640 rows: rows.into_iter().skip(n).collect(),
2641 }),
2642 _ => Err("offset requires row input".into()),
2643 }
2644 }
2645
2646 PlanNode::Aggregate {
2647 input,
2648 function,
2649 field,
2650 } => {
2651 if *function == AggFunc::Count {
2653 if let PlanNode::SeqScan { table } = input.as_ref() {
2654 let mut count: i64 = 0;
2655 self.catalog
2656 .for_each_row_raw(table, |_rid, _data| {
2657 count += 1;
2658 })
2659 .map_err(|e| e.to_string())?;
2660 return Ok(QueryResult::Scalar(Value::Int(count)));
2661 }
2662 if let PlanNode::Filter {
2665 input: inner,
2666 predicate,
2667 } = input.as_ref()
2668 {
2669 if let PlanNode::SeqScan { table } = inner.as_ref() {
2670 let schema = self
2671 .catalog
2672 .schema(table)
2673 .ok_or_else(|| format!("table '{table}' not found"))?
2674 .clone();
2675 let columns: Vec<String> =
2676 schema.columns.iter().map(|c| c.name.clone()).collect();
2677 let fast = FastLayout::new(&schema);
2678 let row_layout = RowLayout::new(&schema);
2679
2680 if let Some(compiled) =
2683 compile_predicate(predicate, &columns, &fast, &schema)
2684 {
2685 let mut count: i64 = 0;
2686 self.catalog
2687 .for_each_row_raw(table, |_rid, data| {
2688 if compiled(data) {
2689 count += 1;
2690 }
2691 })
2692 .map_err(|e| e.to_string())?;
2693 return Ok(QueryResult::Scalar(Value::Int(count)));
2694 }
2695
2696 let pred_cols = predicate_column_indices(predicate, &columns);
2698 let mut count: i64 = 0;
2699 self.catalog
2700 .for_each_row_raw(table, |_rid, data| {
2701 let pred_row =
2702 decode_selective(&schema, &row_layout, data, &pred_cols);
2703 if eval_predicate(predicate, &pred_row, &columns) {
2704 count += 1;
2705 }
2706 })
2707 .map_err(|e| e.to_string())?;
2708
2709 return Ok(QueryResult::Scalar(Value::Int(count)));
2710 }
2711 }
2712 }
2713
2714 if matches!(
2718 function,
2719 AggFunc::Sum
2720 | AggFunc::Avg
2721 | AggFunc::Min
2722 | AggFunc::Max
2723 | AggFunc::CountDistinct
2724 ) {
2725 if let Some(col) = field.as_ref() {
2726 let (table_opt, pred_opt): (Option<&str>, Option<&Expr>) =
2728 match input.as_ref() {
2729 PlanNode::SeqScan { table } => (Some(table.as_str()), None),
2730 PlanNode::Filter {
2731 input: inner,
2732 predicate,
2733 } => {
2734 if let PlanNode::SeqScan { table } = inner.as_ref() {
2735 (Some(table.as_str()), Some(predicate))
2736 } else {
2737 (None, None)
2738 }
2739 }
2740 _ => (None, None),
2741 };
2742 if let Some(table) = table_opt {
2743 if let Some(result) =
2744 self.agg_single_col_fast(table, col, *function, pred_opt)?
2745 {
2746 return Ok(result);
2747 }
2748 }
2749 }
2750 }
2751
2752 let result = self.execute_plan(input)?;
2757 match result {
2758 QueryResult::Rows { columns, rows } => {
2759 match function {
2760 AggFunc::Count => {
2761 Ok(QueryResult::Scalar(Value::Int(rows.len() as i64)))
2762 }
2763 AggFunc::CountDistinct => {
2764 let col = field.as_ref().ok_or("count distinct requires field")?;
2765 let idx = columns
2766 .iter()
2767 .position(|c| c == col)
2768 .ok_or("col not found")?;
2769 let mut seen = std::collections::HashSet::new();
2770 for row in &rows {
2771 let v = &row[idx];
2772 if !v.is_empty() {
2773 seen.insert(v.clone());
2774 }
2775 }
2776 Ok(QueryResult::Scalar(Value::Int(seen.len() as i64)))
2777 }
2778 AggFunc::Avg => {
2779 let col = field.as_ref().ok_or("avg requires field")?;
2780 let idx = columns
2781 .iter()
2782 .position(|c| c == col)
2783 .ok_or("col not found")?;
2784 let sum: f64 = rows
2785 .iter()
2786 .filter_map(|r| match &r[idx] {
2787 Value::Int(v) => Some(*v as f64),
2788 Value::Float(v) => Some(*v),
2789 _ => None,
2790 })
2791 .sum();
2792 let count = rows.len() as f64;
2793 Ok(QueryResult::Scalar(Value::Float(sum / count)))
2794 }
2795 AggFunc::Sum => {
2796 let col = field.as_ref().ok_or("sum requires field")?;
2797 let idx = columns
2798 .iter()
2799 .position(|c| c == col)
2800 .ok_or("col not found")?;
2801 let mut int_sum: i64 = 0;
2807 let mut float_sum: f64 = 0.0;
2808 let mut saw_float = false;
2809 for r in &rows {
2810 match &r[idx] {
2811 Value::Int(v) => int_sum += *v,
2812 Value::Float(v) => {
2813 float_sum += *v;
2814 saw_float = true;
2815 }
2816 _ => {}
2817 }
2818 }
2819 let result = if saw_float {
2820 Value::Float(float_sum + int_sum as f64)
2821 } else {
2822 Value::Int(int_sum)
2823 };
2824 Ok(QueryResult::Scalar(result))
2825 }
2826 AggFunc::Min | AggFunc::Max => {
2827 let col = field.as_ref().ok_or("min/max requires field")?;
2828 let idx = columns
2829 .iter()
2830 .position(|c| c == col)
2831 .ok_or("col not found")?;
2832 let vals: Vec<&Value> = rows.iter().map(|r| &r[idx]).collect();
2833 let result = if *function == AggFunc::Min {
2834 vals.into_iter().min().cloned()
2835 } else {
2836 vals.into_iter().max().cloned()
2837 };
2838 Ok(QueryResult::Scalar(result.unwrap_or(Value::Empty)))
2839 }
2840 }
2841 }
2842 _ => Err("aggregate requires row input".into()),
2843 }
2844 }
2845
2846 PlanNode::Insert { table, assignments } => {
2847 let values = {
2854 let schema = self
2855 .catalog
2856 .schema(table)
2857 .ok_or_else(|| format!("table '{table}' not found"))?;
2858 let mut values = vec![Value::Empty; schema.columns.len()];
2859 for a in assignments {
2860 let idx = schema
2861 .column_index(&a.field)
2862 .ok_or_else(|| format!("column '{}' not found", a.field))?;
2863 values[idx] = literal_to_value(&a.value)?;
2864 }
2865 values
2866 };
2867 self.catalog
2868 .insert(table, &values)
2869 .map_err(|e| e.to_string())?;
2870 self.view_registry.mark_dependents_dirty(table);
2871 Ok(QueryResult::Modified(1))
2872 }
2873
2874 PlanNode::Upsert {
2875 table,
2876 key_column,
2877 assignments,
2878 on_conflict,
2879 } => {
2880 let (values, key_idx) = {
2882 let schema = self
2883 .catalog
2884 .schema(table)
2885 .ok_or_else(|| format!("table '{table}' not found"))?;
2886 let mut values = vec![Value::Empty; schema.columns.len()];
2887 for a in assignments {
2888 let idx = schema
2889 .column_index(&a.field)
2890 .ok_or_else(|| format!("column '{}' not found", a.field))?;
2891 values[idx] = literal_to_value(&a.value)?;
2892 }
2893 let key_idx = schema
2894 .column_index(key_column)
2895 .ok_or_else(|| format!("key column '{key_column}' not found"))?;
2896 (values, key_idx)
2897 };
2898
2899 let key_value = values[key_idx].clone();
2900
2901 let existing = {
2903 let tbl = self
2904 .catalog
2905 .get_table(table)
2906 .ok_or_else(|| format!("table '{table}' not found"))?;
2907 if let Some(btree) = tbl.index(key_column) {
2908 let hit = match &key_value {
2909 Value::Int(k) => btree.lookup_int(*k),
2910 other => btree.lookup(other),
2911 };
2912 hit.and_then(|rid| {
2913 tbl.heap
2914 .get(rid)
2915 .map(|data| (rid, decode_row(&tbl.schema, &data)))
2916 })
2917 } else {
2918 let mut found = None;
2920 for (rid, row) in tbl.scan() {
2921 if row[key_idx] == key_value {
2922 found = Some((rid, row));
2923 break;
2924 }
2925 }
2926 found
2927 }
2928 };
2929
2930 if let Some((rid, mut existing_row)) = existing {
2931 let update_assignments = if on_conflict.is_empty() {
2933 assignments
2934 } else {
2935 on_conflict
2936 };
2937 let changed_cols: Vec<usize> = {
2938 let schema = self
2939 .catalog
2940 .schema(table)
2941 .ok_or_else(|| format!("table '{table}' not found"))?;
2942 let mut indices = Vec::new();
2943 for a in update_assignments {
2944 let idx = schema
2945 .column_index(&a.field)
2946 .ok_or_else(|| format!("column '{}' not found", a.field))?;
2947 if idx != key_idx {
2948 existing_row[idx] = literal_to_value(&a.value)?;
2949 indices.push(idx);
2950 }
2951 }
2952 indices
2953 };
2954 self.catalog
2955 .update_hinted(table, rid, &existing_row, Some(&changed_cols))
2956 .map_err(|e| e.to_string())?;
2957 self.view_registry.mark_dependents_dirty(table);
2958 Ok(QueryResult::Modified(1))
2959 } else {
2960 self.catalog
2962 .insert(table, &values)
2963 .map_err(|e| e.to_string())?;
2964 self.view_registry.mark_dependents_dirty(table);
2965 Ok(QueryResult::Modified(1))
2966 }
2967 }
2968
2969 PlanNode::Update {
2970 input,
2971 table,
2972 assignments,
2973 } => {
2974 let (col_indices, literal_vals): (Vec<usize>, Option<Vec<Value>>) = {
2980 let schema_ref = self
2981 .catalog
2982 .schema(table)
2983 .ok_or_else(|| format!("table '{table}' not found"))?;
2984 let indices: Vec<usize> = assignments
2985 .iter()
2986 .map(|a| {
2987 schema_ref
2988 .column_index(&a.field)
2989 .ok_or_else(|| format!("column '{}' not found", a.field))
2990 })
2991 .collect::<Result<_, _>>()?;
2992 let vals: Result<Vec<Value>, _> = assignments
2993 .iter()
2994 .map(|a| literal_to_value(&a.value))
2995 .collect();
2996 (indices, vals.ok())
2997 };
2998 let resolved_assignments: Option<Vec<(usize, Value)>> =
2999 literal_vals.map(|vals| col_indices.iter().copied().zip(vals).collect());
3000
3001 let changed_cols: Vec<usize> = col_indices.clone();
3004
3005 if let Some(ref resolved_assignments) = resolved_assignments {
3012 if let PlanNode::Filter {
3013 input: inner,
3014 predicate,
3015 } = input.as_ref()
3016 {
3017 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
3018 if t == table {
3019 let fused_result = self.try_fused_scan_update(
3020 table,
3021 predicate,
3022 resolved_assignments,
3023 &changed_cols,
3024 );
3025 if let Some(result) = fused_result {
3026 return result;
3027 }
3028 }
3029 }
3030 }
3031 }
3032
3033 let matching_rids = self.collect_rids_for_mutation(input, table)?;
3035
3036 if let Some(ref resolved_assignments) = resolved_assignments {
3038 let fast_patch: Option<Vec<FastPatch>> = {
3044 let tbl = self
3045 .catalog
3046 .get_table(table)
3047 .ok_or_else(|| format!("table '{table}' not found"))?;
3048 let schema = &tbl.schema;
3049 let all_fixed_nonnull = resolved_assignments.iter().all(|(idx, val)| {
3050 is_fixed_size(schema.columns[*idx].type_id) && !val.is_empty()
3051 });
3052 let no_indexed = !resolved_assignments
3053 .iter()
3054 .any(|(idx, _)| tbl.has_indexed_col(*idx));
3055
3056 if all_fixed_nonnull && no_indexed {
3057 let layout = RowLayout::new(schema);
3058 let bitmap_size = layout.bitmap_size();
3059 let patches: Vec<FastPatch> = resolved_assignments
3060 .iter()
3061 .map(|(idx, val)| {
3062 let fixed_off = layout
3063 .fixed_offset(*idx)
3064 .expect("is_fixed_size already checked");
3065 let field_off = 2 + bitmap_size + fixed_off;
3066 let bytes: FixedBytes = match val {
3067 Value::Int(v) => FixedBytes::I64(v.to_le_bytes()),
3068 Value::Float(v) => FixedBytes::F64(v.to_le_bytes()),
3069 Value::Bool(v) => FixedBytes::Bool(if *v { 1 } else { 0 }),
3070 Value::DateTime(v) => FixedBytes::I64(v.to_le_bytes()),
3071 Value::Uuid(v) => FixedBytes::Uuid(*v),
3072 _ => unreachable!("all_fixed_nonnull guard lied"),
3073 };
3074 FastPatch {
3075 field_off,
3076 bitmap_byte_off: 2 + idx / 8,
3077 bit_mask: 1u8 << (idx % 8),
3078 bytes,
3079 }
3080 })
3081 .collect();
3082 Some(patches)
3083 } else {
3084 None
3085 }
3086 };
3087
3088 if let Some(patches) = fast_patch {
3089 let mut count = 0u64;
3090 for rid in matching_rids {
3091 let ok = self
3096 .catalog
3097 .update_row_bytes_logged(table, rid, |row| {
3098 for p in &patches {
3099 row[p.bitmap_byte_off] &= !p.bit_mask;
3100 let field_bytes = p.bytes.as_slice();
3101 row[p.field_off..p.field_off + field_bytes.len()]
3102 .copy_from_slice(field_bytes);
3103 }
3104 })
3105 .map_err(|e| e.to_string())?;
3106 if ok {
3107 count += 1;
3108 }
3109 }
3110 self.view_registry.mark_dependents_dirty(table);
3111 return Ok(QueryResult::Modified(count));
3112 }
3113
3114 let var_fast: Option<(usize, Option<Vec<u8>>)> = {
3116 let tbl = self
3117 .catalog
3118 .get_table(table)
3119 .ok_or_else(|| format!("table '{table}' not found"))?;
3120 let schema = &tbl.schema;
3121 let is_single = resolved_assignments.len() == 1;
3122 let is_var_col = is_single
3123 && !is_fixed_size(schema.columns[resolved_assignments[0].0].type_id);
3124 let no_indexed = !resolved_assignments
3125 .iter()
3126 .any(|(idx, _)| tbl.has_indexed_col(*idx));
3127
3128 if is_single && is_var_col && no_indexed {
3129 let (idx, val) = &resolved_assignments[0];
3130 let bytes_opt: Option<Vec<u8>> = match val {
3131 Value::Str(s) => Some(s.as_bytes().to_vec()),
3132 Value::Bytes(b) => Some(b.clone()),
3133 Value::Empty => None,
3134 _ => {
3135 return Err(format!(
3136 "type mismatch: cannot assign non-var value to var column '{}'",
3137 schema.columns[*idx].name
3138 ))
3139 }
3140 };
3141 Some((*idx, bytes_opt))
3142 } else {
3143 None
3144 }
3145 };
3146
3147 if let Some((col_idx, new_bytes_opt)) = var_fast {
3148 let new_bytes_ref: Option<&[u8]> = new_bytes_opt.as_deref();
3149 let mut count = 0u64;
3150 let mut fallback_rids: Vec<RowId> = Vec::new();
3151 for rid in &matching_rids {
3152 let ok = self
3158 .catalog
3159 .patch_var_col_logged(table, *rid, col_idx, new_bytes_ref)
3160 .map_err(|e| e.to_string())?;
3161 if ok {
3162 count += 1;
3163 } else {
3164 fallback_rids.push(*rid);
3165 }
3166 }
3167 for rid in fallback_rids {
3168 let mut row = match self.catalog.get(table, rid) {
3169 Some(r) => r,
3170 None => continue,
3171 };
3172 for (idx, val) in resolved_assignments.iter() {
3173 row[*idx] = val.clone();
3174 }
3175 self.catalog
3176 .update_hinted(table, rid, &row, Some(&changed_cols))
3177 .map_err(|e| e.to_string())?;
3178 count += 1;
3179 }
3180 self.view_registry.mark_dependents_dirty(table);
3181 return Ok(QueryResult::Modified(count));
3182 }
3183
3184 let mut count = 0u64;
3186 for rid in matching_rids {
3187 let mut row = match self.catalog.get(table, rid) {
3188 Some(r) => r,
3189 None => continue,
3190 };
3191 for (idx, val) in resolved_assignments.iter() {
3192 row[*idx] = val.clone();
3193 }
3194 self.catalog
3195 .update_hinted(table, rid, &row, Some(&changed_cols))
3196 .map_err(|e| e.to_string())?;
3197 count += 1;
3198 }
3199 self.view_registry.mark_dependents_dirty(table);
3200 return Ok(QueryResult::Modified(count));
3201 } let col_names: Vec<String> = {
3207 let schema_ref = self
3208 .catalog
3209 .schema(table)
3210 .ok_or_else(|| format!("table '{table}' not found"))?;
3211 schema_ref.columns.iter().map(|c| c.name.clone()).collect()
3212 };
3213 let mut count = 0u64;
3214 for rid in matching_rids {
3215 let mut row = match self.catalog.get(table, rid) {
3216 Some(r) => r,
3217 None => continue,
3218 };
3219 for (i, asgn) in assignments.iter().enumerate() {
3220 let val = eval_expr(&asgn.value, &row, &col_names);
3221 row[col_indices[i]] = val;
3222 }
3223 self.catalog
3224 .update_hinted(table, rid, &row, Some(&changed_cols))
3225 .map_err(|e| e.to_string())?;
3226 count += 1;
3227 }
3228 self.view_registry.mark_dependents_dirty(table);
3229 Ok(QueryResult::Modified(count))
3230 }
3231
3232 PlanNode::Delete { input, table } => {
3233 if let PlanNode::Filter {
3254 input: inner,
3255 predicate,
3256 } = input.as_ref()
3257 {
3258 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
3259 if t == table {
3260 let schema = self
3261 .catalog
3262 .schema(table)
3263 .ok_or_else(|| format!("table '{table}' not found"))?;
3264 let columns: Vec<String> =
3265 schema.columns.iter().map(|c| c.name.clone()).collect();
3266 let fast = FastLayout::new(schema);
3267 if let Some(compiled) =
3268 compile_predicate(predicate, &columns, &fast, schema)
3269 {
3270 let count = self
3276 .catalog
3277 .scan_delete_matching_logged(table, |data| compiled(data))
3278 .map_err(|e| e.to_string())?;
3279 self.view_registry.mark_dependents_dirty(table);
3280 return Ok(QueryResult::Modified(count));
3281 }
3282 }
3283 }
3284 } else if let PlanNode::SeqScan { table: t } = input.as_ref() {
3285 if t == table {
3286 let count = self
3290 .catalog
3291 .scan_delete_matching_logged(table, |_| true)
3292 .map_err(|e| e.to_string())?;
3293 self.view_registry.mark_dependents_dirty(table);
3294 return Ok(QueryResult::Modified(count));
3295 }
3296 }
3297
3298 let matching_rids = self.collect_rids_for_mutation(input, table)?;
3299 let count = self
3300 .catalog
3301 .delete_many(table, &matching_rids)
3302 .map_err(|e| e.to_string())?;
3303 self.view_registry.mark_dependents_dirty(table);
3304 Ok(QueryResult::Modified(count))
3305 }
3306
3307 PlanNode::AliasScan { table, alias } => {
3308 let schema = self
3318 .catalog
3319 .schema(table)
3320 .ok_or_else(|| format!("table '{table}' not found"))?
3321 .clone();
3322 let columns: Vec<String> = schema
3323 .columns
3324 .iter()
3325 .map(|c| format!("{alias}.{}", c.name))
3326 .collect();
3327 let rows: Vec<Vec<Value>> = self
3328 .catalog
3329 .scan(table)
3330 .map_err(|e| e.to_string())?
3331 .map(|(_, row)| row)
3332 .collect();
3333 Ok(QueryResult::Rows { columns, rows })
3334 }
3335
3336 PlanNode::NestedLoopJoin {
3337 left,
3338 right,
3339 on,
3340 kind,
3341 } => {
3342 let left_result = self.execute_plan(left)?;
3353 let right_result = self.execute_plan(right)?;
3354 let (left_columns, left_rows) = match left_result {
3355 QueryResult::Rows { columns, rows } => (columns, rows),
3356 _ => return Err("join left side must produce rows".into()),
3357 };
3358 let (right_columns, right_rows) = match right_result {
3359 QueryResult::Rows { columns, rows } => (columns, rows),
3360 _ => return Err("join right side must produce rows".into()),
3361 };
3362
3363 if !matches!(kind, JoinKind::Cross) {
3365 if let Some(pred) = on {
3366 if let Some((l_idx, r_idx)) =
3367 try_extract_equi_join_keys(pred, &left_columns, &right_columns)
3368 {
3369 let result = hash_join(
3370 left_columns,
3371 left_rows,
3372 right_columns,
3373 right_rows,
3374 l_idx,
3375 r_idx,
3376 *kind,
3377 );
3378 if let QueryResult::Rows { ref rows, .. } = result {
3379 check_join_limit(rows.len())?;
3380 }
3381 return Ok(result);
3382 }
3383 }
3384 }
3385
3386 let n_left = left_columns.len();
3388 let n_right = right_columns.len();
3389 let mut columns = Vec::with_capacity(n_left + n_right);
3390 columns.extend(left_columns);
3391 columns.extend(right_columns);
3392
3393 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
3394 let mut combined: Vec<Value> = Vec::with_capacity(n_left + n_right);
3395
3396 for left_row in &left_rows {
3397 let mut matched = false;
3398 for right_row in &right_rows {
3399 combined.clear();
3400 combined.extend_from_slice(left_row);
3401 combined.extend_from_slice(right_row);
3402 let keep = match kind {
3403 JoinKind::Cross => true,
3404 JoinKind::Inner | JoinKind::LeftOuter => match on {
3405 Some(pred) => eval_predicate(pred, &combined, &columns),
3406 None => true,
3410 },
3411 JoinKind::RightOuter => {
3414 unreachable!("planner rewrites RightOuter to LeftOuter")
3415 }
3416 };
3417 if keep {
3418 rows.push(combined.clone());
3419 check_join_limit(rows.len())?;
3420 matched = true;
3421 }
3422 }
3423 if !matched && matches!(kind, JoinKind::LeftOuter) {
3424 let mut row = Vec::with_capacity(n_left + n_right);
3425 row.extend_from_slice(left_row);
3426 row.resize(n_left + n_right, Value::Empty);
3427 rows.push(row);
3428 check_join_limit(rows.len())?;
3429 }
3430 }
3431
3432 Ok(QueryResult::Rows { columns, rows })
3433 }
3434
3435 PlanNode::Distinct { input } => {
3436 let result = self.execute_plan(input)?;
3437 match result {
3438 QueryResult::Rows { columns, rows } => {
3439 let mut seen = std::collections::HashSet::new();
3440 let mut unique_rows = Vec::new();
3441 for row in rows {
3442 if seen.insert(row.clone()) {
3443 unique_rows.push(row);
3444 }
3445 }
3446 Ok(QueryResult::Rows {
3447 columns,
3448 rows: unique_rows,
3449 })
3450 }
3451 other => Ok(other),
3452 }
3453 }
3454
3455 PlanNode::GroupBy {
3456 input,
3457 keys,
3458 aggregates,
3459 having,
3460 } => {
3461 let result = self.execute_plan(input)?;
3462 match result {
3463 QueryResult::Rows { columns, rows } => {
3464 let key_indices: Vec<usize> = keys
3466 .iter()
3467 .map(|k| {
3468 columns
3469 .iter()
3470 .position(|c| c == k)
3471 .ok_or_else(|| format!("group-by column '{k}' not found"))
3472 })
3473 .collect::<Result<Vec<_>, _>>()?;
3474
3475 let agg_field_indices: Vec<usize> = aggregates
3479 .iter()
3480 .map(|a| {
3481 if a.field == "*" {
3482 Ok(usize::MAX)
3483 } else {
3484 columns.iter().position(|c| c == &a.field).ok_or_else(|| {
3485 format!("aggregate column '{}' not found", a.field)
3486 })
3487 }
3488 })
3489 .collect::<Result<Vec<_>, _>>()?;
3490
3491 let mut group_map: rustc_hash::FxHashMap<Vec<Value>, usize> =
3493 rustc_hash::FxHashMap::default();
3494 let mut groups: Vec<(Vec<Value>, Vec<usize>)> = Vec::new();
3495 for (ri, row) in rows.iter().enumerate() {
3496 let key: Vec<Value> =
3497 key_indices.iter().map(|&i| row[i].clone()).collect();
3498 match group_map.get(&key) {
3499 Some(&idx) => groups[idx].1.push(ri),
3500 None => {
3501 let idx = groups.len();
3502 group_map.insert(key.clone(), idx);
3503 groups.push((key, vec![ri]));
3504 }
3505 }
3506 }
3507
3508 let mut out_columns: Vec<String> = keys.clone();
3510 for agg in aggregates.iter() {
3511 out_columns.push(agg.output_name.clone());
3512 }
3513
3514 let mut out_rows: Vec<Vec<Value>> = Vec::with_capacity(groups.len());
3516 for (key_vals, row_indices) in &groups {
3517 let mut row = key_vals.clone();
3518 for (ai, agg) in aggregates.iter().enumerate() {
3519 let col_idx = agg_field_indices[ai];
3520 let val = compute_group_aggregate(
3521 agg.function,
3522 &rows,
3523 row_indices,
3524 col_idx,
3525 );
3526 row.push(val);
3527 }
3528 out_rows.push(row);
3529 }
3530
3531 if let Some(having_expr) = having {
3533 out_rows.retain(|row| eval_predicate(having_expr, row, &out_columns));
3534 }
3535
3536 Ok(QueryResult::Rows {
3537 columns: out_columns,
3538 rows: out_rows,
3539 })
3540 }
3541 _ => Err("group by requires row input".into()),
3542 }
3543 }
3544
3545 PlanNode::CreateTable { name, fields } => {
3546 let columns: Vec<ColumnDef> = fields
3547 .iter()
3548 .enumerate()
3549 .map(|(i, (fname, tname, req))| ColumnDef {
3550 name: fname.clone(),
3551 type_id: type_name_to_id(tname),
3552 required: *req,
3553 position: i as u16,
3554 })
3555 .collect();
3556 let schema = Schema {
3557 table_name: name.clone(),
3558 columns,
3559 };
3560 self.catalog
3561 .create_table(schema)
3562 .map_err(|e| e.to_string())?;
3563 Ok(QueryResult::Created(name.clone()))
3564 }
3565
3566 PlanNode::AlterTable { table, action } => match action {
3567 AlterAction::AddColumn {
3568 name,
3569 type_name,
3570 required,
3571 } => {
3572 let position = self
3573 .catalog
3574 .schema(table)
3575 .ok_or_else(|| format!("table '{table}' not found"))?
3576 .columns
3577 .len() as u16;
3578 let col = ColumnDef {
3579 name: name.clone(),
3580 type_id: type_name_to_id(type_name),
3581 required: *required,
3582 position,
3583 };
3584 self.catalog
3585 .alter_table_add_column(table, col)
3586 .map_err(|e| e.to_string())?;
3587 Ok(QueryResult::Executed {
3588 message: format!("column '{name}' added to '{table}'"),
3589 })
3590 }
3591 AlterAction::DropColumn { name } => {
3592 self.catalog
3593 .alter_table_drop_column(table, name)
3594 .map_err(|e| e.to_string())?;
3595 Ok(QueryResult::Executed {
3596 message: format!("column '{name}' dropped from '{table}'"),
3597 })
3598 }
3599 AlterAction::AddIndex { column } => {
3600 self.catalog
3601 .create_index(table, column)
3602 .map_err(|e| e.to_string())?;
3603 Ok(QueryResult::Executed {
3604 message: format!("index on '{table}.{column}' created"),
3605 })
3606 }
3607 },
3608
3609 PlanNode::DropTable { name } => {
3610 self.catalog.drop_table(name).map_err(|e| e.to_string())?;
3611 Ok(QueryResult::Executed {
3612 message: format!("table '{name}' dropped"),
3613 })
3614 }
3615
3616 PlanNode::CreateView { name, query_text } => {
3617 self.create_view(name, query_text)?;
3618 Ok(QueryResult::Executed {
3619 message: format!("materialized view '{name}' created"),
3620 })
3621 }
3622
3623 PlanNode::RefreshView { name } => {
3624 self.refresh_view(name)?;
3625 Ok(QueryResult::Executed {
3626 message: format!("materialized view '{name}' refreshed"),
3627 })
3628 }
3629
3630 PlanNode::DropView { name } => {
3631 self.drop_view(name)?;
3632 Ok(QueryResult::Executed {
3633 message: format!("materialized view '{name}' dropped"),
3634 })
3635 }
3636
3637 PlanNode::Window { input, windows } => {
3638 let result = self.execute_plan(input)?;
3639 execute_window(result, windows)
3640 }
3641
3642 PlanNode::Union { left, right, all } => {
3643 let left_result = self.execute_plan(left)?;
3644 let right_result = self.execute_plan(right)?;
3645 let (left_cols, left_rows) = match left_result {
3646 QueryResult::Rows { columns, rows } => (columns, rows),
3647 _ => return Err("UNION requires query results on left side".into()),
3648 };
3649 let (_, right_rows) = match right_result {
3650 QueryResult::Rows { columns, rows } => (columns, rows),
3651 _ => return Err("UNION requires query results on right side".into()),
3652 };
3653 let mut combined = left_rows;
3654 if *all {
3655 combined.extend(right_rows);
3657 } else {
3658 let mut seen = std::collections::HashSet::new();
3661 for row in &combined {
3662 seen.insert(row.clone());
3663 }
3664 for row in right_rows {
3665 if seen.insert(row.clone()) {
3666 combined.push(row);
3667 }
3668 }
3669 }
3670 Ok(QueryResult::Rows {
3671 columns: left_cols,
3672 rows: combined,
3673 })
3674 }
3675
3676 PlanNode::Explain { input } => {
3677 let text = format_plan_tree(input, 0);
3678 Ok(QueryResult::Rows {
3679 columns: vec!["plan".to_string()],
3680 rows: text
3681 .lines()
3682 .map(|line| vec![Value::Str(line.to_string())])
3683 .collect(),
3684 })
3685 }
3686
3687 PlanNode::IndexScan { table, column, key } => {
3688 let key_value = literal_to_value(key)?;
3689 let tbl = self
3690 .catalog
3691 .get_table(table)
3692 .ok_or_else(|| format!("table '{table}' not found"))?;
3693 let columns: Vec<String> =
3694 tbl.schema.columns.iter().map(|c| c.name.clone()).collect();
3695
3696 if let Some(btree) = tbl.index(column) {
3706 let hit = match &key_value {
3707 Value::Int(k) => btree.lookup_int(*k),
3708 other => btree.lookup(other),
3709 };
3710 let rows = match hit {
3711 Some(rid) => match tbl.heap.get(rid) {
3712 Some(data) => vec![decode_row(&tbl.schema, &data)],
3713 None => Vec::new(),
3714 },
3715 None => Vec::new(),
3716 };
3717 return Ok(QueryResult::Rows { columns, rows });
3718 }
3719
3720 let schema = &tbl.schema;
3728 let fast = FastLayout::new(schema);
3729 let synth_pred = Expr::BinaryOp(
3730 Box::new(Expr::Field(column.clone())),
3731 BinOp::Eq,
3732 Box::new(key.clone()),
3733 );
3734 if let Some(compiled) = compile_predicate(&synth_pred, &columns, &fast, schema) {
3735 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
3737 self.catalog
3738 .for_each_row_raw(table, |_rid, data| {
3739 if compiled(data) {
3740 rows.push(decode_row(schema, data));
3741 }
3742 })
3743 .map_err(|e| e.to_string())?;
3744 return Ok(QueryResult::Rows { columns, rows });
3745 }
3746
3747 let col_idx = schema
3749 .column_index(column)
3750 .ok_or_else(|| format!("column '{column}' not found"))?;
3751 let rows: Vec<Vec<Value>> = tbl
3752 .scan()
3753 .filter_map(|(_, row)| {
3754 if row[col_idx] == key_value {
3755 Some(row)
3756 } else {
3757 None
3758 }
3759 })
3760 .collect();
3761 Ok(QueryResult::Rows { columns, rows })
3762 }
3763
3764 PlanNode::RangeScan {
3765 table,
3766 column,
3767 start,
3768 end,
3769 } => {
3770 let tbl = self
3771 .catalog
3772 .get_table(table)
3773 .ok_or_else(|| format!("table '{table}' not found"))?;
3774 let columns: Vec<String> =
3775 tbl.schema.columns.iter().map(|c| c.name.clone()).collect();
3776 let schema = &tbl.schema;
3777
3778 let start_val = match start {
3779 Some((expr, _)) => Some(literal_to_value(expr)?),
3780 None => None,
3781 };
3782 let end_val = match end {
3783 Some((expr, _)) => Some(literal_to_value(expr)?),
3784 None => None,
3785 };
3786 let start_inclusive = start.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
3787 let end_inclusive = end.as_ref().map(|(_, inc)| *inc).unwrap_or(true);
3788
3789 if let Some(btree) = tbl.index(column) {
3790 let hits: Vec<(Value, RowId)> = match (&start_val, &end_val) {
3791 (Some(s), Some(e)) => btree.range(s, e).collect(),
3792 (Some(s), None) => btree.range_from(s),
3793 (None, Some(e)) => btree.range_to(e),
3794 (None, None) => {
3795 let rows: Vec<Vec<Value>> = tbl.scan().map(|(_, row)| row).collect();
3796 return Ok(QueryResult::Rows { columns, rows });
3797 }
3798 };
3799 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(hits.len());
3800 for (key, rid) in hits {
3801 if !start_inclusive {
3802 if let Some(ref s) = start_val {
3803 if &key == s {
3804 continue;
3805 }
3806 }
3807 }
3808 if !end_inclusive {
3809 if let Some(ref e) = end_val {
3810 if &key == e {
3811 continue;
3812 }
3813 }
3814 }
3815 if let Some(data) = tbl.heap.get(rid) {
3816 rows.push(decode_row(schema, &data));
3817 }
3818 }
3819 return Ok(QueryResult::Rows { columns, rows });
3820 }
3821
3822 let fast = FastLayout::new(schema);
3824 let synth = synthesize_range_predicate(column, start, end);
3825 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, schema) {
3826 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(64);
3827 self.catalog
3828 .for_each_row_raw(table, |_rid, data| {
3829 if compiled(data) {
3830 rows.push(decode_row(schema, data));
3831 }
3832 })
3833 .map_err(|e| e.to_string())?;
3834 return Ok(QueryResult::Rows { columns, rows });
3835 }
3836
3837 let col_idx = schema
3838 .column_index(column)
3839 .ok_or_else(|| format!("column '{column}' not found"))?;
3840 let rows: Vec<Vec<Value>> = tbl
3841 .scan()
3842 .filter(|(_, row)| {
3843 range_matches(
3844 &row[col_idx],
3845 &start_val,
3846 start_inclusive,
3847 &end_val,
3848 end_inclusive,
3849 )
3850 })
3851 .map(|(_, row)| row)
3852 .collect();
3853 Ok(QueryResult::Rows { columns, rows })
3854 }
3855 }
3856 }
3857
3858 fn create_view(&mut self, name: &str, query_text: &str) -> Result<(), String> {
3863 if self.view_registry.is_view(name) {
3864 return Err(format!("materialized view '{name}' already exists"));
3865 }
3866 let result = self.execute_powql(query_text)?;
3868 let (columns, rows) = match result {
3869 QueryResult::Rows { columns, rows } => (columns, rows),
3870 _ => return Err("view source query must be a SELECT".into()),
3871 };
3872 let schema = self.derive_view_schema(name, &columns, &rows);
3874 self.catalog
3876 .create_table(schema)
3877 .map_err(|e| e.to_string())?;
3878 for row in &rows {
3879 self.catalog.insert(name, row).map_err(|e| e.to_string())?;
3880 }
3881 let depends_on = self.extract_view_deps(query_text);
3883 self.view_registry
3884 .register(ViewDef {
3885 name: name.to_string(),
3886 query: query_text.to_string(),
3887 depends_on,
3888 dirty: false,
3889 })
3890 .map_err(|e| e.to_string())?;
3891 Ok(())
3892 }
3893
3894 fn refresh_view(&mut self, name: &str) -> Result<(), String> {
3897 let def = self
3898 .view_registry
3899 .get(name)
3900 .ok_or_else(|| format!("materialized view '{name}' not found"))?;
3901 let query_text = def.query.clone();
3902 let result = self.execute_powql(&query_text)?;
3904 let (_columns, rows) = match result {
3905 QueryResult::Rows { columns, rows } => (columns, rows),
3906 _ => return Err("view source query must be a SELECT".into()),
3907 };
3908 self.catalog
3912 .scan_delete_matching_logged(name, |_| true)
3913 .map_err(|e| e.to_string())?;
3914 for row in &rows {
3915 self.catalog.insert(name, row).map_err(|e| e.to_string())?;
3916 }
3917 self.view_registry.mark_clean(name);
3918 Ok(())
3919 }
3920
3921 fn drop_view(&mut self, name: &str) -> Result<(), String> {
3923 if !self.view_registry.is_view(name) {
3924 return Err(format!("materialized view '{name}' not found"));
3925 }
3926 self.view_registry
3927 .unregister(name)
3928 .map_err(|e| e.to_string())?;
3929 self.catalog.drop_table(name).map_err(|e| e.to_string())?;
3930 Ok(())
3931 }
3932
3933 fn derive_view_schema(&self, name: &str, columns: &[String], rows: &[Vec<Value>]) -> Schema {
3936 use powdb_storage::types::{ColumnDef, TypeId};
3937 let cols: Vec<ColumnDef> = columns
3938 .iter()
3939 .enumerate()
3940 .map(|(i, col_name)| {
3941 let type_id = rows
3942 .first()
3943 .and_then(|row| row.get(i))
3944 .map(|v| v.type_id())
3945 .unwrap_or(TypeId::Str);
3946 ColumnDef {
3947 name: col_name.clone(),
3948 type_id,
3949 required: false,
3950 position: i as u16,
3951 }
3952 })
3953 .collect();
3954 Schema {
3955 table_name: name.to_string(),
3956 columns: cols,
3957 }
3958 }
3959
3960 fn extract_view_deps(&self, query_text: &str) -> Vec<String> {
3963 use crate::parser::parse;
3964 match parse(query_text) {
3965 Ok(Statement::Query(q)) => {
3966 let mut deps = vec![q.source.clone()];
3967 for j in &q.joins {
3968 deps.push(j.source.clone());
3969 }
3970 deps
3971 }
3972 _ => Vec::new(),
3973 }
3974 }
3975
3976 fn agg_single_col_fast(
3986 &self,
3987 table: &str,
3988 col: &str,
3989 function: AggFunc,
3990 predicate: Option<&Expr>,
3991 ) -> Result<Option<QueryResult>, String> {
3992 let schema = self
3993 .catalog
3994 .schema(table)
3995 .ok_or_else(|| format!("table '{table}' not found"))?
3996 .clone();
3997 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
3998 let col_idx = match schema.column_index(col) {
3999 Some(i) => i,
4000 None => return Ok(None),
4001 };
4002 let col_type = schema.columns[col_idx].type_id;
4009 if col_type != TypeId::Int && col_type != TypeId::Float {
4010 return Ok(None);
4011 }
4012
4013 let fast = FastLayout::new(&schema);
4014 let byte_offset = match fast.fixed_offsets[col_idx] {
4019 Some(o) => o,
4020 None => return Ok(None),
4021 };
4022 let bitmap_byte = col_idx / 8;
4023 let bitmap_bit = (col_idx % 8) as u32;
4024 let data_offset = 2 + fast.bitmap_size + byte_offset;
4025
4026 let compiled_pred: Option<CompiledPredicate> = match predicate {
4028 Some(pred) => match compile_predicate(pred, &columns, &fast, &schema) {
4029 Some(c) => Some(c),
4030 None => return Ok(None), },
4032 None => None,
4033 };
4034
4035 let result = match col_type {
4062 TypeId::Int => match function {
4063 AggFunc::Sum | AggFunc::Avg => {
4064 let mut sum_i128: i128 = 0;
4065 let mut count: i64 = 0;
4066 agg_int_loop!(
4067 self,
4068 table,
4069 compiled_pred,
4070 bitmap_byte,
4071 bitmap_bit,
4072 data_offset,
4073 |v: i64| {
4074 count += 1;
4075 sum_i128 += v as i128;
4076 }
4077 );
4078 if matches!(function, AggFunc::Sum) {
4079 let clamped = sum_i128.clamp(i64::MIN as i128, i64::MAX as i128) as i64;
4080 QueryResult::Scalar(Value::Int(clamped))
4081 } else if count == 0 {
4082 QueryResult::Scalar(Value::Empty)
4083 } else {
4084 let avg = (sum_i128 as f64) / (count as f64);
4085 QueryResult::Scalar(Value::Float(avg))
4086 }
4087 }
4088 AggFunc::Min => {
4089 let mut min_v: Option<i64> = None;
4090 agg_int_loop!(
4091 self,
4092 table,
4093 compiled_pred,
4094 bitmap_byte,
4095 bitmap_bit,
4096 data_offset,
4097 |v: i64| {
4098 min_v = Some(match min_v {
4099 Some(m) => m.min(v),
4100 None => v,
4101 });
4102 }
4103 );
4104 QueryResult::Scalar(min_v.map(Value::Int).unwrap_or(Value::Empty))
4105 }
4106 AggFunc::Max => {
4107 let mut max_v: Option<i64> = None;
4108 agg_int_loop!(
4109 self,
4110 table,
4111 compiled_pred,
4112 bitmap_byte,
4113 bitmap_bit,
4114 data_offset,
4115 |v: i64| {
4116 max_v = Some(match max_v {
4117 Some(m) => m.max(v),
4118 None => v,
4119 });
4120 }
4121 );
4122 QueryResult::Scalar(max_v.map(Value::Int).unwrap_or(Value::Empty))
4123 }
4124 AggFunc::Count => {
4125 let mut count: i64 = 0;
4126 agg_int_loop!(
4127 self,
4128 table,
4129 compiled_pred,
4130 bitmap_byte,
4131 bitmap_bit,
4132 data_offset,
4133 |_v: i64| {
4134 count += 1;
4135 }
4136 );
4137 QueryResult::Scalar(Value::Int(count))
4138 }
4139 AggFunc::CountDistinct => {
4140 let mut seen = rustc_hash::FxHashSet::default();
4141 agg_int_loop!(
4142 self,
4143 table,
4144 compiled_pred,
4145 bitmap_byte,
4146 bitmap_bit,
4147 data_offset,
4148 |v: i64| {
4149 seen.insert(v);
4150 }
4151 );
4152 QueryResult::Scalar(Value::Int(seen.len() as i64))
4153 }
4154 },
4155 TypeId::Float => match function {
4156 AggFunc::Sum => {
4157 let mut sum: f64 = 0.0;
4162 agg_float_loop!(
4163 self,
4164 table,
4165 compiled_pred,
4166 bitmap_byte,
4167 bitmap_bit,
4168 data_offset,
4169 |v: f64| {
4170 sum += v;
4171 }
4172 );
4173 QueryResult::Scalar(Value::Float(sum))
4174 }
4175 AggFunc::Avg => {
4176 let mut sum: f64 = 0.0;
4177 let mut count: i64 = 0;
4178 agg_float_loop!(
4179 self,
4180 table,
4181 compiled_pred,
4182 bitmap_byte,
4183 bitmap_bit,
4184 data_offset,
4185 |v: f64| {
4186 sum += v;
4187 count += 1;
4188 }
4189 );
4190 if count == 0 {
4191 QueryResult::Scalar(Value::Empty)
4192 } else {
4193 QueryResult::Scalar(Value::Float(sum / count as f64))
4194 }
4195 }
4196 AggFunc::Min => {
4197 let mut min_v: Option<f64> = None;
4201 agg_float_loop!(
4202 self,
4203 table,
4204 compiled_pred,
4205 bitmap_byte,
4206 bitmap_bit,
4207 data_offset,
4208 |v: f64| {
4209 min_v = Some(match min_v {
4210 Some(m) => {
4211 if v.total_cmp(&m).is_lt() {
4212 v
4213 } else {
4214 m
4215 }
4216 }
4217 None => v,
4218 });
4219 }
4220 );
4221 QueryResult::Scalar(min_v.map(Value::Float).unwrap_or(Value::Empty))
4222 }
4223 AggFunc::Max => {
4224 let mut max_v: Option<f64> = None;
4225 agg_float_loop!(
4226 self,
4227 table,
4228 compiled_pred,
4229 bitmap_byte,
4230 bitmap_bit,
4231 data_offset,
4232 |v: f64| {
4233 max_v = Some(match max_v {
4234 Some(m) => {
4235 if v.total_cmp(&m).is_gt() {
4236 v
4237 } else {
4238 m
4239 }
4240 }
4241 None => v,
4242 });
4243 }
4244 );
4245 QueryResult::Scalar(max_v.map(Value::Float).unwrap_or(Value::Empty))
4246 }
4247 AggFunc::Count => {
4248 let mut count: i64 = 0;
4249 agg_float_loop!(
4250 self,
4251 table,
4252 compiled_pred,
4253 bitmap_byte,
4254 bitmap_bit,
4255 data_offset,
4256 |_v: f64| {
4257 count += 1;
4258 }
4259 );
4260 QueryResult::Scalar(Value::Int(count))
4261 }
4262 AggFunc::CountDistinct => {
4263 let mut seen = rustc_hash::FxHashSet::default();
4269 agg_float_loop!(
4270 self,
4271 table,
4272 compiled_pred,
4273 bitmap_byte,
4274 bitmap_bit,
4275 data_offset,
4276 |v: f64| {
4277 seen.insert(v.to_bits());
4278 }
4279 );
4280 QueryResult::Scalar(Value::Int(seen.len() as i64))
4281 }
4282 },
4283 _ => unreachable!("type guard above restricts to Int/Float"),
4284 };
4285 Ok(Some(result))
4286 }
4287
4288 fn project_filter_limit_fast(
4291 &self,
4292 table: &str,
4293 fields: &[ProjectField],
4294 limit: usize,
4295 predicate: Option<&Expr>,
4296 ) -> Result<Option<QueryResult>, String> {
4297 let schema = self
4298 .catalog
4299 .schema(table)
4300 .ok_or_else(|| format!("table '{table}' not found"))?
4301 .clone();
4302 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4303
4304 let mut proj_indices: Vec<usize> = Vec::with_capacity(fields.len());
4307 let mut proj_columns: Vec<String> = Vec::with_capacity(fields.len());
4308 for f in fields {
4309 let name = match &f.expr {
4310 Expr::Field(n) => n.clone(),
4311 _ => return Ok(None),
4312 };
4313 let idx = match all_columns.iter().position(|c| c == &name) {
4314 Some(i) => i,
4315 None => return Ok(None),
4316 };
4317 proj_indices.push(idx);
4318 proj_columns.push(f.alias.clone().unwrap_or(name));
4319 }
4320
4321 let fast = FastLayout::new(&schema);
4322 let row_layout = RowLayout::new(&schema);
4323
4324 let compiled_pred: Option<CompiledPredicate> = match predicate {
4325 Some(pred) => match compile_predicate(pred, &all_columns, &fast, &schema) {
4326 Some(c) => Some(c),
4327 None => return Ok(None),
4328 },
4329 None => None,
4330 };
4331
4332 let mut out: Vec<Vec<Value>> = Vec::with_capacity(limit.min(1024));
4333 self.catalog
4338 .try_for_each_row_raw(table, |_rid, data| {
4339 use std::ops::ControlFlow;
4340 if let Some(ref pred) = compiled_pred {
4341 if !pred(data) {
4342 return ControlFlow::Continue(());
4343 }
4344 }
4345 let row: Vec<Value> = proj_indices
4346 .iter()
4347 .map(|&ci| decode_column(&schema, &row_layout, data, ci))
4348 .collect();
4349 out.push(row);
4350 if out.len() >= limit {
4351 ControlFlow::Break(())
4352 } else {
4353 ControlFlow::Continue(())
4354 }
4355 })
4356 .map_err(|e| e.to_string())?;
4357
4358 Ok(Some(QueryResult::Rows {
4359 columns: proj_columns,
4360 rows: out,
4361 }))
4362 }
4363
4364 fn project_filter_sort_limit_fast(
4369 &self,
4370 table: &str,
4371 fields: &[ProjectField],
4372 sort_field: &str,
4373 descending: bool,
4374 limit: usize,
4375 predicate: Option<&Expr>,
4376 ) -> Result<Option<QueryResult>, String> {
4377 if limit == 0 {
4378 return Ok(None);
4381 }
4382 let schema = self
4383 .catalog
4384 .schema(table)
4385 .ok_or_else(|| format!("table '{table}' not found"))?
4386 .clone();
4387 let all_columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4388
4389 let sort_idx = match schema.column_index(sort_field) {
4396 Some(i) => i,
4397 None => return Ok(None),
4398 };
4399 let sort_col_type = schema.columns[sort_idx].type_id;
4400 if sort_col_type != TypeId::Int && sort_col_type != TypeId::Float {
4401 return Ok(None);
4402 }
4403
4404 let mut proj_indices: Vec<usize> = Vec::with_capacity(fields.len());
4406 let mut proj_columns: Vec<String> = Vec::with_capacity(fields.len());
4407 for f in fields {
4408 let name = match &f.expr {
4409 Expr::Field(n) => n.clone(),
4410 _ => return Ok(None),
4411 };
4412 let idx = match all_columns.iter().position(|c| c == &name) {
4413 Some(i) => i,
4414 None => return Ok(None),
4415 };
4416 proj_indices.push(idx);
4417 proj_columns.push(f.alias.clone().unwrap_or(name));
4418 }
4419
4420 let fast = FastLayout::new(&schema);
4421 let row_layout = RowLayout::new(&schema);
4422 let sort_byte_offset = match fast.fixed_offsets[sort_idx] {
4424 Some(o) => o,
4425 None => return Ok(None),
4426 };
4427 let sort_bitmap_byte = sort_idx / 8;
4428 let sort_bitmap_bit = (sort_idx % 8) as u32;
4429 let sort_data_offset = 2 + fast.bitmap_size + sort_byte_offset;
4430
4431 let compiled_pred: Option<CompiledPredicate> = match predicate {
4432 Some(pred) => match compile_predicate(pred, &all_columns, &fast, &schema) {
4433 Some(c) => Some(c),
4434 None => return Ok(None),
4435 },
4436 None => None,
4437 };
4438
4439 let drained: Vec<Vec<u8>> = match sort_col_type {
4448 TypeId::Int => {
4449 let mut seq: u64 = 0;
4450 let mut heap_desc: BinaryHeap<Reverse<(i64, u64, Vec<u8>)>> =
4451 BinaryHeap::with_capacity(limit);
4452 let mut heap_asc: BinaryHeap<(i64, u64, Vec<u8>)> =
4453 BinaryHeap::with_capacity(limit);
4454
4455 self.catalog
4456 .for_each_row_raw(table, |_rid, data| {
4457 if let Some(ref pred) = compiled_pred {
4458 if !pred(data) {
4459 return;
4460 }
4461 }
4462 let is_null = (data[2 + sort_bitmap_byte] >> sort_bitmap_bit) & 1 == 1;
4464 if is_null {
4465 return;
4466 }
4467 let key = i64::from_le_bytes(
4468 data[sort_data_offset..sort_data_offset + 8]
4469 .try_into()
4470 .unwrap(),
4471 );
4472 let id = seq;
4473 seq += 1;
4474
4475 if descending {
4476 if heap_desc.len() < limit {
4477 heap_desc.push(Reverse((key, id, data.to_vec())));
4478 } else if let Some(Reverse((top_key, _, _))) = heap_desc.peek() {
4479 if key > *top_key {
4480 heap_desc.pop();
4481 heap_desc.push(Reverse((key, id, data.to_vec())));
4482 }
4483 }
4484 } else if heap_asc.len() < limit {
4485 heap_asc.push((key, id, data.to_vec()));
4486 } else if let Some((top_key, _, _)) = heap_asc.peek() {
4487 if key < *top_key {
4488 heap_asc.pop();
4489 heap_asc.push((key, id, data.to_vec()));
4490 }
4491 }
4492 })
4493 .map_err(|e| e.to_string())?;
4494
4495 let mut drained: Vec<(i64, u64, Vec<u8>)> = if descending {
4496 heap_desc.into_iter().map(|Reverse(t)| t).collect()
4497 } else {
4498 heap_asc.into_iter().collect()
4499 };
4500 if descending {
4501 drained.sort_unstable_by(|a, b| b.0.cmp(&a.0).then(a.1.cmp(&b.1)));
4502 } else {
4503 drained.sort_unstable_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
4504 }
4505 drained.into_iter().map(|(_, _, d)| d).collect()
4506 }
4507 TypeId::Float => {
4508 let mut seq: u64 = 0;
4517 let mut heap_desc: BinaryHeap<Reverse<(u64, u64, Vec<u8>)>> =
4518 BinaryHeap::with_capacity(limit);
4519 let mut heap_asc: BinaryHeap<(u64, u64, Vec<u8>)> =
4520 BinaryHeap::with_capacity(limit);
4521
4522 self.catalog
4523 .for_each_row_raw(table, |_rid, data| {
4524 if let Some(ref pred) = compiled_pred {
4525 if !pred(data) {
4526 return;
4527 }
4528 }
4529 let is_null = (data[2 + sort_bitmap_byte] >> sort_bitmap_bit) & 1 == 1;
4530 if is_null {
4531 return;
4532 }
4533 let bits = u64::from_le_bytes(
4534 data[sort_data_offset..sort_data_offset + 8]
4535 .try_into()
4536 .unwrap(),
4537 );
4538 let key = f64_bits_to_sortable_u64(bits);
4539 let id = seq;
4540 seq += 1;
4541
4542 if descending {
4543 if heap_desc.len() < limit {
4544 heap_desc.push(Reverse((key, id, data.to_vec())));
4545 } else if let Some(Reverse((top_key, _, _))) = heap_desc.peek() {
4546 if key > *top_key {
4547 heap_desc.pop();
4548 heap_desc.push(Reverse((key, id, data.to_vec())));
4549 }
4550 }
4551 } else if heap_asc.len() < limit {
4552 heap_asc.push((key, id, data.to_vec()));
4553 } else if let Some((top_key, _, _)) = heap_asc.peek() {
4554 if key < *top_key {
4555 heap_asc.pop();
4556 heap_asc.push((key, id, data.to_vec()));
4557 }
4558 }
4559 })
4560 .map_err(|e| e.to_string())?;
4561
4562 let mut drained: Vec<(u64, u64, Vec<u8>)> = if descending {
4563 heap_desc.into_iter().map(|Reverse(t)| t).collect()
4564 } else {
4565 heap_asc.into_iter().collect()
4566 };
4567 if descending {
4568 drained.sort_unstable_by(|a, b| b.0.cmp(&a.0).then(a.1.cmp(&b.1)));
4569 } else {
4570 drained.sort_unstable_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
4571 }
4572 drained.into_iter().map(|(_, _, d)| d).collect()
4573 }
4574 _ => unreachable!("type guard above restricts to Int/Float"),
4575 };
4576
4577 let rows: Vec<Vec<Value>> = drained
4578 .into_iter()
4579 .map(|data| {
4580 proj_indices
4581 .iter()
4582 .map(|&ci| decode_column(&schema, &row_layout, &data, ci))
4583 .collect()
4584 })
4585 .collect();
4586
4587 Ok(Some(QueryResult::Rows {
4588 columns: proj_columns,
4589 rows,
4590 }))
4591 }
4592
4593 fn try_fused_scan_update(
4610 &mut self,
4611 table: &str,
4612 predicate: &Expr,
4613 resolved: &[(usize, Value)],
4614 changed_cols: &[usize],
4615 ) -> Option<Result<QueryResult, String>> {
4616 let compiled = {
4619 let schema = self.catalog.schema(table)?;
4620 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4621 let fast = FastLayout::new(schema);
4622 compile_predicate(predicate, &columns, &fast, schema)?
4623 };
4624
4625 let fixed_patches: Option<Vec<FastPatch>> = {
4627 let tbl = self.catalog.get_table(table)?;
4628 let schema = &tbl.schema;
4629 let all_fixed_nonnull = resolved
4630 .iter()
4631 .all(|(idx, val)| is_fixed_size(schema.columns[*idx].type_id) && !val.is_empty());
4632 let no_indexed = !resolved.iter().any(|(idx, _)| tbl.has_indexed_col(*idx));
4633 if all_fixed_nonnull && no_indexed {
4634 let layout = RowLayout::new(schema);
4635 let bitmap_size = layout.bitmap_size();
4636 Some(
4637 resolved
4638 .iter()
4639 .map(|(idx, val)| {
4640 let fixed_off = layout
4641 .fixed_offset(*idx)
4642 .expect("is_fixed_size already checked");
4643 let field_off = 2 + bitmap_size + fixed_off;
4644 let bytes: FixedBytes = match val {
4645 Value::Int(v) => FixedBytes::I64(v.to_le_bytes()),
4646 Value::Float(v) => FixedBytes::F64(v.to_le_bytes()),
4647 Value::Bool(v) => FixedBytes::Bool(if *v { 1 } else { 0 }),
4648 Value::DateTime(v) => FixedBytes::I64(v.to_le_bytes()),
4649 Value::Uuid(v) => FixedBytes::Uuid(*v),
4650 _ => unreachable!("all_fixed_nonnull guard"),
4651 };
4652 FastPatch {
4653 field_off,
4654 bitmap_byte_off: 2 + idx / 8,
4655 bit_mask: 1u8 << (idx % 8),
4656 bytes,
4657 }
4658 })
4659 .collect(),
4660 )
4661 } else {
4662 None
4663 }
4664 };
4665 if let Some(patches) = fixed_patches {
4666 let result = self
4667 .catalog
4668 .scan_patch_matching_logged(table, compiled, |row| {
4669 for p in &patches {
4670 row[p.bitmap_byte_off] &= !p.bit_mask;
4671 let field_bytes = p.bytes.as_slice();
4672 row[p.field_off..p.field_off + field_bytes.len()]
4673 .copy_from_slice(field_bytes);
4674 }
4675 Some(row.len() as u16)
4676 })
4677 .map_err(|e| e.to_string());
4678 match result {
4679 Ok((count, _)) => {
4680 self.view_registry.mark_dependents_dirty(table);
4681 return Some(Ok(QueryResult::Modified(count)));
4682 }
4683 Err(e) => return Some(Err(e)),
4684 }
4685 }
4686
4687 let var_patch: Option<(usize, Option<Vec<u8>>)> = {
4689 let tbl = self.catalog.get_table(table)?;
4690 let schema = &tbl.schema;
4691 let is_single = resolved.len() == 1;
4692 let is_var = is_single && !is_fixed_size(schema.columns[resolved[0].0].type_id);
4693 let no_indexed = !resolved.iter().any(|(idx, _)| tbl.has_indexed_col(*idx));
4694 if is_single && is_var && no_indexed {
4695 let (idx, val) = &resolved[0];
4696 let bytes_opt = match val {
4697 Value::Str(s) => Some(s.as_bytes().to_vec()),
4698 Value::Bytes(b) => Some(b.clone()),
4699 Value::Empty => None,
4700 _ => return None, };
4702 Some((*idx, bytes_opt))
4703 } else {
4704 None
4705 }
4706 };
4707 if let Some((col_idx, ref new_bytes_opt)) = var_patch {
4708 let layout = {
4710 let schema = self.catalog.schema(table)?;
4711 RowLayout::new(schema)
4712 };
4713 let new_bytes_ref: Option<&[u8]> = new_bytes_opt.as_deref();
4714 let result = self
4715 .catalog
4716 .scan_patch_matching_logged(table, compiled, |row| {
4717 patch_var_column_in_place(row, &layout, col_idx, new_bytes_ref)
4718 })
4719 .map_err(|e| e.to_string());
4720 match result {
4721 Ok((mut count, fallback_rids)) => {
4722 for rid in fallback_rids {
4724 let mut row = match self.catalog.get(table, rid) {
4725 Some(r) => r,
4726 None => continue,
4727 };
4728 for (idx, val) in resolved.iter() {
4729 row[*idx] = val.clone();
4730 }
4731 self.catalog
4732 .update_hinted(table, rid, &row, Some(changed_cols))
4733 .map_err(|e| e.to_string())
4734 .ok();
4735 count += 1;
4736 }
4737 self.view_registry.mark_dependents_dirty(table);
4738 return Some(Ok(QueryResult::Modified(count)));
4739 }
4740 Err(e) => return Some(Err(e)),
4741 }
4742 }
4743
4744 None }
4746
4747 fn collect_rids_for_mutation(
4753 &mut self,
4754 input: &PlanNode,
4755 table: &str,
4756 ) -> Result<Vec<RowId>, String> {
4757 match input {
4758 PlanNode::SeqScan { table: t } if t == table => {
4759 let rids: Vec<RowId> = self
4761 .catalog
4762 .scan(table)
4763 .map_err(|e| e.to_string())?
4764 .map(|(rid, _)| rid)
4765 .collect();
4766 Ok(rids)
4767 }
4768 PlanNode::IndexScan {
4769 table: t,
4770 column,
4771 key,
4772 } if t == table => {
4773 let key_value = literal_to_value(key)?;
4774
4775 {
4784 let tbl = self
4785 .catalog
4786 .get_table(table)
4787 .ok_or_else(|| format!("table '{table}' not found"))?;
4788 if let Some(btree) = tbl.index(column) {
4789 let hit = match &key_value {
4790 Value::Int(k) => btree.lookup_int(*k),
4791 other => btree.lookup(other),
4792 };
4793 return Ok(match hit {
4794 Some(rid) => vec![rid],
4795 None => Vec::new(),
4796 });
4797 }
4798 }
4799
4800 let schema = self
4805 .catalog
4806 .schema(table)
4807 .ok_or_else(|| format!("table '{table}' not found"))?;
4808 let columns: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
4809 let fast = FastLayout::new(schema);
4810 let synth = Expr::BinaryOp(
4811 Box::new(Expr::Field(column.clone())),
4812 BinOp::Eq,
4813 Box::new(key.clone()),
4814 );
4815 if let Some(compiled) = compile_predicate(&synth, &columns, &fast, schema) {
4816 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4818 self.catalog
4819 .for_each_row_raw(table, |rid, data| {
4820 if compiled(data) {
4821 rids.push(rid);
4822 }
4823 })
4824 .map_err(|e| e.to_string())?;
4825 return Ok(rids);
4826 }
4827
4828 let col_idx = schema
4830 .column_index(column)
4831 .ok_or_else(|| format!("column '{column}' not found"))?;
4832 let rids: Vec<RowId> = self
4833 .catalog
4834 .scan(table)
4835 .map_err(|e| e.to_string())?
4836 .filter_map(|(rid, row)| {
4837 if row[col_idx] == key_value {
4838 Some(rid)
4839 } else {
4840 None
4841 }
4842 })
4843 .collect();
4844 Ok(rids)
4845 }
4846 PlanNode::Filter {
4847 input: inner,
4848 predicate,
4849 } => {
4850 if let PlanNode::SeqScan { table: t } = inner.as_ref() {
4851 if t != table {
4852 return self.generic_rid_match(input, table);
4853 }
4854 let schema = self
4855 .catalog
4856 .schema(table)
4857 .ok_or_else(|| format!("table '{table}' not found"))?;
4858 let columns: Vec<String> =
4859 schema.columns.iter().map(|c| c.name.clone()).collect();
4860 let fast = FastLayout::new(schema);
4861 let row_layout = RowLayout::new(schema);
4862
4863 if let Some(compiled) = compile_predicate(predicate, &columns, &fast, schema) {
4865 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4867 self.catalog
4868 .for_each_row_raw(table, |rid, data| {
4869 if compiled(data) {
4870 rids.push(rid);
4871 }
4872 })
4873 .map_err(|e| e.to_string())?;
4874 return Ok(rids);
4875 }
4876
4877 let pred_cols = predicate_column_indices(predicate, &columns);
4879 let mut rids: Vec<RowId> = Vec::with_capacity(64);
4880 self.catalog
4881 .for_each_row_raw(table, |rid, data| {
4882 let pred_row = decode_selective(schema, &row_layout, data, &pred_cols);
4883 if eval_predicate(predicate, &pred_row, &columns) {
4884 rids.push(rid);
4885 }
4886 })
4887 .map_err(|e| e.to_string())?;
4888 return Ok(rids);
4889 }
4890 self.generic_rid_match(input, table)
4891 }
4892 _ => self.generic_rid_match(input, table),
4893 }
4894 }
4895
4896 fn generic_rid_match(&mut self, input: &PlanNode, table: &str) -> Result<Vec<RowId>, String> {
4900 let result = self.execute_plan(input)?;
4901 let rows = match result {
4902 QueryResult::Rows { rows, .. } => rows,
4903 _ => return Err("mutation source must be rows".into()),
4904 };
4905 let matching: Vec<RowId> = self
4906 .catalog
4907 .scan(table)
4908 .map_err(|e| e.to_string())?
4909 .filter(|(_, row)| rows.iter().any(|r| r == row))
4910 .map(|(rid, _)| rid)
4911 .collect();
4912 Ok(matching)
4913 }
4914
4915 pub fn catalog(&self) -> &Catalog {
4916 &self.catalog
4917 }
4918
4919 pub fn catalog_mut(&mut self) -> &mut Catalog {
4920 &mut self.catalog
4921 }
4922}
4923
4924#[derive(Clone, Copy)]
4928struct FastPatch {
4929 field_off: usize,
4932 bitmap_byte_off: usize,
4936 bit_mask: u8,
4938 bytes: FixedBytes,
4940}
4941
4942#[derive(Clone, Copy)]
4943enum FixedBytes {
4944 I64([u8; 8]),
4945 F64([u8; 8]),
4946 Bool(u8),
4947 Uuid([u8; 16]),
4948}
4949
4950impl FixedBytes {
4951 #[inline]
4952 fn as_slice(&self) -> &[u8] {
4953 match self {
4954 FixedBytes::I64(b) => b.as_slice(),
4955 FixedBytes::F64(b) => b.as_slice(),
4956 FixedBytes::Bool(b) => std::slice::from_ref(b),
4957 FixedBytes::Uuid(b) => b.as_slice(),
4958 }
4959 }
4960}
4961
4962fn type_name_to_id(name: &str) -> TypeId {
4963 match name {
4964 "str" => TypeId::Str,
4965 "int" => TypeId::Int,
4966 "float" => TypeId::Float,
4967 "bool" => TypeId::Bool,
4968 "datetime" => TypeId::DateTime,
4969 "uuid" => TypeId::Uuid,
4970 "bytes" => TypeId::Bytes,
4971 _ => TypeId::Str,
4972 }
4973}
4974
4975fn collect_field_refs(expr: &Expr, out: &mut Vec<String>) {
4981 match expr {
4982 Expr::Field(name) => out.push(name.clone()),
4983 Expr::QualifiedField { qualifier, field } => {
4984 out.push(format!("{qualifier}.{field}"));
4985 }
4986 Expr::BinaryOp(l, _, r) => {
4987 collect_field_refs(l, out);
4988 collect_field_refs(r, out);
4989 }
4990 Expr::UnaryOp(_, inner) => collect_field_refs(inner, out),
4991 Expr::FunctionCall(_, inner) => collect_field_refs(inner, out),
4992 Expr::Coalesce(l, r) => {
4993 collect_field_refs(l, out);
4994 collect_field_refs(r, out);
4995 }
4996 Expr::InList { expr, list, .. } => {
4997 collect_field_refs(expr, out);
4998 for item in list {
4999 collect_field_refs(item, out);
5000 }
5001 }
5002 Expr::ScalarFunc(_, args) => {
5003 for a in args {
5004 collect_field_refs(a, out);
5005 }
5006 }
5007 Expr::Cast(inner, _) => {
5008 collect_field_refs(inner, out);
5009 }
5010 Expr::Case { whens, else_expr } => {
5011 for (c, r) in whens {
5012 collect_field_refs(c, out);
5013 collect_field_refs(r, out);
5014 }
5015 if let Some(e) = else_expr {
5016 collect_field_refs(e, out);
5017 }
5018 }
5019 _ => {}
5020 }
5021}
5022
5023fn substitute_outer_refs(
5030 expr: &Expr,
5031 subquery_source: &str,
5032 catalog: &Catalog,
5033 outer_row: &[Value],
5034 outer_columns: &[String],
5035) -> Expr {
5036 let sub_cols: Vec<String> = catalog
5037 .schema(subquery_source)
5038 .map(|s| s.columns.iter().map(|c| c.name.clone()).collect())
5039 .unwrap_or_default();
5040 substitute_outer_refs_inner(expr, &sub_cols, outer_row, outer_columns)
5041}
5042
5043fn substitute_outer_refs_inner(
5044 expr: &Expr,
5045 sub_cols: &[String],
5046 outer_row: &[Value],
5047 outer_columns: &[String],
5048) -> Expr {
5049 match expr {
5050 Expr::Field(name) => {
5051 if sub_cols.iter().any(|c| c == name) {
5052 expr.clone()
5053 } else if let Some(i) = outer_columns.iter().position(|c| c == name) {
5054 value_to_expr(outer_row[i].clone())
5055 } else {
5056 expr.clone()
5057 }
5058 }
5059 Expr::BinaryOp(l, op, r) => {
5060 let l = substitute_outer_refs_inner(l, sub_cols, outer_row, outer_columns);
5061 let r = substitute_outer_refs_inner(r, sub_cols, outer_row, outer_columns);
5062 Expr::BinaryOp(Box::new(l), *op, Box::new(r))
5063 }
5064 Expr::UnaryOp(op, inner) => {
5065 let inner = substitute_outer_refs_inner(inner, sub_cols, outer_row, outer_columns);
5066 Expr::UnaryOp(*op, Box::new(inner))
5067 }
5068 Expr::InList {
5069 expr: e,
5070 list,
5071 negated,
5072 } => {
5073 let e = substitute_outer_refs_inner(e, sub_cols, outer_row, outer_columns);
5074 let list = list
5075 .iter()
5076 .map(|item| substitute_outer_refs_inner(item, sub_cols, outer_row, outer_columns))
5077 .collect();
5078 Expr::InList {
5079 expr: Box::new(e),
5080 list,
5081 negated: *negated,
5082 }
5083 }
5084 Expr::Coalesce(l, r) => {
5085 let l = substitute_outer_refs_inner(l, sub_cols, outer_row, outer_columns);
5086 let r = substitute_outer_refs_inner(r, sub_cols, outer_row, outer_columns);
5087 Expr::Coalesce(Box::new(l), Box::new(r))
5088 }
5089 other => other.clone(),
5090 }
5091}
5092
5093fn is_correlated_subquery(subquery: &QueryExpr, catalog: &Catalog) -> bool {
5094 let filter = match &subquery.filter {
5095 Some(f) => f,
5096 None => return false,
5097 };
5098 let schema = match catalog.schema(&subquery.source) {
5099 Some(s) => s,
5100 None => return false, };
5102 let table_cols: Vec<String> = schema.columns.iter().map(|c| c.name.clone()).collect();
5103 let mut refs = Vec::new();
5104 collect_field_refs(filter, &mut refs);
5105 refs.iter().any(|r| {
5108 if r.contains('.') {
5112 let alias = subquery.alias.as_deref().unwrap_or(&subquery.source);
5113 !r.starts_with(alias)
5114 } else {
5115 !table_cols.iter().any(|c| c == r)
5116 }
5117 })
5118}
5119
5120fn contains_subquery(expr: &Expr) -> bool {
5121 match expr {
5122 Expr::InSubquery { .. } => true,
5123 Expr::ExistsSubquery { .. } => true,
5124 Expr::BinaryOp(l, _, r) => contains_subquery(l) || contains_subquery(r),
5125 Expr::UnaryOp(_, inner) => contains_subquery(inner),
5126 Expr::InList { expr, list, .. } => {
5127 contains_subquery(expr) || list.iter().any(contains_subquery)
5128 }
5129 Expr::Case { whens, else_expr } => {
5130 whens
5131 .iter()
5132 .any(|(c, r)| contains_subquery(c) || contains_subquery(r))
5133 || else_expr.as_ref().is_some_and(|e| contains_subquery(e))
5134 }
5135 Expr::ScalarFunc(_, args) => args.iter().any(contains_subquery),
5136 Expr::Cast(inner, _) => contains_subquery(inner),
5137 Expr::FunctionCall(_, inner) => contains_subquery(inner),
5138 Expr::Coalesce(l, r) => contains_subquery(l) || contains_subquery(r),
5139 _ => false,
5140 }
5141}
5142
5143fn value_to_expr(val: Value) -> Expr {
5144 match val {
5145 Value::Int(v) => Expr::Literal(Literal::Int(v)),
5146 Value::Float(v) => Expr::Literal(Literal::Float(v)),
5147 Value::Str(v) => Expr::Literal(Literal::String(v)),
5148 Value::Bool(v) => Expr::Literal(Literal::Bool(v)),
5149 _ => Expr::Literal(Literal::Int(0)),
5150 }
5151}
5152
5153fn literal_to_value(expr: &Expr) -> Result<Value, String> {
5154 match expr {
5155 Expr::Literal(Literal::Int(v)) => Ok(Value::Int(*v)),
5156 Expr::Literal(Literal::Float(v)) => Ok(Value::Float(*v)),
5157 Expr::Literal(Literal::String(v)) => Ok(Value::Str(v.clone())),
5158 Expr::Literal(Literal::Bool(v)) => Ok(Value::Bool(*v)),
5159 _ => Err("expected literal value".into()),
5160 }
5161}
5162
5163#[inline]
5168fn literal_value_from(lit: &Literal) -> Value {
5169 match lit {
5170 Literal::Int(v) => Value::Int(*v),
5171 Literal::Float(v) => Value::Float(*v),
5172 Literal::String(v) => Value::Str(v.clone()),
5173 Literal::Bool(v) => Value::Bool(*v),
5174 }
5175}
5176
5177#[inline]
5184fn literal_value_take(lit: &mut Literal) -> Value {
5185 match lit {
5186 Literal::Int(v) => Value::Int(*v),
5187 Literal::Float(v) => Value::Float(*v),
5188 Literal::String(v) => Value::Str(std::mem::take(v)),
5189 Literal::Bool(v) => Value::Bool(*v),
5190 }
5191}
5192
5193fn eval_expr(expr: &Expr, row: &[Value], columns: &[String]) -> Value {
5194 match expr {
5195 Expr::Field(name) => columns
5196 .iter()
5197 .position(|c| c == name)
5198 .map(|i| row[i].clone())
5199 .unwrap_or(Value::Empty),
5200 Expr::QualifiedField { qualifier, field } => {
5201 let q = qualifier.as_bytes();
5207 let f = field.as_bytes();
5208 let idx = columns.iter().position(|c| {
5209 let b = c.as_bytes();
5210 b.len() == q.len() + 1 + f.len()
5211 && b[..q.len()] == *q
5212 && b[q.len()] == b'.'
5213 && b[q.len() + 1..] == *f
5214 });
5215 idx.map(|i| row[i].clone()).unwrap_or(Value::Empty)
5216 }
5217 Expr::Literal(lit) => match lit {
5218 Literal::Int(v) => Value::Int(*v),
5219 Literal::Float(v) => Value::Float(*v),
5220 Literal::String(v) => Value::Str(v.clone()),
5221 Literal::Bool(v) => Value::Bool(*v),
5222 },
5223 Expr::BinaryOp(left, op, right) => {
5224 let l = eval_expr(left, row, columns);
5225 let r = eval_expr(right, row, columns);
5226 eval_binop(&l, *op, &r)
5227 }
5228 Expr::Coalesce(left, right) => {
5229 let l = eval_expr(left, row, columns);
5230 if l.is_empty() {
5231 eval_expr(right, row, columns)
5232 } else {
5233 l
5234 }
5235 }
5236 Expr::InList {
5237 expr,
5238 list,
5239 negated,
5240 } => {
5241 let val = eval_expr(expr, row, columns);
5242 let found = list.iter().any(|item| {
5243 let iv = eval_expr(item, row, columns);
5244 val == iv
5245 });
5246 Value::Bool(if *negated { !found } else { found })
5247 }
5248 Expr::InSubquery { .. } => {
5249 Value::Empty
5251 }
5252 Expr::ExistsSubquery { .. } => {
5253 Value::Empty
5256 }
5257 Expr::UnaryOp(op, inner) => {
5258 let v = eval_expr(inner, row, columns);
5259 match op {
5260 UnaryOp::Not => match v {
5261 Value::Bool(b) => Value::Bool(!b),
5262 _ => Value::Empty,
5263 },
5264 UnaryOp::Exists => Value::Bool(!v.is_empty()),
5265 UnaryOp::NotExists => Value::Bool(v.is_empty()),
5266 UnaryOp::IsNull => Value::Bool(v.is_empty()),
5267 UnaryOp::IsNotNull => Value::Bool(!v.is_empty()),
5268 }
5269 }
5270 Expr::ScalarFunc(func, args) => {
5271 let vals: Vec<Value> = args.iter().map(|a| eval_expr(a, row, columns)).collect();
5272 eval_scalar_func(*func, &vals)
5273 }
5274 Expr::Case { whens, else_expr } => {
5275 for (condition, result) in whens {
5276 if eval_predicate(condition, row, columns) {
5277 return eval_expr(result, row, columns);
5278 }
5279 }
5280 match else_expr {
5281 Some(e) => eval_expr(e, row, columns),
5282 None => Value::Empty,
5283 }
5284 }
5285 Expr::Cast(inner, cast_type) => {
5286 let val = eval_expr(inner, row, columns);
5287 eval_cast(val, *cast_type)
5288 }
5289 Expr::FunctionCall(_, _) | Expr::Param(_) | Expr::Window { .. } => Value::Empty,
5290 }
5291}
5292
5293fn eval_predicate(expr: &Expr, row: &[Value], columns: &[String]) -> bool {
5294 match eval_expr(expr, row, columns) {
5295 Value::Bool(b) => b,
5296 _ => false,
5297 }
5298}
5299
5300fn eval_scalar_func(func: ScalarFn, args: &[Value]) -> Value {
5301 match func {
5302 ScalarFn::Upper => match args.first() {
5303 Some(Value::Str(s)) => Value::Str(s.to_uppercase()),
5304 _ => Value::Empty,
5305 },
5306 ScalarFn::Lower => match args.first() {
5307 Some(Value::Str(s)) => Value::Str(s.to_lowercase()),
5308 _ => Value::Empty,
5309 },
5310 ScalarFn::Length => match args.first() {
5311 Some(Value::Str(s)) => Value::Int(s.len() as i64),
5312 _ => Value::Empty,
5313 },
5314 ScalarFn::Trim => match args.first() {
5315 Some(Value::Str(s)) => Value::Str(s.trim().to_string()),
5316 _ => Value::Empty,
5317 },
5318 ScalarFn::Substring => {
5319 if args.len() < 3 {
5320 return Value::Empty;
5321 }
5322 match (&args[0], &args[1], &args[2]) {
5323 (Value::Str(s), Value::Int(start), Value::Int(len)) => {
5324 let start = (*start as usize).saturating_sub(1); let len = *len as usize;
5326 let sub: String = s.chars().skip(start).take(len).collect();
5327 Value::Str(sub)
5328 }
5329 _ => Value::Empty,
5330 }
5331 }
5332 ScalarFn::Concat => {
5333 let mut result = String::new();
5334 for v in args {
5335 match v {
5336 Value::Str(s) => result.push_str(s),
5337 Value::Int(n) => result.push_str(&n.to_string()),
5338 Value::Float(f) => result.push_str(&f.to_string()),
5339 Value::Bool(b) => result.push_str(if *b { "true" } else { "false" }),
5340 _ => {}
5341 }
5342 }
5343 Value::Str(result)
5344 }
5345 ScalarFn::Abs => match args.first() {
5347 Some(Value::Int(n)) => Value::Int(n.abs()),
5348 Some(Value::Float(f)) => Value::Float(f.abs()),
5349 _ => Value::Empty,
5350 },
5351 ScalarFn::Round => {
5352 let decimals = match args.get(1) {
5353 Some(Value::Int(d)) => *d as i32,
5354 _ => 0,
5355 };
5356 match args.first() {
5357 Some(Value::Float(f)) => {
5358 let factor = 10_f64.powi(decimals);
5359 Value::Float((f * factor).round() / factor)
5360 }
5361 Some(Value::Int(n)) => Value::Int(*n),
5362 _ => Value::Empty,
5363 }
5364 }
5365 ScalarFn::Ceil => match args.first() {
5366 Some(Value::Float(f)) => Value::Float(f.ceil()),
5367 Some(Value::Int(n)) => Value::Int(*n),
5368 _ => Value::Empty,
5369 },
5370 ScalarFn::Floor => match args.first() {
5371 Some(Value::Float(f)) => Value::Float(f.floor()),
5372 Some(Value::Int(n)) => Value::Int(*n),
5373 _ => Value::Empty,
5374 },
5375 ScalarFn::Sqrt => match args.first() {
5376 Some(Value::Float(f)) if *f >= 0.0 => Value::Float(f.sqrt()),
5377 Some(Value::Int(n)) if *n >= 0 => Value::Float((*n as f64).sqrt()),
5378 _ => Value::Empty,
5379 },
5380 ScalarFn::Pow => match (args.first(), args.get(1)) {
5381 (Some(Value::Float(base)), Some(Value::Float(exp))) => Value::Float(base.powf(*exp)),
5382 (Some(Value::Float(base)), Some(Value::Int(exp))) => {
5383 Value::Float(base.powi(*exp as i32))
5384 }
5385 (Some(Value::Int(base)), Some(Value::Int(exp))) => {
5386 if *exp >= 0 && *exp <= u32::MAX as i64 {
5387 match base.checked_pow(*exp as u32) {
5388 Some(v) => Value::Int(v),
5389 None => Value::Float((*base as f64).powi(*exp as i32)),
5390 }
5391 } else {
5392 Value::Float((*base as f64).powi(*exp as i32))
5393 }
5394 }
5395 (Some(Value::Int(base)), Some(Value::Float(exp))) => {
5396 Value::Float((*base as f64).powf(*exp))
5397 }
5398 _ => Value::Empty,
5399 },
5400 ScalarFn::Now => {
5402 use std::time::{SystemTime, UNIX_EPOCH};
5403 let micros = SystemTime::now()
5404 .duration_since(UNIX_EPOCH)
5405 .unwrap_or_default()
5406 .as_micros() as i64;
5407 Value::DateTime(micros)
5408 }
5409 ScalarFn::Extract => {
5410 let part = match args.first() {
5412 Some(Value::Str(s)) => s.as_str(),
5413 _ => return Value::Empty,
5414 };
5415 let micros = match args.get(1) {
5416 Some(Value::DateTime(m)) => *m,
5417 Some(Value::Int(m)) => *m, _ => return Value::Empty,
5419 };
5420 datetime_extract(part, micros)
5421 }
5422 ScalarFn::DateAdd => {
5423 let micros = match args.first() {
5425 Some(Value::DateTime(m)) => *m,
5426 Some(Value::Int(m)) => *m,
5427 _ => return Value::Empty,
5428 };
5429 let amount = match args.get(1) {
5430 Some(Value::Int(n)) => *n,
5431 _ => return Value::Empty,
5432 };
5433 let unit = match args.get(2) {
5434 Some(Value::Str(s)) => s.as_str(),
5435 _ => return Value::Empty,
5436 };
5437 let delta_micros = match unit {
5438 "microsecond" | "microseconds" | "us" => amount,
5439 "millisecond" | "milliseconds" | "ms" => amount * 1_000,
5440 "second" | "seconds" | "s" => amount * 1_000_000,
5441 "minute" | "minutes" | "m" => amount * 60_000_000,
5442 "hour" | "hours" | "h" => amount * 3_600_000_000,
5443 "day" | "days" | "d" => amount * 86_400_000_000,
5444 _ => return Value::Empty,
5445 };
5446 Value::DateTime(micros + delta_micros)
5447 }
5448 ScalarFn::DateDiff => {
5449 let m1 = match args.first() {
5451 Some(Value::DateTime(m)) => *m,
5452 Some(Value::Int(m)) => *m,
5453 _ => return Value::Empty,
5454 };
5455 let m2 = match args.get(1) {
5456 Some(Value::DateTime(m)) => *m,
5457 Some(Value::Int(m)) => *m,
5458 _ => return Value::Empty,
5459 };
5460 let unit = match args.get(2) {
5461 Some(Value::Str(s)) => s.as_str(),
5462 _ => return Value::Empty,
5463 };
5464 let diff = m1 - m2;
5465 let result = match unit {
5466 "microsecond" | "microseconds" | "us" => diff,
5467 "millisecond" | "milliseconds" | "ms" => diff / 1_000,
5468 "second" | "seconds" | "s" => diff / 1_000_000,
5469 "minute" | "minutes" | "m" => diff / 60_000_000,
5470 "hour" | "hours" | "h" => diff / 3_600_000_000,
5471 "day" | "days" | "d" => diff / 86_400_000_000,
5472 _ => return Value::Empty,
5473 };
5474 Value::Int(result)
5475 }
5476 }
5477}
5478
5479fn datetime_extract(part: &str, micros: i64) -> Value {
5481 let total_secs = micros / 1_000_000;
5483 let micro_rem = micros % 1_000_000;
5484
5485 let days_since_epoch = if total_secs >= 0 {
5487 total_secs / 86400
5488 } else {
5489 (total_secs - 86399) / 86400
5490 };
5491 let secs_of_day = total_secs - days_since_epoch * 86400;
5492
5493 match part {
5494 "hour" => Value::Int(secs_of_day / 3600),
5495 "minute" => Value::Int((secs_of_day % 3600) / 60),
5496 "second" => Value::Int(secs_of_day % 60),
5497 "millisecond" => Value::Int(micro_rem / 1000),
5498 "microsecond" => Value::Int(micro_rem),
5499 "epoch" => Value::Int(total_secs),
5500 "year" | "month" | "day" => {
5501 let z = days_since_epoch + 719468;
5503 let era = if z >= 0 { z } else { z - 146096 } / 146097;
5504 let doe = (z - era * 146097) as u32;
5505 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
5506 let y = (yoe as i64) + era * 400;
5507 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
5508 let mp = (5 * doy + 2) / 153;
5509 let d = doy - (153 * mp + 2) / 5 + 1;
5510 let m = if mp < 10 { mp + 3 } else { mp - 9 };
5511 let y = if m <= 2 { y + 1 } else { y };
5512 match part {
5513 "year" => Value::Int(y),
5514 "month" => Value::Int(m as i64),
5515 "day" => Value::Int(d as i64),
5516 _ => unreachable!(),
5517 }
5518 }
5519 _ => Value::Empty,
5520 }
5521}
5522
5523fn eval_cast(val: Value, target: CastType) -> Value {
5525 match target {
5526 CastType::Int => match val {
5527 Value::Int(n) => Value::Int(n),
5528 Value::Float(f) => Value::Int(f as i64),
5529 Value::Bool(b) => Value::Int(if b { 1 } else { 0 }),
5530 Value::Str(s) => s.parse::<i64>().map(Value::Int).unwrap_or(Value::Empty),
5531 Value::DateTime(m) => Value::Int(m),
5532 _ => Value::Empty,
5533 },
5534 CastType::Float => match val {
5535 Value::Float(f) => Value::Float(f),
5536 Value::Int(n) => Value::Float(n as f64),
5537 Value::Str(s) => s.parse::<f64>().map(Value::Float).unwrap_or(Value::Empty),
5538 Value::Bool(b) => Value::Float(if b { 1.0 } else { 0.0 }),
5539 _ => Value::Empty,
5540 },
5541 CastType::Str => match val {
5542 Value::Str(s) => Value::Str(s),
5543 Value::Int(n) => Value::Str(n.to_string()),
5544 Value::Float(f) => Value::Str(f.to_string()),
5545 Value::Bool(b) => Value::Str(b.to_string()),
5546 Value::DateTime(m) => Value::Str(m.to_string()),
5547 _ => Value::Empty,
5548 },
5549 CastType::Bool => match val {
5550 Value::Bool(b) => Value::Bool(b),
5551 Value::Int(n) => Value::Bool(n != 0),
5552 Value::Str(s) => match s.as_str() {
5553 "true" | "1" | "yes" => Value::Bool(true),
5554 "false" | "0" | "no" => Value::Bool(false),
5555 _ => Value::Empty,
5556 },
5557 _ => Value::Empty,
5558 },
5559 CastType::DateTime => match val {
5560 Value::DateTime(m) => Value::DateTime(m),
5561 Value::Int(m) => Value::DateTime(m),
5562 _ => Value::Empty,
5563 },
5564 }
5565}
5566
5567fn execute_window(result: QueryResult, windows: &[WindowDef]) -> Result<QueryResult, String> {
5579 let (mut columns, mut rows) = match result {
5580 QueryResult::Rows { columns, rows } => (columns, rows),
5581 _ => return Err("window function requires row input".into()),
5582 };
5583
5584 for wdef in windows {
5585 let part_indices: Vec<usize> = wdef
5587 .partition_by
5588 .iter()
5589 .map(|name| {
5590 columns
5591 .iter()
5592 .position(|c| c == name)
5593 .ok_or_else(|| format!("window partition column '{name}' not found"))
5594 })
5595 .collect::<Result<Vec<_>, _>>()?;
5596
5597 let ord_indices: Vec<(usize, bool)> = wdef
5598 .order_by
5599 .iter()
5600 .map(|sk| {
5601 columns
5602 .iter()
5603 .position(|c| c == &sk.field)
5604 .map(|i| (i, sk.descending))
5605 .ok_or_else(|| format!("window order column '{}' not found", sk.field))
5606 })
5607 .collect::<Result<Vec<_>, _>>()?;
5608
5609 let arg_col_idx: Option<usize> = if let Some(arg) = wdef.args.first() {
5611 match arg {
5612 Expr::Field(name) => {
5613 if name == "*" {
5614 None } else {
5616 Some(
5617 columns
5618 .iter()
5619 .position(|c| c == name)
5620 .ok_or_else(|| format!("window arg column '{name}' not found"))?,
5621 )
5622 }
5623 }
5624 _ => None,
5625 }
5626 } else {
5627 None
5628 };
5629
5630 let n = rows.len();
5634 let mut indices: Vec<usize> = (0..n).collect();
5635 indices.sort_by(|&a, &b| {
5636 for &pi in &part_indices {
5638 let cmp = rows[a][pi].cmp(&rows[b][pi]);
5639 if cmp != std::cmp::Ordering::Equal {
5640 return cmp;
5641 }
5642 }
5643 for &(oi, desc) in &ord_indices {
5645 let cmp = rows[a][oi].cmp(&rows[b][oi]);
5646 if cmp != std::cmp::Ordering::Equal {
5647 return if desc { cmp.reverse() } else { cmp };
5648 }
5649 }
5650 std::cmp::Ordering::Equal
5651 });
5652
5653 let mut win_values: Vec<Value> = vec![Value::Empty; n];
5655 let mut partition_start = 0usize;
5656 let mut running_count: i64 = 0;
5658 let mut running_int_sum: i64 = 0;
5659 let mut running_float_sum: f64 = 0.0;
5660 let mut running_saw_float = false;
5661 let mut running_min: Option<Value> = None;
5662 let mut running_max: Option<Value> = None;
5663 let mut rank_counter: i64 = 0;
5664 let mut dense_rank_counter: i64 = 0;
5665 let mut prev_order_key: Option<Vec<Value>> = None;
5666 let mut same_rank_count: i64 = 0;
5667
5668 for sorted_pos in 0..n {
5669 let row_idx = indices[sorted_pos];
5670
5671 let new_partition = if sorted_pos == 0 {
5673 true
5674 } else {
5675 let prev_row_idx = indices[sorted_pos - 1];
5676 part_indices
5677 .iter()
5678 .any(|&pi| rows[row_idx][pi] != rows[prev_row_idx][pi])
5679 };
5680
5681 if new_partition {
5682 partition_start = sorted_pos;
5683 running_count = 0;
5684 running_int_sum = 0;
5685 running_float_sum = 0.0;
5686 running_saw_float = false;
5687 running_min = None;
5688 running_max = None;
5689 rank_counter = 0;
5690 dense_rank_counter = 0;
5691 prev_order_key = None;
5692 same_rank_count = 0;
5693 }
5694
5695 let current_order_key: Vec<Value> = ord_indices
5697 .iter()
5698 .map(|&(oi, _)| rows[row_idx][oi].clone())
5699 .collect();
5700 let same_as_prev = prev_order_key.as_ref() == Some(¤t_order_key);
5701
5702 let value = match wdef.function {
5703 WindowFunc::RowNumber => Value::Int((sorted_pos - partition_start + 1) as i64),
5704 WindowFunc::Rank => {
5705 if same_as_prev {
5706 same_rank_count += 1;
5707 } else {
5708 rank_counter += same_rank_count + 1;
5709 same_rank_count = 0;
5710 if rank_counter == 0 {
5711 rank_counter = 1;
5712 }
5713 }
5714 Value::Int(rank_counter)
5715 }
5716 WindowFunc::DenseRank => {
5717 if !same_as_prev {
5718 dense_rank_counter += 1;
5719 }
5720 Value::Int(dense_rank_counter)
5721 }
5722 WindowFunc::Sum => {
5723 if let Some(ci) = arg_col_idx {
5724 match &rows[row_idx][ci] {
5725 Value::Int(v) => running_int_sum += v,
5726 Value::Float(v) => {
5727 running_float_sum += v;
5728 running_saw_float = true;
5729 }
5730 _ => {}
5731 }
5732 }
5733 if running_saw_float {
5734 Value::Float(running_float_sum + running_int_sum as f64)
5735 } else {
5736 Value::Int(running_int_sum)
5737 }
5738 }
5739 WindowFunc::Avg => {
5740 if let Some(ci) = arg_col_idx {
5741 match &rows[row_idx][ci] {
5742 Value::Int(v) => {
5743 running_float_sum += *v as f64;
5744 running_count += 1;
5745 }
5746 Value::Float(v) => {
5747 running_float_sum += v;
5748 running_count += 1;
5749 }
5750 _ => {}
5751 }
5752 }
5753 if running_count == 0 {
5754 Value::Empty
5755 } else {
5756 Value::Float(running_float_sum / running_count as f64)
5757 }
5758 }
5759 WindowFunc::Count => {
5760 if let Some(ci) = arg_col_idx {
5761 if !rows[row_idx][ci].is_empty() {
5762 running_count += 1;
5763 }
5764 } else {
5765 running_count += 1;
5767 }
5768 Value::Int(running_count)
5769 }
5770 WindowFunc::Min => {
5771 if let Some(ci) = arg_col_idx {
5772 let v = &rows[row_idx][ci];
5773 if !v.is_empty() {
5774 running_min = Some(match &running_min {
5775 None => v.clone(),
5776 Some(cur) => {
5777 if v < cur {
5778 v.clone()
5779 } else {
5780 cur.clone()
5781 }
5782 }
5783 });
5784 }
5785 }
5786 running_min.clone().unwrap_or(Value::Empty)
5787 }
5788 WindowFunc::Max => {
5789 if let Some(ci) = arg_col_idx {
5790 let v = &rows[row_idx][ci];
5791 if !v.is_empty() {
5792 running_max = Some(match &running_max {
5793 None => v.clone(),
5794 Some(cur) => {
5795 if v > cur {
5796 v.clone()
5797 } else {
5798 cur.clone()
5799 }
5800 }
5801 });
5802 }
5803 }
5804 running_max.clone().unwrap_or(Value::Empty)
5805 }
5806 };
5807
5808 prev_order_key = Some(current_order_key);
5809 win_values[row_idx] = value;
5810 }
5811
5812 for (ri, row) in rows.iter_mut().enumerate() {
5814 row.push(win_values[ri].clone());
5815 }
5816 columns.push(wdef.output_name.clone());
5817 }
5818
5819 Ok(QueryResult::Rows { columns, rows })
5820}
5821
5822fn compute_group_aggregate(
5824 func: AggFunc,
5825 all_rows: &[Vec<Value>],
5826 row_indices: &[usize],
5827 col_idx: usize,
5828) -> Value {
5829 match func {
5830 AggFunc::Count => {
5831 if col_idx == usize::MAX {
5832 return Value::Int(row_indices.len() as i64);
5834 }
5835 let count = row_indices
5836 .iter()
5837 .filter(|&&ri| !all_rows[ri][col_idx].is_empty())
5838 .count();
5839 Value::Int(count as i64)
5840 }
5841 AggFunc::CountDistinct => {
5842 let mut seen = std::collections::HashSet::new();
5843 for &ri in row_indices {
5844 let v = &all_rows[ri][col_idx];
5845 if !v.is_empty() {
5846 seen.insert(v.clone());
5847 }
5848 }
5849 Value::Int(seen.len() as i64)
5850 }
5851 AggFunc::Sum => {
5852 let mut int_sum: i64 = 0;
5857 let mut float_sum: f64 = 0.0;
5858 let mut saw_float = false;
5859 for &ri in row_indices {
5860 match &all_rows[ri][col_idx] {
5861 Value::Int(v) => int_sum += v,
5862 Value::Float(v) => {
5863 float_sum += *v;
5864 saw_float = true;
5865 }
5866 _ => {}
5867 }
5868 }
5869 if saw_float {
5870 Value::Float(float_sum + int_sum as f64)
5871 } else {
5872 Value::Int(int_sum)
5873 }
5874 }
5875 AggFunc::Avg => {
5876 let mut sum = 0.0f64;
5877 let mut count = 0usize;
5878 for &ri in row_indices {
5879 match &all_rows[ri][col_idx] {
5880 Value::Int(v) => {
5881 sum += *v as f64;
5882 count += 1;
5883 }
5884 Value::Float(v) => {
5885 sum += *v;
5886 count += 1;
5887 }
5888 _ => {}
5889 }
5890 }
5891 if count == 0 {
5892 Value::Empty
5893 } else {
5894 Value::Float(sum / count as f64)
5895 }
5896 }
5897 AggFunc::Min => row_indices
5898 .iter()
5899 .map(|&ri| &all_rows[ri][col_idx])
5900 .filter(|v| !v.is_empty())
5901 .min()
5902 .cloned()
5903 .unwrap_or(Value::Empty),
5904 AggFunc::Max => row_indices
5905 .iter()
5906 .map(|&ri| &all_rows[ri][col_idx])
5907 .filter(|v| !v.is_empty())
5908 .max()
5909 .cloned()
5910 .unwrap_or(Value::Empty),
5911 }
5912}
5913
5914fn try_extract_equi_join_keys(
5928 pred: &Expr,
5929 left_columns: &[String],
5930 right_columns: &[String],
5931) -> Option<(usize, usize)> {
5932 let (lhs, op, rhs) = match pred {
5933 Expr::BinaryOp(l, op, r) => (l.as_ref(), *op, r.as_ref()),
5934 _ => return None,
5935 };
5936 if op != BinOp::Eq {
5937 return None;
5938 }
5939 if let (Some(li), Some(ri)) = (
5941 resolve_side_column(lhs, left_columns),
5942 resolve_side_column(rhs, right_columns),
5943 ) {
5944 return Some((li, ri));
5945 }
5946 if let (Some(li), Some(ri)) = (
5949 resolve_side_column(rhs, left_columns),
5950 resolve_side_column(lhs, right_columns),
5951 ) {
5952 return Some((li, ri));
5953 }
5954 None
5955}
5956
5957fn resolve_side_column(expr: &Expr, columns: &[String]) -> Option<usize> {
5958 match expr {
5959 Expr::QualifiedField { qualifier, field } => {
5960 let q = qualifier.as_bytes();
5965 let f = field.as_bytes();
5966 columns.iter().position(|c| {
5967 let b = c.as_bytes();
5968 b.len() == q.len() + 1 + f.len()
5969 && b[..q.len()] == *q
5970 && b[q.len()] == b'.'
5971 && b[q.len() + 1..] == *f
5972 })
5973 }
5974 Expr::Field(name) => columns.iter().position(|c| c == name),
5975 _ => None,
5976 }
5977}
5978
5979fn hash_join(
5991 left_columns: Vec<String>,
5992 left_rows: Vec<Vec<Value>>,
5993 right_columns: Vec<String>,
5994 right_rows: Vec<Vec<Value>>,
5995 left_key_idx: usize,
5996 right_key_idx: usize,
5997 kind: JoinKind,
5998) -> QueryResult {
5999 use rustc_hash::FxHashMap;
6000
6001 let n_left = left_columns.len();
6002 let n_right = right_columns.len();
6003 let mut columns = Vec::with_capacity(n_left + n_right);
6004 columns.extend(left_columns);
6005 columns.extend(right_columns);
6006
6007 let mut build: FxHashMap<Value, Vec<usize>> =
6010 FxHashMap::with_capacity_and_hasher(right_rows.len(), Default::default());
6011 for (i, row) in right_rows.iter().enumerate() {
6012 if matches!(row[right_key_idx], Value::Empty) {
6016 continue;
6017 }
6018 build.entry(row[right_key_idx].clone()).or_default().push(i);
6019 }
6020
6021 let mut rows: Vec<Vec<Value>> = Vec::with_capacity(left_rows.len());
6024
6025 for left_row in &left_rows {
6026 let key = &left_row[left_key_idx];
6027 let matched = if matches!(key, Value::Empty) {
6028 None
6029 } else {
6030 build.get(key)
6031 };
6032 match matched {
6033 Some(matches) if !matches.is_empty() => {
6034 for &ri in matches {
6035 let right_row = &right_rows[ri];
6036 let mut combined = Vec::with_capacity(n_left + n_right);
6037 combined.extend_from_slice(left_row);
6038 combined.extend_from_slice(right_row);
6039 rows.push(combined);
6040 }
6041 }
6042 _ => {
6043 if matches!(kind, JoinKind::LeftOuter) {
6044 let mut row = Vec::with_capacity(n_left + n_right);
6045 row.extend_from_slice(left_row);
6046 row.resize(n_left + n_right, Value::Empty);
6047 rows.push(row);
6048 }
6049 }
6050 }
6051 }
6052
6053 QueryResult::Rows { columns, rows }
6054}
6055
6056fn lower_unindexed_range_scans(catalog: &Catalog, plan: &PlanNode) -> PlanNode {
6069 match plan {
6070 PlanNode::RangeScan {
6071 table,
6072 column,
6073 start,
6074 end,
6075 } => {
6076 if let Some(tbl) = catalog.get_table(table) {
6077 if tbl.index(column).is_some() {
6078 return plan.clone();
6079 }
6080 }
6081 let pred = synthesize_range_predicate(column, start, end);
6082 PlanNode::Filter {
6083 input: Box::new(PlanNode::SeqScan {
6084 table: table.clone(),
6085 }),
6086 predicate: pred,
6087 }
6088 }
6089 PlanNode::Filter { input, predicate } => PlanNode::Filter {
6090 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6091 predicate: predicate.clone(),
6092 },
6093 PlanNode::Project { input, fields } => PlanNode::Project {
6094 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6095 fields: fields.clone(),
6096 },
6097 PlanNode::Sort { input, keys } => PlanNode::Sort {
6098 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6099 keys: keys.clone(),
6100 },
6101 PlanNode::Limit { input, count } => PlanNode::Limit {
6102 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6103 count: count.clone(),
6104 },
6105 PlanNode::Offset { input, count } => PlanNode::Offset {
6106 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6107 count: count.clone(),
6108 },
6109 PlanNode::Aggregate {
6110 input,
6111 function,
6112 field,
6113 } => PlanNode::Aggregate {
6114 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6115 function: *function,
6116 field: field.clone(),
6117 },
6118 PlanNode::Distinct { input } => PlanNode::Distinct {
6119 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6120 },
6121 PlanNode::GroupBy {
6122 input,
6123 keys,
6124 aggregates,
6125 having,
6126 } => PlanNode::GroupBy {
6127 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6128 keys: keys.clone(),
6129 aggregates: aggregates.clone(),
6130 having: having.clone(),
6131 },
6132 PlanNode::Update {
6133 input,
6134 table,
6135 assignments,
6136 } => PlanNode::Update {
6137 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6138 table: table.clone(),
6139 assignments: assignments.clone(),
6140 },
6141 PlanNode::Delete { input, table } => PlanNode::Delete {
6142 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6143 table: table.clone(),
6144 },
6145 PlanNode::Window { input, windows } => PlanNode::Window {
6146 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6147 windows: windows.clone(),
6148 },
6149 PlanNode::Union { left, right, all } => PlanNode::Union {
6150 left: Box::new(lower_unindexed_range_scans(catalog, left)),
6151 right: Box::new(lower_unindexed_range_scans(catalog, right)),
6152 all: *all,
6153 },
6154 PlanNode::Explain { input } => PlanNode::Explain {
6155 input: Box::new(lower_unindexed_range_scans(catalog, input)),
6156 },
6157 PlanNode::NestedLoopJoin {
6158 left,
6159 right,
6160 on,
6161 kind,
6162 } => PlanNode::NestedLoopJoin {
6163 left: Box::new(lower_unindexed_range_scans(catalog, left)),
6164 right: Box::new(lower_unindexed_range_scans(catalog, right)),
6165 on: on.clone(),
6166 kind: *kind,
6167 },
6168 _ => plan.clone(),
6170 }
6171}
6172
6173fn synthesize_range_predicate(
6175 column: &str,
6176 start: &Option<(Expr, bool)>,
6177 end: &Option<(Expr, bool)>,
6178) -> Expr {
6179 let lower = start.as_ref().map(|(expr, inclusive)| {
6180 let op = if *inclusive { BinOp::Gte } else { BinOp::Gt };
6181 Expr::BinaryOp(
6182 Box::new(Expr::Field(column.to_string())),
6183 op,
6184 Box::new(expr.clone()),
6185 )
6186 });
6187 let upper = end.as_ref().map(|(expr, inclusive)| {
6188 let op = if *inclusive { BinOp::Lte } else { BinOp::Lt };
6189 Expr::BinaryOp(
6190 Box::new(Expr::Field(column.to_string())),
6191 op,
6192 Box::new(expr.clone()),
6193 )
6194 });
6195 match (lower, upper) {
6196 (Some(l), Some(u)) => Expr::BinaryOp(Box::new(l), BinOp::And, Box::new(u)),
6197 (Some(l), None) => l,
6198 (None, Some(u)) => u,
6199 (None, None) => Expr::Literal(Literal::Bool(true)),
6200 }
6201}
6202
6203fn range_matches(
6205 val: &Value,
6206 start: &Option<Value>,
6207 start_inc: bool,
6208 end: &Option<Value>,
6209 end_inc: bool,
6210) -> bool {
6211 if let Some(ref s) = start {
6212 if start_inc {
6213 if val < s {
6214 return false;
6215 }
6216 } else if val <= s {
6217 return false;
6218 }
6219 }
6220 if let Some(ref e) = end {
6221 if end_inc {
6222 if val > e {
6223 return false;
6224 }
6225 } else if val >= e {
6226 return false;
6227 }
6228 }
6229 true
6230}
6231
6232fn format_plan_tree(plan: &PlanNode, depth: usize) -> String {
6235 let indent = " ".repeat(depth);
6236 match plan {
6237 PlanNode::SeqScan { table } => format!("{indent}SeqScan table={table}"),
6238 PlanNode::AliasScan { table, alias } => {
6239 format!("{indent}AliasScan table={table} alias={alias}")
6240 }
6241 PlanNode::IndexScan { table, column, key } => {
6242 format!("{indent}IndexScan table={table} column={column} key={key:?}")
6243 }
6244 PlanNode::RangeScan {
6245 table,
6246 column,
6247 start,
6248 end,
6249 } => {
6250 let s = match start {
6251 Some((expr, inc)) => {
6252 let op = if *inc { ">=" } else { ">" };
6253 format!("{op}{expr:?}")
6254 }
6255 None => "unbounded".to_string(),
6256 };
6257 let e = match end {
6258 Some((expr, inc)) => {
6259 let op = if *inc { "<=" } else { "<" };
6260 format!("{op}{expr:?}")
6261 }
6262 None => "unbounded".to_string(),
6263 };
6264 format!("{indent}RangeScan table={table} column={column} [{s}, {e}]")
6265 }
6266 PlanNode::Filter { input, predicate } => {
6267 let child = format_plan_tree(input, depth + 1);
6268 format!("{indent}Filter predicate={predicate:?}\n{child}")
6269 }
6270 PlanNode::Project { input, fields } => {
6271 let names: Vec<String> = fields
6272 .iter()
6273 .map(|f| match &f.alias {
6274 Some(a) => format!("{a}: {:?}", f.expr),
6275 None => format!("{:?}", f.expr),
6276 })
6277 .collect();
6278 let child = format_plan_tree(input, depth + 1);
6279 format!("{indent}Project fields=[{}]\n{child}", names.join(", "))
6280 }
6281 PlanNode::Sort { input, keys } => {
6282 let ks: Vec<String> = keys
6283 .iter()
6284 .map(|k| {
6285 if k.descending {
6286 format!("{} desc", k.field)
6287 } else {
6288 k.field.clone()
6289 }
6290 })
6291 .collect();
6292 let child = format_plan_tree(input, depth + 1);
6293 format!("{indent}Sort keys=[{}]\n{child}", ks.join(", "))
6294 }
6295 PlanNode::Limit { input, count } => {
6296 let child = format_plan_tree(input, depth + 1);
6297 format!("{indent}Limit count={count:?}\n{child}")
6298 }
6299 PlanNode::Offset { input, count } => {
6300 let child = format_plan_tree(input, depth + 1);
6301 format!("{indent}Offset count={count:?}\n{child}")
6302 }
6303 PlanNode::Aggregate {
6304 input,
6305 function,
6306 field,
6307 } => {
6308 let f = field.as_deref().unwrap_or("*");
6309 let child = format_plan_tree(input, depth + 1);
6310 format!("{indent}Aggregate fn={function:?} field={f}\n{child}")
6311 }
6312 PlanNode::NestedLoopJoin {
6313 left,
6314 right,
6315 on,
6316 kind,
6317 } => {
6318 let left_child = format_plan_tree(left, depth + 1);
6319 let right_child = format_plan_tree(right, depth + 1);
6320 let on_str = match on {
6321 Some(pred) => format!("{pred:?}"),
6322 None => "none".to_string(),
6323 };
6324 format!("{indent}NestedLoopJoin kind={kind:?} on={on_str}\n{left_child}\n{right_child}")
6325 }
6326 PlanNode::Distinct { input } => {
6327 let child = format_plan_tree(input, depth + 1);
6328 format!("{indent}Distinct\n{child}")
6329 }
6330 PlanNode::GroupBy {
6331 input,
6332 keys,
6333 aggregates,
6334 having,
6335 } => {
6336 let agg_strs: Vec<String> = aggregates
6337 .iter()
6338 .map(|a| format!("{:?}({}) as {}", a.function, a.field, a.output_name))
6339 .collect();
6340 let having_str = match having {
6341 Some(h) => format!(" having={h:?}"),
6342 None => String::new(),
6343 };
6344 let child = format_plan_tree(input, depth + 1);
6345 format!(
6346 "{indent}GroupBy keys=[{}] aggs=[{}]{having_str}\n{child}",
6347 keys.join(", "),
6348 agg_strs.join(", "),
6349 )
6350 }
6351 PlanNode::Insert { table, assignments } => {
6352 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
6353 format!("{indent}Insert table={table} cols=[{}]", cols.join(", "))
6354 }
6355 PlanNode::Upsert {
6356 table,
6357 key_column,
6358 assignments,
6359 on_conflict,
6360 } => {
6361 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
6362 let conflict_cols: Vec<&str> = on_conflict.iter().map(|a| a.field.as_str()).collect();
6363 if conflict_cols.is_empty() {
6364 format!(
6365 "{indent}Upsert table={table} key={key_column} cols=[{}]",
6366 cols.join(", ")
6367 )
6368 } else {
6369 format!(
6370 "{indent}Upsert table={table} key={key_column} cols=[{}] on_conflict=[{}]",
6371 cols.join(", "),
6372 conflict_cols.join(", ")
6373 )
6374 }
6375 }
6376 PlanNode::Update {
6377 input,
6378 table,
6379 assignments,
6380 } => {
6381 let cols: Vec<&str> = assignments.iter().map(|a| a.field.as_str()).collect();
6382 let child = format_plan_tree(input, depth + 1);
6383 format!(
6384 "{indent}Update table={table} set=[{}]\n{child}",
6385 cols.join(", ")
6386 )
6387 }
6388 PlanNode::Delete { input, table } => {
6389 let child = format_plan_tree(input, depth + 1);
6390 format!("{indent}Delete table={table}\n{child}")
6391 }
6392 PlanNode::CreateTable { name, fields } => {
6393 let fs: Vec<String> = fields
6394 .iter()
6395 .map(|(n, t, r)| {
6396 if *r {
6397 format!("{n}: {t} required")
6398 } else {
6399 format!("{n}: {t}")
6400 }
6401 })
6402 .collect();
6403 format!("{indent}CreateTable name={name} fields=[{}]", fs.join(", "))
6404 }
6405 PlanNode::AlterTable { table, action } => {
6406 format!("{indent}AlterTable table={table} action={action:?}")
6407 }
6408 PlanNode::DropTable { name } => format!("{indent}DropTable name={name}"),
6409 PlanNode::CreateView { name, .. } => format!("{indent}CreateView name={name}"),
6410 PlanNode::RefreshView { name } => format!("{indent}RefreshView name={name}"),
6411 PlanNode::DropView { name } => format!("{indent}DropView name={name}"),
6412 PlanNode::Window { input, windows } => {
6413 let ws: Vec<String> = windows
6414 .iter()
6415 .map(|w| format!("{:?} as {}", w.function, w.output_name))
6416 .collect();
6417 let child = format_plan_tree(input, depth + 1);
6418 format!("{indent}Window fns=[{}]\n{child}", ws.join(", "))
6419 }
6420 PlanNode::Union { left, right, all } => {
6421 let kind = if *all { "UNION ALL" } else { "UNION" };
6422 let left_child = format_plan_tree(left, depth + 1);
6423 let right_child = format_plan_tree(right, depth + 1);
6424 format!("{indent}{kind}\n{left_child}\n{right_child}")
6425 }
6426 PlanNode::Explain { input } => {
6427 let child = format_plan_tree(input, depth + 1);
6428 format!("{indent}Explain\n{child}")
6429 }
6430 }
6431}
6432
6433struct FastLayout {
6440 bitmap_size: usize,
6442 fixed_offsets: Vec<Option<usize>>,
6444 fixed_region_size: usize,
6446 var_indices: Vec<Option<usize>>,
6448 n_var: usize,
6450}
6451
6452impl FastLayout {
6453 fn new(schema: &Schema) -> Self {
6454 let n_cols = schema.columns.len();
6455 let bitmap_size = n_cols.div_ceil(8);
6456 let mut fixed_offsets = vec![None; n_cols];
6457 let mut var_indices = vec![None; n_cols];
6458 let mut fixed_pos: usize = 0;
6459 let mut var_count: usize = 0;
6460
6461 for (i, col) in schema.columns.iter().enumerate() {
6462 if is_fixed_size(col.type_id) {
6463 fixed_offsets[i] = Some(fixed_pos);
6464 fixed_pos += fixed_size(col.type_id).unwrap();
6465 } else {
6466 var_indices[i] = Some(var_count);
6467 var_count += 1;
6468 }
6469 }
6470
6471 FastLayout {
6472 bitmap_size,
6473 fixed_offsets,
6474 fixed_region_size: fixed_pos,
6475 var_indices,
6476 n_var: var_count,
6477 }
6478 }
6479
6480 #[inline]
6482 fn var_offset_table_start(&self) -> usize {
6483 2 + self.bitmap_size + self.fixed_region_size
6484 }
6485
6486 #[inline]
6488 fn var_data_start(&self) -> usize {
6489 self.var_offset_table_start() + (self.n_var + 1) * 2
6490 }
6491}
6492
6493type CompiledPredicate = Box<dyn Fn(&[u8]) -> bool>;
6494
6495#[inline]
6508fn f64_bits_to_sortable_u64(bits: u64) -> u64 {
6509 if bits & 0x8000_0000_0000_0000 == 0 {
6513 bits ^ 0x8000_0000_0000_0000
6514 } else {
6515 !bits
6516 }
6517}
6518
6519enum CompiledLeaf {
6524 Int {
6526 data_offset: usize,
6527 bitmap_byte: usize,
6528 bitmap_bit: u8,
6529 op: BinOp,
6530 literal: i64,
6531 },
6532 Float {
6539 data_offset: usize,
6540 bitmap_byte: usize,
6541 bitmap_bit: u8,
6542 op: BinOp,
6543 literal: f64,
6544 },
6545 IsNull {
6547 bitmap_byte: usize,
6548 bitmap_bit: u8,
6549 want_null: bool,
6550 },
6551 StrEq {
6553 var_offset_table_start: usize,
6554 var_data_start: usize,
6555 var_idx: usize,
6556 bitmap_byte: usize,
6557 bitmap_bit: u8,
6558 negate: bool,
6559 needle: Vec<u8>,
6560 },
6561}
6562
6563impl CompiledLeaf {
6564 #[inline]
6567 fn eval(&self, data: &[u8]) -> bool {
6568 match self {
6569 CompiledLeaf::Int {
6570 data_offset,
6571 bitmap_byte,
6572 bitmap_bit,
6573 op,
6574 literal,
6575 } => {
6576 let is_null = (data[2 + bitmap_byte] >> bitmap_bit) & 1 == 1;
6577 if is_null {
6578 return false;
6579 }
6580 let val =
6581 i64::from_le_bytes(data[*data_offset..*data_offset + 8].try_into().unwrap());
6582 match op {
6583 BinOp::Eq => val == *literal,
6584 BinOp::Neq => val != *literal,
6585 BinOp::Lt => val < *literal,
6586 BinOp::Gt => val > *literal,
6587 BinOp::Lte => val <= *literal,
6588 BinOp::Gte => val >= *literal,
6589 _ => false,
6590 }
6591 }
6592 CompiledLeaf::Float {
6593 data_offset,
6594 bitmap_byte,
6595 bitmap_bit,
6596 op,
6597 literal,
6598 } => {
6599 let is_null = (data[2 + bitmap_byte] >> bitmap_bit) & 1 == 1;
6600 if is_null {
6601 return false;
6602 }
6603 let val =
6604 f64::from_le_bytes(data[*data_offset..*data_offset + 8].try_into().unwrap());
6605 let ord = val.total_cmp(literal);
6610 match op {
6611 BinOp::Eq => ord.is_eq(),
6612 BinOp::Neq => !ord.is_eq(),
6613 BinOp::Lt => ord.is_lt(),
6614 BinOp::Gt => ord.is_gt(),
6615 BinOp::Lte => !ord.is_gt(),
6616 BinOp::Gte => !ord.is_lt(),
6617 _ => false,
6618 }
6619 }
6620 CompiledLeaf::IsNull {
6621 bitmap_byte,
6622 bitmap_bit,
6623 want_null,
6624 } => {
6625 let is_null = (data[2 + bitmap_byte] >> bitmap_bit) & 1 == 1;
6626 if *want_null {
6627 is_null
6628 } else {
6629 !is_null
6630 }
6631 }
6632 CompiledLeaf::StrEq {
6633 var_offset_table_start,
6634 var_data_start,
6635 var_idx,
6636 bitmap_byte,
6637 bitmap_bit,
6638 negate,
6639 needle,
6640 } => {
6641 let is_null = (data[2 + bitmap_byte] >> bitmap_bit) & 1 == 1;
6642 if is_null {
6643 return false;
6644 }
6645 let off_pos = var_offset_table_start + var_idx * 2;
6646 let next_pos = var_offset_table_start + (var_idx + 1) * 2;
6647 let start =
6648 u16::from_le_bytes(data[off_pos..off_pos + 2].try_into().unwrap()) as usize;
6649 let end =
6650 u16::from_le_bytes(data[next_pos..next_pos + 2].try_into().unwrap()) as usize;
6651 let slice = &data[var_data_start + start..var_data_start + end];
6652 let eq = slice == needle.as_slice();
6653 if *negate {
6654 !eq
6655 } else {
6656 eq
6657 }
6658 }
6659 }
6660 }
6661}
6662
6663fn compile_predicate(
6677 expr: &Expr,
6678 columns: &[String],
6679 layout: &FastLayout,
6680 schema: &Schema,
6681) -> Option<CompiledPredicate> {
6682 let mut leaves: Vec<CompiledLeaf> = Vec::new();
6683 flatten_and_compile(expr, columns, layout, schema, &mut leaves)?;
6684 if leaves.is_empty() {
6685 return None;
6686 }
6687 if leaves.len() == 1 {
6688 let leaf = leaves.into_iter().next().unwrap();
6690 return Some(Box::new(move |data: &[u8]| leaf.eval(data)));
6691 }
6692 Some(Box::new(move |data: &[u8]| {
6693 for leaf in &leaves {
6696 if !leaf.eval(data) {
6697 return false;
6698 }
6699 }
6700 true
6701 }))
6702}
6703
6704fn flatten_and_compile(
6707 expr: &Expr,
6708 columns: &[String],
6709 layout: &FastLayout,
6710 schema: &Schema,
6711 out: &mut Vec<CompiledLeaf>,
6712) -> Option<()> {
6713 match expr {
6714 Expr::BinaryOp(left, BinOp::And, right) => {
6715 flatten_and_compile(left, columns, layout, schema, out)?;
6716 flatten_and_compile(right, columns, layout, schema, out)?;
6717 Some(())
6718 }
6719 Expr::BinaryOp(left, op, right) => {
6720 if let Some(leaf) = build_int_leaf(left, *op, right, columns, layout, schema) {
6721 out.push(leaf);
6722 return Some(());
6723 }
6724 if let Some(leaf) = build_float_leaf(left, *op, right, columns, layout, schema) {
6725 out.push(leaf);
6726 return Some(());
6727 }
6728 if let Some(leaf) = build_str_eq_leaf(left, *op, right, columns, layout, schema) {
6729 out.push(leaf);
6730 return Some(());
6731 }
6732 None
6733 }
6734 Expr::UnaryOp(op, inner) if *op == UnaryOp::IsNull || *op == UnaryOp::IsNotNull => {
6735 if let Expr::Field(name) = inner.as_ref() {
6736 let col_idx = columns.iter().position(|c| c == name)?;
6737 let bitmap_byte = col_idx / 8;
6738 let bitmap_bit = (col_idx % 8) as u8;
6739 let want_null = *op == UnaryOp::IsNull;
6740 out.push(CompiledLeaf::IsNull {
6741 bitmap_byte,
6742 bitmap_bit,
6743 want_null,
6744 });
6745 Some(())
6746 } else {
6747 None
6748 }
6749 }
6750 _ => None,
6751 }
6752}
6753
6754fn build_int_leaf(
6764 left: &Expr,
6765 op: BinOp,
6766 right: &Expr,
6767 columns: &[String],
6768 layout: &FastLayout,
6769 schema: &Schema,
6770) -> Option<CompiledLeaf> {
6771 let (field_name, literal_val, op) = match (left, right) {
6772 (Expr::Field(name), Expr::Literal(Literal::Int(v))) => (name, *v, op),
6773 (Expr::Literal(Literal::Int(v)), Expr::Field(name)) => {
6774 let flipped = match op {
6775 BinOp::Lt => BinOp::Gt,
6776 BinOp::Gt => BinOp::Lt,
6777 BinOp::Lte => BinOp::Gte,
6778 BinOp::Gte => BinOp::Lte,
6779 other => other, };
6781 (name, *v, flipped)
6782 }
6783 _ => return None,
6784 };
6785
6786 let col_idx = columns.iter().position(|c| c == field_name)?;
6787 if schema.columns[col_idx].type_id != TypeId::Int {
6790 return None;
6791 }
6792 let byte_offset = layout.fixed_offsets[col_idx]?;
6793 let bitmap_byte = col_idx / 8;
6794 let bitmap_bit = (col_idx % 8) as u8;
6795 let data_offset = 2 + layout.bitmap_size + byte_offset;
6796
6797 Some(CompiledLeaf::Int {
6798 data_offset,
6799 bitmap_byte,
6800 bitmap_bit,
6801 op,
6802 literal: literal_val,
6803 })
6804}
6805
6806fn build_float_leaf(
6815 left: &Expr,
6816 op: BinOp,
6817 right: &Expr,
6818 columns: &[String],
6819 layout: &FastLayout,
6820 schema: &Schema,
6821) -> Option<CompiledLeaf> {
6822 let (field_name, literal_val, op) = match (left, right) {
6826 (Expr::Field(name), Expr::Literal(Literal::Float(v))) => (name, *v, op),
6827 (Expr::Field(name), Expr::Literal(Literal::Int(v))) => (name, *v as f64, op),
6828 (Expr::Literal(Literal::Float(v)), Expr::Field(name)) => {
6829 let flipped = match op {
6830 BinOp::Lt => BinOp::Gt,
6831 BinOp::Gt => BinOp::Lt,
6832 BinOp::Lte => BinOp::Gte,
6833 BinOp::Gte => BinOp::Lte,
6834 other => other,
6835 };
6836 (name, *v, flipped)
6837 }
6838 (Expr::Literal(Literal::Int(v)), Expr::Field(name)) => {
6839 let flipped = match op {
6840 BinOp::Lt => BinOp::Gt,
6841 BinOp::Gt => BinOp::Lt,
6842 BinOp::Lte => BinOp::Gte,
6843 BinOp::Gte => BinOp::Lte,
6844 other => other,
6845 };
6846 (name, *v as f64, flipped)
6847 }
6848 _ => return None,
6849 };
6850
6851 let col_idx = columns.iter().position(|c| c == field_name)?;
6852 if schema.columns[col_idx].type_id != TypeId::Float {
6857 return None;
6858 }
6859 let byte_offset = layout.fixed_offsets[col_idx]?;
6860 let bitmap_byte = col_idx / 8;
6861 let bitmap_bit = (col_idx % 8) as u8;
6862 let data_offset = 2 + layout.bitmap_size + byte_offset;
6863
6864 Some(CompiledLeaf::Float {
6865 data_offset,
6866 bitmap_byte,
6867 bitmap_bit,
6868 op,
6869 literal: literal_val,
6870 })
6871}
6872
6873fn build_str_eq_leaf(
6875 left: &Expr,
6876 op: BinOp,
6877 right: &Expr,
6878 columns: &[String],
6879 layout: &FastLayout,
6880 schema: &Schema,
6881) -> Option<CompiledLeaf> {
6882 if op != BinOp::Eq && op != BinOp::Neq {
6883 return None;
6884 }
6885 let (field_name, literal_str) = match (left, right) {
6886 (Expr::Field(name), Expr::Literal(Literal::String(s))) => (name, s.clone()),
6887 (Expr::Literal(Literal::String(s)), Expr::Field(name)) => (name, s.clone()),
6888 _ => return None,
6889 };
6890
6891 let col_idx = columns.iter().position(|c| c == field_name)?;
6892 if schema.columns[col_idx].type_id != TypeId::Str {
6893 return None;
6894 }
6895 let var_idx = layout.var_indices[col_idx]?;
6896 let var_offset_table_start = layout.var_offset_table_start();
6897 let var_data_start = layout.var_data_start();
6898 let bitmap_byte = col_idx / 8;
6899 let bitmap_bit = (col_idx % 8) as u8;
6900 let negate = op == BinOp::Neq;
6901
6902 Some(CompiledLeaf::StrEq {
6903 var_offset_table_start,
6904 var_data_start,
6905 var_idx,
6906 bitmap_byte,
6907 bitmap_bit,
6908 negate,
6909 needle: literal_str.into_bytes(),
6910 })
6911}
6912
6913fn predicate_column_indices(expr: &Expr, columns: &[String]) -> Vec<usize> {
6915 let mut indices = Vec::new();
6916 collect_field_indices(expr, columns, &mut indices);
6917 indices.sort_unstable();
6918 indices.dedup();
6919 indices
6920}
6921
6922fn collect_field_indices(expr: &Expr, columns: &[String], out: &mut Vec<usize>) {
6923 match expr {
6924 Expr::Field(name) => {
6925 if let Some(idx) = columns.iter().position(|c| c == name) {
6926 out.push(idx);
6927 }
6928 }
6929 Expr::BinaryOp(left, _, right) => {
6930 collect_field_indices(left, columns, out);
6931 collect_field_indices(right, columns, out);
6932 }
6933 Expr::Coalesce(left, right) => {
6934 collect_field_indices(left, columns, out);
6935 collect_field_indices(right, columns, out);
6936 }
6937 Expr::UnaryOp(_, inner) => {
6938 collect_field_indices(inner, columns, out);
6939 }
6940 Expr::FunctionCall(_, inner) => {
6941 collect_field_indices(inner, columns, out);
6942 }
6943 Expr::ScalarFunc(_, args) => {
6944 for arg in args {
6945 collect_field_indices(arg, columns, out);
6946 }
6947 }
6948 Expr::Cast(inner, _) => {
6949 collect_field_indices(inner, columns, out);
6950 }
6951 Expr::Case { whens, else_expr } => {
6952 for (cond, result) in whens {
6953 collect_field_indices(cond, columns, out);
6954 collect_field_indices(result, columns, out);
6955 }
6956 if let Some(e) = else_expr {
6957 collect_field_indices(e, columns, out);
6958 }
6959 }
6960 Expr::InList { expr, list, .. } => {
6961 collect_field_indices(expr, columns, out);
6962 for item in list {
6963 collect_field_indices(item, columns, out);
6964 }
6965 }
6966 Expr::InSubquery { expr, .. } => {
6967 collect_field_indices(expr, columns, out);
6968 }
6969 _ => {}
6970 }
6971}
6972
6973fn decode_selective(
6977 schema: &Schema,
6978 layout: &RowLayout,
6979 data: &[u8],
6980 col_indices: &[usize],
6981) -> Vec<Value> {
6982 let n_cols = schema.columns.len();
6983 let mut values = vec![Value::Empty; n_cols];
6984 for &ci in col_indices {
6985 values[ci] = decode_column(schema, layout, data, ci);
6986 }
6987 values
6988}
6989
6990fn eval_binop(left: &Value, op: BinOp, right: &Value) -> Value {
6991 match op {
6992 BinOp::Eq => Value::Bool(left == right),
6993 BinOp::Neq => Value::Bool(left != right),
6994 BinOp::Lt => Value::Bool(left < right),
6995 BinOp::Gt => Value::Bool(left > right),
6996 BinOp::Lte => Value::Bool(left <= right),
6997 BinOp::Gte => Value::Bool(left >= right),
6998 BinOp::And => match (left, right) {
6999 (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a && *b),
7000 _ => Value::Bool(false),
7001 },
7002 BinOp::Or => match (left, right) {
7003 (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a || *b),
7004 _ => Value::Bool(false),
7005 },
7006 BinOp::Add => match (left, right) {
7007 (Value::Int(a), Value::Int(b)) => Value::Int(a.saturating_add(*b)),
7008 (Value::Float(a), Value::Float(b)) => Value::Float(a + b),
7009 (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 + b),
7010 (Value::Float(a), Value::Int(b)) => Value::Float(a + *b as f64),
7011 _ => Value::Empty,
7012 },
7013 BinOp::Sub => match (left, right) {
7014 (Value::Int(a), Value::Int(b)) => Value::Int(a.saturating_sub(*b)),
7015 (Value::Float(a), Value::Float(b)) => Value::Float(a - b),
7016 (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 - b),
7017 (Value::Float(a), Value::Int(b)) => Value::Float(a - *b as f64),
7018 _ => Value::Empty,
7019 },
7020 BinOp::Mul => match (left, right) {
7021 (Value::Int(a), Value::Int(b)) => Value::Int(a.saturating_mul(*b)),
7022 (Value::Float(a), Value::Float(b)) => Value::Float(a * b),
7023 (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 * b),
7024 (Value::Float(a), Value::Int(b)) => Value::Float(a * *b as f64),
7025 _ => Value::Empty,
7026 },
7027 BinOp::Div => match (left, right) {
7028 (Value::Int(a), Value::Int(b)) if *b != 0 => Value::Int(a / b),
7029 (Value::Float(a), Value::Float(b)) => Value::Float(a / b),
7030 (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 / b),
7031 (Value::Float(a), Value::Int(b)) => Value::Float(a / *b as f64),
7032 _ => Value::Empty,
7033 },
7034 BinOp::Like => match (left, right) {
7035 (Value::Str(text), Value::Str(pattern)) => Value::Bool(like_match(text, pattern)),
7036 _ => Value::Bool(false),
7037 },
7038 }
7039}
7040
7041fn like_match(text: &str, pattern: &str) -> bool {
7044 let t: Vec<char> = text.chars().collect();
7045 let p: Vec<char> = pattern.chars().collect();
7046 like_dp(&t, &p, 0, 0)
7047}
7048
7049fn like_dp(t: &[char], p: &[char], ti: usize, pi: usize) -> bool {
7050 if pi == p.len() {
7051 return ti == t.len();
7052 }
7053 if p[pi] == '%' {
7054 let mut pi2 = pi;
7057 while pi2 < p.len() && p[pi2] == '%' {
7058 pi2 += 1;
7059 }
7060 for i in ti..=t.len() {
7061 if like_dp(t, p, i, pi2) {
7062 return true;
7063 }
7064 }
7065 false
7066 } else if ti < t.len() && (p[pi] == '_' || p[pi] == t[ti]) {
7067 like_dp(t, p, ti + 1, pi + 1)
7068 } else {
7069 false
7070 }
7071}
7072
7073#[cfg(test)]
7074mod tests {
7075 use super::*;
7076 use std::sync::atomic::{AtomicU32, Ordering};
7077
7078 static TEST_COUNTER: AtomicU32 = AtomicU32::new(0);
7079
7080 fn test_engine() -> Engine {
7081 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
7082 let dir = std::env::temp_dir().join(format!("powdb_exec_{}_{}", std::process::id(), id));
7083 let mut engine = Engine::new(&dir).unwrap();
7084 engine
7085 .execute_powql("type User { required name: str, required email: str, age: int }")
7086 .unwrap();
7087 engine
7088 .execute_powql(r#"insert User { name := "Alice", email := "alice@ex.com", age := 30 }"#)
7089 .unwrap();
7090 engine
7091 .execute_powql(r#"insert User { name := "Bob", email := "bob@ex.com", age := 25 }"#)
7092 .unwrap();
7093 engine
7094 .execute_powql(
7095 r#"insert User { name := "Charlie", email := "charlie@ex.com", age := 35 }"#,
7096 )
7097 .unwrap();
7098 engine
7099 }
7100
7101 #[test]
7102 fn test_scan_all() {
7103 let mut engine = test_engine();
7104 let result = engine.execute_powql("User").unwrap();
7105 match result {
7106 QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 3),
7107 _ => panic!("expected rows"),
7108 }
7109 }
7110
7111 #[test]
7112 fn test_filter() {
7113 let mut engine = test_engine();
7114 let result = engine.execute_powql("User filter .age > 28").unwrap();
7115 match result {
7116 QueryResult::Rows { rows, .. } => {
7117 assert_eq!(rows.len(), 2); }
7119 _ => panic!("expected rows"),
7120 }
7121 }
7122
7123 #[test]
7124 fn test_projection() {
7125 let mut engine = test_engine();
7126 let result = engine.execute_powql("User { name }").unwrap();
7127 match result {
7128 QueryResult::Rows { columns, rows } => {
7129 assert_eq!(columns, vec!["name"]);
7130 assert_eq!(rows.len(), 3);
7131 }
7132 _ => panic!("expected rows"),
7133 }
7134 }
7135
7136 #[test]
7137 fn test_insert_and_count() {
7138 let mut engine = test_engine();
7139 let result = engine.execute_powql("count(User)").unwrap();
7140 match result {
7141 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 3),
7142 _ => panic!("expected scalar int"),
7143 }
7144 }
7145
7146 #[test]
7147 fn test_update() {
7148 let mut engine = test_engine();
7149 engine
7150 .execute_powql(r#"User filter .name = "Alice" update { age := 31 }"#)
7151 .unwrap();
7152 let result = engine
7153 .execute_powql(r#"User filter .name = "Alice" { name, age }"#)
7154 .unwrap();
7155 match result {
7156 QueryResult::Rows { rows, .. } => {
7157 assert_eq!(rows[0][1], Value::Int(31));
7158 }
7159 _ => panic!("expected rows"),
7160 }
7161 }
7162
7163 #[test]
7164 fn test_delete() {
7165 let mut engine = test_engine();
7166 engine
7167 .execute_powql(r#"User filter .name = "Bob" delete"#)
7168 .unwrap();
7169 let result = engine.execute_powql("count(User)").unwrap();
7170 match result {
7171 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 2),
7172 _ => panic!("expected scalar int"),
7173 }
7174 }
7175
7176 #[test]
7177 fn test_order_limit() {
7178 let mut engine = test_engine();
7179 let result = engine
7180 .execute_powql("User order .age desc limit 2 { name, age }")
7181 .unwrap();
7182 match result {
7183 QueryResult::Rows { rows, .. } => {
7184 assert_eq!(rows.len(), 2);
7185 assert_eq!(rows[0][0], Value::Str("Charlie".into())); assert_eq!(rows[1][0], Value::Str("Alice".into())); }
7188 _ => panic!("expected rows"),
7189 }
7190 }
7191
7192 #[test]
7197 fn test_order_by_missing_column_errors() {
7198 let mut engine = test_engine();
7199 let err = engine
7200 .execute_powql("User order .nonexistent desc")
7201 .expect_err("sort on missing column must error, not panic");
7202 assert!(
7203 err.contains("nonexistent"),
7204 "error should name the missing column, got: {err}"
7205 );
7206 }
7207
7208 fn product_engine() -> Engine {
7218 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
7219 let dir =
7220 std::env::temp_dir().join(format!("powdb_limit_offset_{}_{}", std::process::id(), id));
7221 let mut engine = Engine::new(&dir).unwrap();
7222 engine
7223 .execute_powql("type Product { required id: int, required name: str }")
7224 .unwrap();
7225 for i in 0..5i64 {
7226 let q = format!(r#"insert Product {{ id := {i}, name := "p{i}" }}"#);
7227 engine.execute_powql(&q).unwrap();
7228 }
7229 engine
7230 }
7231
7232 #[test]
7233 fn test_limit_offset_combined() {
7234 let mut engine = product_engine();
7238 let result = engine
7239 .execute_powql("Product order .id limit 3 offset 1 { .id }")
7240 .unwrap();
7241 match result {
7242 QueryResult::Rows { rows, .. } => {
7243 assert_eq!(
7244 rows.len(),
7245 3,
7246 "limit 3 offset 1 on 5 rows must return 3 rows"
7247 );
7248 assert_eq!(rows[0][0], Value::Int(1));
7249 assert_eq!(rows[1][0], Value::Int(2));
7250 assert_eq!(rows[2][0], Value::Int(3));
7251 }
7252 _ => panic!("expected rows"),
7253 }
7254
7255 let result = engine
7257 .execute_powql("Product order .id limit 2 offset 1 { .id }")
7258 .unwrap();
7259 match result {
7260 QueryResult::Rows { rows, .. } => {
7261 assert_eq!(
7262 rows.len(),
7263 2,
7264 "limit 2 offset 1 on 5 rows must return 2 rows"
7265 );
7266 assert_eq!(rows[0][0], Value::Int(1));
7267 assert_eq!(rows[1][0], Value::Int(2));
7268 }
7269 _ => panic!("expected rows"),
7270 }
7271 }
7272
7273 #[test]
7274 fn test_limit_offset_combined_with_order() {
7275 let mut engine = product_engine();
7278 let result = engine
7279 .execute_powql("Product order .name limit 3 offset 1 { .name }")
7280 .unwrap();
7281 match result {
7282 QueryResult::Rows { rows, .. } => {
7283 assert_eq!(rows.len(), 3);
7284 assert_eq!(rows[0][0], Value::Str("p1".into()));
7285 assert_eq!(rows[1][0], Value::Str("p2".into()));
7286 assert_eq!(rows[2][0], Value::Str("p3".into()));
7287 }
7288 _ => panic!("expected rows"),
7289 }
7290 }
7291
7292 #[test]
7293 fn test_offset_then_limit_keyword_order() {
7294 let mut engine = product_engine();
7297 let result = engine
7298 .execute_powql("Product order .id offset 1 limit 3 { .id }")
7299 .unwrap();
7300 match result {
7301 QueryResult::Rows { rows, .. } => {
7302 assert_eq!(rows.len(), 3);
7303 assert_eq!(rows[0][0], Value::Int(1));
7304 assert_eq!(rows[1][0], Value::Int(2));
7305 assert_eq!(rows[2][0], Value::Int(3));
7306 }
7307 _ => panic!("expected rows"),
7308 }
7309 }
7310
7311 fn mission_a_engine(n: i64) -> Engine {
7326 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
7327 let dir =
7328 std::env::temp_dir().join(format!("powdb_mission_a_{}_{}", std::process::id(), id));
7329 let mut engine = Engine::new(&dir).unwrap();
7330 engine
7331 .execute_powql(
7332 "type User { required id: int, required name: str, required age: int, \
7333 required status: str, required email: str, required created_at: int }",
7334 )
7335 .unwrap();
7336 engine.catalog_mut().create_index("User", "id").unwrap();
7337 let statuses = ["active", "inactive", "pending"];
7338 for i in 0..n {
7339 let age = 18 + (i % 60);
7340 let status = statuses[(i as usize) % 3];
7341 let created_at = 1_700_000_000_i64 + i;
7342 let q = format!(
7343 r#"insert User {{ id := {i}, name := "user_{i}", age := {age}, status := "{status}", email := "user_{i}@example.com", created_at := {created_at} }}"#
7344 );
7345 engine.execute_powql(&q).unwrap();
7346 }
7347 engine
7348 }
7349
7350 #[test]
7351 fn test_fastpath_point_lookup_nonindexed() {
7352 let mut engine = mission_a_engine(50);
7355 let result = engine
7356 .execute_powql(r#"User filter .email = "user_17@example.com""#)
7357 .unwrap();
7358 match result {
7359 QueryResult::Rows { rows, .. } => {
7360 assert_eq!(rows.len(), 1);
7361 assert_eq!(rows[0][0], Value::Int(17));
7363 }
7364 _ => panic!("expected rows"),
7365 }
7366 }
7367
7368 #[test]
7369 fn test_fastpath_scan_filter_project_top100() {
7370 let mut engine = mission_a_engine(1000);
7372 let result = engine
7373 .execute_powql("User filter .age > 30 limit 100 { .id, .name }")
7374 .unwrap();
7375 match result {
7376 QueryResult::Rows { columns, rows } => {
7377 assert_eq!(columns, vec!["id", "name"]);
7378 assert_eq!(rows.len(), 100);
7379 for row in &rows {
7382 if let Value::Int(id) = row[0] {
7383 assert!(18 + (id % 60) > 30, "id={id} has age={}", 18 + (id % 60));
7384 } else {
7385 panic!("expected int id");
7386 }
7387 }
7388 }
7389 _ => panic!("expected rows"),
7390 }
7391 }
7392
7393 #[test]
7394 fn test_fastpath_scan_filter_sort_limit10_desc() {
7395 let mut engine = mission_a_engine(500);
7397 let result = engine
7398 .execute_powql(
7399 "User filter .age > 20 order .created_at desc limit 10 { .id, .created_at }",
7400 )
7401 .unwrap();
7402 match result {
7403 QueryResult::Rows { rows, .. } => {
7404 assert_eq!(rows.len(), 10);
7405 let keys: Vec<i64> = rows
7407 .iter()
7408 .map(|r| {
7409 if let Value::Int(v) = r[1] {
7410 v
7411 } else {
7412 panic!("expected int");
7413 }
7414 })
7415 .collect();
7416 for w in keys.windows(2) {
7417 assert!(w[0] >= w[1], "not desc sorted: {keys:?}");
7418 }
7419 assert_eq!(rows[0][0], Value::Int(499));
7422 }
7423 _ => panic!("expected rows"),
7424 }
7425 }
7426
7427 #[test]
7428 fn test_fastpath_scan_filter_sort_limit10_asc() {
7429 let mut engine = mission_a_engine(500);
7430 let result = engine
7431 .execute_powql("User filter .age > 20 order .created_at limit 10 { .id, .created_at }")
7432 .unwrap();
7433 match result {
7434 QueryResult::Rows { rows, .. } => {
7435 assert_eq!(rows.len(), 10);
7436 let keys: Vec<i64> = rows
7437 .iter()
7438 .map(|r| {
7439 if let Value::Int(v) = r[1] {
7440 v
7441 } else {
7442 panic!("expected int");
7443 }
7444 })
7445 .collect();
7446 for w in keys.windows(2) {
7447 assert!(w[0] <= w[1], "not asc sorted: {keys:?}");
7448 }
7449 }
7450 _ => panic!("expected rows"),
7451 }
7452 }
7453
7454 #[test]
7455 fn test_fastpath_agg_sum() {
7456 let n: i64 = 300;
7458 let mut engine = mission_a_engine(n);
7459 let result = engine.execute_powql("sum(User { .age })").unwrap();
7460 let expected: i64 = (0..n).map(|i| 18 + (i % 60)).sum();
7461 match result {
7462 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, expected),
7463 other => panic!("expected Int, got {other:?}"),
7464 }
7465 }
7466
7467 #[test]
7468 fn test_fastpath_agg_sum_with_filter() {
7469 let n: i64 = 300;
7470 let mut engine = mission_a_engine(n);
7471 let result = engine
7472 .execute_powql("sum(User filter .age > 30 { .age })")
7473 .unwrap();
7474 let expected: i64 = (0..n).map(|i| 18 + (i % 60)).filter(|a| *a > 30).sum();
7475 match result {
7476 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, expected),
7477 other => panic!("expected Int, got {other:?}"),
7478 }
7479 }
7480
7481 #[test]
7482 fn test_fastpath_agg_avg() {
7483 let n: i64 = 300;
7484 let mut engine = mission_a_engine(n);
7485 let result = engine.execute_powql("avg(User { .age })").unwrap();
7486 let total: f64 = (0..n).map(|i| (18 + (i % 60)) as f64).sum();
7487 let expected = total / n as f64;
7488 match result {
7489 QueryResult::Scalar(Value::Float(v)) => {
7490 assert!((v - expected).abs() < 1e-9, "expected {expected}, got {v}");
7491 }
7492 other => panic!("expected Float, got {other:?}"),
7493 }
7494 }
7495
7496 #[test]
7497 fn test_fastpath_agg_min_max() {
7498 let n: i64 = 300;
7499 let mut engine = mission_a_engine(n);
7500 let result_min = engine.execute_powql("min(User { .age })").unwrap();
7502 match result_min {
7503 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, 18),
7504 other => panic!("expected Int, got {other:?}"),
7505 }
7506 let result_max = engine.execute_powql("max(User { .age })").unwrap();
7507 match result_max {
7508 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, 77),
7509 other => panic!("expected Int, got {other:?}"),
7510 }
7511 }
7512
7513 #[test]
7514 fn test_fastpath_multi_col_and_filter() {
7515 let n: i64 = 300;
7517 let mut engine = mission_a_engine(n);
7518 let result = engine
7519 .execute_powql(r#"count(User filter .age > 30 and .status = "active")"#)
7520 .unwrap();
7521 let statuses = ["active", "inactive", "pending"];
7523 let expected = (0..n)
7524 .filter(|i| {
7525 let age = 18 + (i % 60);
7526 let status = statuses[(*i as usize) % 3];
7527 age > 30 && status == "active"
7528 })
7529 .count() as i64;
7530 match result {
7531 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, expected),
7532 other => panic!("expected Int, got {other:?}"),
7533 }
7534 }
7535
7536 #[test]
7537 fn test_fastpath_update_by_pk() {
7538 let mut engine = mission_a_engine(50);
7540 let result = engine
7541 .execute_powql("User filter .id = 25 update { age := 99 }")
7542 .unwrap();
7543 match result {
7544 QueryResult::Modified(n) => assert_eq!(n, 1),
7545 _ => panic!("expected Modified"),
7546 }
7547 let lookup = engine
7549 .execute_powql("User filter .id = 25 { .age }")
7550 .unwrap();
7551 match lookup {
7552 QueryResult::Rows { rows, .. } => {
7553 assert_eq!(rows.len(), 1);
7554 assert_eq!(rows[0][0], Value::Int(99));
7555 }
7556 _ => panic!("expected rows"),
7557 }
7558 let neighbour = engine
7560 .execute_powql("User filter .id = 24 { .age }")
7561 .unwrap();
7562 if let QueryResult::Rows { rows, .. } = neighbour {
7563 assert_eq!(rows[0][0], Value::Int(42));
7564 }
7565 }
7566
7567 #[test]
7568 fn test_fastpath_update_by_filter_single_pass() {
7569 let n: i64 = 2000;
7575 let mut engine = mission_a_engine(n);
7576 let result = engine
7577 .execute_powql("User filter .age > 50 update { age := 5 }")
7578 .unwrap();
7579 let expected = (0..n).filter(|i| 18 + (i % 60) > 50).count() as u64;
7580 match result {
7581 QueryResult::Modified(nn) => assert_eq!(nn, expected),
7582 _ => panic!("expected Modified"),
7583 }
7584 let check_zero = engine
7591 .execute_powql(r#"count(User filter .age > 50)"#)
7592 .unwrap();
7593 match check_zero {
7594 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, 0, "some rows still have age > 50"),
7595 _ => panic!("expected Int"),
7596 }
7597 let check_five = engine
7598 .execute_powql(r#"count(User filter .age = 5)"#)
7599 .unwrap();
7600 match check_five {
7601 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v as u64, expected),
7602 _ => panic!("expected Int"),
7603 }
7604 let total = engine.execute_powql("count(User)").unwrap();
7606 match total {
7607 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v, n),
7608 _ => panic!("expected Int"),
7609 }
7610 }
7611
7612 #[test]
7613 fn test_fastpath_delete_by_filter_single_pass() {
7614 let n: i64 = 2000;
7615 let mut engine = mission_a_engine(n);
7616 let to_delete = (0..n).filter(|i| 18 + (i % 60) > 60).count() as u64;
7617 let result = engine
7618 .execute_powql("User filter .age > 60 delete")
7619 .unwrap();
7620 match result {
7621 QueryResult::Modified(nn) => assert_eq!(nn, to_delete),
7622 _ => panic!("expected Modified"),
7623 }
7624 let count = engine.execute_powql("count(User)").unwrap();
7625 match count {
7626 QueryResult::Scalar(Value::Int(v)) => assert_eq!(v as u64, n as u64 - to_delete),
7627 _ => panic!("expected Int"),
7628 }
7629 }
7630
7631 #[test]
7632 fn test_fastpath_delete_by_pk() {
7633 let mut engine = mission_a_engine(30);
7634 let result = engine.execute_powql("User filter .id = 7 delete").unwrap();
7635 match result {
7636 QueryResult::Modified(n) => assert_eq!(n, 1),
7637 _ => panic!("expected Modified"),
7638 }
7639 let lookup = engine.execute_powql("User filter .id = 7").unwrap();
7641 match lookup {
7642 QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 0),
7643 _ => panic!("expected rows"),
7644 }
7645 let other = engine.execute_powql("User filter .id = 8 { .id }").unwrap();
7647 match other {
7648 QueryResult::Rows { rows, .. } => {
7649 assert_eq!(rows.len(), 1);
7650 assert_eq!(rows[0][0], Value::Int(8));
7651 }
7652 _ => panic!("expected rows"),
7653 }
7654 }
7655
7656 #[test]
7657 fn test_fastpath_update_by_filter_matches_generic() {
7658 let n: i64 = 500;
7662 let mut engine = mission_a_engine(n);
7663 let count_before = engine
7664 .execute_powql(r#"count(User filter .status = "active")"#)
7665 .unwrap();
7666 let expected_count = match count_before {
7667 QueryResult::Scalar(Value::Int(v)) => v as u64,
7668 _ => panic!("expected Int"),
7669 };
7670
7671 let upd = engine
7672 .execute_powql(r#"User filter .status = "active" update { age := 42 }"#)
7673 .unwrap();
7674 match upd {
7675 QueryResult::Modified(n) => assert_eq!(n, expected_count),
7676 _ => panic!("expected Modified"),
7677 }
7678
7679 let count_after = engine
7681 .execute_powql(r#"count(User filter .age = 42)"#)
7682 .unwrap();
7683 match count_after {
7684 QueryResult::Scalar(Value::Int(v)) => {
7685 assert!(v as u64 >= expected_count);
7689 }
7690 _ => panic!("expected Int"),
7691 }
7692 }
7693
7694 #[test]
7697 fn test_prepared_insert_reuses_template() {
7698 let mut engine = test_engine();
7699 let prep = engine
7700 .prepare(r#"insert User { name := "seed", email := "seed@ex.com", age := 0 }"#)
7701 .expect("prepare");
7702 assert_eq!(prep.param_count, 3);
7704
7705 for i in 0..5 {
7706 engine
7707 .execute_prepared(
7708 &prep,
7709 &[
7710 Literal::String(format!("user{i}")),
7711 Literal::String(format!("u{i}@ex.com")),
7712 Literal::Int(20 + i as i64),
7713 ],
7714 )
7715 .expect("execute_prepared");
7716 }
7717
7718 let count = engine.execute_powql("count(User)").unwrap();
7720 match count {
7721 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 8),
7722 _ => panic!("expected scalar"),
7723 }
7724 }
7725
7726 #[test]
7727 fn test_prepared_update_by_pk() {
7728 let mut engine = test_engine();
7729 let prep = engine
7730 .prepare(r#"User filter .name = "seed" update { age := 0 }"#)
7731 .expect("prepare");
7732 assert_eq!(prep.param_count, 2);
7734
7735 engine
7736 .execute_prepared(&prep, &[Literal::String("Alice".into()), Literal::Int(99)])
7737 .expect("execute_prepared");
7738
7739 let result = engine
7740 .execute_powql(r#"User filter .name = "Alice" { age }"#)
7741 .unwrap();
7742 match result {
7743 QueryResult::Rows { rows, .. } => {
7744 assert_eq!(rows[0][0], Value::Int(99));
7745 }
7746 _ => panic!("expected rows"),
7747 }
7748 }
7749
7750 #[test]
7751 fn test_prepared_wrong_arity_errors() {
7752 let mut engine = test_engine();
7753 let prep = engine
7754 .prepare(r#"User filter .age > 0 { name }"#)
7755 .expect("prepare");
7756 assert_eq!(prep.param_count, 1);
7757 let err = engine.execute_prepared(&prep, &[]).unwrap_err();
7758 assert!(err.contains("expects 1 literal"));
7759 }
7760
7761 fn join_engine() -> Engine {
7768 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
7769 let dir = std::env::temp_dir().join(format!("powdb_join_{}_{}", std::process::id(), id));
7770 let mut engine = Engine::new(&dir).unwrap();
7771 engine
7772 .execute_powql("type User { required id: int, required name: str }")
7773 .unwrap();
7774 engine
7775 .execute_powql(
7776 "type Order { required id: int, required user_id: int, required total: int }",
7777 )
7778 .unwrap();
7779 engine
7780 .execute_powql(r#"insert User { id := 1, name := "Alice" }"#)
7781 .unwrap();
7782 engine
7783 .execute_powql(r#"insert User { id := 2, name := "Bob" }"#)
7784 .unwrap();
7785 engine
7786 .execute_powql(r#"insert User { id := 3, name := "Charlie" }"#)
7787 .unwrap();
7788 engine
7789 .execute_powql(r#"insert Order { id := 10, user_id := 1, total := 100 }"#)
7790 .unwrap();
7791 engine
7792 .execute_powql(r#"insert Order { id := 11, user_id := 1, total := 200 }"#)
7793 .unwrap();
7794 engine
7795 .execute_powql(r#"insert Order { id := 12, user_id := 2, total := 50 }"#)
7796 .unwrap();
7797 engine
7798 .execute_powql(r#"insert Order { id := 13, user_id := 99, total := 999 }"#)
7799 .unwrap();
7800 engine
7801 }
7802
7803 #[test]
7804 fn test_inner_join_matches_rows() {
7805 let mut engine = join_engine();
7806 let result = engine
7807 .execute_powql("User as u join Order as o on u.id = o.user_id")
7808 .unwrap();
7809 match result {
7810 QueryResult::Rows { columns, rows } => {
7811 assert_eq!(rows.len(), 3);
7814 assert!(columns.contains(&"u.id".to_string()));
7816 assert!(columns.contains(&"u.name".to_string()));
7817 assert!(columns.contains(&"o.id".to_string()));
7818 assert!(columns.contains(&"o.user_id".to_string()));
7819 assert!(columns.contains(&"o.total".to_string()));
7820 }
7821 _ => panic!("expected rows"),
7822 }
7823 }
7824
7825 #[test]
7826 fn test_inner_join_with_qualified_projection_and_filter() {
7827 let mut engine = join_engine();
7828 let result = engine
7829 .execute_powql(
7830 "User as u join Order as o on u.id = o.user_id \
7831 filter o.total > 75 { u.name, o.total }",
7832 )
7833 .unwrap();
7834 match result {
7835 QueryResult::Rows { columns, rows } => {
7836 assert_eq!(columns, vec!["u.name", "o.total"]);
7837 assert_eq!(rows.len(), 2);
7839 let names: Vec<_> = rows.iter().map(|r| r[0].clone()).collect();
7840 assert!(names
7841 .iter()
7842 .all(|v| matches!(v, Value::Str(s) if s == "Alice")));
7843 }
7844 _ => panic!("expected rows"),
7845 }
7846 }
7847
7848 #[test]
7849 fn test_join_projection_with_aliased_right_table_column() {
7850 let mut engine = join_engine();
7854 let result = engine
7855 .execute_powql("User as u join Order as o on u.id = o.user_id { u.name, tot: o.total }")
7856 .unwrap();
7857 match result {
7858 QueryResult::Rows { columns, rows } => {
7859 assert_eq!(columns, vec!["u.name", "tot"]);
7860 assert_eq!(rows.len(), 3);
7861 for row in &rows {
7863 assert!(
7864 matches!(row[1], Value::Int(_)),
7865 "tot should be Int, got {:?}",
7866 row[1]
7867 );
7868 }
7869 }
7870 _ => panic!("expected rows"),
7871 }
7872 }
7873
7874 #[test]
7875 fn test_match_keyword_rejected_as_invalid_join() {
7876 let mut engine = join_engine();
7881 let err = engine
7882 .execute_powql("User match Order on u.id = o.user_id { u.name }")
7883 .unwrap_err();
7884 assert!(
7885 err.to_string().to_lowercase().contains("match")
7886 || err.to_string().to_lowercase().contains("trailing")
7887 || err.to_string().to_lowercase().contains("unexpected"),
7888 "expected parse error mentioning trailing/unexpected token, got: {err}"
7889 );
7890 }
7891
7892 #[test]
7893 fn test_left_outer_join_emits_orphan_left_rows() {
7894 let mut engine = join_engine();
7895 let result = engine
7896 .execute_powql("User as u left join Order as o on u.id = o.user_id")
7897 .unwrap();
7898 match result {
7899 QueryResult::Rows { rows, columns } => {
7900 assert_eq!(rows.len(), 4);
7902 let u_name_idx = columns.iter().position(|c| c == "u.name").unwrap();
7904 let o_total_idx = columns.iter().position(|c| c == "o.total").unwrap();
7905 let charlie = rows
7906 .iter()
7907 .find(|r| matches!(&r[u_name_idx], Value::Str(s) if s == "Charlie"))
7908 .expect("Charlie row present");
7909 assert_eq!(charlie[o_total_idx], Value::Empty);
7910 }
7911 _ => panic!("expected rows"),
7912 }
7913 }
7914
7915 #[test]
7916 fn test_right_outer_join_emits_orphan_right_rows() {
7917 let mut engine = join_engine();
7918 let result = engine
7921 .execute_powql("User as u right join Order as o on u.id = o.user_id")
7922 .unwrap();
7923 match result {
7924 QueryResult::Rows { rows, columns } => {
7925 assert_eq!(rows.len(), 4);
7927 let u_name_idx = columns.iter().position(|c| c == "u.name").unwrap();
7928 let o_total_idx = columns.iter().position(|c| c == "o.total").unwrap();
7929 let orphan = rows
7930 .iter()
7931 .find(|r| r[o_total_idx] == Value::Int(999))
7932 .expect("orphan order row present");
7933 assert_eq!(orphan[u_name_idx], Value::Empty);
7934 }
7935 _ => panic!("expected rows"),
7936 }
7937 }
7938
7939 #[test]
7940 fn test_cross_join_emits_full_product() {
7941 let mut engine = join_engine();
7942 let result = engine
7943 .execute_powql("User as u cross join Order as o")
7944 .unwrap();
7945 match result {
7946 QueryResult::Rows { rows, .. } => {
7947 assert_eq!(rows.len(), 3 * 4);
7948 }
7949 _ => panic!("expected rows"),
7950 }
7951 }
7952
7953 #[test]
7954 fn test_hash_join_handles_swapped_predicate_orientation() {
7955 let mut engine = join_engine();
7959 let result = engine
7960 .execute_powql("User as u join Order as o on o.user_id = u.id { u.name, o.total }")
7961 .unwrap();
7962 match result {
7963 QueryResult::Rows { rows, columns } => {
7964 assert_eq!(columns, vec!["u.name", "o.total"]);
7965 assert_eq!(rows.len(), 3);
7966 }
7967 _ => panic!("expected rows"),
7968 }
7969 }
7970
7971 #[test]
7972 fn test_non_equi_join_falls_back_to_nested_loop() {
7973 let mut engine = join_engine();
7976 let result = engine
7977 .execute_powql("User as u join Order as o on u.id < o.user_id")
7978 .unwrap();
7979 match result {
7980 QueryResult::Rows { rows, columns } => {
7981 assert_eq!(rows.len(), 4);
7989 let u_id_idx = columns.iter().position(|c| c == "u.id").unwrap();
7990 let o_uid_idx = columns.iter().position(|c| c == "o.user_id").unwrap();
7991 for row in &rows {
7992 match (&row[u_id_idx], &row[o_uid_idx]) {
7993 (Value::Int(u), Value::Int(o)) => assert!(u < o),
7994 _ => panic!("expected int columns"),
7995 }
7996 }
7997 }
7998 _ => panic!("expected rows"),
7999 }
8000 }
8001
8002 #[test]
8003 fn test_hash_join_with_string_key() {
8004 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
8007 let dir = std::env::temp_dir().join(format!("powdb_strjoin_{}_{}", std::process::id(), id));
8008 let mut engine = Engine::new(&dir).unwrap();
8009 engine
8010 .execute_powql("type A { required code: str, required label: str }")
8011 .unwrap();
8012 engine
8013 .execute_powql("type B { required code: str, required score: int }")
8014 .unwrap();
8015 engine
8016 .execute_powql(r#"insert A { code := "x", label := "X-label" }"#)
8017 .unwrap();
8018 engine
8019 .execute_powql(r#"insert A { code := "y", label := "Y-label" }"#)
8020 .unwrap();
8021 engine
8022 .execute_powql(r#"insert B { code := "x", score := 100 }"#)
8023 .unwrap();
8024 engine
8025 .execute_powql(r#"insert B { code := "y", score := 200 }"#)
8026 .unwrap();
8027 engine
8028 .execute_powql(r#"insert B { code := "z", score := 300 }"#)
8029 .unwrap();
8030
8031 let result = engine
8032 .execute_powql("A as a join B as b on a.code = b.code { a.label, b.score }")
8033 .unwrap();
8034 match result {
8035 QueryResult::Rows { rows, .. } => {
8036 assert_eq!(rows.len(), 2);
8038 }
8039 _ => panic!("expected rows"),
8040 }
8041 }
8042
8043 #[test]
8044 fn test_multi_join_chain() {
8045 let mut engine = join_engine();
8047 engine
8048 .execute_powql("type Product { required id: int, required name: str }")
8049 .unwrap();
8050 engine
8051 .execute_powql(r#"insert Product { id := 100, name := "Widget" }"#)
8052 .unwrap();
8053 engine
8054 .execute_powql(r#"insert Product { id := 200, name := "Gadget" }"#)
8055 .unwrap();
8056 let result = engine
8059 .execute_powql(
8060 "User as u join Order as o on u.id = o.user_id \
8061 cross join Product as p",
8062 )
8063 .unwrap();
8064 match result {
8065 QueryResult::Rows { rows, columns } => {
8066 assert_eq!(rows.len(), 6);
8068 assert!(columns.contains(&"u.name".to_string()));
8069 assert!(columns.contains(&"o.total".to_string()));
8070 assert!(columns.contains(&"p.name".to_string()));
8071 }
8072 _ => panic!("expected rows"),
8073 }
8074 }
8075
8076 #[test]
8079 fn test_distinct_deduplicates_rows() {
8080 let mut engine = test_engine();
8081 engine
8083 .execute_powql(
8084 r#"insert User { name := "Alice", email := "alice2@ex.com", age := 25 }"#,
8085 )
8086 .unwrap();
8087 let result = engine.execute_powql("User distinct { .name }").unwrap();
8088 match result {
8089 QueryResult::Rows { rows, .. } => {
8090 let names: Vec<&Value> = rows.iter().map(|r| &r[0]).collect();
8091 assert_eq!(names.len(), 3);
8093 let alice_count = names
8094 .iter()
8095 .filter(|v| matches!(v, Value::Str(s) if s == "Alice"))
8096 .count();
8097 assert_eq!(alice_count, 1);
8098 assert!(names
8099 .iter()
8100 .any(|v| matches!(v, Value::Str(s) if s == "Bob")));
8101 assert!(names
8102 .iter()
8103 .any(|v| matches!(v, Value::Str(s) if s == "Charlie")));
8104 }
8105 _ => panic!("expected rows"),
8106 }
8107 }
8108
8109 #[test]
8110 fn test_in_list_filter() {
8111 let mut engine = test_engine();
8112 let result = engine
8113 .execute_powql(r#"User filter .name in ("Alice", "Bob") { .name }"#)
8114 .unwrap();
8115 match result {
8116 QueryResult::Rows { rows, .. } => {
8117 assert_eq!(rows.len(), 2);
8118 }
8119 _ => panic!("expected rows"),
8120 }
8121 }
8122
8123 #[test]
8124 fn test_not_in_list_filter() {
8125 let mut engine = test_engine();
8126 let result = engine
8127 .execute_powql(r#"User filter .name not in ("Alice") { .name }"#)
8128 .unwrap();
8129 match result {
8130 QueryResult::Rows { rows, .. } => {
8131 assert_eq!(rows.len(), 2);
8133 }
8134 _ => panic!("expected rows"),
8135 }
8136 }
8137
8138 #[test]
8139 fn test_between_filter() {
8140 let mut engine = test_engine();
8141 let result = engine
8142 .execute_powql("User filter .age between 25 and 30 { .name, .age }")
8143 .unwrap();
8144 match result {
8145 QueryResult::Rows { rows, .. } => {
8146 assert_eq!(rows.len(), 2);
8148 }
8149 _ => panic!("expected rows"),
8150 }
8151 }
8152
8153 #[test]
8154 fn test_between_filter_float_column_int_literals() {
8155 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
8160 let dir = std::env::temp_dir().join(format!(
8161 "powdb_exec_between_float_{}_{}",
8162 std::process::id(),
8163 id
8164 ));
8165 let mut engine = Engine::new(&dir).unwrap();
8166 engine
8167 .execute_powql("type Product { required name: str, required price: float }")
8168 .unwrap();
8169 engine
8170 .execute_powql(r#"insert Product { name := "Cable", price := 29.0 }"#)
8171 .unwrap();
8172 engine
8173 .execute_powql(r#"insert Product { name := "Speaker", price := 175.5 }"#)
8174 .unwrap();
8175 engine
8176 .execute_powql(r#"insert Product { name := "Monitor", price := 450.0 }"#)
8177 .unwrap();
8178 engine
8179 .execute_powql(r#"insert Product { name := "Laptop", price := 1299.0 }"#)
8180 .unwrap();
8181
8182 let result = engine
8183 .execute_powql("Product filter .price between 100 and 500 { .name, .price }")
8184 .unwrap();
8185 match result {
8186 QueryResult::Rows { rows, .. } => {
8187 assert_eq!(
8188 rows.len(),
8189 2,
8190 "expected 2 rows in [100, 500] range, got {}: {:?}",
8191 rows.len(),
8192 rows
8193 );
8194 let names: Vec<&str> = rows
8196 .iter()
8197 .map(|r| match &r[0] {
8198 Value::Str(s) => s.as_str(),
8199 _ => panic!("expected string name"),
8200 })
8201 .collect();
8202 assert!(names.contains(&"Speaker"));
8203 assert!(names.contains(&"Monitor"));
8204 }
8205 _ => panic!("expected rows"),
8206 }
8207 }
8208
8209 #[test]
8210 fn test_not_between_filter() {
8211 let mut engine = test_engine();
8212 let result = engine
8213 .execute_powql("User filter .age not between 26 and 29 { .name }")
8214 .unwrap();
8215 match result {
8216 QueryResult::Rows { rows, .. } => {
8217 assert_eq!(rows.len(), 3);
8219 }
8220 _ => panic!("expected rows"),
8221 }
8222 }
8223
8224 #[test]
8225 fn test_like_prefix_match() {
8226 let mut engine = test_engine();
8227 let result = engine
8228 .execute_powql(r#"User filter .name like "Ali%" { .name }"#)
8229 .unwrap();
8230 match result {
8231 QueryResult::Rows { rows, .. } => {
8232 assert_eq!(rows.len(), 1);
8233 assert!(matches!(&rows[0][0], Value::Str(s) if s == "Alice"));
8234 }
8235 _ => panic!("expected rows"),
8236 }
8237 }
8238
8239 #[test]
8240 fn test_like_wildcard_underscore() {
8241 let mut engine = test_engine();
8242 let result = engine
8243 .execute_powql(r#"User filter .name like "_ob" { .name }"#)
8244 .unwrap();
8245 match result {
8246 QueryResult::Rows { rows, .. } => {
8247 assert_eq!(rows.len(), 1);
8248 assert!(matches!(&rows[0][0], Value::Str(s) if s == "Bob"));
8249 }
8250 _ => panic!("expected rows"),
8251 }
8252 }
8253
8254 #[test]
8255 fn test_not_like_filter() {
8256 let mut engine = test_engine();
8257 let result = engine
8258 .execute_powql(r#"User filter .name not like "A%" { .name }"#)
8259 .unwrap();
8260 match result {
8261 QueryResult::Rows { rows, .. } => {
8262 assert_eq!(rows.len(), 2);
8264 }
8265 _ => panic!("expected rows"),
8266 }
8267 }
8268
8269 #[test]
8270 fn test_in_list_with_integers() {
8271 let mut engine = test_engine();
8272 let result = engine
8273 .execute_powql("User filter .age in (25, 30) { .name }")
8274 .unwrap();
8275 match result {
8276 QueryResult::Rows { rows, .. } => {
8277 assert_eq!(rows.len(), 2);
8278 }
8279 _ => panic!("expected rows"),
8280 }
8281 }
8282
8283 #[test]
8284 fn test_like_full_match() {
8285 let mut engine = test_engine();
8286 let result = engine
8288 .execute_powql(r#"User filter .name like "Alice" { .name }"#)
8289 .unwrap();
8290 match result {
8291 QueryResult::Rows { rows, .. } => {
8292 assert_eq!(rows.len(), 1);
8293 }
8294 _ => panic!("expected rows"),
8295 }
8296 }
8297
8298 #[test]
8301 fn test_group_by_count() {
8302 let mut engine = test_engine();
8306 let result = engine
8307 .execute_powql("User group .name { .name, n: count(.name) }")
8308 .unwrap();
8309 match result {
8310 QueryResult::Rows { columns, rows } => {
8311 assert_eq!(columns, vec!["name", "n"]);
8312 assert_eq!(rows.len(), 3); for row in &rows {
8315 assert_eq!(row[1], Value::Int(1));
8316 }
8317 }
8318 _ => panic!("expected rows"),
8319 }
8320 }
8321
8322 #[test]
8323 fn test_group_by_sum_avg() {
8324 let mut engine = test_engine();
8327 let result = engine
8329 .execute_powql("User group .email { .email, total_age: sum(.age) }")
8330 .unwrap();
8331 match result {
8332 QueryResult::Rows { rows, .. } => {
8333 assert_eq!(rows.len(), 3);
8335 }
8336 _ => panic!("expected rows"),
8337 }
8338 }
8339
8340 #[test]
8341 fn test_group_by_with_filter() {
8342 let mut engine = test_engine();
8343 let result = engine
8345 .execute_powql("User filter .age >= 30 group .name { .name, n: count(.name) }")
8346 .unwrap();
8347 match result {
8348 QueryResult::Rows { rows, .. } => {
8349 assert_eq!(rows.len(), 2);
8351 }
8352 _ => panic!("expected rows"),
8353 }
8354 }
8355
8356 #[test]
8357 fn test_group_by_having() {
8358 let mut engine = mission_a_engine(30);
8360 let result = engine
8363 .execute_powql(
8364 "User group .status having count(.name) > 5 { .status, n: count(.name) }",
8365 )
8366 .unwrap();
8367 match result {
8368 QueryResult::Rows { columns, rows } => {
8369 assert_eq!(columns, vec!["status", "n"]);
8370 assert_eq!(rows.len(), 3);
8372 for row in &rows {
8373 assert_eq!(row[1], Value::Int(10));
8374 }
8375 }
8376 _ => panic!("expected rows"),
8377 }
8378 }
8379
8380 #[test]
8381 fn test_group_by_having_filters_groups() {
8382 let mut engine = mission_a_engine(30);
8383 let result = engine
8385 .execute_powql("User group .status having count(.name) > 100 { .status }")
8386 .unwrap();
8387 match result {
8388 QueryResult::Rows { rows, .. } => {
8389 assert_eq!(rows.len(), 0);
8390 }
8391 _ => panic!("expected rows"),
8392 }
8393 }
8394
8395 #[test]
8396 fn test_group_by_having_with_aliased_projection_agg() {
8397 let mut engine = mission_a_engine(30);
8402 let result = engine
8404 .execute_powql(
8405 "User group .status having count(.name) >= 11 { .status, cnt: count(.name) }",
8406 )
8407 .unwrap();
8408 match result {
8409 QueryResult::Rows { rows, .. } => {
8410 assert_eq!(rows.len(), 0, "HAVING >= 11 should filter all groups");
8411 }
8412 _ => panic!("expected rows"),
8413 }
8414 let result = engine
8416 .execute_powql(
8417 "User group .status having count(.name) >= 10 { .status, cnt: count(.name) }",
8418 )
8419 .unwrap();
8420 match result {
8421 QueryResult::Rows { rows, .. } => {
8422 assert_eq!(rows.len(), 3);
8423 for row in &rows {
8424 assert_eq!(row[1], Value::Int(10));
8425 }
8426 }
8427 _ => panic!("expected rows"),
8428 }
8429 }
8430
8431 #[test]
8432 fn test_group_by_having_post_projection() {
8433 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
8437 let dir =
8438 std::env::temp_dir().join(format!("powdb_having_post_{}_{}", std::process::id(), id));
8439 let mut engine = Engine::new(&dir).unwrap();
8440 engine
8441 .execute_powql("type Person { required name: str, required age: int, city: str }")
8442 .unwrap();
8443 for (name, age, city) in [
8444 ("Alice", 30, "NYC"),
8445 ("Bob", 24, "SF"),
8446 ("Carol", 41, "LA"),
8447 ("Dave", 28, "NYC"),
8448 ("Eve", 35, "Austin"),
8449 ] {
8450 engine
8451 .execute_powql(&format!(
8452 r#"insert Person {{ name := "{name}", age := {age}, city := "{city}" }}"#
8453 ))
8454 .unwrap();
8455 }
8456 let result = engine
8457 .execute_powql("Person group .city { .city, cnt: count(.name) } having cnt >= 2")
8458 .unwrap();
8459 match result {
8460 QueryResult::Rows { rows, .. } => {
8461 assert_eq!(rows.len(), 1, "only NYC has >= 2 people, got: {rows:?}");
8462 assert_eq!(rows[0][0], Value::Str("NYC".into()));
8463 assert_eq!(rows[0][1], Value::Int(2));
8464 }
8465 _ => panic!("expected rows"),
8466 }
8467 }
8468
8469 #[test]
8470 fn test_having_without_group_by_errors() {
8471 let mut engine = test_engine();
8472 let err = engine.execute_powql("User { .name } having count(.name) > 1");
8473 assert!(
8474 err.is_err(),
8475 "HAVING without GROUP BY should be a parse error"
8476 );
8477 }
8478
8479 #[test]
8480 fn test_group_by_having_reproduces_ts_client_case() {
8481 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
8484 let dir =
8485 std::env::temp_dir().join(format!("powdb_having_ts_{}_{}", std::process::id(), id));
8486 let mut engine = Engine::new(&dir).unwrap();
8487 engine
8488 .execute_powql("type Person { required name: str, required age: int, city: str }")
8489 .unwrap();
8490 for (name, age, city) in [
8491 ("Alice", 30, "NYC"),
8492 ("Bob", 24, "SF"),
8493 ("Carol", 41, "LA"),
8494 ("Dave", 28, "NYC"),
8495 ("Eve", 35, "Austin"),
8496 ] {
8497 engine
8498 .execute_powql(&format!(
8499 r#"insert Person {{ name := "{name}", age := {age}, city := "{city}" }}"#
8500 ))
8501 .unwrap();
8502 }
8503 let result = engine
8504 .execute_powql(
8505 "Person group .city having count(.name) >= 2 { .city, cnt: count(.name) }",
8506 )
8507 .unwrap();
8508 match result {
8509 QueryResult::Rows { rows, .. } => {
8510 assert_eq!(rows.len(), 1, "only NYC has >= 2 people, got: {rows:?}");
8511 assert_eq!(rows[0][0], Value::Str("NYC".into()));
8512 assert_eq!(rows[0][1], Value::Int(2));
8513 }
8514 _ => panic!("expected rows"),
8515 }
8516 }
8517
8518 #[test]
8519 fn test_group_by_having_filters_some_groups() {
8520 let mut engine = test_engine();
8522 engine
8524 .execute_powql(r#"insert User { name := "Alice", email := "a2@ex.com", age := 31 }"#)
8525 .unwrap();
8526 engine
8527 .execute_powql(r#"insert User { name := "Alice", email := "a3@ex.com", age := 32 }"#)
8528 .unwrap();
8529 let result = engine
8531 .execute_powql("User group .name having count(.name) >= 2 { .name, cnt: count(.name) }")
8532 .unwrap();
8533 match result {
8534 QueryResult::Rows { rows, .. } => {
8535 assert_eq!(rows.len(), 1);
8536 assert_eq!(rows[0][0], Value::Str("Alice".into()));
8537 assert_eq!(rows[0][1], Value::Int(3));
8538 }
8539 _ => panic!("expected rows"),
8540 }
8541 }
8542
8543 #[test]
8544 fn test_group_by_min_max() {
8545 let mut engine = mission_a_engine(30);
8546 let result = engine.execute_powql(
8551 r#"User filter .status = "active" group .status { .status, lo: min(.age), hi: max(.age) }"#,
8552 ).unwrap();
8553 match result {
8554 QueryResult::Rows { columns, rows } => {
8555 assert_eq!(columns, vec!["status", "lo", "hi"]);
8556 assert_eq!(rows.len(), 1);
8557 assert_eq!(rows[0][0], Value::Str("active".into()));
8558 assert_eq!(rows[0][1], Value::Int(18));
8559 assert_eq!(rows[0][2], Value::Int(45));
8560 }
8561 _ => panic!("expected rows"),
8562 }
8563 }
8564
8565 #[test]
8566 fn test_group_by_avg() {
8567 let mut engine = mission_a_engine(6);
8568 let result = engine
8573 .execute_powql(
8574 r#"User filter .status = "active" group .status { .status, a: avg(.age) }"#,
8575 )
8576 .unwrap();
8577 match result {
8578 QueryResult::Rows { rows, .. } => {
8579 assert_eq!(rows.len(), 1);
8580 match &rows[0][1] {
8581 Value::Float(v) => assert!((v - 19.5).abs() < 0.001),
8582 other => panic!("expected float, got {other:?}"),
8583 }
8584 }
8585 _ => panic!("expected rows"),
8586 }
8587 }
8588
8589 #[test]
8592 fn test_is_null_filter() {
8593 let mut engine = test_engine();
8594 engine
8595 .execute_powql(r#"insert User { name := "Diana", email := "diana@ex.com" }"#)
8596 .unwrap();
8597 let result = engine
8598 .execute_powql("User filter .age is null { .name }")
8599 .unwrap();
8600 match result {
8601 QueryResult::Rows { rows, .. } => {
8602 assert_eq!(rows.len(), 1);
8603 assert_eq!(rows[0][0], Value::Str("Diana".into()));
8604 }
8605 _ => panic!("expected rows"),
8606 }
8607 }
8608
8609 #[test]
8610 fn test_is_not_null_filter() {
8611 let mut engine = test_engine();
8612 engine
8613 .execute_powql(r#"insert User { name := "Diana", email := "diana@ex.com" }"#)
8614 .unwrap();
8615 let result = engine
8616 .execute_powql("User filter .age is not null { .name }")
8617 .unwrap();
8618 match result {
8619 QueryResult::Rows { rows, .. } => {
8620 assert_eq!(rows.len(), 3);
8621 }
8622 _ => panic!("expected rows"),
8623 }
8624 }
8625
8626 #[test]
8627 fn test_is_null_count() {
8628 let mut engine = test_engine();
8629 engine
8630 .execute_powql(r#"insert User { name := "Diana", email := "diana@ex.com" }"#)
8631 .unwrap();
8632 let result = engine
8633 .execute_powql("count(User filter .age is null)")
8634 .unwrap();
8635 match result {
8636 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 1),
8637 _ => panic!("expected scalar int"),
8638 }
8639 }
8640
8641 #[test]
8642 fn test_is_null_combined_with_and() {
8643 let mut engine = test_engine();
8644 engine
8645 .execute_powql(r#"insert User { name := "Diana", email := "diana@ex.com" }"#)
8646 .unwrap();
8647 engine
8648 .execute_powql(r#"insert User { name := "Eve", email := "eve@ex.com" }"#)
8649 .unwrap();
8650 let result = engine
8651 .execute_powql(r#"User filter .age is null and .name = "Diana" { .name }"#)
8652 .unwrap();
8653 match result {
8654 QueryResult::Rows { rows, .. } => {
8655 assert_eq!(rows.len(), 1);
8656 assert_eq!(rows[0][0], Value::Str("Diana".into()));
8657 }
8658 _ => panic!("expected rows"),
8659 }
8660 }
8661
8662 #[test]
8663 fn test_eq_null_matches_is_null() {
8664 let mut engine = test_engine();
8665 engine
8666 .execute_powql(r#"insert User { name := "Diana", email := "diana@ex.com" }"#)
8667 .unwrap();
8668 let result = engine
8669 .execute_powql("User filter .age = null { .name }")
8670 .unwrap();
8671 match result {
8672 QueryResult::Rows { rows, .. } => {
8673 assert_eq!(rows.len(), 1);
8674 assert_eq!(rows[0][0], Value::Str("Diana".into()));
8675 }
8676 _ => panic!("expected rows"),
8677 }
8678 }
8679
8680 #[test]
8681 fn test_neq_null_matches_is_not_null() {
8682 let mut engine = test_engine();
8683 engine
8684 .execute_powql(r#"insert User { name := "Diana", email := "diana@ex.com" }"#)
8685 .unwrap();
8686 let result = engine
8687 .execute_powql("User filter .age != null { .name }")
8688 .unwrap();
8689 match result {
8690 QueryResult::Rows { rows, .. } => {
8691 assert_eq!(rows.len(), 3);
8692 }
8693 _ => panic!("expected rows"),
8694 }
8695 }
8696
8697 #[test]
8700 fn test_upper_in_filter() {
8701 let mut engine = test_engine();
8702 let result = engine
8703 .execute_powql(r#"User filter upper(.name) = "ALICE""#)
8704 .unwrap();
8705 match result {
8706 QueryResult::Rows { rows, .. } => {
8707 assert_eq!(rows.len(), 1);
8708 assert_eq!(rows[0][0], Value::Str("Alice".into()));
8709 }
8710 _ => panic!("expected rows"),
8711 }
8712 }
8713
8714 #[test]
8715 fn test_lower_in_projection() {
8716 let mut engine = test_engine();
8717 let result = engine.execute_powql("User { low: lower(.email) }").unwrap();
8718 match result {
8719 QueryResult::Rows { columns, rows } => {
8720 assert_eq!(columns, vec!["low"]);
8721 assert_eq!(rows.len(), 3);
8722 assert_eq!(rows[0][0], Value::Str("alice@ex.com".into()));
8723 }
8724 _ => panic!("expected rows"),
8725 }
8726 }
8727
8728 #[test]
8729 fn test_length_in_projection() {
8730 let mut engine = test_engine();
8731 let result = engine
8732 .execute_powql("User { .name, len: length(.name) }")
8733 .unwrap();
8734 match result {
8735 QueryResult::Rows { columns, rows } => {
8736 assert_eq!(columns, vec!["name", "len"]);
8737 assert_eq!(rows[0][1], Value::Int(5));
8738 assert_eq!(rows[1][1], Value::Int(3));
8739 assert_eq!(rows[2][1], Value::Int(7));
8740 }
8741 _ => panic!("expected rows"),
8742 }
8743 }
8744
8745 #[test]
8746 fn test_substring_in_projection() {
8747 let mut engine = test_engine();
8748 let result = engine
8749 .execute_powql("User { sub: substring(.name, 1, 3) }")
8750 .unwrap();
8751 match result {
8752 QueryResult::Rows { rows, .. } => {
8753 assert_eq!(rows[0][0], Value::Str("Ali".into()));
8754 assert_eq!(rows[1][0], Value::Str("Bob".into()));
8755 assert_eq!(rows[2][0], Value::Str("Cha".into()));
8756 }
8757 _ => panic!("expected rows"),
8758 }
8759 }
8760
8761 #[test]
8762 fn test_concat_in_projection() {
8763 let mut engine = test_engine();
8764 let result = engine
8765 .execute_powql(r#"User { full: concat(.name, " - ", .email) }"#)
8766 .unwrap();
8767 match result {
8768 QueryResult::Rows { rows, .. } => {
8769 assert_eq!(rows[0][0], Value::Str("Alice - alice@ex.com".into()));
8770 assert_eq!(rows[1][0], Value::Str("Bob - bob@ex.com".into()));
8771 assert_eq!(rows[2][0], Value::Str("Charlie - charlie@ex.com".into()));
8772 }
8773 _ => panic!("expected rows"),
8774 }
8775 }
8776
8777 #[test]
8778 fn test_concat_coerces_int() {
8779 let mut engine = test_engine();
8780 let result = engine
8781 .execute_powql(r#"User { info: concat(.name, " age=", .age) }"#)
8782 .unwrap();
8783 match result {
8784 QueryResult::Rows { rows, .. } => {
8785 assert_eq!(rows[0][0], Value::Str("Alice age=30".into()));
8786 }
8787 _ => panic!("expected rows"),
8788 }
8789 }
8790
8791 #[test]
8794 fn test_case_in_projection() {
8795 let mut engine = test_engine();
8796 let result = engine.execute_powql(
8797 r#"User { .name, label: case when .age > 30 then "senior" when .age >= 30 then "exactly30" else "young" end }"#
8798 ).unwrap();
8799 match result {
8800 QueryResult::Rows { columns, rows } => {
8801 assert_eq!(columns, vec!["name", "label"]);
8802 assert_eq!(rows.len(), 3);
8803 for row in &rows {
8804 let name = &row[0];
8805 let label = &row[1];
8806 match name {
8807 Value::Str(n) if n == "Alice" => {
8808 assert_eq!(label, &Value::Str("exactly30".into()))
8809 }
8810 Value::Str(n) if n == "Bob" => {
8811 assert_eq!(label, &Value::Str("young".into()))
8812 }
8813 Value::Str(n) if n == "Charlie" => {
8814 assert_eq!(label, &Value::Str("senior".into()))
8815 }
8816 _ => panic!("unexpected name: {name:?}"),
8817 }
8818 }
8819 }
8820 _ => panic!("expected rows"),
8821 }
8822 }
8823
8824 #[test]
8825 fn test_case_in_filter() {
8826 let mut engine = test_engine();
8827 let result = engine
8828 .execute_powql(r#"User filter case when .age > 30 then true else false end"#)
8829 .unwrap();
8830 match result {
8831 QueryResult::Rows { rows, .. } => {
8832 assert_eq!(rows.len(), 1);
8833 assert_eq!(rows[0][0], Value::Str("Charlie".into()));
8834 }
8835 _ => panic!("expected rows"),
8836 }
8837 }
8838
8839 #[test]
8840 fn test_case_without_else_returns_empty() {
8841 let mut engine = test_engine();
8842 let result = engine
8843 .execute_powql(r#"User { .name, label: case when .age > 100 then "old" end }"#)
8844 .unwrap();
8845 match result {
8846 QueryResult::Rows { rows, .. } => {
8847 for row in &rows {
8848 assert_eq!(row[1], Value::Empty);
8849 }
8850 }
8851 _ => panic!("expected rows"),
8852 }
8853 }
8854
8855 #[test]
8858 fn test_mul_in_projection() {
8859 let mut engine = test_engine();
8860 let result = engine
8861 .execute_powql("User { .name, double_age: .age * 2 }")
8862 .unwrap();
8863 match result {
8864 QueryResult::Rows { columns, rows } => {
8865 assert_eq!(columns, vec!["name", "double_age"]);
8866 let ages: Vec<_> = rows.iter().map(|r| &r[1]).collect();
8868 assert!(ages.contains(&&Value::Int(60)));
8869 assert!(ages.contains(&&Value::Int(50)));
8870 assert!(ages.contains(&&Value::Int(70)));
8871 }
8872 _ => panic!("expected rows"),
8873 }
8874 }
8875
8876 #[test]
8877 fn test_div_in_filter() {
8878 let mut engine = test_engine();
8879 let result = engine.execute_powql("User filter .age / 10 > 2").unwrap();
8880 match result {
8881 QueryResult::Rows { rows, .. } => {
8882 assert_eq!(rows.len(), 2);
8884 }
8885 _ => panic!("expected rows"),
8886 }
8887 }
8888
8889 #[test]
8892 fn test_multi_order_by() {
8893 let mut engine = test_engine();
8894 engine
8896 .execute_powql(r#"insert User { name := "Dave", email := "dave@ex.com", age := 30 }"#)
8897 .unwrap();
8898 let result = engine
8899 .execute_powql("User order .age asc, .name asc { .name, .age }")
8900 .unwrap();
8901 match result {
8902 QueryResult::Rows { rows, .. } => {
8903 assert_eq!(rows[0][0], Value::Str("Bob".into()));
8905 assert_eq!(rows[1][0], Value::Str("Alice".into()));
8906 assert_eq!(rows[2][0], Value::Str("Dave".into()));
8907 assert_eq!(rows[3][0], Value::Str("Charlie".into()));
8908 }
8909 _ => panic!("expected rows"),
8910 }
8911 }
8912
8913 #[test]
8914 fn test_multi_order_mixed_direction() {
8915 let mut engine = test_engine();
8916 engine
8917 .execute_powql(r#"insert User { name := "Dave", email := "dave@ex.com", age := 30 }"#)
8918 .unwrap();
8919 let result = engine
8920 .execute_powql("User order .age asc, .name desc { .name, .age }")
8921 .unwrap();
8922 match result {
8923 QueryResult::Rows { rows, .. } => {
8924 assert_eq!(rows[0][0], Value::Str("Bob".into()));
8926 assert_eq!(rows[1][0], Value::Str("Dave".into()));
8927 assert_eq!(rows[2][0], Value::Str("Alice".into()));
8928 assert_eq!(rows[3][0], Value::Str("Charlie".into()));
8929 }
8930 _ => panic!("expected rows"),
8931 }
8932 }
8933
8934 #[test]
8937 fn test_alter_add_column() {
8938 let mut engine = test_engine();
8939 let result = engine
8940 .execute_powql("alter User add column status: str")
8941 .unwrap();
8942 match result {
8943 QueryResult::Executed { message } => {
8944 assert!(message.contains("status"));
8945 assert!(message.contains("User"));
8946 }
8947 other => panic!("expected Executed, got {other:?}"),
8948 }
8949 engine.execute_powql(r#"insert User { name := "Eve", email := "eve@ex.com", age := 22, status := "active" }"#).unwrap();
8951 let result = engine
8952 .execute_powql(r#"User filter .name = "Eve" { .name, .status }"#)
8953 .unwrap();
8954 match result {
8955 QueryResult::Rows { columns, rows } => {
8956 assert_eq!(columns, vec!["name", "status"]);
8957 assert_eq!(rows.len(), 1);
8958 assert_eq!(rows[0][1], Value::Str("active".into()));
8959 }
8960 other => panic!("expected rows, got {other:?}"),
8961 }
8962 }
8963
8964 #[test]
8965 fn test_alter_add_column_reads_old_rows() {
8966 let mut engine = test_engine();
8976 engine
8977 .execute_powql("alter User add column country: str")
8978 .unwrap();
8979 let result = engine.execute_powql("User").unwrap();
8981 match result {
8982 QueryResult::Rows { columns, rows } => {
8983 assert!(columns.contains(&"country".to_string()));
8984 assert_eq!(rows.len(), 3, "three old rows must still be readable");
8985 let country_idx = columns
8986 .iter()
8987 .position(|c| c == "country")
8988 .expect("country column");
8989 for row in &rows {
8990 assert_eq!(
8991 row[country_idx],
8992 Value::Empty,
8993 "backfilled column must be Empty"
8994 );
8995 }
8996 }
8997 other => panic!("expected rows, got {other:?}"),
8998 }
8999 }
9000
9001 #[test]
9002 fn test_alter_add_required_column_fails() {
9003 let mut engine = test_engine();
9008 let err = engine
9009 .execute_powql("alter User add column required country: str")
9010 .expect_err("required-column add on non-empty table must fail");
9011 let msg = err.to_string().to_lowercase();
9012 assert!(
9013 msg.contains("required") || msg.contains("backfill"),
9014 "error should mention required/backfill, got: {err}"
9015 );
9016 let result = engine.execute_powql("User").unwrap();
9018 if let QueryResult::Rows { columns, .. } = result {
9019 assert!(
9020 !columns.contains(&"country".to_string()),
9021 "failed alter must not mutate the schema"
9022 );
9023 }
9024 }
9025
9026 #[test]
9027 fn test_alter_add_column_then_update_old_row() {
9028 let mut engine = test_engine();
9033 engine
9034 .execute_powql("alter User add column country: str")
9035 .unwrap();
9036 engine
9037 .execute_powql(r#"User filter .name = "Alice" update { country := "US" }"#)
9038 .unwrap();
9039
9040 let result = engine
9041 .execute_powql(r#"User filter .name = "Alice" { .name, .country }"#)
9042 .unwrap();
9043 match result {
9044 QueryResult::Rows { rows, .. } => {
9045 assert_eq!(rows.len(), 1);
9046 assert_eq!(rows[0][0], Value::Str("Alice".into()));
9047 assert_eq!(rows[0][1], Value::Str("US".into()));
9048 }
9049 other => panic!("expected rows, got {other:?}"),
9050 }
9051
9052 let result = engine.execute_powql("User").unwrap();
9054 match result {
9055 QueryResult::Rows { columns, rows } => {
9056 assert_eq!(rows.len(), 3);
9057 let country_idx = columns
9058 .iter()
9059 .position(|c| c == "country")
9060 .expect("country column");
9061 let empties = rows
9062 .iter()
9063 .filter(|r| r[country_idx] == Value::Empty)
9064 .count();
9065 assert_eq!(
9066 empties, 2,
9067 "two unchanged old rows must still read as Empty"
9068 );
9069 }
9070 other => panic!("expected rows, got {other:?}"),
9071 }
9072 }
9073
9074 #[test]
9075 fn test_alter_drop_column() {
9076 let mut engine = test_engine();
9077 engine
9078 .execute_powql("alter User drop column email")
9079 .unwrap();
9080 let result = engine.execute_powql("User { .name, .age }").unwrap();
9081 match result {
9082 QueryResult::Rows { columns, rows } => {
9083 assert_eq!(columns, vec!["name", "age"]);
9084 assert_eq!(rows.len(), 3);
9085 }
9086 other => panic!("expected rows, got {other:?}"),
9087 }
9088 }
9089
9090 #[test]
9091 fn test_drop_table() {
9092 let mut engine = test_engine();
9093 let result = engine.execute_powql("drop User").unwrap();
9094 match result {
9095 QueryResult::Executed { message } => {
9096 assert!(message.contains("User"));
9097 assert!(message.contains("dropped"));
9098 }
9099 other => panic!("expected Executed, got {other:?}"),
9100 }
9101 assert!(engine.execute_powql("User").is_err());
9103 }
9104
9105 #[test]
9106 fn test_drop_nonexistent_table_errors() {
9107 let mut engine = test_engine();
9108 assert!(engine.execute_powql("drop NonExistent").is_err());
9109 }
9110
9111 #[test]
9112 fn test_alter_add_duplicate_column_errors() {
9113 let mut engine = test_engine();
9114 assert!(engine.execute_powql("alter User add name: str").is_err());
9115 }
9116
9117 #[test]
9118 fn test_alter_drop_nonexistent_column_errors() {
9119 let mut engine = test_engine();
9120 assert!(engine
9121 .execute_powql("alter User drop column nonexistent")
9122 .is_err());
9123 }
9124
9125 #[test]
9126 fn test_alter_add_index_creates_index() {
9127 let mut engine = test_engine();
9128 let result = engine.execute_powql("alter User add index .email").unwrap();
9129 match result {
9130 QueryResult::Executed { message } => {
9131 assert!(message.contains("User.email"), "message: {message}");
9132 }
9133 other => panic!("expected Executed, got {other:?}"),
9134 }
9135 let result = engine
9137 .execute_powql(r#"User filter .email = "alice@ex.com" { .name }"#)
9138 .unwrap();
9139 match result {
9140 QueryResult::Rows { rows, .. } => {
9141 assert_eq!(rows.len(), 1);
9142 assert_eq!(rows[0][0], Value::Str("Alice".into()));
9143 }
9144 other => panic!("expected rows, got {other:?}"),
9145 }
9146 }
9147
9148 #[test]
9149 fn test_parse_rejects_trailing_tokens() {
9150 let mut engine = test_engine();
9154 assert!(engine.execute_powql("User create_index .email").is_err());
9155 assert!(engine.execute_powql("User add_column score: int").is_err());
9156 assert!(engine.execute_powql("User drop_column email").is_err());
9157 }
9158
9159 #[test]
9162 fn test_in_subquery_basic() {
9163 let mut engine = test_engine();
9164 engine
9166 .execute_powql("type VIP { required name: str }")
9167 .unwrap();
9168 engine
9169 .execute_powql(r#"insert VIP { name := "Alice" }"#)
9170 .unwrap();
9171 engine
9172 .execute_powql(r#"insert VIP { name := "Charlie" }"#)
9173 .unwrap();
9174
9175 let result = engine
9176 .execute_powql("User filter .name in (VIP { .name }) { .name, .age }")
9177 .unwrap();
9178 match result {
9179 QueryResult::Rows { rows, .. } => {
9180 assert_eq!(rows.len(), 2);
9181 let names: Vec<_> = rows.iter().map(|r| &r[0]).collect();
9182 assert!(names.contains(&&Value::Str("Alice".into())));
9183 assert!(names.contains(&&Value::Str("Charlie".into())));
9184 }
9185 _ => panic!("expected rows"),
9186 }
9187 }
9188
9189 #[test]
9190 fn test_not_in_subquery() {
9191 let mut engine = test_engine();
9192 engine
9193 .execute_powql("type VIP { required name: str }")
9194 .unwrap();
9195 engine
9196 .execute_powql(r#"insert VIP { name := "Alice" }"#)
9197 .unwrap();
9198 engine
9199 .execute_powql(r#"insert VIP { name := "Charlie" }"#)
9200 .unwrap();
9201
9202 let result = engine
9203 .execute_powql("User filter .name not in (VIP { .name }) { .name }")
9204 .unwrap();
9205 match result {
9206 QueryResult::Rows { rows, .. } => {
9207 assert_eq!(rows.len(), 1);
9208 assert_eq!(rows[0][0], Value::Str("Bob".into()));
9209 }
9210 _ => panic!("expected rows"),
9211 }
9212 }
9213
9214 #[test]
9215 fn test_in_subquery_with_filter() {
9216 let mut engine = test_engine();
9217 engine
9218 .execute_powql("type Score { required name: str, required points: int }")
9219 .unwrap();
9220 engine
9221 .execute_powql(r#"insert Score { name := "Alice", points := 100 }"#)
9222 .unwrap();
9223 engine
9224 .execute_powql(r#"insert Score { name := "Bob", points := 50 }"#)
9225 .unwrap();
9226 engine
9227 .execute_powql(r#"insert Score { name := "Charlie", points := 80 }"#)
9228 .unwrap();
9229
9230 let result = engine
9232 .execute_powql("User filter .name in (Score filter .points > 70 { .name }) { .name }")
9233 .unwrap();
9234 match result {
9235 QueryResult::Rows { rows, .. } => {
9236 assert_eq!(rows.len(), 2);
9237 let names: Vec<_> = rows.iter().map(|r| &r[0]).collect();
9238 assert!(names.contains(&&Value::Str("Alice".into())));
9239 assert!(names.contains(&&Value::Str("Charlie".into())));
9240 }
9241 _ => panic!("expected rows"),
9242 }
9243 }
9244
9245 #[test]
9248 fn test_exists_subquery_uncorrelated_true() {
9249 let mut engine = test_engine();
9250 engine
9253 .execute_powql("type VIP { required name: str }")
9254 .unwrap();
9255 engine
9256 .execute_powql(r#"insert VIP { name := "Alice" }"#)
9257 .unwrap();
9258
9259 let result = engine
9260 .execute_powql("User filter exists (VIP) { .name }")
9261 .unwrap();
9262 match result {
9263 QueryResult::Rows { rows, .. } => {
9264 assert_eq!(rows.len(), 3, "all users should pass when EXISTS is true");
9265 }
9266 _ => panic!("expected rows"),
9267 }
9268 }
9269
9270 #[test]
9271 fn test_exists_subquery_uncorrelated_false() {
9272 let mut engine = test_engine();
9273 engine
9275 .execute_powql("type VIP { required name: str }")
9276 .unwrap();
9277
9278 let result = engine
9279 .execute_powql("User filter exists (VIP) { .name }")
9280 .unwrap();
9281 match result {
9282 QueryResult::Rows { rows, .. } => {
9283 assert_eq!(rows.len(), 0, "no rows should pass when EXISTS is false");
9284 }
9285 _ => panic!("expected rows"),
9286 }
9287 }
9288
9289 #[test]
9290 fn test_not_exists_subquery() {
9291 let mut engine = test_engine();
9292 engine
9294 .execute_powql("type VIP { required name: str }")
9295 .unwrap();
9296
9297 let result = engine
9298 .execute_powql("User filter not exists (VIP) { .name }")
9299 .unwrap();
9300 match result {
9301 QueryResult::Rows { rows, .. } => {
9302 assert_eq!(rows.len(), 3);
9303 }
9304 _ => panic!("expected rows"),
9305 }
9306
9307 engine
9309 .execute_powql(r#"insert VIP { name := "Alice" }"#)
9310 .unwrap();
9311 let result = engine
9312 .execute_powql("User filter not exists (VIP) { .name }")
9313 .unwrap();
9314 match result {
9315 QueryResult::Rows { rows, .. } => {
9316 assert_eq!(rows.len(), 0);
9317 }
9318 _ => panic!("expected rows"),
9319 }
9320 }
9321
9322 #[test]
9323 fn test_exists_subquery_with_inner_filter() {
9324 let mut engine = test_engine();
9325 engine
9328 .execute_powql("type Score { required name: str, required points: int }")
9329 .unwrap();
9330 engine
9331 .execute_powql(r#"insert Score { name := "Alice", points := 100 }"#)
9332 .unwrap();
9333
9334 let result = engine
9336 .execute_powql("User filter exists (Score filter .points > 50) { .name }")
9337 .unwrap();
9338 match result {
9339 QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 3),
9340 _ => panic!("expected rows"),
9341 }
9342 }
9343
9344 #[test]
9345 fn test_exists_subquery_with_inner_filter_no_match() {
9346 let mut engine = test_engine();
9349 engine
9350 .execute_powql("type Score { required name: str, required points: int }")
9351 .unwrap();
9352 engine
9353 .execute_powql(r#"insert Score { name := "Alice", points := 100 }"#)
9354 .unwrap();
9355
9356 let result = engine
9358 .execute_powql("User filter exists (Score filter .points > 1000) { .name }")
9359 .unwrap();
9360 match result {
9361 QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 0),
9362 _ => panic!("expected rows"),
9363 }
9364 }
9365
9366 #[test]
9369 fn test_create_materialized_view() {
9370 let mut engine = test_engine();
9371 let result = engine
9372 .execute_powql(r#"materialize OldUsers as User filter .age > 28"#)
9373 .unwrap();
9374 match result {
9375 QueryResult::Executed { message } => {
9376 assert!(message.contains("OldUsers"));
9377 }
9378 _ => panic!("expected Executed"),
9379 }
9380 let result = engine.execute_powql("OldUsers").unwrap();
9382 match result {
9383 QueryResult::Rows { rows, .. } => {
9384 assert_eq!(rows.len(), 2); }
9386 _ => panic!("expected rows"),
9387 }
9388 }
9389
9390 #[test]
9391 fn test_view_auto_refresh_on_insert() {
9392 let mut engine = test_engine();
9393 engine
9394 .execute_powql(r#"materialize OldUsers as User filter .age > 28"#)
9395 .unwrap();
9396 engine
9398 .execute_powql(r#"insert User { name := "Dave", email := "dave@ex.com", age := 40 }"#)
9399 .unwrap();
9400 let result = engine.execute_powql("OldUsers").unwrap();
9402 match result {
9403 QueryResult::Rows { rows, .. } => {
9404 assert_eq!(rows.len(), 3); }
9406 _ => panic!("expected rows"),
9407 }
9408 }
9409
9410 #[test]
9411 fn test_view_auto_refresh_on_delete() {
9412 let mut engine = test_engine();
9413 engine
9414 .execute_powql(r#"materialize OldUsers as User filter .age > 28"#)
9415 .unwrap();
9416 engine
9418 .execute_powql(r#"User filter .name = "Alice" delete"#)
9419 .unwrap();
9420 let result = engine.execute_powql("OldUsers").unwrap();
9422 match result {
9423 QueryResult::Rows { rows, .. } => {
9424 assert_eq!(rows.len(), 1);
9425 }
9426 _ => panic!("expected rows"),
9427 }
9428 }
9429
9430 #[test]
9431 fn test_view_auto_refresh_on_update() {
9432 let mut engine = test_engine();
9433 engine
9434 .execute_powql(r#"materialize OldUsers as User filter .age > 28"#)
9435 .unwrap();
9436 engine
9438 .execute_powql(r#"User filter .name = "Bob" update { age := 50 }"#)
9439 .unwrap();
9440 let result = engine.execute_powql("OldUsers").unwrap();
9441 match result {
9442 QueryResult::Rows { rows, .. } => {
9443 assert_eq!(rows.len(), 3); }
9445 _ => panic!("expected rows"),
9446 }
9447 }
9448
9449 #[test]
9450 fn test_explicit_refresh() {
9451 let mut engine = test_engine();
9452 engine
9453 .execute_powql(r#"materialize OldUsers as User filter .age > 28"#)
9454 .unwrap();
9455 engine
9456 .execute_powql(r#"insert User { name := "Eve", email := "eve@ex.com", age := 55 }"#)
9457 .unwrap();
9458 let result = engine.execute_powql("refresh OldUsers").unwrap();
9460 match result {
9461 QueryResult::Executed { message } => {
9462 assert!(message.contains("refreshed"));
9463 }
9464 _ => panic!("expected Executed"),
9465 }
9466 let result = engine.execute_powql("OldUsers").unwrap();
9468 match result {
9469 QueryResult::Rows { rows, .. } => {
9470 assert_eq!(rows.len(), 3);
9471 }
9472 _ => panic!("expected rows"),
9473 }
9474 }
9475
9476 #[test]
9477 fn test_drop_view() {
9478 let mut engine = test_engine();
9479 engine
9480 .execute_powql(r#"materialize OldUsers as User filter .age > 28"#)
9481 .unwrap();
9482 let result = engine.execute_powql("drop view OldUsers").unwrap();
9483 match result {
9484 QueryResult::Executed { message } => {
9485 assert!(message.contains("dropped"));
9486 }
9487 _ => panic!("expected Executed"),
9488 }
9489 let err = engine.execute_powql("OldUsers").unwrap_err();
9491 assert!(err.contains("not found"));
9492 }
9493
9494 #[test]
9495 fn test_view_with_projection() {
9496 let mut engine = test_engine();
9497 engine
9498 .execute_powql(r#"materialize UserNames as User { .name }"#)
9499 .unwrap();
9500 let result = engine.execute_powql("UserNames").unwrap();
9501 match result {
9502 QueryResult::Rows { columns, rows } => {
9503 assert_eq!(columns, vec!["name".to_string()]);
9504 assert_eq!(rows.len(), 3);
9505 }
9506 _ => panic!("expected rows"),
9507 }
9508 }
9509
9510 #[test]
9511 fn test_view_no_stale_reads() {
9512 let mut engine = test_engine();
9513 engine
9514 .execute_powql(r#"materialize AllUsers as User"#)
9515 .unwrap();
9516 let result = engine.execute_powql("AllUsers").unwrap();
9518 match &result {
9519 QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 3),
9520 _ => panic!("expected rows"),
9521 }
9522 engine
9524 .execute_powql(r#"insert User { name := "D", email := "d@ex.com", age := 1 }"#)
9525 .unwrap();
9526 engine
9527 .execute_powql(r#"insert User { name := "E", email := "e@ex.com", age := 2 }"#)
9528 .unwrap();
9529 let result = engine.execute_powql("AllUsers").unwrap();
9531 match result {
9532 QueryResult::Rows { rows, .. } => assert_eq!(rows.len(), 5),
9533 _ => panic!("expected rows"),
9534 }
9535 }
9536
9537 #[test]
9538 fn test_duplicate_view_creation_fails() {
9539 let mut engine = test_engine();
9540 engine.execute_powql(r#"materialize V as User"#).unwrap();
9541 let err = engine
9542 .execute_powql(r#"materialize V as User"#)
9543 .unwrap_err();
9544 assert!(err.contains("already exists"));
9545 }
9546
9547 #[test]
9548 fn test_drop_nonexistent_view_fails() {
9549 let mut engine = test_engine();
9550 let err = engine.execute_powql("drop view NoSuchView").unwrap_err();
9551 assert!(err.contains("not found"));
9552 }
9553
9554 #[test]
9557 fn test_union_deduplicates() {
9558 let mut engine = test_engine();
9559 engine.execute_powql("type A { name: str }").unwrap();
9560 engine.execute_powql("type B { name: str }").unwrap();
9561 engine
9562 .execute_powql(r#"insert A { name := "alice" }"#)
9563 .unwrap();
9564 engine
9565 .execute_powql(r#"insert A { name := "bob" }"#)
9566 .unwrap();
9567 engine
9568 .execute_powql(r#"insert B { name := "bob" }"#)
9569 .unwrap();
9570 engine
9571 .execute_powql(r#"insert B { name := "carol" }"#)
9572 .unwrap();
9573 let result = engine.execute_powql("A union B").unwrap();
9574 let rows = match result {
9575 QueryResult::Rows { rows, .. } => rows,
9576 _ => panic!(),
9577 };
9578 assert_eq!(rows.len(), 3);
9580 }
9581
9582 #[test]
9583 fn test_union_all_keeps_duplicates() {
9584 let mut engine = test_engine();
9585 engine.execute_powql("type X { val: int }").unwrap();
9586 engine.execute_powql("type Y { val: int }").unwrap();
9587 engine.execute_powql("insert X { val := 1 }").unwrap();
9588 engine.execute_powql("insert X { val := 2 }").unwrap();
9589 engine.execute_powql("insert Y { val := 2 }").unwrap();
9590 engine.execute_powql("insert Y { val := 3 }").unwrap();
9591 let result = engine.execute_powql("X union all Y").unwrap();
9592 let rows = match result {
9593 QueryResult::Rows { rows, .. } => rows,
9594 _ => panic!(),
9595 };
9596 assert_eq!(rows.len(), 4);
9598 }
9599
9600 #[test]
9601 fn test_union_with_filters() {
9602 let mut engine = test_engine();
9603 engine
9604 .execute_powql("type Emp { name: str, dept: str }")
9605 .unwrap();
9606 engine
9607 .execute_powql(r#"insert Emp { name := "alice", dept := "eng" }"#)
9608 .unwrap();
9609 engine
9610 .execute_powql(r#"insert Emp { name := "bob", dept := "sales" }"#)
9611 .unwrap();
9612 engine
9613 .execute_powql(r#"insert Emp { name := "carol", dept := "eng" }"#)
9614 .unwrap();
9615 let result = engine
9616 .execute_powql(r#"Emp filter .dept = "eng" union Emp filter .dept = "sales""#)
9617 .unwrap();
9618 let rows = match result {
9619 QueryResult::Rows { rows, .. } => rows,
9620 _ => panic!(),
9621 };
9622 assert_eq!(rows.len(), 3);
9623 }
9624
9625 #[test]
9626 fn test_union_chain_three_tables() {
9627 let mut engine = test_engine();
9628 engine.execute_powql("type T1 { v: int }").unwrap();
9629 engine.execute_powql("type T2 { v: int }").unwrap();
9630 engine.execute_powql("type T3 { v: int }").unwrap();
9631 engine.execute_powql("insert T1 { v := 1 }").unwrap();
9632 engine.execute_powql("insert T2 { v := 2 }").unwrap();
9633 engine.execute_powql("insert T3 { v := 3 }").unwrap();
9634 let result = engine.execute_powql("T1 union T2 union T3").unwrap();
9635 let rows = match result {
9636 QueryResult::Rows { rows, .. } => rows,
9637 _ => panic!(),
9638 };
9639 assert_eq!(rows.len(), 3);
9640 }
9641
9642 #[test]
9643 fn test_union_uses_left_side_columns() {
9644 let mut engine = test_engine();
9645 engine.execute_powql("type L { name: str }").unwrap();
9646 engine.execute_powql("type R { name: str }").unwrap();
9647 engine.execute_powql(r#"insert L { name := "a" }"#).unwrap();
9648 engine.execute_powql(r#"insert R { name := "b" }"#).unwrap();
9649 let result = engine.execute_powql("L union R").unwrap();
9650 match result {
9651 QueryResult::Rows { columns, rows } => {
9652 assert_eq!(columns, vec!["name".to_string()]);
9653 assert_eq!(rows.len(), 2);
9654 }
9655 _ => panic!("expected rows"),
9656 }
9657 }
9658
9659 #[test]
9662 fn test_count_distinct_standalone() {
9663 let mut engine = test_engine();
9664 engine.execute_powql("type Color { name: str }").unwrap();
9665 engine
9666 .execute_powql(r#"insert Color { name := "red" }"#)
9667 .unwrap();
9668 engine
9669 .execute_powql(r#"insert Color { name := "blue" }"#)
9670 .unwrap();
9671 engine
9672 .execute_powql(r#"insert Color { name := "red" }"#)
9673 .unwrap();
9674 engine
9675 .execute_powql(r#"insert Color { name := "green" }"#)
9676 .unwrap();
9677 let result = engine
9678 .execute_powql("count(distinct Color { .name })")
9679 .unwrap();
9680 match result {
9681 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 3), _ => panic!("expected scalar int"),
9683 }
9684 }
9685
9686 #[test]
9687 fn test_count_distinct_in_group_by() {
9688 let mut engine = test_engine();
9689 engine
9690 .execute_powql("type Sale { dept: str, item: str }")
9691 .unwrap();
9692 engine
9693 .execute_powql(r#"insert Sale { dept := "eng", item := "laptop" }"#)
9694 .unwrap();
9695 engine
9696 .execute_powql(r#"insert Sale { dept := "eng", item := "laptop" }"#)
9697 .unwrap();
9698 engine
9699 .execute_powql(r#"insert Sale { dept := "eng", item := "monitor" }"#)
9700 .unwrap();
9701 engine
9702 .execute_powql(r#"insert Sale { dept := "sales", item := "phone" }"#)
9703 .unwrap();
9704 let result = engine
9705 .execute_powql("Sale group .dept { .dept, count(distinct .item) }")
9706 .unwrap();
9707 let rows = match result {
9708 QueryResult::Rows { rows, .. } => rows,
9709 _ => panic!(),
9710 };
9711 let eng_row = rows
9713 .iter()
9714 .find(|r| r[0] == Value::Str("eng".into()))
9715 .unwrap();
9716 let sales_row = rows
9717 .iter()
9718 .find(|r| r[0] == Value::Str("sales".into()))
9719 .unwrap();
9720 assert_eq!(eng_row[1], Value::Int(2));
9721 assert_eq!(sales_row[1], Value::Int(1));
9722 }
9723
9724 #[test]
9725 fn test_count_distinct_with_filter() {
9726 let mut engine = test_engine();
9727 engine
9729 .execute_powql(r#"insert User { name := "Dave", email := "d@e.com", age := 30 }"#)
9730 .unwrap();
9731 let result = engine
9732 .execute_powql("count(distinct User { .age })")
9733 .unwrap();
9734 match result {
9735 QueryResult::Scalar(Value::Int(n)) => {
9736 assert_eq!(n, 3);
9738 }
9739 _ => panic!("expected scalar int"),
9740 }
9741 }
9742
9743 #[test]
9746 fn test_update_with_arithmetic_expression() {
9747 let mut engine = test_engine();
9748 engine
9750 .execute_powql(r#"User filter .name = "Alice" update { age := .age + 5 }"#)
9751 .unwrap();
9752 let result = engine
9753 .execute_powql(r#"User filter .name = "Alice""#)
9754 .unwrap();
9755 let rows = match result {
9756 QueryResult::Rows { rows, .. } => rows,
9757 _ => panic!(),
9758 };
9759 assert_eq!(rows[0][2], Value::Int(35)); }
9761
9762 #[test]
9763 fn test_update_with_multiply_expression() {
9764 let mut engine = test_engine();
9765 engine
9767 .execute_powql("User update { age := .age * 2 }")
9768 .unwrap();
9769 let result = engine.execute_powql("User").unwrap();
9770 let rows = match result {
9771 QueryResult::Rows { rows, .. } => rows,
9772 _ => panic!(),
9773 };
9774 let ages: Vec<i64> = rows
9775 .iter()
9776 .map(|r| match &r[2] {
9777 Value::Int(v) => *v,
9778 _ => 0,
9779 })
9780 .collect();
9781 assert!(ages.contains(&60)); assert!(ages.contains(&50)); assert!(ages.contains(&70)); }
9785
9786 #[test]
9787 fn test_update_expression_with_filter() {
9788 let mut engine = test_engine();
9789 engine
9791 .execute_powql("User filter .age > 28 update { age := .age + 1 }")
9792 .unwrap();
9793 let result = engine
9794 .execute_powql(r#"User filter .name = "Alice""#)
9795 .unwrap();
9796 let rows = match result {
9797 QueryResult::Rows { rows, .. } => rows,
9798 _ => panic!(),
9799 };
9800 assert_eq!(rows[0][2], Value::Int(31)); let result = engine
9802 .execute_powql(r#"User filter .name = "Bob""#)
9803 .unwrap();
9804 let rows = match result {
9805 QueryResult::Rows { rows, .. } => rows,
9806 _ => panic!(),
9807 };
9808 assert_eq!(rows[0][2], Value::Int(25)); }
9810
9811 #[test]
9812 fn test_update_literal_still_uses_fast_path() {
9813 let mut engine = test_engine();
9815 engine
9816 .execute_powql(r#"User filter .name = "Alice" update { age := 99 }"#)
9817 .unwrap();
9818 let result = engine
9819 .execute_powql(r#"User filter .name = "Alice""#)
9820 .unwrap();
9821 let rows = match result {
9822 QueryResult::Rows { rows, .. } => rows,
9823 _ => panic!(),
9824 };
9825 assert_eq!(rows[0][2], Value::Int(99));
9826 }
9827
9828 #[test]
9831 fn test_group_by_count_star() {
9832 let mut engine = test_engine();
9833 engine
9836 .execute_powql(r#"insert User { name := "Dave", email := "d@e.com", age := 30 }"#)
9837 .unwrap();
9838 let result = engine
9839 .execute_powql("User group .age { .age, count(*) }")
9840 .unwrap();
9841 let rows = match result {
9842 QueryResult::Rows { rows, .. } => rows,
9843 _ => panic!(),
9844 };
9845 let age30 = rows.iter().find(|r| r[0] == Value::Int(30)).unwrap();
9846 assert_eq!(age30[1], Value::Int(2)); let age25 = rows.iter().find(|r| r[0] == Value::Int(25)).unwrap();
9848 assert_eq!(age25[1], Value::Int(1)); }
9850
9851 #[test]
9852 fn test_group_by_count_star_with_having() {
9853 let mut engine = test_engine();
9854 engine
9855 .execute_powql(r#"insert User { name := "Dave", email := "d@e.com", age := 30 }"#)
9856 .unwrap();
9857 let result = engine
9858 .execute_powql("User group .age having count(*) > 1 { .age, count(*) }")
9859 .unwrap();
9860 let rows = match result {
9861 QueryResult::Rows { rows, .. } => rows,
9862 _ => panic!(),
9863 };
9864 assert_eq!(rows.len(), 1);
9865 assert_eq!(rows[0][0], Value::Int(30)); }
9867
9868 fn product_mix_engine() -> Engine {
9873 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
9874 let dir =
9875 std::env::temp_dir().join(format!("powdb_product_mix_{}_{}", std::process::id(), id));
9876 let mut engine = Engine::new(&dir).unwrap();
9877 engine
9878 .execute_powql(
9879 "type Product { required name: str, required price: float, required stock: int }",
9880 )
9881 .unwrap();
9882 engine
9883 .execute_powql(r#"insert Product { name := "Apple", price := 1.5, stock := 10 }"#)
9884 .unwrap();
9885 engine
9886 .execute_powql(r#"insert Product { name := "Banana", price := 0.25, stock := 4 }"#)
9887 .unwrap();
9888 engine
9889 .execute_powql(r#"insert Product { name := "Cherry", price := 2.0, stock := 3 }"#)
9890 .unwrap();
9891 engine
9892 }
9893
9894 fn as_float(v: &Value) -> f64 {
9895 match v {
9896 Value::Float(f) => *f,
9897 other => panic!("expected Float, got {other:?}"),
9898 }
9899 }
9900
9901 #[test]
9902 fn test_arith_float_times_int() {
9903 let mut engine = product_mix_engine();
9904 let result = engine
9905 .execute_powql("Product { .name, total: .price * .stock }")
9906 .unwrap();
9907 match result {
9908 QueryResult::Rows { columns, rows } => {
9909 assert_eq!(columns, vec!["name", "total"]);
9910 let mut by_name: std::collections::HashMap<String, f64> =
9911 std::collections::HashMap::new();
9912 for row in &rows {
9913 let name = match &row[0] {
9914 Value::Str(s) => s.clone(),
9915 _ => panic!(),
9916 };
9917 by_name.insert(name, as_float(&row[1]));
9918 }
9919 assert!((by_name["Apple"] - 15.0).abs() < 1e-9);
9920 assert!((by_name["Banana"] - 1.0).abs() < 1e-9);
9921 assert!((by_name["Cherry"] - 6.0).abs() < 1e-9);
9922 }
9923 _ => panic!("expected rows"),
9924 }
9925 }
9926
9927 #[test]
9928 fn test_arith_int_plus_float() {
9929 let mut engine = product_mix_engine();
9930 let result = engine
9932 .execute_powql("Product { .name, bumped: .stock + .price }")
9933 .unwrap();
9934 match result {
9935 QueryResult::Rows { rows, .. } => {
9936 let mut by_name: std::collections::HashMap<String, f64> =
9937 std::collections::HashMap::new();
9938 for row in &rows {
9939 let name = match &row[0] {
9940 Value::Str(s) => s.clone(),
9941 _ => panic!(),
9942 };
9943 by_name.insert(name, as_float(&row[1]));
9944 }
9945 assert!((by_name["Apple"] - 11.5).abs() < 1e-9);
9946 assert!((by_name["Banana"] - 4.25).abs() < 1e-9);
9947 assert!((by_name["Cherry"] - 5.0).abs() < 1e-9);
9948 }
9949 _ => panic!("expected rows"),
9950 }
9951 }
9952
9953 #[test]
9954 fn test_arith_float_div_int() {
9955 let mut engine = product_mix_engine();
9956 let result = engine
9957 .execute_powql("Product { .name, unit: .price / .stock }")
9958 .unwrap();
9959 match result {
9960 QueryResult::Rows { rows, .. } => {
9961 let mut by_name: std::collections::HashMap<String, f64> =
9962 std::collections::HashMap::new();
9963 for row in &rows {
9964 let name = match &row[0] {
9965 Value::Str(s) => s.clone(),
9966 _ => panic!(),
9967 };
9968 by_name.insert(name, as_float(&row[1]));
9969 }
9970 assert!((by_name["Apple"] - 0.15).abs() < 1e-9);
9971 assert!((by_name["Banana"] - 0.0625).abs() < 1e-9);
9972 assert!((by_name["Cherry"] - (2.0 / 3.0)).abs() < 1e-9);
9973 }
9974 _ => panic!("expected rows"),
9975 }
9976 }
9977
9978 #[test]
9979 fn test_arith_int_minus_float() {
9980 let mut engine = product_mix_engine();
9981 let result = engine
9982 .execute_powql("Product { .name, delta: .stock - .price }")
9983 .unwrap();
9984 match result {
9985 QueryResult::Rows { rows, .. } => {
9986 let mut by_name: std::collections::HashMap<String, f64> =
9987 std::collections::HashMap::new();
9988 for row in &rows {
9989 let name = match &row[0] {
9990 Value::Str(s) => s.clone(),
9991 _ => panic!(),
9992 };
9993 by_name.insert(name, as_float(&row[1]));
9994 }
9995 assert!((by_name["Apple"] - 8.5).abs() < 1e-9);
9996 assert!((by_name["Banana"] - 3.75).abs() < 1e-9);
9997 assert!((by_name["Cherry"] - 1.0).abs() < 1e-9);
9998 }
9999 _ => panic!("expected rows"),
10000 }
10001 }
10002
10003 #[test]
10008 fn test_sum_float_scalar() {
10009 let mut engine = product_mix_engine();
10010 let result = engine.execute_powql("sum(Product { .price })").unwrap();
10011 match result {
10012 QueryResult::Scalar(v) => {
10013 assert!(
10015 (as_float(&v) - 3.75).abs() < 1e-9,
10016 "expected 3.75, got {v:?}"
10017 );
10018 }
10019 _ => panic!("expected scalar result, got {result:?}"),
10020 }
10021 }
10022
10023 #[test]
10027 fn test_sum_float_group_by() {
10028 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
10029 let dir =
10030 std::env::temp_dir().join(format!("powdb_sum_float_gb_{}_{}", std::process::id(), id));
10031 let mut engine = Engine::new(&dir).unwrap();
10032 engine
10033 .execute_powql("type Sale { required region: str, required amount: float }")
10034 .unwrap();
10035 engine
10036 .execute_powql(r#"insert Sale { region := "E", amount := 1.5 }"#)
10037 .unwrap();
10038 engine
10039 .execute_powql(r#"insert Sale { region := "E", amount := 2.25 }"#)
10040 .unwrap();
10041 engine
10042 .execute_powql(r#"insert Sale { region := "W", amount := 4.0 }"#)
10043 .unwrap();
10044 engine
10045 .execute_powql(r#"insert Sale { region := "W", amount := 0.5 }"#)
10046 .unwrap();
10047
10048 let result = engine
10049 .execute_powql("Sale group .region { .region, total: sum(.amount) }")
10050 .unwrap();
10051 match result {
10052 QueryResult::Rows { columns, rows } => {
10053 assert_eq!(columns, vec!["region", "total"]);
10054 let mut by_region: std::collections::HashMap<String, f64> =
10055 std::collections::HashMap::new();
10056 for row in &rows {
10057 let region = match &row[0] {
10058 Value::Str(s) => s.clone(),
10059 _ => panic!(),
10060 };
10061 by_region.insert(region, as_float(&row[1]));
10062 }
10063 assert!(
10064 (by_region["E"] - 3.75).abs() < 1e-9,
10065 "E: {:?}",
10066 by_region.get("E")
10067 );
10068 assert!(
10069 (by_region["W"] - 4.5).abs() < 1e-9,
10070 "W: {:?}",
10071 by_region.get("W")
10072 );
10073 }
10074 _ => panic!("expected rows, got {result:?}"),
10075 }
10076 }
10077
10078 fn float_fast_engine() -> Engine {
10097 let id = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
10098 let dir =
10099 std::env::temp_dir().join(format!("powdb_float_fast_{}_{}", std::process::id(), id));
10100 let mut engine = Engine::new(&dir).unwrap();
10101 engine
10102 .execute_powql("type Price { required name: str, price: float, required qty: int }")
10103 .unwrap();
10104 let rows = [
10107 ("a", "price := 1.5", "qty := 1"),
10108 ("b", "price := 0.25", "qty := 2"),
10109 ("c", "price := 2.0", "qty := 3"),
10110 ("d", "price := -3.5", "qty := 4"),
10111 ("e", "price := 10.0", "qty := 5"),
10112 ("f", "price := 0.5", "qty := 6"),
10113 ("g", "price := 100.0", "qty := 7"),
10114 ("h", "price := -0.0", "qty := 8"),
10115 ];
10116 for (name, price, qty) in rows {
10117 engine
10118 .execute_powql(&format!(
10119 r#"insert Price {{ name := "{name}", {price}, {qty} }}"#
10120 ))
10121 .unwrap();
10122 }
10123 engine
10124 }
10125
10126 #[test]
10127 fn test_d10_agg_sum_float_fast_path() {
10128 let mut engine = float_fast_engine();
10129 let result = engine.execute_powql("sum(Price { .price })").unwrap();
10130 match result {
10132 QueryResult::Scalar(v) => {
10133 assert!((as_float(&v) - 110.75).abs() < 1e-9, "got {v:?}");
10134 }
10135 _ => panic!("expected scalar, got {result:?}"),
10136 }
10137 }
10138
10139 #[test]
10140 fn test_d10_agg_avg_float_fast_path() {
10141 let mut engine = float_fast_engine();
10142 let result = engine.execute_powql("avg(Price { .price })").unwrap();
10143 match result {
10145 QueryResult::Scalar(v) => {
10146 assert!((as_float(&v) - 13.84375).abs() < 1e-9, "got {v:?}");
10147 }
10148 _ => panic!("expected scalar, got {result:?}"),
10149 }
10150 }
10151
10152 #[test]
10153 fn test_d10_agg_min_float_fast_path() {
10154 let mut engine = float_fast_engine();
10155 let result = engine.execute_powql("min(Price { .price })").unwrap();
10156 match result {
10157 QueryResult::Scalar(v) => {
10158 assert!((as_float(&v) - (-3.5)).abs() < 1e-9, "got {v:?}");
10159 }
10160 _ => panic!("expected scalar, got {result:?}"),
10161 }
10162 }
10163
10164 #[test]
10165 fn test_d10_agg_max_float_fast_path() {
10166 let mut engine = float_fast_engine();
10167 let result = engine.execute_powql("max(Price { .price })").unwrap();
10168 match result {
10169 QueryResult::Scalar(v) => {
10170 assert!((as_float(&v) - 100.0).abs() < 1e-9, "got {v:?}");
10171 }
10172 _ => panic!("expected scalar, got {result:?}"),
10173 }
10174 }
10175
10176 #[test]
10177 fn test_d10_agg_count_distinct_float_fast_path() {
10178 let mut engine = float_fast_engine();
10179 let result = engine
10180 .execute_powql("count(distinct Price { .price })")
10181 .unwrap();
10182 match result {
10186 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 8, "got {n}"),
10187 _ => panic!("expected scalar int, got {result:?}"),
10188 }
10189 }
10190
10191 #[test]
10192 fn test_d10_agg_float_with_compiled_where() {
10193 let mut engine = float_fast_engine();
10196 let result = engine
10197 .execute_powql("sum(Price filter .price > 1.0 { .price })")
10198 .unwrap();
10199 match result {
10201 QueryResult::Scalar(v) => {
10202 assert!((as_float(&v) - 113.5).abs() < 1e-9, "got {v:?}");
10203 }
10204 _ => panic!("expected scalar, got {result:?}"),
10205 }
10206 }
10207
10208 #[test]
10209 fn test_d10_agg_float_with_compiled_where_int_literal() {
10210 let mut engine = float_fast_engine();
10214 let result = engine
10215 .execute_powql("sum(Price filter .price > 1 { .price })")
10216 .unwrap();
10217 match result {
10218 QueryResult::Scalar(v) => {
10219 assert!((as_float(&v) - 113.5).abs() < 1e-9, "got {v:?}");
10220 }
10221 _ => panic!("expected scalar, got {result:?}"),
10222 }
10223 }
10224
10225 #[test]
10226 fn test_d10_agg_float_with_reversed_literal() {
10227 let mut engine = float_fast_engine();
10230 let result = engine
10231 .execute_powql("count(Price filter 1.0 < .price { .price })")
10232 .unwrap();
10233 match result {
10235 QueryResult::Scalar(Value::Int(n)) => assert_eq!(n, 4, "got {n}"),
10236 _ => panic!("expected scalar int, got {result:?}"),
10237 }
10238 }
10239
10240 #[test]
10241 fn test_d10_sort_float_desc_limit_fast_path() {
10242 let mut engine = float_fast_engine();
10245 let result = engine
10246 .execute_powql("Price order .price desc limit 3 { .name, .price }")
10247 .unwrap();
10248 match result {
10249 QueryResult::Rows { columns, rows } => {
10250 assert_eq!(columns, vec!["name", "price"]);
10251 assert_eq!(rows.len(), 3);
10252 assert_eq!(rows[0][0], Value::Str("g".into())); assert!((as_float(&rows[0][1]) - 100.0).abs() < 1e-9);
10254 assert_eq!(rows[1][0], Value::Str("e".into())); assert!((as_float(&rows[1][1]) - 10.0).abs() < 1e-9);
10256 assert_eq!(rows[2][0], Value::Str("c".into())); assert!((as_float(&rows[2][1]) - 2.0).abs() < 1e-9);
10258 }
10259 _ => panic!("expected rows, got {result:?}"),
10260 }
10261 }
10262
10263 #[test]
10264 fn test_d10_sort_float_asc_limit_fast_path() {
10265 let mut engine = float_fast_engine();
10267 let result = engine
10268 .execute_powql("Price order .price limit 3 { .name, .price }")
10269 .unwrap();
10270 match result {
10271 QueryResult::Rows { rows, .. } => {
10272 assert_eq!(rows.len(), 3);
10273 assert_eq!(rows[0][0], Value::Str("d".into())); assert_eq!(rows[1][0], Value::Str("h".into())); assert_eq!(rows[2][0], Value::Str("b".into())); }
10278 _ => panic!("expected rows, got {result:?}"),
10279 }
10280 }
10281
10282 #[test]
10283 fn test_d10_sort_float_with_compiled_filter() {
10284 let mut engine = float_fast_engine();
10287 let result = engine
10288 .execute_powql("Price filter .price > 0.0 order .price desc limit 2 { .name }")
10289 .unwrap();
10290 match result {
10291 QueryResult::Rows { rows, .. } => {
10292 assert_eq!(rows.len(), 2);
10293 assert_eq!(rows[0][0], Value::Str("g".into())); assert_eq!(rows[1][0], Value::Str("e".into())); }
10296 _ => panic!("expected rows, got {result:?}"),
10297 }
10298 }
10299
10300 #[test]
10301 fn test_f64_sortable_transform_monotonic() {
10302 let samples: [f64; 11] = [
10306 f64::NEG_INFINITY,
10307 -1e100,
10308 -1.0,
10309 -f64::MIN_POSITIVE,
10310 -0.0,
10311 0.0,
10312 f64::MIN_POSITIVE,
10313 1.0,
10314 1e100,
10315 f64::INFINITY,
10316 f64::NAN, ];
10318 let mut sorted = samples;
10319 sorted.sort_by(|a, b| a.total_cmp(b));
10320
10321 let as_sortable: Vec<u64> = sorted
10322 .iter()
10323 .map(|f| f64_bits_to_sortable_u64(f.to_bits()))
10324 .collect();
10325
10326 for pair in as_sortable.windows(2) {
10329 assert!(
10330 pair[0] < pair[1],
10331 "sortable u64 not monotonic: {:#x} >= {:#x}",
10332 pair[0],
10333 pair[1]
10334 );
10335 }
10336 }
10337
10338 #[test]
10341 fn test_explain_simple_scan() {
10342 let mut engine = test_engine();
10343 let result = engine.execute_powql("explain User").unwrap();
10344 match result {
10345 QueryResult::Rows { columns, rows } => {
10346 assert_eq!(columns, vec!["plan"]);
10347 assert!(!rows.is_empty());
10348 assert!(matches!(&rows[0][0], Value::Str(s) if s.contains("SeqScan")));
10349 }
10350 _ => panic!("expected rows"),
10351 }
10352 }
10353
10354 #[test]
10355 fn test_explain_filter() {
10356 let mut engine = test_engine();
10357 let result = engine
10358 .execute_powql("explain User filter .age > 30")
10359 .unwrap();
10360 match result {
10361 QueryResult::Rows { rows, .. } => {
10362 let plan_text: String = rows
10363 .iter()
10364 .map(|r| match &r[0] {
10365 Value::Str(s) => s.as_str(),
10366 _ => "",
10367 })
10368 .collect::<Vec<_>>()
10369 .join("\n");
10370 assert!(
10371 plan_text.contains("Filter"),
10372 "plan should show Filter(SeqScan) after lowering unindexed RangeScan"
10373 );
10374 }
10375 _ => panic!("expected rows"),
10376 }
10377 }
10378
10379 #[test]
10380 fn test_explain_does_not_execute() {
10381 let mut engine = test_engine();
10382 let result = engine
10384 .execute_powql(r#"explain insert User { name := "Zara", age := 99 }"#)
10385 .unwrap();
10386 match result {
10387 QueryResult::Rows { rows, .. } => {
10388 let plan_text: String = rows
10389 .iter()
10390 .map(|r| match &r[0] {
10391 Value::Str(s) => s.as_str(),
10392 _ => "",
10393 })
10394 .collect::<Vec<_>>()
10395 .join("\n");
10396 assert!(plan_text.contains("Insert"));
10397 }
10398 _ => panic!("expected rows"),
10399 }
10400 let result = engine.execute_powql("User { .name }").unwrap();
10402 match result {
10403 QueryResult::Rows { rows, .. } => {
10404 assert_eq!(rows.len(), 3, "should still have original 3 users");
10405 }
10406 _ => panic!("expected rows"),
10407 }
10408 }
10409
10410 #[test]
10413 fn test_correlated_in_subquery() {
10414 let mut engine = test_engine();
10415 engine
10417 .execute_powql("type UserOrder { required user_name: str, required total: int }")
10418 .unwrap();
10419 engine
10420 .execute_powql(r#"insert UserOrder { user_name := "Alice", total := 100 }"#)
10421 .unwrap();
10422 engine
10423 .execute_powql(r#"insert UserOrder { user_name := "Alice", total := 200 }"#)
10424 .unwrap();
10425 engine
10426 .execute_powql(r#"insert UserOrder { user_name := "Bob", total := 50 }"#)
10427 .unwrap();
10428
10429 let result = engine.execute_powql(
10432 "User filter .name in (UserOrder filter .user_name = .name { .user_name }) { .name }"
10433 ).unwrap();
10434 match result {
10435 QueryResult::Rows { rows, .. } => {
10436 assert_eq!(rows.len(), 2, "Alice and Bob have orders");
10437 let names: Vec<_> = rows.iter().map(|r| &r[0]).collect();
10438 assert!(names.contains(&&Value::Str("Alice".into())));
10439 assert!(names.contains(&&Value::Str("Bob".into())));
10440 }
10441 _ => panic!("expected rows"),
10442 }
10443 }
10444
10445 #[test]
10446 fn test_correlated_exists_subquery() {
10447 let mut engine = test_engine();
10448 engine
10449 .execute_powql("type UserOrder { required user_name: str, required total: int }")
10450 .unwrap();
10451 engine
10452 .execute_powql(r#"insert UserOrder { user_name := "Alice", total := 100 }"#)
10453 .unwrap();
10454 engine
10455 .execute_powql(r#"insert UserOrder { user_name := "Bob", total := 50 }"#)
10456 .unwrap();
10457
10458 let result = engine
10461 .execute_powql("User filter exists (UserOrder filter .user_name = .name) { .name }")
10462 .unwrap();
10463 match result {
10464 QueryResult::Rows { rows, .. } => {
10465 assert_eq!(rows.len(), 2, "Alice and Bob have orders");
10466 let names: Vec<_> = rows.iter().map(|r| &r[0]).collect();
10467 assert!(names.contains(&&Value::Str("Alice".into())));
10468 assert!(names.contains(&&Value::Str("Bob".into())));
10469 }
10470 _ => panic!("expected rows"),
10471 }
10472 }
10473
10474 #[test]
10475 fn test_correlated_not_exists_subquery() {
10476 let mut engine = test_engine();
10477 engine
10478 .execute_powql("type UserOrder { required user_name: str, required total: int }")
10479 .unwrap();
10480 engine
10481 .execute_powql(r#"insert UserOrder { user_name := "Alice", total := 100 }"#)
10482 .unwrap();
10483
10484 let result = engine
10486 .execute_powql("User filter not exists (UserOrder filter .user_name = .name) { .name }")
10487 .unwrap();
10488 match result {
10489 QueryResult::Rows { rows, .. } => {
10490 assert_eq!(rows.len(), 2, "Bob and Charlie have no orders");
10491 let names: Vec<_> = rows.iter().map(|r| &r[0]).collect();
10492 assert!(names.contains(&&Value::Str("Bob".into())));
10493 assert!(names.contains(&&Value::Str("Charlie".into())));
10494 }
10495 _ => panic!("expected rows"),
10496 }
10497 }
10498
10499 #[test]
10502 fn test_cast_int_to_str() {
10503 let mut engine = test_engine();
10504 let result = engine
10505 .execute_powql(r#"User { s: cast(.age, "str") }"#)
10506 .unwrap();
10507 match result {
10508 QueryResult::Rows { rows, .. } => {
10509 assert_eq!(rows[0][0], Value::Str("30".into()));
10510 assert_eq!(rows[1][0], Value::Str("25".into()));
10511 }
10512 _ => panic!("expected rows"),
10513 }
10514 }
10515
10516 #[test]
10517 fn test_cast_str_to_int() {
10518 let mut engine = test_engine();
10519 engine
10520 .execute_powql(r#"type Numbers { required val: str }"#)
10521 .unwrap();
10522 engine
10523 .execute_powql(r#"insert Numbers { val := "42" }"#)
10524 .unwrap();
10525 let result = engine
10526 .execute_powql(r#"Numbers { n: cast(.val, "int") }"#)
10527 .unwrap();
10528 match result {
10529 QueryResult::Rows { rows, .. } => {
10530 assert_eq!(rows[0][0], Value::Int(42));
10531 }
10532 _ => panic!("expected rows"),
10533 }
10534 }
10535
10536 #[test]
10537 fn test_cast_float_to_int() {
10538 let mut engine = test_engine();
10539 engine
10540 .execute_powql("type Floats { required val: float }")
10541 .unwrap();
10542 engine
10543 .execute_powql("insert Floats { val := 3.7 }")
10544 .unwrap();
10545 let result = engine
10546 .execute_powql(r#"Floats { n: cast(.val, "int") }"#)
10547 .unwrap();
10548 match result {
10549 QueryResult::Rows { rows, .. } => {
10550 assert_eq!(rows[0][0], Value::Int(3));
10551 }
10552 _ => panic!("expected rows"),
10553 }
10554 }
10555
10556 #[test]
10557 fn test_cast_int_to_float() {
10558 let mut engine = test_engine();
10559 let result = engine
10560 .execute_powql(r#"User { f: cast(.age, "float") }"#)
10561 .unwrap();
10562 match result {
10563 QueryResult::Rows { rows, .. } => {
10564 assert_eq!(rows[0][0], Value::Float(30.0));
10565 }
10566 _ => panic!("expected rows"),
10567 }
10568 }
10569
10570 #[test]
10571 fn test_cast_int_to_bool() {
10572 let mut engine = test_engine();
10573 let result = engine
10574 .execute_powql(r#"User { b: cast(.age, "bool") }"#)
10575 .unwrap();
10576 match result {
10577 QueryResult::Rows { rows, .. } => {
10578 assert_eq!(rows[0][0], Value::Bool(true));
10580 }
10581 _ => panic!("expected rows"),
10582 }
10583 }
10584
10585 #[test]
10588 fn test_abs() {
10589 let mut engine = test_engine();
10590 engine
10591 .execute_powql("type Nums { required val: int }")
10592 .unwrap();
10593 engine.execute_powql("insert Nums { val := -42 }").unwrap();
10594 let result = engine.execute_powql("Nums { a: abs(.val) }").unwrap();
10595 match result {
10596 QueryResult::Rows { rows, .. } => {
10597 assert_eq!(rows[0][0], Value::Int(42));
10598 }
10599 _ => panic!("expected rows"),
10600 }
10601 }
10602
10603 #[test]
10604 fn test_round() {
10605 let mut engine = test_engine();
10606 engine
10607 .execute_powql("type Floats { required val: float }")
10608 .unwrap();
10609 engine
10610 .execute_powql("insert Floats { val := 7.56789 }")
10611 .unwrap();
10612 let result = engine
10613 .execute_powql("Floats { r: round(.val, 2) }")
10614 .unwrap();
10615 match result {
10616 QueryResult::Rows { rows, .. } => {
10617 assert_eq!(rows[0][0], Value::Float(7.57));
10618 }
10619 _ => panic!("expected rows"),
10620 }
10621 }
10622
10623 #[test]
10624 fn test_ceil_floor() {
10625 let mut engine = test_engine();
10626 engine
10627 .execute_powql("type Floats { required val: float }")
10628 .unwrap();
10629 engine
10630 .execute_powql("insert Floats { val := 3.2 }")
10631 .unwrap();
10632 let c = engine.execute_powql("Floats { c: ceil(.val) }").unwrap();
10633 let f = engine.execute_powql("Floats { f: floor(.val) }").unwrap();
10634 match (c, f) {
10635 (QueryResult::Rows { rows: cr, .. }, QueryResult::Rows { rows: fr, .. }) => {
10636 assert_eq!(cr[0][0], Value::Float(4.0));
10637 assert_eq!(fr[0][0], Value::Float(3.0));
10638 }
10639 _ => panic!("expected rows"),
10640 }
10641 }
10642
10643 #[test]
10644 fn test_sqrt() {
10645 let mut engine = test_engine();
10646 engine
10647 .execute_powql("type Nums { required val: int }")
10648 .unwrap();
10649 engine.execute_powql("insert Nums { val := 144 }").unwrap();
10650 let result = engine.execute_powql("Nums { s: sqrt(.val) }").unwrap();
10651 match result {
10652 QueryResult::Rows { rows, .. } => {
10653 assert_eq!(rows[0][0], Value::Float(12.0));
10654 }
10655 _ => panic!("expected rows"),
10656 }
10657 }
10658
10659 #[test]
10660 fn test_pow() {
10661 let mut engine = test_engine();
10662 engine
10663 .execute_powql("type Nums { required val: int }")
10664 .unwrap();
10665 engine.execute_powql("insert Nums { val := 3 }").unwrap();
10666 let result = engine.execute_powql("Nums { p: pow(.val, 4) }").unwrap();
10667 match result {
10668 QueryResult::Rows { rows, .. } => {
10669 assert_eq!(rows[0][0], Value::Int(81));
10670 }
10671 _ => panic!("expected rows"),
10672 }
10673 }
10674
10675 #[test]
10678 fn test_now_returns_datetime() {
10679 let mut engine = test_engine();
10680 engine
10681 .execute_powql("type Events { required name: str }")
10682 .unwrap();
10683 engine
10684 .execute_powql(r#"insert Events { name := "test" }"#)
10685 .unwrap();
10686 let result = engine.execute_powql("Events { ts: now() }").unwrap();
10687 match result {
10688 QueryResult::Rows { rows, .. } => match &rows[0][0] {
10689 Value::DateTime(m) => assert!(*m > 0, "now() should return positive timestamp"),
10690 other => panic!("expected DateTime, got {other:?}"),
10691 },
10692 _ => panic!("expected rows"),
10693 }
10694 }
10695
10696 #[test]
10697 fn test_extract_from_datetime() {
10698 let mut engine = test_engine();
10699 engine
10700 .execute_powql("type Events { required ts: datetime }")
10701 .unwrap();
10702 engine
10707 .execute_powql("insert Events { ts := 1705321845000000 }")
10708 .unwrap();
10709 let result = engine.execute_powql(r#"Events { y: extract("year", .ts), m: extract("month", .ts), d: extract("day", .ts), h: extract("hour", .ts) }"#).unwrap();
10710 match result {
10711 QueryResult::Rows { rows, .. } => {
10712 assert_eq!(rows[0][0], Value::Int(2024));
10713 assert_eq!(rows[0][1], Value::Int(1));
10714 assert_eq!(rows[0][2], Value::Int(15));
10715 assert_eq!(rows[0][3], Value::Int(12));
10716 }
10717 _ => panic!("expected rows"),
10718 }
10719 }
10720
10721 #[test]
10722 fn test_date_add() {
10723 let mut engine = test_engine();
10724 engine
10725 .execute_powql("type Events { required ts: datetime }")
10726 .unwrap();
10727 let base = 1705321845000000_i64; engine
10729 .execute_powql(&format!("insert Events {{ ts := {base} }}"))
10730 .unwrap();
10731 let result = engine
10732 .execute_powql(r#"Events { later: date_add(.ts, 2, "hours") }"#)
10733 .unwrap();
10734 match result {
10735 QueryResult::Rows { rows, .. } => {
10736 assert_eq!(rows[0][0], Value::DateTime(base + 2 * 3_600_000_000));
10737 }
10738 _ => panic!("expected rows"),
10739 }
10740 }
10741
10742 #[test]
10743 fn test_date_diff() {
10744 let mut engine = test_engine();
10745 engine
10746 .execute_powql("type Events { required start_ts: datetime, required end_ts: datetime }")
10747 .unwrap();
10748 let t1 = 1705321845000000_i64; let t2 = t1 + 3 * 86_400_000_000; engine
10751 .execute_powql(&format!(
10752 "insert Events {{ start_ts := {t1}, end_ts := {t2} }}"
10753 ))
10754 .unwrap();
10755 let result = engine
10756 .execute_powql(r#"Events { diff: date_diff(.end_ts, .start_ts, "days") }"#)
10757 .unwrap();
10758 match result {
10759 QueryResult::Rows { rows, .. } => {
10760 assert_eq!(rows[0][0], Value::Int(3));
10761 }
10762 _ => panic!("expected rows"),
10763 }
10764 }
10765}