1use crate::error::{KitError, Result};
15use crate::schema::{core_row_to_json, Row};
16use mongreldb_core::memtable::Row as CoreRow;
17use mongreldb_core::query::Condition;
18use mongreldb_kit_core::query::{
19 AggFunc, Aggregate, AggregateQuery, Direction, Expr, JoinKind, JoinQuery, Literal, OrderBy,
20 Query, Select,
21};
22use mongreldb_kit_core::schema::{Schema as KitSchema, Table as KitTable};
23use serde_json::{Map, Value};
24use std::cmp::Ordering;
25use std::collections::{HashMap, HashSet};
26
27pub type JoinRow = Map<String, Value>;
31
32type TableFetch<'a> = Box<dyn Fn(&str, Option<&[Condition]>) -> Result<Vec<Row>> + 'a>;
36
37pub(crate) struct ExecCtx<'a> {
43 schema: Option<&'a KitSchema>,
44 fetch: TableFetch<'a>,
45 ctes: HashMap<String, Vec<Row>>,
46}
47
48impl<'a> ExecCtx<'a> {
49 pub(crate) fn new(
50 schema: Option<&'a KitSchema>,
51 fetch: impl Fn(&str, Option<&[Condition]>) -> Result<Vec<Row>> + 'a,
52 ) -> Self {
53 Self {
54 schema,
55 fetch: Box::new(fetch),
56 ctes: HashMap::new(),
57 }
58 }
59
60 pub(crate) fn add_cte(&mut self, name: String, rows: Vec<Row>) {
61 self.ctes.insert(name, rows);
62 }
63
64 fn table_rows(&self, name: &str) -> Result<Vec<Row>> {
65 if let Some(rows) = self.ctes.get(name) {
66 return Ok(rows.clone());
67 }
68 (self.fetch)(name, None)
69 }
70
71 fn table_rows_filtered(&self, name: &str, conditions: &[Condition]) -> Result<Vec<Row>> {
75 if conditions.is_empty() || self.ctes.contains_key(name) {
76 return self.table_rows(name);
77 }
78 (self.fetch)(name, Some(conditions))
79 }
80
81 fn table_def(&self, name: &str) -> Option<&KitTable> {
82 self.schema.and_then(|s| s.table(name))
83 }
84}
85
86trait Scope {
88 fn get(&self, name: &str) -> Value;
89}
90
91struct FlatScope<'a>(&'a Map<String, Value>);
93
94impl Scope for FlatScope<'_> {
95 fn get(&self, name: &str) -> Value {
96 self.0.get(name).cloned().unwrap_or(Value::Null)
97 }
98}
99
100struct JoinScope<'a>(&'a Map<String, Value>);
103
104impl Scope for JoinScope<'_> {
105 fn get(&self, name: &str) -> Value {
106 if let Some((alias, col)) = name.split_once('.') {
107 return self
108 .0
109 .get(alias)
110 .and_then(|t| t.as_object())
111 .and_then(|o| o.get(col))
112 .cloned()
113 .unwrap_or(Value::Null);
114 }
115 for value in self.0.values() {
116 if let Some(obj) = value.as_object() {
117 if let Some(v) = obj.get(name) {
118 return v.clone();
119 }
120 }
121 }
122 Value::Null
123 }
124}
125
126pub fn execute_select(
134 table: &KitTable,
135 visible_rows: Vec<CoreRow>,
136 select: &Select,
137) -> Result<Vec<Row>> {
138 let rows: Vec<Row> = visible_rows
139 .into_iter()
140 .map(|r| core_row_to_json(&r, table))
141 .collect::<Result<Vec<_>>>()?;
142 let mut ctx = ExecCtx::new(None, |name: &str, _conds: Option<&[Condition]>| {
143 Err(KitError::Validation(format!(
144 "table {name} is not available outside a transaction context"
145 )))
146 });
147 ctx.add_cte(table.name.clone(), rows);
148 run_select(&ctx, select)
149}
150
151pub fn execute_query(
153 table: &KitTable,
154 visible_rows: Vec<CoreRow>,
155 query: &Query,
156) -> Result<Vec<Row>> {
157 match query {
158 Query::Select(select) => execute_select(table, visible_rows, select),
159 _ => Err(KitError::Validation(
160 "only SELECT queries are supported by execute_query".into(),
161 )),
162 }
163}
164
165pub(crate) fn run_select(ctx: &ExecCtx, select: &Select) -> Result<Vec<Row>> {
168 let (mut rows, residual_needed) = match &select.filter {
173 Some(filter) => {
174 let pushed = ctx
175 .table_def(&select.table)
176 .and_then(|t| crate::pushdown::translate_predicate(t, filter));
177 match pushed {
178 Some(plan) if plan.can_push() => {
179 let fetched = ctx.table_rows_filtered(&select.table, &plan.conditions)?;
180 (fetched, !plan.fully_translated)
181 }
182 _ => (ctx.table_rows(&select.table)?, true),
183 }
184 }
185 None => (ctx.table_rows(&select.table)?, false),
186 };
187
188 if residual_needed {
190 if let Some(filter) = &select.filter {
191 let mut kept = Vec::with_capacity(rows.len());
192 for r in rows {
193 if eval_pred(filter, &FlatScope(&r.values), ctx)? {
194 kept.push(r);
195 }
196 }
197 rows = kept;
198 }
199 }
200
201 for order in &select.order_by {
202 sort_rows(ctx, &mut rows, &select.table, order)?;
203 }
204
205 apply_limit_offset(&mut rows, select.limit, select.offset);
206 Ok(rows)
207}
208
209pub(crate) fn project_distinct(select: &Select, rows: Vec<Row>) -> Vec<Row> {
212 let cols: Vec<String> = select
213 .columns
214 .iter()
215 .filter_map(|e| match e {
216 Expr::Column(n) => Some(n.clone()),
217 _ => None,
218 })
219 .collect();
220 let mut seen = HashSet::new();
221 let mut out = Vec::new();
222 for r in rows {
223 let values = if cols.is_empty() {
224 r.values
225 } else {
226 let mut m = Map::new();
227 for c in &cols {
228 m.insert(c.clone(), r.values.get(c).cloned().unwrap_or(Value::Null));
229 }
230 m
231 };
232 let key = serde_json::to_string(&values).unwrap_or_default();
233 if seen.insert(key) {
234 out.push(Row { row_id: 0, values });
235 }
236 }
237 out
238}
239
240pub(crate) fn run_aggregate(ctx: &ExecCtx, query: &AggregateQuery) -> Result<Vec<Row>> {
243 let (mut rows, residual_needed) = match &query.filter {
245 Some(filter) => {
246 let pushed = ctx
247 .table_def(&query.table)
248 .and_then(|t| crate::pushdown::translate_predicate(t, filter));
249 match pushed {
250 Some(plan) if plan.can_push() => {
251 let fetched = ctx.table_rows_filtered(&query.table, &plan.conditions)?;
252 (fetched, !plan.fully_translated)
253 }
254 _ => (ctx.table_rows(&query.table)?, true),
255 }
256 }
257 None => (ctx.table_rows(&query.table)?, false),
258 };
259
260 if residual_needed {
261 if let Some(filter) = &query.filter {
262 let mut kept = Vec::with_capacity(rows.len());
263 for r in rows {
264 if eval_pred(filter, &FlatScope(&r.values), ctx)? {
265 kept.push(r);
266 }
267 }
268 rows = kept;
269 }
270 }
271
272 let mut groups: Vec<(Vec<Value>, Vec<Row>)> = Vec::new();
274 if query.group_by.is_empty() {
275 groups.push((Vec::new(), rows));
276 } else {
277 let mut index: HashMap<String, usize> = HashMap::new();
278 for r in rows {
279 let key_vals: Vec<Value> = query
280 .group_by
281 .iter()
282 .map(|c| r.values.get(c).cloned().unwrap_or(Value::Null))
283 .collect();
284 let key_str = serde_json::to_string(&key_vals).unwrap_or_default();
285 match index.get(&key_str) {
286 Some(&i) => groups[i].1.push(r),
287 None => {
288 index.insert(key_str, groups.len());
289 groups.push((key_vals, vec![r]));
290 }
291 }
292 }
293 }
294
295 let mut out = Vec::with_capacity(groups.len());
296 for (key_vals, group_rows) in groups {
297 let mut values = Map::new();
298 for (col, val) in query.group_by.iter().zip(key_vals.iter()) {
299 values.insert(col.clone(), val.clone());
300 }
301 for agg in &query.aggregates {
302 values.insert(agg.alias.clone(), compute_aggregate(agg, &group_rows)?);
303 }
304 if let Some(having) = &query.having {
305 if !eval_pred(having, &FlatScope(&values), ctx)? {
306 continue;
307 }
308 }
309 out.push(Row { row_id: 0, values });
310 }
311 Ok(out)
312}
313
314fn compute_aggregate(agg: &Aggregate, rows: &[Row]) -> Result<Value> {
315 match agg.func {
316 AggFunc::Count => {
317 let n = match (&agg.column, agg.distinct) {
318 (None, _) => rows.len(),
320 (Some(col), true) => distinct_non_null(rows, col).len(),
322 (Some(col), false) => rows
324 .iter()
325 .filter(|r| !r.values.get(col).map(Value::is_null).unwrap_or(true))
326 .count(),
327 };
328 Ok(Value::Number((n as i64).into()))
329 }
330 AggFunc::Sum | AggFunc::Avg => {
331 let col = require_agg_column(agg)?;
332 let (nums, all_int): (Vec<f64>, bool) = if agg.distinct {
334 let vals = distinct_non_null(rows, col);
335 let all_int = vals.iter().all(|v| v.as_i64().is_some());
336 (vals.iter().filter_map(|&v| num_of(v)).collect(), all_int)
337 } else {
338 let all_int = rows
339 .iter()
340 .filter_map(|r| r.values.get(col))
341 .filter(|v| !v.is_null())
342 .all(|v| v.as_i64().is_some());
343 (numeric_values(rows, col), all_int)
344 };
345 if nums.is_empty() {
346 return Ok(Value::Null);
347 }
348 let sum: f64 = nums.iter().sum();
349 if matches!(agg.func, AggFunc::Avg) {
350 return Ok(number_value(sum / nums.len() as f64));
351 }
352 if all_int {
354 Ok(Value::Number((sum as i64).into()))
355 } else {
356 Ok(number_value(sum))
357 }
358 }
359 AggFunc::Min | AggFunc::Max => {
360 let col = require_agg_column(agg)?;
361 let mut best: Option<&Value> = None;
362 for r in rows {
363 let Some(v) = r.values.get(col) else { continue };
364 if v.is_null() {
365 continue;
366 }
367 best = Some(match best {
368 None => v,
369 Some(cur) => {
370 let take = matches!(
371 (json_cmp(v, cur), agg.func),
372 (Some(Ordering::Less), AggFunc::Min)
373 | (Some(Ordering::Greater), AggFunc::Max)
374 );
375 if take {
376 v
377 } else {
378 cur
379 }
380 }
381 });
382 }
383 Ok(best.cloned().unwrap_or(Value::Null))
384 }
385 }
386}
387
388fn require_agg_column(agg: &Aggregate) -> Result<&String> {
389 agg.column
390 .as_ref()
391 .ok_or_else(|| KitError::Validation(format!("aggregate {:?} requires a column", agg.func)))
392}
393
394fn numeric_values(rows: &[Row], col: &str) -> Vec<f64> {
395 rows.iter()
396 .filter_map(|r| r.values.get(col))
397 .filter_map(num_of)
398 .collect()
399}
400
401fn num_of(v: &Value) -> Option<f64> {
402 match v {
403 Value::Number(n) => n.as_f64(),
404 _ => None,
405 }
406}
407
408fn number_value(f: f64) -> Value {
409 serde_json::Number::from_f64(f)
410 .map(Value::Number)
411 .unwrap_or(Value::Null)
412}
413
414fn distinct_non_null<'a>(rows: &'a [Row], col: &str) -> Vec<&'a Value> {
418 let mut seen = std::collections::HashSet::new();
419 let mut out = Vec::new();
420 for r in rows {
421 if let Some(v) = r.values.get(col) {
422 if v.is_null() {
423 continue;
424 }
425 if seen.insert(serde_json::to_string(v).unwrap_or_default()) {
426 out.push(v);
427 }
428 }
429 }
430 out
431}
432
433fn fk_join_condition(
443 right_table: &KitTable,
444 right_alias: &str,
445 on: &Expr,
446 left_rows: &[JoinRow],
447) -> Option<Condition> {
448 let Expr::Eq(a, b) = on else { return None };
449 let (Expr::Column(ca), Expr::Column(cb)) = (a.as_ref(), b.as_ref()) else {
450 return None;
451 };
452 let prefix = format!("{right_alias}.");
453 let (right_qual, left_qual) = match (ca.starts_with(&prefix), cb.starts_with(&prefix)) {
455 (true, false) => (ca.as_str(), cb.as_str()),
456 (false, true) => (cb.as_str(), ca.as_str()),
457 _ => return None,
458 };
459 let right_col = right_qual.strip_prefix(&prefix)?;
460 if !crate::pushdown::has_declared_bitmap_index(right_table, right_col) {
461 return None;
462 }
463 let col = right_table.column(right_col)?;
464 let (left_alias, left_col) = left_qual.split_once('.')?;
465
466 let mut seen = std::collections::HashSet::new();
467 let mut values = Vec::new();
468 for row in left_rows {
469 let key = row
470 .get(left_alias)
471 .and_then(|t| t.as_object())
472 .and_then(|o| o.get(left_col))
473 .and_then(|v| crate::pushdown::value_index_key(v, col.storage_type));
474 if let Some(key) = key {
475 if seen.insert(key.clone()) {
476 values.push(key);
477 }
478 }
479 }
480 if values.is_empty() {
481 return None;
482 }
483 Some(Condition::BitmapIn {
484 column_id: col.id as u16,
485 values,
486 })
487}
488
489fn conjuncts(expr: &Expr) -> Vec<&Expr> {
492 match expr {
493 Expr::And(parts) => parts.iter().flat_map(conjuncts).collect(),
494 other => vec![other],
495 }
496}
497
498fn collect_aliases(expr: &Expr, out: &mut HashSet<String>) {
501 match expr {
502 Expr::Column(name) => {
503 if let Some((alias, _)) = name.split_once('.') {
504 out.insert(alias.to_string());
505 }
506 }
507 Expr::And(parts) | Expr::Or(parts) => {
508 for p in parts {
509 collect_aliases(p, out);
510 }
511 }
512 Expr::Not(e) => collect_aliases(e, out),
513 Expr::Eq(a, b)
514 | Expr::Ne(a, b)
515 | Expr::Gt(a, b)
516 | Expr::Gte(a, b)
517 | Expr::Lt(a, b)
518 | Expr::Lte(a, b) => {
519 collect_aliases(a, out);
520 collect_aliases(b, out);
521 }
522 Expr::In(a, _)
523 | Expr::NotIn(a, _)
524 | Expr::Like(a, _)
525 | Expr::Contains(a, _)
526 | Expr::BytesPrefix(a, _)
527 | Expr::IsNull(a)
528 | Expr::IsNotNull(a) => collect_aliases(a, out),
529 Expr::InSubquery(a, _) => collect_aliases(a, out),
530 Expr::Exists(_) | Expr::NotExists(_) => {
531 out.insert("__subquery__".to_string());
532 }
533 Expr::Literal(_) => {}
534 };
535}
536
537fn single_alias(expr: &Expr) -> Option<String> {
541 let mut set = HashSet::new();
542 collect_aliases(expr, &mut set);
543 if set.len() == 1 {
544 return set.into_iter().next();
545 }
546 None
547}
548
549fn strip_alias_prefix(expr: &Expr, alias: &str) -> Expr {
552 match expr {
553 Expr::Column(name) => match name.split_once('.') {
554 Some((a, col)) if a == alias => Expr::Column(col.to_string()),
555 _ => Expr::Column(name.clone()),
556 },
557 Expr::And(parts) => Expr::And(parts.iter().map(|p| strip_alias_prefix(p, alias)).collect()),
558 Expr::Or(parts) => Expr::Or(parts.iter().map(|p| strip_alias_prefix(p, alias)).collect()),
559 Expr::Not(e) => Expr::Not(Box::new(strip_alias_prefix(e, alias))),
560 Expr::Eq(a, b) => Expr::Eq(
561 Box::new(strip_alias_prefix(a, alias)),
562 Box::new(strip_alias_prefix(b, alias)),
563 ),
564 Expr::Ne(a, b) => Expr::Ne(
565 Box::new(strip_alias_prefix(a, alias)),
566 Box::new(strip_alias_prefix(b, alias)),
567 ),
568 Expr::Gt(a, b) => Expr::Gt(
569 Box::new(strip_alias_prefix(a, alias)),
570 Box::new(strip_alias_prefix(b, alias)),
571 ),
572 Expr::Gte(a, b) => Expr::Gte(
573 Box::new(strip_alias_prefix(a, alias)),
574 Box::new(strip_alias_prefix(b, alias)),
575 ),
576 Expr::Lt(a, b) => Expr::Lt(
577 Box::new(strip_alias_prefix(a, alias)),
578 Box::new(strip_alias_prefix(b, alias)),
579 ),
580 Expr::Lte(a, b) => Expr::Lte(
581 Box::new(strip_alias_prefix(a, alias)),
582 Box::new(strip_alias_prefix(b, alias)),
583 ),
584 Expr::In(a, list) => Expr::In(Box::new(strip_alias_prefix(a, alias)), list.clone()),
585 Expr::NotIn(a, list) => Expr::NotIn(Box::new(strip_alias_prefix(a, alias)), list.clone()),
586 Expr::IsNull(a) => Expr::IsNull(Box::new(strip_alias_prefix(a, alias))),
587 Expr::IsNotNull(a) => Expr::IsNotNull(Box::new(strip_alias_prefix(a, alias))),
588 Expr::Like(a, pat) => Expr::Like(Box::new(strip_alias_prefix(a, alias)), pat.clone()),
589 Expr::Contains(a, needle) => {
590 Expr::Contains(Box::new(strip_alias_prefix(a, alias)), needle.clone())
591 }
592 Expr::BytesPrefix(a, prefix) => {
593 Expr::BytesPrefix(Box::new(strip_alias_prefix(a, alias)), prefix.clone())
594 }
595 Expr::InSubquery(a, sub) => {
596 Expr::InSubquery(Box::new(strip_alias_prefix(a, alias)), sub.clone())
597 }
598 other => other.clone(),
599 }
600}
601
602fn split_join_filter(filter: Option<&Expr>) -> (HashMap<String, Vec<Expr>>, Vec<&Expr>) {
607 let mut side_filters: HashMap<String, Vec<Expr>> = HashMap::new();
608 let mut residual: Vec<&Expr> = Vec::new();
609 let parts = match filter {
610 Some(f) => conjuncts(f),
611 None => return (side_filters, residual),
612 };
613 for c in parts {
614 match single_alias(c) {
615 Some(alias) => {
616 let stripped = strip_alias_prefix(c, &alias);
617 side_filters.entry(alias).or_default().push(stripped);
618 }
619 None => residual.push(c),
620 }
621 }
622 (side_filters, residual)
623}
624
625fn side_conditions(ctx: &ExecCtx, table: &str, conjuncts: &[Expr]) -> Vec<Condition> {
629 let Some(t) = ctx.table_def(table) else {
630 return Vec::new();
631 };
632 conjuncts
633 .iter()
634 .filter_map(|c| crate::pushdown::translate_predicate(t, c))
635 .flat_map(|p| p.conditions)
636 .collect()
637}
638
639fn passes_side_filter(conjuncts: &[Expr], row: &Map<String, Value>, ctx: &ExecCtx) -> Result<bool> {
641 if conjuncts.is_empty() {
642 return Ok(true);
643 }
644 let scope = FlatScope(row);
645 for c in conjuncts {
646 if !eval_pred(c, &scope, ctx)? {
647 return Ok(false);
648 }
649 }
650 Ok(true)
651}
652
653pub(crate) fn run_join(ctx: &ExecCtx, query: &JoinQuery) -> Result<Vec<JoinRow>> {
654 let base_alias = query.alias.clone().unwrap_or_else(|| query.table.clone());
655
656 let (side_filters, residual) = split_join_filter(query.filter.as_ref());
661
662 let base_conjuncts = side_filters.get(&base_alias).cloned().unwrap_or_default();
664 let base_conds = side_conditions(ctx, &query.table, &base_conjuncts);
665 let base_rows = if base_conds.is_empty() {
666 ctx.table_rows(&query.table)?
667 } else {
668 ctx.table_rows_filtered(&query.table, &base_conds)?
669 };
670 let mut acc: Vec<JoinRow> = Vec::with_capacity(base_rows.len());
671 for r in base_rows {
672 if passes_side_filter(&base_conjuncts, &r.values, ctx)? {
673 let mut m = Map::new();
674 m.insert(base_alias.clone(), Value::Object(r.values));
675 acc.push(m);
676 }
677 }
678
679 for join in &query.joins {
680 let right_alias = join.alias.clone().unwrap_or_else(|| join.table.clone());
681 let right_conjuncts = side_filters.get(&right_alias).cloned().unwrap_or_default();
683 let mut right_conds = side_conditions(ctx, &join.table, &right_conjuncts);
684 let fk_cond = match (join.kind, join.on.as_ref()) {
688 (JoinKind::Cross, _) | (_, None) => None,
689 (_, Some(on)) => ctx
690 .table_def(&join.table)
691 .and_then(|t| fk_join_condition(t, &right_alias, on, &acc)),
692 };
693 if let Some(c) = fk_cond {
694 right_conds.push(c);
695 }
696 let right_rows_fetched = if right_conds.is_empty() {
697 ctx.table_rows(&join.table)?
698 } else {
699 ctx.table_rows_filtered(&join.table, &right_conds)?
700 };
701 let mut right_rows: Vec<Row> = Vec::with_capacity(right_rows_fetched.len());
703 for r in right_rows_fetched {
704 if passes_side_filter(&right_conjuncts, &r.values, ctx)? {
705 right_rows.push(r);
706 }
707 }
708 let mut next = Vec::new();
709 for left in acc {
710 let mut matched = false;
711 for rr in &right_rows {
712 let mut combined = left.clone();
713 combined.insert(right_alias.clone(), Value::Object(rr.values.clone()));
714 let keep = match join.kind {
715 JoinKind::Cross => true,
716 _ => match &join.on {
717 Some(on) => eval_pred(on, &JoinScope(&combined), ctx)?,
718 None => true,
719 },
720 };
721 if keep {
722 matched = true;
723 next.push(combined);
724 }
725 }
726 if !matched && join.kind == JoinKind::Left {
727 let mut combined = left;
728 combined.insert(right_alias.clone(), Value::Null);
729 next.push(combined);
730 }
731 }
732 acc = next;
733 }
734
735 if !residual.is_empty() {
737 let mut kept = Vec::with_capacity(acc.len());
738 for row in acc {
739 let ok = residual.iter().try_fold(true, |acc, c| {
740 eval_pred(c, &JoinScope(&row), ctx).map(|v| acc && v)
741 })?;
742 if ok {
743 kept.push(row);
744 }
745 }
746 acc = kept;
747 }
748
749 for order in &query.order_by {
750 let key = match &order.expr {
751 Expr::Column(n) => n.clone(),
752 other => {
753 return Err(KitError::Validation(format!(
754 "unsupported order by: {other:?}"
755 )))
756 }
757 };
758 acc.sort_by(|a, b| {
759 let av = JoinScope(a).get(&key);
760 let bv = JoinScope(b).get(&key);
761 let ord = json_cmp(&av, &bv).unwrap_or(Ordering::Equal);
762 match order.direction {
763 Direction::Asc => ord,
764 Direction::Desc => ord.reverse(),
765 }
766 });
767 }
768
769 apply_limit_offset(&mut acc, query.limit, query.offset);
770 Ok(acc)
771}
772
773fn eval_pred<S: Scope>(expr: &Expr, scope: &S, ctx: &ExecCtx) -> Result<bool> {
776 Ok(match expr {
777 Expr::Column(name) => truthy(&scope.get(name)),
778 Expr::Literal(lit) => truthy(&literal_to_value(lit)),
779 Expr::And(parts) => {
780 for p in parts {
781 if !eval_pred(p, scope, ctx)? {
782 return Ok(false);
783 }
784 }
785 true
786 }
787 Expr::Or(parts) => {
788 for p in parts {
789 if eval_pred(p, scope, ctx)? {
790 return Ok(true);
791 }
792 }
793 false
794 }
795 Expr::Not(inner) => !eval_pred(inner, scope, ctx)?,
796 Expr::Eq(a, b) => cmp(a, b, scope, ctx)? == Some(Ordering::Equal),
797 Expr::Ne(a, b) => cmp(a, b, scope, ctx)? != Some(Ordering::Equal),
798 Expr::Gt(a, b) => cmp(a, b, scope, ctx)? == Some(Ordering::Greater),
799 Expr::Gte(a, b) => cmp(a, b, scope, ctx)?.is_some_and(|o| o != Ordering::Less),
800 Expr::Lt(a, b) => cmp(a, b, scope, ctx)? == Some(Ordering::Less),
801 Expr::Lte(a, b) => cmp(a, b, scope, ctx)?.is_some_and(|o| o != Ordering::Greater),
802 Expr::In(col, list) => {
803 let v = eval_val(col, scope, ctx)?;
804 list.iter().any(|lit| v == literal_to_value(lit))
805 }
806 Expr::NotIn(col, list) => {
807 let v = eval_val(col, scope, ctx)?;
808 list.iter().all(|lit| v != literal_to_value(lit))
809 }
810 Expr::IsNull(inner) => eval_val(inner, scope, ctx)?.is_null(),
811 Expr::IsNotNull(inner) => !eval_val(inner, scope, ctx)?.is_null(),
812 Expr::Like(col, pattern) => match eval_val(col, scope, ctx)? {
813 Value::String(s) => like(&s, pattern),
814 _ => false,
815 },
816 Expr::Contains(col, needle) => match eval_val(col, scope, ctx)? {
817 Value::String(s) => s.contains(needle.as_str()),
818 _ => false,
819 },
820 Expr::BytesPrefix(col, prefix) => match eval_val(col, scope, ctx)? {
821 Value::String(s) => s.as_bytes().starts_with(prefix.as_bytes()),
827 Value::Array(elems) => {
828 bytes_from_json_array(&elems).is_some_and(|b| b.starts_with(prefix.as_bytes()))
829 }
830 _ => false,
831 },
832 Expr::InSubquery(col, sub) => {
833 let v = eval_val(col, scope, ctx)?;
838 let key = subquery_column(sub);
839 run_select(ctx, sub)?
840 .iter()
841 .any(|r| subquery_value(r, key.as_deref()) == v)
842 }
843 Expr::Exists(sub) => !run_select(ctx, sub)?.is_empty(),
844 Expr::NotExists(sub) => run_select(ctx, sub)?.is_empty(),
845 })
846}
847
848fn eval_val<S: Scope>(expr: &Expr, scope: &S, _ctx: &ExecCtx) -> Result<Value> {
849 Ok(match expr {
850 Expr::Column(name) => scope.get(name),
851 Expr::Literal(lit) => literal_to_value(lit),
852 other => {
853 return Err(KitError::Validation(format!(
854 "expression {other:?} cannot be used as a scalar value"
855 )))
856 }
857 })
858}
859
860fn cmp<S: Scope>(a: &Expr, b: &Expr, scope: &S, ctx: &ExecCtx) -> Result<Option<Ordering>> {
861 Ok(json_cmp(
862 &eval_val(a, scope, ctx)?,
863 &eval_val(b, scope, ctx)?,
864 ))
865}
866
867fn subquery_column(select: &Select) -> Option<String> {
870 select.columns.iter().find_map(|e| match e {
871 Expr::Column(n) => Some(n.clone()),
872 _ => None,
873 })
874}
875
876fn subquery_value(row: &Row, key: Option<&str>) -> Value {
877 match key {
878 Some(k) => row.values.get(k).cloned().unwrap_or(Value::Null),
879 None => row.values.values().next().cloned().unwrap_or(Value::Null),
880 }
881}
882
883fn literal_to_value(lit: &Literal) -> Value {
884 match lit {
885 Literal::Null => Value::Null,
886 Literal::Bool(b) => Value::Bool(*b),
887 Literal::Int(i) => Value::Number((*i).into()),
888 Literal::Float(f) => serde_json::to_value(*f).unwrap_or(Value::Null),
889 Literal::Text(s) => Value::String(s.clone()),
890 Literal::Json(v) => v.clone(),
891 }
892}
893
894fn json_cmp(a: &Value, b: &Value) -> Option<Ordering> {
895 match (a, b) {
896 (Value::Null, Value::Null) => Some(Ordering::Equal),
897 (Value::Null, _) | (_, Value::Null) => None,
898 (Value::Bool(a), Value::Bool(b)) => Some(a.cmp(b)),
899 (Value::Number(a), Value::Number(b)) => {
900 if let (Some(ai), Some(bi)) = (a.as_i64(), b.as_i64()) {
901 Some(ai.cmp(&bi))
902 } else {
903 let af = a.as_f64().unwrap_or(f64::NAN);
904 let bf = b.as_f64().unwrap_or(f64::NAN);
905 af.partial_cmp(&bf)
906 }
907 }
908 (Value::String(a), Value::String(b)) => Some(a.cmp(b)),
909 (Value::Array(a), Value::Array(b)) => compare_arrays(a, b),
910 (Value::Object(a), Value::Object(b)) => compare_objects(a, b),
911 _ => None,
912 }
913}
914
915fn compare_arrays(a: &[Value], b: &[Value]) -> Option<Ordering> {
916 let len_cmp = a.len().partial_cmp(&b.len())?;
917 if len_cmp != Ordering::Equal {
918 return Some(len_cmp);
919 }
920 for (x, y) in a.iter().zip(b.iter()) {
921 match json_cmp(x, y) {
922 Some(Ordering::Equal) => {}
923 other => return other,
924 }
925 }
926 Some(Ordering::Equal)
927}
928
929fn compare_objects(a: &Map<String, Value>, b: &Map<String, Value>) -> Option<Ordering> {
930 let len_cmp = a.len().partial_cmp(&b.len())?;
931 if len_cmp != Ordering::Equal {
932 return Some(len_cmp);
933 }
934 let mut a_keys: Vec<&String> = a.keys().collect();
935 let mut b_keys: Vec<&String> = b.keys().collect();
936 a_keys.sort();
937 b_keys.sort();
938 for (ak, bk) in a_keys.iter().zip(b_keys.iter()) {
939 match ak.cmp(bk) {
940 Ordering::Equal => {}
941 other => return Some(other),
942 }
943 let av = a.get(*ak).unwrap();
944 let bv = b.get(*bk).unwrap();
945 match json_cmp(av, bv) {
946 Some(Ordering::Equal) => {}
947 other => return other,
948 }
949 }
950 Some(Ordering::Equal)
951}
952
953fn truthy(v: &Value) -> bool {
954 match v {
955 Value::Null => false,
956 Value::Bool(b) => *b,
957 Value::Number(n) => n.as_f64().map(|f| f != 0.0).unwrap_or(false),
958 Value::String(s) => !s.is_empty(),
959 Value::Array(a) => !a.is_empty(),
960 Value::Object(o) => !o.is_empty(),
961 }
962}
963
964fn sort_rows(ctx: &ExecCtx, rows: &mut [Row], table: &str, order: &OrderBy) -> Result<()> {
965 let col_name = match &order.expr {
966 Expr::Column(name) => name.clone(),
967 other => {
968 return Err(KitError::Validation(format!(
969 "unsupported order by: {other:?}"
970 )))
971 }
972 };
973 if let Some(t) = ctx.table_def(table) {
976 if t.column(&col_name).is_none() {
977 return Err(KitError::Validation(format!(
978 "unknown order column {col_name}"
979 )));
980 }
981 }
982
983 rows.sort_by(|a, b| {
984 let av = a.values.get(&col_name).cloned().unwrap_or(Value::Null);
985 let bv = b.values.get(&col_name).cloned().unwrap_or(Value::Null);
986 let ord = json_cmp(&av, &bv).unwrap_or(Ordering::Equal);
987 match order.direction {
988 Direction::Asc => ord,
989 Direction::Desc => ord.reverse(),
990 }
991 });
992 Ok(())
993}
994
995fn apply_limit_offset<T>(rows: &mut Vec<T>, limit: Option<usize>, offset: Option<usize>) {
996 let offset = offset.unwrap_or(0);
997 if offset > 0 || limit.is_some() {
998 let start = offset.min(rows.len());
999 let end = limit
1000 .map(|l| start + l)
1001 .unwrap_or(rows.len())
1002 .min(rows.len());
1003 *rows = rows.drain(start..end).collect();
1004 }
1005}
1006
1007fn like(text: &str, pattern: &str) -> bool {
1008 let regex = match regex_like(pattern) {
1009 Ok(re) => re,
1010 Err(_) => return false,
1011 };
1012 regex.is_match(text)
1013}
1014
1015fn bytes_from_json_array(elems: &[Value]) -> Option<Vec<u8>> {
1019 let mut out = Vec::with_capacity(elems.len());
1020 for v in elems {
1021 match v {
1022 Value::Number(n) => out.push(n.as_u64()? as u8),
1023 _ => return None,
1024 }
1025 }
1026 Some(out)
1027}
1028
1029fn regex_like(pattern: &str) -> Result<regex::Regex> {
1030 let mut out = String::with_capacity(pattern.len() * 2);
1031 out.push('^');
1032 for ch in pattern.chars() {
1033 match ch {
1034 '%' => out.push_str(".*"),
1035 '_' => out.push('.'),
1036 c => {
1037 if regex::escape(&c.to_string()).len() > 1 {
1038 out.push_str(®ex::escape(&c.to_string()));
1039 } else {
1040 out.push(c);
1041 }
1042 }
1043 }
1044 }
1045 out.push('$');
1046 regex::Regex::new(&out).map_err(|e| KitError::Validation(format!("invalid LIKE pattern: {e}")))
1047}