reddb_engine/query/
scalar.rs1#![deny(
4 clippy::unwrap_used,
5 clippy::too_many_lines,
6 clippy::cast_possible_truncation
7)]
8
9use reddb_rql::ast::{FieldRef, QueryExpr, SelectItem};
10use reddb_rql::sql_lowering::effective_table_projections;
11use reddb_types::Value;
12
13use super::evaluator;
14use super::labels::projection_name;
15
16#[derive(Debug)]
17pub struct ScalarResult {
18 pub columns: Vec<String>,
19 pub rows: Vec<Vec<Value>>,
20}
21
22impl ScalarResult {
23 pub fn to_json(&self) -> reddb_types::json::Value {
25 reddb_types::json!({
26 "columns": self.columns,
27 "rows": self.rows.iter().map(|row| {
28 row.iter().map(Value::to_json).collect::<Vec<_>>()
29 }).collect::<Vec<_>>()
30 })
31 }
32}
33
34#[derive(Debug)]
35pub enum ScalarError {
36 Parse(reddb_rql::ParseError),
37 Unsupported,
38 Evaluation(evaluator::EvalError),
39}
40
41impl std::fmt::Display for ScalarError {
42 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Self::Parse(error) => error.fmt(formatter),
45 Self::Unsupported => formatter.write_str(
46 "only storage-free scalar SELECT statements are available in this experimental executor",
47 ),
48 Self::Evaluation(error) => error.fmt(formatter),
49 }
50 }
51}
52
53impl std::error::Error for ScalarError {}
54
55pub fn execute(sql: &str) -> Result<ScalarResult, ScalarError> {
58 let statement = reddb_rql::parse_frontend(sql).map_err(ScalarError::Parse)?;
59 let QueryExpr::Table(query) = statement.into_query_expr() else {
60 return Err(ScalarError::Unsupported);
61 };
62 if query.table != "any"
63 || query.source.is_some()
64 || query.alias.is_some()
65 || query.select_items.is_empty()
66 || query.where_expr.is_some()
67 || query.filter.is_some()
68 || !query.group_by_exprs.is_empty()
69 || !query.group_by.is_empty()
70 || query.having_expr.is_some()
71 || query.having.is_some()
72 || !query.order_by.is_empty()
73 || query.limit_param.is_some()
74 || query.offset_param.is_some()
75 || query.expand.is_some()
76 || query.as_of.is_some()
77 || query.sessionize.is_some()
78 {
79 return Err(ScalarError::Unsupported);
80 }
81 let projections = effective_table_projections(&query);
82 let empty_row = |_: &FieldRef| None;
83 let mut values = Vec::with_capacity(query.select_items.len());
84 for item in &query.select_items {
85 let SelectItem::Expr { expr, .. } = item else {
86 return Err(ScalarError::Unsupported);
87 };
88 values.push(evaluator::evaluate(expr, &empty_row).map_err(ScalarError::Evaluation)?);
89 }
90 let rows = if query.limit == Some(0) || query.offset.is_some_and(|offset| offset > 0) {
91 Vec::new()
92 } else {
93 vec![values]
94 };
95 Ok(ScalarResult {
96 columns: projections.iter().map(projection_name).collect(),
97 rows,
98 })
99}