icydb_core/db/query/builder/
scalar_projection.rs1use crate::{
10 db::{QueryError, query::plan::expr::Expr},
11 value::Value,
12};
13
14pub trait ValueProjectionExpr {
24 fn field(&self) -> &str;
26
27 fn sql_label(&self) -> String;
29
30 fn apply_value(&self, value: Value) -> Result<Value, QueryError>;
32}
33
34#[must_use]
37pub(in crate::db) fn render_scalar_projection_expr_sql_label(expr: &Expr) -> String {
38 match expr {
39 Expr::Field(field) => field.as_str().to_string(),
40 Expr::Literal(value) => render_scalar_projection_literal(value),
41 Expr::FunctionCall { function, args } => {
42 let rendered_args = args
43 .iter()
44 .map(render_scalar_projection_expr_sql_label)
45 .collect::<Vec<_>>()
46 .join(", ");
47
48 format!("{}({rendered_args})", function.sql_label())
49 }
50 Expr::Binary { op, left, right } => {
51 let left = render_scalar_projection_expr_sql_label(left.as_ref());
52 let right = render_scalar_projection_expr_sql_label(right.as_ref());
53
54 format!("{left} {} {right}", binary_op_sql_label(*op))
55 }
56 Expr::Aggregate(aggregate) => {
57 let kind = aggregate.kind().sql_label();
58 let distinct = if aggregate.is_distinct() {
59 "DISTINCT "
60 } else {
61 ""
62 };
63
64 if let Some(input_expr) = aggregate.input_expr() {
65 let input = render_scalar_projection_expr_sql_label(input_expr);
66
67 return format!("{kind}({distinct}{input})");
68 }
69
70 format!("{kind}({distinct}*)")
71 }
72 #[cfg(test)]
73 Expr::Alias { expr, .. } => render_scalar_projection_expr_sql_label(expr.as_ref()),
74 #[cfg(test)]
75 Expr::Unary { .. } => "expr".to_string(),
76 }
77}
78
79const fn binary_op_sql_label(op: crate::db::query::plan::expr::BinaryOp) -> &'static str {
80 match op {
81 crate::db::query::plan::expr::BinaryOp::Add => "+",
82 crate::db::query::plan::expr::BinaryOp::Sub => "-",
83 crate::db::query::plan::expr::BinaryOp::Mul => "*",
84 crate::db::query::plan::expr::BinaryOp::Div => "/",
85 #[cfg(test)]
86 crate::db::query::plan::expr::BinaryOp::And => "AND",
87 #[cfg(test)]
88 crate::db::query::plan::expr::BinaryOp::Eq => "=",
89 }
90}
91
92fn render_scalar_projection_literal(value: &Value) -> String {
93 match value {
94 Value::Null => "NULL".to_string(),
95 Value::Text(text) => format!("'{}'", text.replace('\'', "''")),
96 Value::Int(value) => value.to_string(),
97 Value::Int128(value) => value.to_string(),
98 Value::IntBig(value) => value.to_string(),
99 Value::Uint(value) => value.to_string(),
100 Value::Uint128(value) => value.to_string(),
101 Value::UintBig(value) => value.to_string(),
102 Value::Decimal(value) => value.to_string(),
103 Value::Float32(value) => value.to_string(),
104 Value::Float64(value) => value.to_string(),
105 Value::Bool(value) => value.to_string().to_uppercase(),
106 other => format!("{other:?}"),
107 }
108}