1use std::cmp::Ordering;
4use std::collections::HashMap;
5
6use regex::Regex;
7
8use crate::errors::MdqlError;
9use crate::model::{Row, Value};
10use crate::query_parser::*;
11use crate::schema::Schema;
12
13pub fn execute_query(
14 query: &SelectQuery,
15 rows: &[Row],
16 _schema: &Schema,
17) -> crate::errors::Result<(Vec<Row>, Vec<String>)> {
18 if let Some(ref sub) = query.subquery {
19 let (sub_rows, _sub_cols) = execute_inner(sub, rows, None)?;
20 return execute_inner(query, &sub_rows, None);
21 }
22 execute_inner(query, rows, None)
23}
24
25#[allow(dead_code)]
26pub(crate) fn execute_query_indexed(
27 query: &SelectQuery,
28 rows: &[Row],
29 schema: &Schema,
30 index: Option<&crate::index::TableIndex>,
31 searcher: Option<&crate::search::TableSearcher>,
32) -> crate::errors::Result<(Vec<Row>, Vec<String>)> {
33 let fts_results = if let (Some(ref wc), Some(searcher)) = (&query.where_clause, searcher) {
35 collect_fts_results(wc, schema, searcher)
36 } else {
37 HashMap::new()
38 };
39
40 execute_with_fts(query, rows, index, &fts_results)
41}
42
43#[allow(dead_code)]
44fn collect_fts_results(
45 clause: &WhereClause,
46 schema: &Schema,
47 searcher: &crate::search::TableSearcher,
48) -> HashMap<(String, String), std::collections::HashSet<String>> {
49 let mut results = HashMap::new();
50 collect_fts_results_inner(clause, schema, searcher, &mut results);
51 results
52}
53
54#[allow(dead_code)]
55fn collect_fts_results_inner(
56 clause: &WhereClause,
57 schema: &Schema,
58 searcher: &crate::search::TableSearcher,
59 results: &mut HashMap<(String, String), std::collections::HashSet<String>>,
60) {
61 match clause {
62 WhereClause::Comparison(cmp) => {
63 if (cmp.op == CmpOp::Like || cmp.op == CmpOp::NotLike) && schema.sections.contains_key(&cmp.column) {
64 if let Some(SqlValue::String(pattern)) = &cmp.value {
65 let search_term = pattern.replace('%', " ").replace('_', " ").trim().to_string();
67 if !search_term.is_empty() {
68 if let Ok(paths) = searcher.search(&search_term, Some(&cmp.column)) {
69 let key = (cmp.column.clone(), pattern.clone());
70 results.insert(key, paths.into_iter().collect());
71 }
72 }
73 }
74 }
75 }
76 WhereClause::BoolOp(bop) => {
77 collect_fts_results_inner(&bop.left, schema, searcher, results);
78 collect_fts_results_inner(&bop.right, schema, searcher, results);
79 }
80 }
81}
82
83type FtsResults = HashMap<(String, String), std::collections::HashSet<String>>;
84
85fn execute_with_fts(
86 query: &SelectQuery,
87 rows: &[Row],
88 index: Option<&crate::index::TableIndex>,
89 fts: &FtsResults,
90) -> crate::errors::Result<(Vec<Row>, Vec<String>)> {
91 let mut all_columns: Vec<String> = Vec::new();
93 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
94 for r in rows {
95 for k in r.keys() {
96 if seen.insert(k.clone()) {
97 all_columns.push(k.clone());
98 }
99 }
100 }
101
102 let has_aggregates = match &query.columns {
104 ColumnList::Named(exprs) => exprs.iter().any(|e| e.is_aggregate()),
105 _ => false,
106 };
107
108 let columns: Vec<String> = match &query.columns {
110 ColumnList::All => all_columns,
111 ColumnList::Named(exprs) => exprs.iter().map(|e| e.output_name()).collect(),
112 };
113
114 if let ColumnList::Named(_) = &query.columns {
119 let mut seen = std::collections::HashSet::new();
120 for c in &columns {
121 if !seen.insert(c.as_str()) {
122 return Err(MdqlError::QueryExecution(format!(
123 "duplicate output column '{}' — give each projection a unique name with AS",
124 c
125 )));
126 }
127 }
128 }
129
130 let filtered: Vec<Row> = if let Some(ref wc) = query.where_clause {
132 let candidate_paths = index.and_then(|idx| try_index_filter(wc, idx));
133 if let Some(paths) = candidate_paths {
134 rows.iter()
135 .filter(|r| {
136 r.get("path")
137 .and_then(|v| v.as_str())
138 .map_or(false, |p| paths.contains(p))
139 })
140 .filter(|r| evaluate_with_fts(wc, r, fts))
141 .cloned()
142 .collect()
143 } else {
144 rows.iter()
145 .filter(|r| evaluate_with_fts(wc, r, fts))
146 .cloned()
147 .collect()
148 }
149 } else {
150 rows.to_vec()
151 };
152
153 let mut result = if has_aggregates || query.group_by.is_some() {
155 let exprs = match &query.columns {
156 ColumnList::Named(exprs) => exprs.clone(),
157 _ => return Err(MdqlError::QueryExecution(
158 "SELECT * with GROUP BY is not supported".into(),
159 )),
160 };
161 let group_keys = query.group_by.as_deref().unwrap_or(&[]);
162 aggregate_rows(&filtered, &exprs, group_keys)?
163 } else {
164 filtered
165 };
166
167 if let Some(ref having) = query.having {
169 result.retain(|row| evaluate(having, row));
170 }
171
172 let has_windows = match &query.columns {
174 ColumnList::Named(exprs) => exprs.iter().any(|e| match e {
175 SelectExpr::Expr { expr, .. } => expr.contains_window(),
176 _ => false,
177 }),
178 _ => false,
179 };
180 if has_windows {
181 if let ColumnList::Named(ref exprs) = query.columns {
182 compute_windows(&mut result, exprs)?;
183 }
184 }
185
186 if let Some(ref order_by) = query.order_by {
188 let resolved = resolve_order_aliases(order_by, &query.columns);
189 sort_rows(&mut result, &resolved);
190 }
191
192 if let Some(limit) = query.limit {
194 result.truncate(limit as usize);
195 }
196
197 if !matches!(query.columns, ColumnList::All) {
199 let named_exprs = match &query.columns {
200 ColumnList::Named(exprs) => exprs,
201 _ => unreachable!(),
202 };
203
204 let has_expr_cols = named_exprs.iter().any(|e| matches!(e, SelectExpr::Expr { .. }));
208 let already_aggregated = has_aggregates || query.group_by.is_some();
209 if has_expr_cols && !already_aggregated {
210 for row in &mut result {
211 for expr in named_exprs {
212 if let SelectExpr::Expr { expr: e, alias } = expr {
213 if e.contains_window() { continue; }
214 let name = alias.clone().unwrap_or_else(|| e.display_name());
215 let val = evaluate_expr(e, row);
216 row.insert(name, val);
217 }
218 }
219 }
220 }
221
222 let col_set: std::collections::HashSet<&str> =
223 columns.iter().map(|s| s.as_str()).collect();
224 for row in &mut result {
225 row.retain(|k, _| col_set.contains(k.as_str()));
226 }
227 }
228
229 for row in &mut result {
236 for col in &columns {
237 if !row.contains_key(col) {
238 row.insert(col.clone(), Value::Null);
239 }
240 }
241 }
242
243 Ok((result, columns))
244}
245
246fn aggregate_rows(
247 rows: &[Row],
248 exprs: &[SelectExpr],
249 group_keys: &[String],
250) -> crate::errors::Result<Vec<Row>> {
251 let mut groups: Vec<(Vec<Value>, Vec<&Row>)> = Vec::new();
253 let mut key_index: HashMap<Vec<String>, usize> = HashMap::new();
254
255 if group_keys.is_empty() {
256 let all_refs: Vec<&Row> = rows.iter().collect();
258 groups.push((vec![], all_refs));
259 } else {
260 for row in rows {
261 let key: Vec<String> = group_keys
262 .iter()
263 .map(|k| {
264 row.get(k)
265 .map(|v| v.to_display_string())
266 .unwrap_or_default()
267 })
268 .collect();
269 let key_vals: Vec<Value> = group_keys
270 .iter()
271 .map(|k| row.get(k).cloned().unwrap_or(Value::Null))
272 .collect();
273 if let Some(&idx) = key_index.get(&key) {
274 groups[idx].1.push(row);
275 } else {
276 let idx = groups.len();
277 key_index.insert(key, idx);
278 groups.push((key_vals, vec![row]));
279 }
280 }
281 }
282
283 let mut result = Vec::new();
285 for (key_vals, group_rows) in &groups {
286 let mut out = Row::new();
287
288 for (i, k) in group_keys.iter().enumerate() {
290 out.insert(k.clone(), key_vals[i].clone());
291 }
292
293 for expr in exprs {
295 match expr {
296 SelectExpr::Column(name) => {
297 if !out.contains_key(name) {
299 if let Some(first) = group_rows.first() {
300 out.insert(
301 name.clone(),
302 first.get(name).cloned().unwrap_or(Value::Null),
303 );
304 }
305 }
306 }
307 SelectExpr::Aggregate { func, arg, arg_expr, alias } => {
308 let out_name = alias
309 .clone()
310 .unwrap_or_else(|| expr.output_name());
311 let val = compute_aggregate(func, arg, arg_expr.as_ref(), group_rows);
312 out.insert(out_name, val);
313 }
314 SelectExpr::Expr { expr: e, alias } => {
315 let out_name = alias.clone().unwrap_or_else(|| e.display_name());
316 if e.contains_aggregate() {
317 let val = evaluate_agg_expr(e, group_rows);
318 out.insert(out_name, val);
319 } else if let Some(first) = group_rows.first() {
320 let val = evaluate_expr(e, first);
321 out.insert(out_name, val);
322 }
323 }
324 }
325 }
326
327 result.push(out);
328 }
329
330 Ok(result)
331}
332
333fn resolve_agg_value<'a>(arg: &str, arg_expr: Option<&Expr>, row: &'a Row) -> Value {
336 if let Some(expr) = arg_expr {
337 evaluate_expr(expr, row)
338 } else {
339 row.get(arg).cloned().unwrap_or(Value::Null)
340 }
341}
342
343fn compute_aggregate(func: &AggFunc, arg: &str, arg_expr: Option<&Expr>, rows: &[&Row]) -> Value {
344 match func {
345 AggFunc::Count => {
346 if arg == "*" && arg_expr.is_none() {
347 Value::Int(rows.len() as i64)
348 } else {
349 let count = rows
350 .iter()
351 .filter(|r| {
352 let v = resolve_agg_value(arg, arg_expr, r);
353 !v.is_null()
354 })
355 .count();
356 Value::Int(count as i64)
357 }
358 }
359 AggFunc::Sum => {
360 let mut total = 0.0f64;
361 let mut has_any = false;
362 for r in rows {
363 let v = resolve_agg_value(arg, arg_expr, r);
364 match v {
365 Value::Int(n) => { total += n as f64; has_any = true; }
366 Value::Float(f) => { total += f; has_any = true; }
367 _ => {}
368 }
369 }
370 if has_any { Value::Float(total) } else { Value::Null }
371 }
372 AggFunc::Avg => {
373 let mut total = 0.0f64;
374 let mut count = 0usize;
375 for r in rows {
376 let v = resolve_agg_value(arg, arg_expr, r);
377 match v {
378 Value::Int(n) => { total += n as f64; count += 1; }
379 Value::Float(f) => { total += f; count += 1; }
380 _ => {}
381 }
382 }
383 if count > 0 { Value::Float(total / count as f64) } else { Value::Null }
384 }
385 AggFunc::Min => {
386 let mut min_val: Option<Value> = None;
387 for r in rows {
388 let v = resolve_agg_value(arg, arg_expr, r);
389 if v.is_null() { continue; }
390 min_val = Some(match min_val {
391 None => v,
392 Some(ref current) => {
393 if v.partial_cmp(current) == Some(std::cmp::Ordering::Less) {
394 v
395 } else {
396 current.clone()
397 }
398 }
399 });
400 }
401 min_val.unwrap_or(Value::Null)
402 }
403 AggFunc::Max => {
404 let mut max_val: Option<Value> = None;
405 for r in rows {
406 let v = resolve_agg_value(arg, arg_expr, r);
407 if v.is_null() { continue; }
408 max_val = Some(match max_val {
409 None => v,
410 Some(ref current) => {
411 if v.partial_cmp(current) == Some(std::cmp::Ordering::Greater) {
412 v
413 } else {
414 current.clone()
415 }
416 }
417 });
418 }
419 max_val.unwrap_or(Value::Null)
420 }
421 }
422}
423
424fn compute_windows(rows: &mut Vec<Row>, select_exprs: &[SelectExpr]) -> crate::errors::Result<()> {
425 for se in select_exprs {
426 if let SelectExpr::Expr { expr, alias } = se {
427 if let Expr::Window { func, args, over } = expr {
428 let col_name = alias.clone().unwrap_or_else(|| expr.display_name());
429 compute_single_window(rows, func, args, over, &col_name)?;
430 }
431 }
432 }
433 Ok(())
434}
435
436fn compute_single_window(
437 rows: &mut Vec<Row>,
438 func: &WindowFunc,
439 args: &[Expr],
440 over: &WindowSpec,
441 col_name: &str,
442) -> crate::errors::Result<()> {
443 let mut partitions: Vec<Vec<usize>> = Vec::new();
444 let mut partition_map: HashMap<Vec<String>, usize> = HashMap::new();
445
446 for (i, row) in rows.iter().enumerate() {
447 let key: Vec<String> = over.partition_by.iter()
448 .map(|col| row.get(col).map(|v| v.to_display_string()).unwrap_or_default())
449 .collect();
450 if let Some(&idx) = partition_map.get(&key) {
451 partitions[idx].push(i);
452 } else {
453 let idx = partitions.len();
454 partition_map.insert(key, idx);
455 partitions.push(vec![i]);
456 }
457 }
458
459 for partition in &mut partitions {
460 if !over.order_by.is_empty() {
461 partition.sort_by(|&a, &b| {
462 for spec in &over.order_by {
463 let (va, vb) = if let Some(ref expr) = spec.expr {
464 (evaluate_expr(expr, &rows[a]), evaluate_expr(expr, &rows[b]))
465 } else {
466 (
467 rows[a].get(&spec.column).cloned().unwrap_or(Value::Null),
468 rows[b].get(&spec.column).cloned().unwrap_or(Value::Null),
469 )
470 };
471 let ordering = match (&va, &vb) {
472 (Value::Null, Value::Null) => Ordering::Equal,
473 (Value::Null, _) => Ordering::Greater,
474 (_, Value::Null) => Ordering::Less,
475 (a_val, b_val) => compare_model_values(a_val, b_val).unwrap_or(Ordering::Equal),
476 };
477 let ordering = if spec.descending { ordering.reverse() } else { ordering };
478 if ordering != Ordering::Equal {
479 return ordering;
480 }
481 }
482 Ordering::Equal
483 });
484 }
485 }
486
487 let mut values: Vec<(usize, Value)> = Vec::new();
488
489 for partition in &partitions {
490 match func {
491 WindowFunc::RowNumber => {
492 for (i, &row_idx) in partition.iter().enumerate() {
493 values.push((row_idx, Value::Int((i + 1) as i64)));
494 }
495 }
496 WindowFunc::Rank => {
497 let mut rank = 1usize;
498 for (i, &row_idx) in partition.iter().enumerate() {
499 if i > 0 {
500 let prev_idx = partition[i - 1];
501 let same = over.order_by.iter().all(|spec| {
502 let va = if let Some(ref expr) = spec.expr {
503 evaluate_expr(expr, &rows[prev_idx])
504 } else {
505 rows[prev_idx].get(&spec.column).cloned().unwrap_or(Value::Null)
506 };
507 let vb = if let Some(ref expr) = spec.expr {
508 evaluate_expr(expr, &rows[row_idx])
509 } else {
510 rows[row_idx].get(&spec.column).cloned().unwrap_or(Value::Null)
511 };
512 va == vb
513 });
514 if !same {
515 rank = i + 1;
516 }
517 }
518 values.push((row_idx, Value::Int(rank as i64)));
519 }
520 }
521 WindowFunc::DenseRank => {
522 let mut rank = 1usize;
523 for (i, &row_idx) in partition.iter().enumerate() {
524 if i > 0 {
525 let prev_idx = partition[i - 1];
526 let same = over.order_by.iter().all(|spec| {
527 let va = if let Some(ref expr) = spec.expr {
528 evaluate_expr(expr, &rows[prev_idx])
529 } else {
530 rows[prev_idx].get(&spec.column).cloned().unwrap_or(Value::Null)
531 };
532 let vb = if let Some(ref expr) = spec.expr {
533 evaluate_expr(expr, &rows[row_idx])
534 } else {
535 rows[row_idx].get(&spec.column).cloned().unwrap_or(Value::Null)
536 };
537 va == vb
538 });
539 if !same {
540 rank += 1;
541 }
542 }
543 values.push((row_idx, Value::Int(rank as i64)));
544 }
545 }
546 WindowFunc::Lag => {
547 let offset = if args.len() > 1 {
548 if let Expr::Literal(SqlValue::Int(n)) = &args[1] { *n as usize } else { 1 }
549 } else {
550 1
551 };
552 for (i, &row_idx) in partition.iter().enumerate() {
553 let val = if i >= offset && !args.is_empty() {
554 evaluate_expr(&args[0], &rows[partition[i - offset]])
555 } else {
556 Value::Null
557 };
558 values.push((row_idx, val));
559 }
560 }
561 WindowFunc::Lead => {
562 let offset = if args.len() > 1 {
563 if let Expr::Literal(SqlValue::Int(n)) = &args[1] { *n as usize } else { 1 }
564 } else {
565 1
566 };
567 for (i, &row_idx) in partition.iter().enumerate() {
568 let val = if i + offset < partition.len() && !args.is_empty() {
569 evaluate_expr(&args[0], &rows[partition[i + offset]])
570 } else {
571 Value::Null
572 };
573 values.push((row_idx, val));
574 }
575 }
576 WindowFunc::Agg(agg_func) => {
577 let partition_rows: Vec<&Row> = partition.iter().map(|&i| &rows[i]).collect();
578 let (arg_name, arg_expr_opt) = if args.is_empty() {
579 ("*".to_string(), None)
580 } else {
581 (args[0].display_name(), Some(&args[0]))
582 };
583 let agg_val = compute_aggregate(agg_func, &arg_name, arg_expr_opt, &partition_rows);
584 for &row_idx in partition {
585 values.push((row_idx, agg_val.clone()));
586 }
587 }
588 }
589 }
590
591 for (row_idx, val) in values {
592 rows[row_idx].insert(col_name.to_string(), val);
593 }
594
595 Ok(())
596}
597
598fn evaluate_with_fts(clause: &WhereClause, row: &Row, fts: &FtsResults) -> bool {
599 match clause {
600 WhereClause::BoolOp(bop) => {
601 let left = evaluate_with_fts(&bop.left, row, fts);
602 match bop.op {
603 BoolOpKind::And => left && evaluate_with_fts(&bop.right, row, fts),
604 BoolOpKind::Or => left || evaluate_with_fts(&bop.right, row, fts),
605 }
606 }
607 WhereClause::Comparison(cmp) => {
608 if cmp.op == CmpOp::Like || cmp.op == CmpOp::NotLike {
610 if let Some(SqlValue::String(pattern)) = &cmp.value {
611 let key = (cmp.column.clone(), pattern.clone());
612 if let Some(matching_paths) = fts.get(&key) {
613 let row_path = row.get("path").and_then(|v| v.as_str()).unwrap_or("");
614 let matched = matching_paths.contains(row_path);
615 return if cmp.op == CmpOp::Like { matched } else { !matched };
616 }
617 }
618 }
619 evaluate_comparison(cmp, row)
620 }
621 }
622}
623
624pub use crate::query_join::execute_join_query;
625
626pub(crate) fn execute_inner(
627 query: &SelectQuery,
628 rows: &[Row],
629 index: Option<&crate::index::TableIndex>,
630) -> crate::errors::Result<(Vec<Row>, Vec<String>)> {
631 let empty_fts = HashMap::new();
632 execute_with_fts(query, rows, index, &empty_fts)
633}
634
635pub fn evaluate(clause: &WhereClause, row: &Row) -> bool {
636 match clause {
637 WhereClause::BoolOp(bop) => {
638 let left = evaluate(&bop.left, row);
639 match bop.op {
640 BoolOpKind::And => left && evaluate(&bop.right, row),
641 BoolOpKind::Or => left || evaluate(&bop.right, row),
642 }
643 }
644 WhereClause::Comparison(cmp) => evaluate_comparison(cmp, row),
645 }
646}
647
648pub(crate) fn evaluate_expr(expr: &Expr, row: &Row) -> Value {
650 match expr {
651 Expr::Literal(SqlValue::Int(n)) => Value::Int(*n),
652 Expr::Literal(SqlValue::Float(f)) => Value::Float(*f),
653 Expr::Literal(SqlValue::String(s)) => Value::String(s.clone()),
654 Expr::Literal(SqlValue::Null) => Value::Null,
655 Expr::Literal(SqlValue::List(_)) => Value::Null,
656 Expr::Column(name) => {
657 if let Some(val) = row.get(name) {
658 return val.clone();
659 }
660 for (i, _) in name.match_indices('.') {
662 let dict_col = &name[..i];
663 let dict_key = &name[i + 1..];
664 if let Some(Value::Dict(map)) = row.get(dict_col) {
665 return map.get(dict_key).cloned().unwrap_or(Value::Null);
666 }
667 }
668 Value::Null
669 }
670 Expr::UnaryMinus(inner) => {
671 match evaluate_expr(inner, row) {
672 Value::Int(n) => Value::Int(-n),
673 Value::Float(f) => Value::Float(-f),
674 Value::Null => Value::Null,
675 _ => Value::Null, }
677 }
678 Expr::BinaryOp { left, op, right } => {
679 let lv = evaluate_expr(left, row);
680 let rv = evaluate_expr(right, row);
681
682 if lv.is_null() || rv.is_null() {
684 return Value::Null;
685 }
686
687 match (&lv, &rv) {
689 (Value::Int(a), Value::Int(b)) => {
690 match op {
691 ArithOp::Add => Value::Int(a.wrapping_add(*b)),
692 ArithOp::Sub => Value::Int(a.wrapping_sub(*b)),
693 ArithOp::Mul => Value::Int(a.wrapping_mul(*b)),
694 ArithOp::Div => {
695 if *b == 0 { Value::Null } else { Value::Int(a / b) }
696 }
697 ArithOp::Mod => {
698 if *b == 0 { Value::Null } else { Value::Int(a % b) }
699 }
700 }
701 }
702 _ => {
703 let a = match &lv {
705 Value::Int(n) => *n as f64,
706 Value::Float(f) => *f,
707 _ => return Value::Null,
708 };
709 let b = match &rv {
710 Value::Int(n) => *n as f64,
711 Value::Float(f) => *f,
712 _ => return Value::Null,
713 };
714 match op {
715 ArithOp::Add => Value::Float(a + b),
716 ArithOp::Sub => Value::Float(a - b),
717 ArithOp::Mul => Value::Float(a * b),
718 ArithOp::Div => {
719 if b == 0.0 { Value::Null } else { Value::Float(a / b) }
720 }
721 ArithOp::Mod => {
722 if b == 0.0 { Value::Null } else { Value::Float(a % b) }
723 }
724 }
725 }
726 }
727 }
728 Expr::Case { whens, else_expr } => {
729 for (condition, result) in whens {
730 if evaluate(condition, row) {
731 return evaluate_expr(result, row);
732 }
733 }
734 match else_expr {
735 Some(e) => evaluate_expr(e, row),
736 None => Value::Null,
737 }
738 }
739 Expr::CurrentDate => {
740 Value::Date(chrono::Local::now().naive_local().date())
741 }
742 Expr::CurrentTimestamp => {
743 Value::DateTime(chrono::Local::now().naive_local())
744 }
745 Expr::DateAdd { date, days } => {
746 let date_val = evaluate_expr(date, row);
747 let days_val = evaluate_expr(days, row);
748 let n = match &days_val {
749 Value::Int(n) => *n,
750 Value::Float(f) => *f as i64,
751 _ => return Value::Null,
752 };
753 let duration = chrono::Duration::days(n);
754 match date_val {
755 Value::Date(d) => {
756 match d.checked_add_signed(duration) {
757 Some(result) => Value::Date(result),
758 None => Value::Null,
759 }
760 }
761 Value::DateTime(dt) => {
762 match dt.checked_add_signed(duration) {
763 Some(result) => Value::DateTime(result),
764 None => Value::Null,
765 }
766 }
767 _ => Value::Null,
768 }
769 }
770 Expr::DateDiff { left, right } => {
771 let lv = evaluate_expr(left, row);
772 let rv = evaluate_expr(right, row);
773 let left_date = match &lv {
774 Value::Date(d) => d.and_hms_opt(0, 0, 0).unwrap(),
775 Value::DateTime(dt) => *dt,
776 _ => return Value::Null,
777 };
778 let right_date = match &rv {
779 Value::Date(d) => d.and_hms_opt(0, 0, 0).unwrap(),
780 Value::DateTime(dt) => *dt,
781 _ => return Value::Null,
782 };
783 Value::Int((left_date - right_date).num_days())
784 }
785 Expr::Aggregate { func, arg, .. } => {
786 let func_name = match func {
788 AggFunc::Count => "COUNT",
789 AggFunc::Sum => "SUM",
790 AggFunc::Avg => "AVG",
791 AggFunc::Min => "MIN",
792 AggFunc::Max => "MAX",
793 };
794 let col = format!("{}({})", func_name, arg);
795 row.get(&col).cloned().unwrap_or(Value::Null)
796 }
797 Expr::Subquery(_) => Value::Null,
798 Expr::Window { .. } => {
799 let display = expr.display_name();
800 row.get(&display).cloned().unwrap_or(Value::Null)
801 }
802 }
803}
804
805fn evaluate_agg_expr(expr: &Expr, group_rows: &[&Row]) -> Value {
806 match expr {
807 Expr::Aggregate { func, arg, arg_expr } => {
808 compute_aggregate(func, arg, arg_expr.as_deref(), group_rows)
809 }
810 Expr::BinaryOp { left, op, right } => {
811 let lv = evaluate_agg_expr(left, group_rows);
812 let rv = evaluate_agg_expr(right, group_rows);
813 apply_arith_op(op, &lv, &rv)
814 }
815 Expr::UnaryMinus(inner) => {
816 match evaluate_agg_expr(inner, group_rows) {
817 Value::Int(n) => Value::Int(-n),
818 Value::Float(f) => Value::Float(-f),
819 _ => Value::Null,
820 }
821 }
822 other => {
823 if let Some(first) = group_rows.first() {
824 evaluate_expr(other, first)
825 } else {
826 Value::Null
827 }
828 }
829 }
830}
831
832fn apply_arith_op(op: &ArithOp, lv: &Value, rv: &Value) -> Value {
833 if lv.is_null() || rv.is_null() {
834 return Value::Null;
835 }
836 match (lv, rv) {
837 (Value::Int(a), Value::Int(b)) => match op {
838 ArithOp::Add => Value::Int(a.wrapping_add(*b)),
839 ArithOp::Sub => Value::Int(a.wrapping_sub(*b)),
840 ArithOp::Mul => Value::Int(a.wrapping_mul(*b)),
841 ArithOp::Div => if *b == 0 { Value::Null } else { Value::Int(a / b) },
842 ArithOp::Mod => if *b == 0 { Value::Null } else { Value::Int(a % b) },
843 },
844 _ => {
845 let a = match lv {
846 Value::Int(n) => *n as f64,
847 Value::Float(f) => *f,
848 _ => return Value::Null,
849 };
850 let b = match rv {
851 Value::Int(n) => *n as f64,
852 Value::Float(f) => *f,
853 _ => return Value::Null,
854 };
855 match op {
856 ArithOp::Add => Value::Float(a + b),
857 ArithOp::Sub => Value::Float(a - b),
858 ArithOp::Mul => Value::Float(a * b),
859 ArithOp::Div => if b == 0.0 { Value::Null } else { Value::Float(a / b) },
860 ArithOp::Mod => if b == 0.0 { Value::Null } else { Value::Float(a % b) },
861 }
862 }
863 }
864}
865
866fn evaluate_comparison(cmp: &Comparison, row: &Row) -> bool {
867 if let (Some(left_expr), Some(right_expr)) = (&cmp.left_expr, &cmp.right_expr) {
869 if matches!(cmp.op, CmpOp::Eq | CmpOp::Ne | CmpOp::Lt | CmpOp::Gt | CmpOp::Le | CmpOp::Ge) {
870 let left_val = evaluate_expr(left_expr, row);
871 let right_val = evaluate_expr(right_expr, row);
872
873 if left_val.is_null() || right_val.is_null() {
875 return false;
876 }
877
878 let ord = compare_model_values(&left_val, &right_val);
880
881 return match cmp.op {
882 CmpOp::Eq => ord == Some(Ordering::Equal),
883 CmpOp::Ne => ord != Some(Ordering::Equal),
884 CmpOp::Lt => ord == Some(Ordering::Less),
885 CmpOp::Gt => ord == Some(Ordering::Greater),
886 CmpOp::Le => matches!(ord, Some(Ordering::Less | Ordering::Equal)),
887 CmpOp::Ge => matches!(ord, Some(Ordering::Greater | Ordering::Equal)),
888 _ => false,
889 };
890 }
891 }
892
893 let actual = row.get(&cmp.column);
895
896 if cmp.op == CmpOp::IsNull {
897 return actual.map_or(true, |v| v.is_null());
898 }
899 if cmp.op == CmpOp::IsNotNull {
900 return actual.map_or(false, |v| !v.is_null());
901 }
902
903 let actual = match actual {
904 Some(v) if !v.is_null() => v,
905 _ => return false,
906 };
907
908 let expected = match &cmp.value {
909 Some(v) => v,
910 None => return false,
911 };
912
913 match cmp.op {
914 CmpOp::Eq => eq_match(actual, expected),
915 CmpOp::Ne => !eq_match(actual, expected),
916 CmpOp::Lt => compare_values(actual, expected) == Some(Ordering::Less),
917 CmpOp::Gt => compare_values(actual, expected) == Some(Ordering::Greater),
918 CmpOp::Le => matches!(compare_values(actual, expected), Some(Ordering::Less | Ordering::Equal)),
919 CmpOp::Ge => matches!(compare_values(actual, expected), Some(Ordering::Greater | Ordering::Equal)),
920 CmpOp::Like => like_match(actual, expected),
921 CmpOp::NotLike => !like_match(actual, expected),
922 CmpOp::In => {
923 if let SqlValue::List(items) = expected {
924 items.iter().any(|v| eq_match(actual, v))
925 } else {
926 eq_match(actual, expected)
927 }
928 }
929 CmpOp::IsNull | CmpOp::IsNotNull => unreachable!(),
930 }
931}
932
933fn compare_model_values(a: &Value, b: &Value) -> Option<Ordering> {
935 match (a, b) {
936 (Value::Int(x), Value::Float(y)) => (*x as f64).partial_cmp(y),
937 (Value::Float(x), Value::Int(y)) => x.partial_cmp(&(*y as f64)),
938 _ => a.partial_cmp(b),
939 }
940}
941
942fn coerce_sql_to_value(sql_val: &SqlValue, target: &Value) -> Value {
943 match sql_val {
944 SqlValue::Null => Value::Null,
945 SqlValue::String(s) => {
946 match target {
947 Value::Int(_) => s.parse::<i64>().map(Value::Int).unwrap_or(Value::String(s.clone())),
948 Value::Float(_) => s.parse::<f64>().map(Value::Float).unwrap_or(Value::String(s.clone())),
949 Value::Date(_) => {
950 chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d")
951 .map(Value::Date)
952 .unwrap_or(Value::String(s.clone()))
953 }
954 Value::DateTime(_) => {
955 chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S")
956 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f"))
957 .map(Value::DateTime)
958 .unwrap_or(Value::String(s.clone()))
959 }
960 _ => Value::String(s.clone()),
961 }
962 }
963 SqlValue::Int(n) => {
964 match target {
965 Value::Float(_) => Value::Float(*n as f64),
966 _ => Value::Int(*n),
967 }
968 }
969 SqlValue::Float(f) => Value::Float(*f),
970 SqlValue::List(_) => Value::Null, }
972}
973
974fn eq_match(actual: &Value, expected: &SqlValue) -> bool {
975 if let Value::List(items) = actual {
977 if let SqlValue::String(s) = expected {
978 return items.contains(s);
979 }
980 }
981
982 let coerced = coerce_sql_to_value(expected, actual);
983 actual == &coerced
984}
985
986fn like_match(actual: &Value, pattern: &SqlValue) -> bool {
987 let pattern_str = match pattern {
988 SqlValue::String(s) => s,
989 _ => return false,
990 };
991
992 let mut regex_str = String::from("(?is)^");
994 for ch in pattern_str.chars() {
995 match ch {
996 '%' => regex_str.push_str(".*"),
997 '_' => regex_str.push('.'),
998 c => {
999 if regex::escape(&c.to_string()) != c.to_string() {
1000 regex_str.push_str(®ex::escape(&c.to_string()));
1001 } else {
1002 regex_str.push(c);
1003 }
1004 }
1005 }
1006 }
1007 regex_str.push('$');
1008
1009 let re = match Regex::new(®ex_str) {
1010 Ok(r) => r,
1011 Err(_) => return false,
1012 };
1013
1014 match actual {
1015 Value::List(items) => items.iter().any(|item| re.is_match(item)),
1016 _ => re.is_match(&actual.to_display_string()),
1017 }
1018}
1019
1020fn compare_values(actual: &Value, expected: &SqlValue) -> Option<Ordering> {
1021 let coerced = coerce_sql_to_value(expected, actual);
1022 actual.partial_cmp(&coerced)
1023}
1024
1025fn sql_value_to_index_value(sv: &SqlValue) -> Value {
1027 match sv {
1028 SqlValue::String(s) => {
1029 if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
1031 return Value::DateTime(dt);
1032 }
1033 if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f") {
1034 return Value::DateTime(dt);
1035 }
1036 if let Ok(d) = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d") {
1038 return Value::Date(d);
1039 }
1040 Value::String(s.clone())
1041 }
1042 SqlValue::Int(n) => Value::Int(*n),
1043 SqlValue::Float(f) => Value::Float(*f),
1044 SqlValue::Null => Value::Null,
1045 SqlValue::List(_) => Value::Null,
1046 }
1047}
1048
1049fn try_index_filter(
1053 clause: &WhereClause,
1054 index: &crate::index::TableIndex,
1055) -> Option<std::collections::HashSet<String>> {
1056 match clause {
1057 WhereClause::Comparison(cmp) => {
1058 if !index.has_index(&cmp.column) {
1059 return None;
1060 }
1061 match cmp.op {
1062 CmpOp::Eq => {
1063 let val = sql_value_to_index_value(cmp.value.as_ref()?);
1064 let paths = index.lookup_eq(&cmp.column, &val);
1065 Some(paths.into_iter().map(|s| s.to_string()).collect())
1066 }
1067 CmpOp::Lt => {
1068 let val = sql_value_to_index_value(cmp.value.as_ref()?);
1069 let range_paths = index.lookup_range(&cmp.column, None, Some(&val));
1072 let eq_paths: std::collections::HashSet<&str> = index.lookup_eq(&cmp.column, &val).into_iter().collect();
1073 Some(range_paths.into_iter().filter(|p| !eq_paths.contains(p)).map(|s| s.to_string()).collect())
1074 }
1075 CmpOp::Gt => {
1076 let val = sql_value_to_index_value(cmp.value.as_ref()?);
1077 let range_paths = index.lookup_range(&cmp.column, Some(&val), None);
1078 let eq_paths: std::collections::HashSet<&str> = index.lookup_eq(&cmp.column, &val).into_iter().collect();
1079 Some(range_paths.into_iter().filter(|p| !eq_paths.contains(p)).map(|s| s.to_string()).collect())
1080 }
1081 CmpOp::Le => {
1082 let val = sql_value_to_index_value(cmp.value.as_ref()?);
1083 let paths = index.lookup_range(&cmp.column, None, Some(&val));
1084 Some(paths.into_iter().map(|s| s.to_string()).collect())
1085 }
1086 CmpOp::Ge => {
1087 let val = sql_value_to_index_value(cmp.value.as_ref()?);
1088 let paths = index.lookup_range(&cmp.column, Some(&val), None);
1089 Some(paths.into_iter().map(|s| s.to_string()).collect())
1090 }
1091 CmpOp::In => {
1092 if let Some(SqlValue::List(items)) = &cmp.value {
1093 let vals: Vec<Value> = items.iter().map(sql_value_to_index_value).collect();
1094 let paths = index.lookup_in(&cmp.column, &vals);
1095 Some(paths.into_iter().map(|s| s.to_string()).collect())
1096 } else {
1097 None
1098 }
1099 }
1100 _ => None, }
1102 }
1103 WhereClause::BoolOp(bop) => {
1104 let left = try_index_filter(&bop.left, index);
1105 let right = try_index_filter(&bop.right, index);
1106 match bop.op {
1107 BoolOpKind::And => {
1108 match (left, right) {
1109 (Some(l), Some(r)) => Some(l.intersection(&r).cloned().collect()),
1110 (Some(l), None) => Some(l), (None, Some(r)) => Some(r),
1112 (None, None) => None,
1113 }
1114 }
1115 BoolOpKind::Or => {
1116 match (left, right) {
1117 (Some(l), Some(r)) => Some(l.union(&r).cloned().collect()),
1118 _ => None, }
1120 }
1121 }
1122 }
1123 }
1124}
1125
1126fn resolve_order_aliases(specs: &[OrderSpec], columns: &ColumnList) -> Vec<OrderSpec> {
1129 let named = match columns {
1130 ColumnList::Named(exprs) => exprs,
1131 _ => return specs.to_vec(),
1132 };
1133
1134 let alias_map: HashMap<String, &Expr> = named
1136 .iter()
1137 .filter_map(|se| match se {
1138 SelectExpr::Expr { expr, alias: Some(a) } if !expr.contains_window() => {
1139 Some((a.clone(), expr))
1140 }
1141 _ => None,
1142 })
1143 .collect();
1144
1145 specs
1146 .iter()
1147 .map(|spec| {
1148 if let Some(expr) = alias_map.get(&spec.column) {
1150 OrderSpec {
1151 column: spec.column.clone(),
1152 expr: Some((*expr).clone()),
1153 descending: spec.descending,
1154 }
1155 } else {
1156 spec.clone()
1157 }
1158 })
1159 .collect()
1160}
1161
1162fn sort_rows(rows: &mut Vec<Row>, specs: &[OrderSpec]) {
1163 rows.sort_by(|a, b| {
1164 for spec in specs {
1165 let (va, vb) = if let Some(ref expr) = spec.expr {
1166 (evaluate_expr(expr, a), evaluate_expr(expr, b))
1167 } else {
1168 (
1169 a.get(&spec.column).cloned().unwrap_or(Value::Null),
1170 b.get(&spec.column).cloned().unwrap_or(Value::Null),
1171 )
1172 };
1173
1174 let ordering = match (&va, &vb) {
1176 (Value::Null, Value::Null) => Ordering::Equal,
1177 (Value::Null, _) => Ordering::Greater,
1178 (_, Value::Null) => Ordering::Less,
1179 (a_val, b_val) => {
1180 compare_model_values(a_val, b_val).unwrap_or(Ordering::Equal)
1181 }
1182 };
1183
1184 let ordering = if spec.descending {
1185 ordering.reverse()
1186 } else {
1187 ordering
1188 };
1189
1190 if ordering != Ordering::Equal {
1191 return ordering;
1192 }
1193 }
1194 Ordering::Equal
1195 });
1196}
1197
1198pub(crate) fn sql_value_to_value(sql_val: &SqlValue) -> Value {
1200 match sql_val {
1201 SqlValue::Null => Value::Null,
1202 SqlValue::String(s) => Value::String(s.clone()),
1203 SqlValue::Int(n) => Value::Int(*n),
1204 SqlValue::Float(f) => Value::Float(*f),
1205 SqlValue::List(items) => {
1206 let strings: Vec<String> = items
1207 .iter()
1208 .filter_map(|v| match v {
1209 SqlValue::String(s) => Some(s.clone()),
1210 _ => None,
1211 })
1212 .collect();
1213 Value::List(strings)
1214 }
1215 }
1216}
1217
1218#[cfg(test)]
1219mod tests {
1220 use super::*;
1221
1222 fn make_rows() -> Vec<Row> {
1223 vec![
1224 Row::from([
1225 ("path".into(), Value::String("a.md".into())),
1226 ("title".into(), Value::String("Alpha".into())),
1227 ("count".into(), Value::Int(10)),
1228 ]),
1229 Row::from([
1230 ("path".into(), Value::String("b.md".into())),
1231 ("title".into(), Value::String("Beta".into())),
1232 ("count".into(), Value::Int(5)),
1233 ]),
1234 Row::from([
1235 ("path".into(), Value::String("c.md".into())),
1236 ("title".into(), Value::String("Gamma".into())),
1237 ("count".into(), Value::Int(20)),
1238 ]),
1239 ]
1240 }
1241
1242 #[test]
1243 fn test_select_all() {
1244 let q = SelectQuery {
1245 columns: ColumnList::All,
1246 table: "test".into(),
1247 table_alias: None,
1248 subquery: None,
1249 joins: vec![],
1250 where_clause: None,
1251 group_by: None,
1252 having: None,
1253 order_by: None,
1254 limit: None,
1255 ctes: vec![],
1256 };
1257 let (rows, _cols) = execute_inner(&q, &make_rows(), None).unwrap();
1258 assert_eq!(rows.len(), 3);
1259 }
1260
1261 #[test]
1262 fn test_select_nonexistent_column_null_filled() {
1263 let q = parse_query("SELECT title, missing_col, count FROM test").unwrap();
1266 let q = match q {
1267 Statement::Select(s) => s,
1268 _ => panic!("expected SELECT"),
1269 };
1270 let (rows, cols) = execute_inner(&q, &make_rows(), None).unwrap();
1271 assert_eq!(cols, vec!["title", "missing_col", "count"]);
1272 assert_eq!(rows.len(), 3);
1273 for row in &rows {
1274 assert_eq!(row.len(), cols.len(), "row keys must match header length");
1275 for c in &cols {
1276 assert!(row.contains_key(c), "row missing header column {c}");
1277 }
1278 assert_eq!(row.get("missing_col"), Some(&Value::Null));
1279 }
1280 }
1281
1282 #[test]
1283 fn test_select_duplicate_output_column_errors() {
1284 let q = parse_query("SELECT title, title FROM test").unwrap();
1287 let q = match q {
1288 Statement::Select(s) => s,
1289 _ => panic!("expected SELECT"),
1290 };
1291 let err = execute_inner(&q, &make_rows(), None);
1292 assert!(err.is_err());
1293 let msg = err.unwrap_err().to_string();
1294 assert!(msg.contains("duplicate output column"), "got: {msg}");
1295 }
1296
1297 #[test]
1298 fn test_select_all_sparse_rows_aligned() {
1299 let rows = vec![
1302 Row::from([
1303 ("path".into(), Value::String("a.md".into())),
1304 ("title".into(), Value::String("Alpha".into())),
1305 ("kill_reason".into(), Value::String("no edge".into())),
1306 ]),
1307 Row::from([
1308 ("path".into(), Value::String("b.md".into())),
1309 ("title".into(), Value::String("Beta".into())),
1310 ]),
1311 ];
1312 let q = parse_query("SELECT * FROM test").unwrap();
1313 let q = match q {
1314 Statement::Select(s) => s,
1315 _ => panic!("expected SELECT"),
1316 };
1317 let (result, cols) = execute_inner(&q, &rows, None).unwrap();
1318 assert!(cols.contains(&"kill_reason".to_string()));
1319 for row in &result {
1320 assert_eq!(row.len(), cols.len(), "row keys must match header length");
1321 for c in &cols {
1322 assert!(row.contains_key(c), "row missing header column {c}");
1323 }
1324 }
1325 let beta = result.iter().find(|r| r.get("path") == Some(&Value::String("b.md".into()))).unwrap();
1327 assert_eq!(beta.get("kill_reason"), Some(&Value::Null));
1328 }
1329
1330 #[test]
1331 fn test_where_gt() {
1332 let q = SelectQuery {
1333 columns: ColumnList::All,
1334 table: "test".into(),
1335 table_alias: None,
1336 subquery: None,
1337 joins: vec![],
1338 where_clause: Some(WhereClause::Comparison(Comparison {
1339 column: "count".into(),
1340 op: CmpOp::Gt,
1341 value: Some(SqlValue::Int(5)),
1342 left_expr: Some(Expr::Column("count".into())),
1343 right_expr: Some(Expr::Literal(SqlValue::Int(5))),
1344 })),
1345 group_by: None,
1346 having: None,
1347 order_by: None,
1348 limit: None,
1349 ctes: vec![],
1350 };
1351 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1352 assert_eq!(rows.len(), 2);
1353 }
1354
1355 #[test]
1356 fn test_order_by_desc() {
1357 let q = SelectQuery {
1358 columns: ColumnList::All,
1359 table: "test".into(),
1360 table_alias: None,
1361 subquery: None,
1362 joins: vec![],
1363 where_clause: None,
1364 group_by: None,
1365 having: None,
1366 order_by: Some(vec![OrderSpec {
1367 column: "count".into(),
1368 expr: Some(Expr::Column("count".into())),
1369 descending: true,
1370 }]),
1371 limit: None,
1372 ctes: vec![],
1373 };
1374 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1375 assert_eq!(rows[0]["count"], Value::Int(20));
1376 assert_eq!(rows[2]["count"], Value::Int(5));
1377 }
1378
1379 #[test]
1380 fn test_limit() {
1381 let q = SelectQuery {
1382 columns: ColumnList::All,
1383 table: "test".into(),
1384 table_alias: None,
1385 subquery: None,
1386 joins: vec![],
1387 where_clause: None,
1388 group_by: None,
1389 having: None,
1390 order_by: None,
1391 limit: Some(2),
1392 ctes: vec![],
1393 };
1394 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1395 assert_eq!(rows.len(), 2);
1396 }
1397
1398 #[test]
1399 fn test_like() {
1400 let q = SelectQuery {
1401 columns: ColumnList::All,
1402 table: "test".into(),
1403 table_alias: None,
1404 subquery: None,
1405 joins: vec![],
1406 where_clause: Some(WhereClause::Comparison(Comparison {
1407 column: "title".into(),
1408 op: CmpOp::Like,
1409 value: Some(SqlValue::String("%lph%".into())),
1410 left_expr: Some(Expr::Column("title".into())),
1411 right_expr: None,
1412 })),
1413 group_by: None,
1414 having: None,
1415 order_by: None,
1416 limit: None,
1417 ctes: vec![],
1418 };
1419 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1420 assert_eq!(rows.len(), 1);
1421 assert_eq!(rows[0]["title"], Value::String("Alpha".into()));
1422 }
1423
1424 #[test]
1425 fn test_is_null() {
1426 let mut rows = make_rows();
1427 rows[1].insert("optional".into(), Value::Null);
1428
1429 let q = SelectQuery {
1430 columns: ColumnList::All,
1431 table: "test".into(),
1432 table_alias: None,
1433 subquery: None,
1434 joins: vec![],
1435 where_clause: Some(WhereClause::Comparison(Comparison {
1436 column: "optional".into(),
1437 op: CmpOp::IsNull,
1438 value: None,
1439 left_expr: Some(Expr::Column("optional".into())),
1440 right_expr: None,
1441 })),
1442 group_by: None,
1443 having: None,
1444 order_by: None,
1445 limit: None,
1446 ctes: vec![],
1447 };
1448 let (result, _) = execute_inner(&q, &rows, None).unwrap();
1449 assert_eq!(result.len(), 3);
1451 }
1452
1453 #[test]
1456 fn test_evaluate_expr_literal() {
1457 let row = Row::new();
1458 assert_eq!(evaluate_expr(&Expr::Literal(SqlValue::Int(42)), &row), Value::Int(42));
1459 assert_eq!(evaluate_expr(&Expr::Literal(SqlValue::Float(3.14)), &row), Value::Float(3.14));
1460 assert_eq!(evaluate_expr(&Expr::Literal(SqlValue::Null), &row), Value::Null);
1461 }
1462
1463 #[test]
1464 fn test_evaluate_expr_column() {
1465 let row = Row::from([("x".into(), Value::Int(10))]);
1466 assert_eq!(evaluate_expr(&Expr::Column("x".into()), &row), Value::Int(10));
1467 assert_eq!(evaluate_expr(&Expr::Column("missing".into()), &row), Value::Null);
1468 }
1469
1470 #[test]
1471 fn test_evaluate_expr_int_arithmetic() {
1472 let row = Row::from([("a".into(), Value::Int(10)), ("b".into(), Value::Int(3))]);
1473 let add = Expr::BinaryOp {
1474 left: Box::new(Expr::Column("a".into())),
1475 op: ArithOp::Add,
1476 right: Box::new(Expr::Column("b".into())),
1477 };
1478 assert_eq!(evaluate_expr(&add, &row), Value::Int(13));
1479
1480 let sub = Expr::BinaryOp {
1481 left: Box::new(Expr::Column("a".into())),
1482 op: ArithOp::Sub,
1483 right: Box::new(Expr::Column("b".into())),
1484 };
1485 assert_eq!(evaluate_expr(&sub, &row), Value::Int(7));
1486
1487 let mul = Expr::BinaryOp {
1488 left: Box::new(Expr::Column("a".into())),
1489 op: ArithOp::Mul,
1490 right: Box::new(Expr::Column("b".into())),
1491 };
1492 assert_eq!(evaluate_expr(&mul, &row), Value::Int(30));
1493
1494 let div = Expr::BinaryOp {
1495 left: Box::new(Expr::Column("a".into())),
1496 op: ArithOp::Div,
1497 right: Box::new(Expr::Column("b".into())),
1498 };
1499 assert_eq!(evaluate_expr(&div, &row), Value::Int(3)); let modulo = Expr::BinaryOp {
1502 left: Box::new(Expr::Column("a".into())),
1503 op: ArithOp::Mod,
1504 right: Box::new(Expr::Column("b".into())),
1505 };
1506 assert_eq!(evaluate_expr(&modulo, &row), Value::Int(1));
1507 }
1508
1509 #[test]
1510 fn test_evaluate_expr_float_coercion() {
1511 let row = Row::from([("a".into(), Value::Int(10)), ("b".into(), Value::Float(3.0))]);
1512 let add = Expr::BinaryOp {
1513 left: Box::new(Expr::Column("a".into())),
1514 op: ArithOp::Add,
1515 right: Box::new(Expr::Column("b".into())),
1516 };
1517 assert_eq!(evaluate_expr(&add, &row), Value::Float(13.0));
1518 }
1519
1520 #[test]
1521 fn test_evaluate_expr_null_propagation() {
1522 let row = Row::from([("a".into(), Value::Int(10))]);
1523 let add = Expr::BinaryOp {
1524 left: Box::new(Expr::Column("a".into())),
1525 op: ArithOp::Add,
1526 right: Box::new(Expr::Column("missing".into())),
1527 };
1528 assert_eq!(evaluate_expr(&add, &row), Value::Null);
1529 }
1530
1531 #[test]
1532 fn test_evaluate_expr_div_by_zero() {
1533 let row = Row::from([("a".into(), Value::Int(10)), ("b".into(), Value::Int(0))]);
1534 let div = Expr::BinaryOp {
1535 left: Box::new(Expr::Column("a".into())),
1536 op: ArithOp::Div,
1537 right: Box::new(Expr::Column("b".into())),
1538 };
1539 assert_eq!(evaluate_expr(&div, &row), Value::Null);
1540 }
1541
1542 #[test]
1543 fn test_evaluate_expr_unary_minus() {
1544 let row = Row::from([("x".into(), Value::Int(5))]);
1545 let neg = Expr::UnaryMinus(Box::new(Expr::Column("x".into())));
1546 assert_eq!(evaluate_expr(&neg, &row), Value::Int(-5));
1547 }
1548
1549 #[test]
1550 fn test_select_with_expression() {
1551 let stmt = crate::query_parser::parse_query(
1553 "SELECT count * 2 AS doubled FROM test"
1554 ).unwrap();
1555 if let crate::query_parser::Statement::Select(q) = stmt {
1556 let (rows, cols) = execute_inner(&q, &make_rows(), None).unwrap();
1557 assert_eq!(cols, vec!["doubled"]);
1558 assert_eq!(rows.len(), 3);
1559 let values: Vec<Value> = rows.iter().map(|r| r["doubled"].clone()).collect();
1561 assert!(values.contains(&Value::Int(20)));
1562 assert!(values.contains(&Value::Int(10)));
1563 assert!(values.contains(&Value::Int(40)));
1564 } else {
1565 panic!("Expected Select");
1566 }
1567 }
1568
1569 #[test]
1570 fn test_where_with_expression() {
1571 let stmt = crate::query_parser::parse_query(
1573 "SELECT * FROM test WHERE count * 2 > 15"
1574 ).unwrap();
1575 if let crate::query_parser::Statement::Select(q) = stmt {
1576 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1577 assert_eq!(rows.len(), 2);
1579 } else {
1580 panic!("Expected Select");
1581 }
1582 }
1583
1584 #[test]
1585 fn test_order_by_expression() {
1586 let stmt = crate::query_parser::parse_query(
1588 "SELECT title, count FROM test ORDER BY count * -1 ASC"
1589 ).unwrap();
1590 if let crate::query_parser::Statement::Select(q) = stmt {
1591 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1592 assert_eq!(rows[0]["count"], Value::Int(20));
1594 assert_eq!(rows[1]["count"], Value::Int(10));
1595 assert_eq!(rows[2]["count"], Value::Int(5));
1596 } else {
1597 panic!("Expected Select");
1598 }
1599 }
1600
1601 #[test]
1604 fn test_case_when_eval_basic() {
1605 let row = Row::from([("status".into(), Value::String("ACTIVE".into()))]);
1606 let expr = Expr::Case {
1607 whens: vec![(
1608 WhereClause::Comparison(Comparison {
1609 column: "status".into(),
1610 op: CmpOp::Eq,
1611 value: Some(SqlValue::String("ACTIVE".into())),
1612 left_expr: Some(Expr::Column("status".into())),
1613 right_expr: Some(Expr::Literal(SqlValue::String("ACTIVE".into()))),
1614 }),
1615 Box::new(Expr::Literal(SqlValue::Int(1))),
1616 )],
1617 else_expr: Some(Box::new(Expr::Literal(SqlValue::Int(0)))),
1618 };
1619 assert_eq!(evaluate_expr(&expr, &row), Value::Int(1));
1620 }
1621
1622 #[test]
1623 fn test_case_when_eval_else() {
1624 let row = Row::from([("status".into(), Value::String("KILLED".into()))]);
1625 let expr = Expr::Case {
1626 whens: vec![(
1627 WhereClause::Comparison(Comparison {
1628 column: "status".into(),
1629 op: CmpOp::Eq,
1630 value: Some(SqlValue::String("ACTIVE".into())),
1631 left_expr: Some(Expr::Column("status".into())),
1632 right_expr: Some(Expr::Literal(SqlValue::String("ACTIVE".into()))),
1633 }),
1634 Box::new(Expr::Literal(SqlValue::Int(1))),
1635 )],
1636 else_expr: Some(Box::new(Expr::Literal(SqlValue::Int(0)))),
1637 };
1638 assert_eq!(evaluate_expr(&expr, &row), Value::Int(0));
1639 }
1640
1641 #[test]
1642 fn test_case_when_eval_no_else_null() {
1643 let row = Row::from([("x".into(), Value::Int(99))]);
1644 let expr = Expr::Case {
1645 whens: vec![(
1646 WhereClause::Comparison(Comparison {
1647 column: "x".into(),
1648 op: CmpOp::Eq,
1649 value: Some(SqlValue::Int(1)),
1650 left_expr: Some(Expr::Column("x".into())),
1651 right_expr: Some(Expr::Literal(SqlValue::Int(1))),
1652 }),
1653 Box::new(Expr::Literal(SqlValue::String("one".into()))),
1654 )],
1655 else_expr: None,
1656 };
1657 assert_eq!(evaluate_expr(&expr, &row), Value::Null);
1658 }
1659
1660 #[test]
1661 fn test_case_when_in_aggregate_query() {
1662 let stmt = crate::query_parser::parse_query(
1665 "SELECT SUM(CASE WHEN count > 5 THEN count ELSE 0 END) AS total FROM test"
1666 ).unwrap();
1667 if let crate::query_parser::Statement::Select(q) = stmt {
1668 let (rows, cols) = execute_inner(&q, &make_rows(), None).unwrap();
1669 assert_eq!(cols, vec!["total"]);
1670 assert_eq!(rows.len(), 1);
1671 assert_eq!(rows[0]["total"], Value::Float(30.0));
1672 } else {
1673 panic!("Expected Select");
1674 }
1675 }
1676
1677 #[test]
1678 fn test_case_when_with_unary_minus_in_aggregate() {
1679 let stmt = crate::query_parser::parse_query(
1682 "SELECT SUM(CASE WHEN title = 'Alpha' THEN count ELSE -count END) AS net FROM test"
1683 ).unwrap();
1684 if let crate::query_parser::Statement::Select(q) = stmt {
1685 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1686 assert_eq!(rows.len(), 1);
1687 assert_eq!(rows[0]["net"], Value::Float(-15.0));
1688 } else {
1689 panic!("Expected Select");
1690 }
1691 }
1692
1693 #[test]
1694 fn test_dateadd_with_dict_in_group_by() {
1695 use indexmap::IndexMap;
1697 let mut params = IndexMap::new();
1698 params.insert("exit_days".to_string(), Value::Int(21));
1699
1700 let rows = vec![
1701 Row::from([
1702 ("o.token".into(), Value::String("BTC".into())),
1703 ("o.event_date".into(), Value::Date(
1704 chrono::NaiveDate::from_ymd_opt(2026, 1, 1).unwrap()
1705 )),
1706 ("o.size".into(), Value::Int(100)),
1707 ("s.params".into(), Value::Dict(params.clone())),
1708 ]),
1709 Row::from([
1710 ("o.token".into(), Value::String("BTC".into())),
1711 ("o.event_date".into(), Value::Date(
1712 chrono::NaiveDate::from_ymd_opt(2026, 1, 1).unwrap()
1713 )),
1714 ("o.size".into(), Value::Int(50)),
1715 ("s.params".into(), Value::Dict(params.clone())),
1716 ]),
1717 ];
1718
1719 let q = SelectQuery {
1720 columns: ColumnList::Named(vec![
1721 SelectExpr::Column("o.token".into()),
1722 SelectExpr::Column("o.event_date".into()),
1723 SelectExpr::Expr {
1724 expr: Expr::DateAdd {
1725 date: Box::new(Expr::Column("o.event_date".into())),
1726 days: Box::new(Expr::Column("s.params.exit_days".into())),
1727 },
1728 alias: Some("exit_date".into()),
1729 },
1730 SelectExpr::Aggregate {
1731 func: AggFunc::Sum,
1732 arg: "o.size".into(),
1733 arg_expr: Some(Expr::Column("o.size".into())),
1734 alias: Some("total".into()),
1735 },
1736 ]),
1737 table: "orders".into(),
1738 table_alias: None,
1739 subquery: None,
1740 joins: vec![],
1741 where_clause: None,
1742 group_by: Some(vec!["o.token".into(), "o.event_date".into()]),
1743 having: None,
1744 order_by: None,
1745 limit: None,
1746 ctes: vec![],
1747 };
1748
1749 let (rows, cols) = execute_inner(&q, &rows, None).unwrap();
1750 assert_eq!(rows.len(), 1);
1751 assert!(cols.contains(&"exit_date".to_string()));
1752 assert_eq!(rows[0]["total"], Value::Float(150.0));
1753 assert_eq!(
1755 rows[0]["exit_date"],
1756 Value::Date(chrono::NaiveDate::from_ymd_opt(2026, 1, 22).unwrap())
1757 );
1758 }
1759
1760 #[test]
1761 fn test_aggregate_arithmetic() {
1762 let stmt = crate::query_parser::parse_query(
1766 "SELECT SUM(count) / COUNT(*) AS avg_count FROM test"
1767 ).unwrap();
1768 if let crate::query_parser::Statement::Select(q) = stmt {
1769 let (rows, cols) = execute_inner(&q, &make_rows(), None).unwrap();
1770 assert_eq!(cols, vec!["avg_count"]);
1771 assert_eq!(rows.len(), 1);
1772 match &rows[0]["avg_count"] {
1773 Value::Float(f) => assert!((f - 11.666666666666666).abs() < 0.001),
1774 other => panic!("Expected Float, got {:?}", other),
1775 }
1776 } else {
1777 panic!("Expected Select");
1778 }
1779 }
1780
1781 #[test]
1782 fn test_aggregate_subtraction_with_group_by() {
1783 let rows = vec![
1784 {
1785 let mut r = Row::new();
1786 r.insert("token".into(), Value::String("BTC".into()));
1787 r.insert("side".into(), Value::String("BUY".into()));
1788 r.insert("size".into(), Value::Float(100.0));
1789 r
1790 },
1791 {
1792 let mut r = Row::new();
1793 r.insert("token".into(), Value::String("BTC".into()));
1794 r.insert("side".into(), Value::String("SELL".into()));
1795 r.insert("size".into(), Value::Float(60.0));
1796 r
1797 },
1798 ];
1799 let stmt = crate::query_parser::parse_query(
1800 "SELECT token, SUM(CASE WHEN side = 'BUY' THEN size ELSE 0 END) - SUM(CASE WHEN side = 'SELL' THEN size ELSE 0 END) AS net FROM test GROUP BY token"
1801 ).unwrap();
1802 if let crate::query_parser::Statement::Select(q) = stmt {
1803 let (result, _) = execute_inner(&q, &rows, None).unwrap();
1804 assert_eq!(result.len(), 1);
1805 assert_eq!(result[0]["net"], Value::Float(40.0));
1806 } else {
1807 panic!("Expected Select");
1808 }
1809 }
1810
1811 #[test]
1814 fn test_aggregate_subtraction_no_group() {
1815 let stmt = crate::query_parser::parse_query(
1817 "SELECT SUM(count) - COUNT(*) as diff FROM test"
1818 ).unwrap();
1819 if let crate::query_parser::Statement::Select(q) = stmt {
1820 let (rows, cols) = execute_inner(&q, &make_rows(), None).unwrap();
1821 assert_eq!(cols, vec!["diff"]);
1822 assert_eq!(rows.len(), 1);
1823 assert_eq!(rows[0]["diff"], Value::Float(32.0));
1824 } else {
1825 panic!("Expected Select");
1826 }
1827 }
1828
1829 #[test]
1832 fn test_aggregate_division_with_group_by() {
1833 let rows = vec![
1834 {
1835 let mut r = Row::new();
1836 r.insert("category".into(), Value::String("A".into()));
1837 r.insert("count".into(), Value::Int(10));
1838 r
1839 },
1840 {
1841 let mut r = Row::new();
1842 r.insert("category".into(), Value::String("A".into()));
1843 r.insert("count".into(), Value::Int(20));
1844 r
1845 },
1846 {
1847 let mut r = Row::new();
1848 r.insert("category".into(), Value::String("B".into()));
1849 r.insert("count".into(), Value::Int(6));
1850 r
1851 },
1852 ];
1853 let stmt = crate::query_parser::parse_query(
1856 "SELECT category, SUM(count) / COUNT(*) as ratio FROM test GROUP BY category"
1857 ).unwrap();
1858 if let crate::query_parser::Statement::Select(q) = stmt {
1859 let (result, cols) = execute_inner(&q, &rows, None).unwrap();
1860 assert!(cols.contains(&"ratio".to_string()));
1861 assert_eq!(result.len(), 2);
1862 let group_a = result.iter().find(|r| r["category"] == Value::String("A".into())).unwrap();
1864 let group_b = result.iter().find(|r| r["category"] == Value::String("B".into())).unwrap();
1865 match &group_a["ratio"] {
1866 Value::Float(f) => assert!((f - 15.0).abs() < 0.001),
1867 other => panic!("Expected Float for group A ratio, got {:?}", other),
1868 }
1869 match &group_b["ratio"] {
1870 Value::Float(f) => assert!((f - 6.0).abs() < 0.001),
1871 other => panic!("Expected Float for group B ratio, got {:?}", other),
1872 }
1873 } else {
1874 panic!("Expected Select");
1875 }
1876 }
1877
1878 #[test]
1881 fn test_window_row_number() {
1882 let stmt = crate::query_parser::parse_query(
1883 "SELECT title, ROW_NUMBER() OVER (ORDER BY count DESC) AS rn FROM test"
1884 ).unwrap();
1885 if let crate::query_parser::Statement::Select(q) = stmt {
1886 let (rows, cols) = execute_inner(&q, &make_rows(), None).unwrap();
1887 assert_eq!(cols, vec!["title", "rn"]);
1888 assert_eq!(rows.len(), 3);
1889 let by_title: HashMap<String, i64> = rows.iter()
1890 .map(|r| (r["title"].to_display_string(), match &r["rn"] { Value::Int(n) => *n, _ => panic!("Expected Int") }))
1891 .collect();
1892 assert_eq!(by_title["Gamma"], 1); assert_eq!(by_title["Alpha"], 2); assert_eq!(by_title["Beta"], 3); } else {
1896 panic!("Expected Select");
1897 }
1898 }
1899
1900 #[test]
1901 fn test_window_rank_with_ties() {
1902 let mut rows = make_rows();
1903 rows[0].insert("count".into(), Value::Int(10));
1904 rows[1].insert("count".into(), Value::Int(10));
1905 rows[2].insert("count".into(), Value::Int(5));
1906
1907 let stmt = crate::query_parser::parse_query(
1908 "SELECT title, RANK() OVER (ORDER BY count DESC) AS rnk FROM test"
1909 ).unwrap();
1910 if let crate::query_parser::Statement::Select(q) = stmt {
1911 let (result, _) = execute_inner(&q, &rows, None).unwrap();
1912 let ranks: Vec<i64> = result.iter()
1913 .map(|r| match &r["rnk"] { Value::Int(n) => *n, _ => panic!("Expected Int") })
1914 .collect();
1915 assert!(ranks.contains(&1)); assert!(ranks.iter().filter(|&&r| r == 1).count() == 2);
1917 assert!(ranks.contains(&3)); } else {
1919 panic!("Expected Select");
1920 }
1921 }
1922
1923 #[test]
1924 fn test_window_dense_rank() {
1925 let mut rows = make_rows();
1926 rows[0].insert("count".into(), Value::Int(10));
1927 rows[1].insert("count".into(), Value::Int(10));
1928 rows[2].insert("count".into(), Value::Int(5));
1929
1930 let stmt = crate::query_parser::parse_query(
1931 "SELECT title, DENSE_RANK() OVER (ORDER BY count DESC) AS dr FROM test"
1932 ).unwrap();
1933 if let crate::query_parser::Statement::Select(q) = stmt {
1934 let (result, _) = execute_inner(&q, &rows, None).unwrap();
1935 let ranks: Vec<i64> = result.iter()
1936 .map(|r| match &r["dr"] { Value::Int(n) => *n, _ => panic!("Expected Int") })
1937 .collect();
1938 assert!(ranks.iter().filter(|&&r| r == 1).count() == 2);
1939 assert!(ranks.contains(&2)); assert!(!ranks.contains(&3));
1941 } else {
1942 panic!("Expected Select");
1943 }
1944 }
1945
1946 #[test]
1947 fn test_window_lag() {
1948 let stmt = crate::query_parser::parse_query(
1949 "SELECT title, LAG(count, 1) OVER (ORDER BY count ASC) AS prev FROM test"
1950 ).unwrap();
1951 if let crate::query_parser::Statement::Select(q) = stmt {
1952 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1953 let first = rows.iter().find(|r| r["title"] == Value::String("Beta".into())).unwrap();
1956 assert_eq!(first["prev"], Value::Null);
1957 let second = rows.iter().find(|r| r["title"] == Value::String("Alpha".into())).unwrap();
1958 assert_eq!(second["prev"], Value::Int(5));
1959 let third = rows.iter().find(|r| r["title"] == Value::String("Gamma".into())).unwrap();
1960 assert_eq!(third["prev"], Value::Int(10));
1961 } else {
1962 panic!("Expected Select");
1963 }
1964 }
1965
1966 #[test]
1967 fn test_window_lead() {
1968 let stmt = crate::query_parser::parse_query(
1969 "SELECT title, LEAD(count, 1) OVER (ORDER BY count ASC) AS next FROM test"
1970 ).unwrap();
1971 if let crate::query_parser::Statement::Select(q) = stmt {
1972 let (rows, _) = execute_inner(&q, &make_rows(), None).unwrap();
1973 let first = rows.iter().find(|r| r["title"] == Value::String("Beta".into())).unwrap();
1974 assert_eq!(first["next"], Value::Int(10));
1975 let last = rows.iter().find(|r| r["title"] == Value::String("Gamma".into())).unwrap();
1976 assert_eq!(last["next"], Value::Null);
1977 } else {
1978 panic!("Expected Select");
1979 }
1980 }
1981
1982 #[test]
1983 fn test_window_sum_partition() {
1984 let rows = vec![
1985 Row::from([
1986 ("cat".into(), Value::String("A".into())),
1987 ("val".into(), Value::Int(10)),
1988 ]),
1989 Row::from([
1990 ("cat".into(), Value::String("A".into())),
1991 ("val".into(), Value::Int(20)),
1992 ]),
1993 Row::from([
1994 ("cat".into(), Value::String("B".into())),
1995 ("val".into(), Value::Int(5)),
1996 ]),
1997 ];
1998 let stmt = crate::query_parser::parse_query(
1999 "SELECT cat, val, SUM(val) OVER (PARTITION BY cat) AS cat_total FROM test"
2000 ).unwrap();
2001 if let crate::query_parser::Statement::Select(q) = stmt {
2002 let (result, cols) = execute_inner(&q, &rows, None).unwrap();
2003 assert_eq!(cols, vec!["cat", "val", "cat_total"]);
2004 assert_eq!(result.len(), 3);
2005 let a_rows: Vec<_> = result.iter().filter(|r| r["cat"] == Value::String("A".into())).collect();
2006 assert_eq!(a_rows.len(), 2);
2007 for r in &a_rows {
2008 assert_eq!(r["cat_total"], Value::Float(30.0));
2009 }
2010 let b_row = result.iter().find(|r| r["cat"] == Value::String("B".into())).unwrap();
2011 assert_eq!(b_row["cat_total"], Value::Float(5.0));
2012 } else {
2013 panic!("Expected Select");
2014 }
2015 }
2016
2017 #[test]
2018 fn test_window_with_where_order_limit() {
2019 let stmt = crate::query_parser::parse_query(
2020 "SELECT title, ROW_NUMBER() OVER (ORDER BY count DESC) AS rn FROM test WHERE count > 4 ORDER BY rn LIMIT 2"
2021 ).unwrap();
2022 if let crate::query_parser::Statement::Select(q) = stmt {
2023 let (result, _) = execute_inner(&q, &make_rows(), None).unwrap();
2024 assert_eq!(result.len(), 2);
2025 assert_eq!(result[0]["rn"], Value::Int(1));
2026 assert_eq!(result[1]["rn"], Value::Int(2));
2027 } else {
2028 panic!("Expected Select");
2029 }
2030 }
2031}