use crate::{
db::{QueryError, query::plan::expr::Expr},
value::Value,
};
pub trait ValueProjectionExpr {
fn field(&self) -> &str;
fn sql_label(&self) -> String;
fn apply_value(&self, value: Value) -> Result<Value, QueryError>;
}
#[must_use]
pub(in crate::db) fn render_scalar_projection_expr_sql_label(expr: &Expr) -> String {
match expr {
Expr::Field(field) => field.as_str().to_string(),
Expr::Literal(value) => render_scalar_projection_literal(value),
Expr::FunctionCall { function, args } => {
let rendered_args = args
.iter()
.map(render_scalar_projection_expr_sql_label)
.collect::<Vec<_>>()
.join(", ");
format!("{}({rendered_args})", function.sql_label())
}
Expr::Binary { op, left, right } => {
let left = render_scalar_projection_expr_sql_label(left.as_ref());
let right = render_scalar_projection_expr_sql_label(right.as_ref());
format!("{left} {} {right}", binary_op_sql_label(*op))
}
Expr::Aggregate(aggregate) => {
let kind = aggregate.kind().sql_label();
let distinct = if aggregate.is_distinct() {
"DISTINCT "
} else {
""
};
if let Some(field) = aggregate.target_field() {
return format!("{kind}({distinct}{field})");
}
format!("{kind}({distinct}*)")
}
#[cfg(test)]
Expr::Alias { expr, .. } => render_scalar_projection_expr_sql_label(expr.as_ref()),
#[cfg(test)]
Expr::Unary { .. } => "expr".to_string(),
}
}
const fn binary_op_sql_label(op: crate::db::query::plan::expr::BinaryOp) -> &'static str {
match op {
crate::db::query::plan::expr::BinaryOp::Add => "+",
crate::db::query::plan::expr::BinaryOp::Sub => "-",
crate::db::query::plan::expr::BinaryOp::Mul => "*",
crate::db::query::plan::expr::BinaryOp::Div => "/",
#[cfg(test)]
crate::db::query::plan::expr::BinaryOp::And => "AND",
#[cfg(test)]
crate::db::query::plan::expr::BinaryOp::Eq => "=",
}
}
fn render_scalar_projection_literal(value: &Value) -> String {
match value {
Value::Null => "NULL".to_string(),
Value::Text(text) => format!("'{}'", text.replace('\'', "''")),
Value::Int(value) => value.to_string(),
Value::Int128(value) => value.to_string(),
Value::IntBig(value) => value.to_string(),
Value::Uint(value) => value.to_string(),
Value::Uint128(value) => value.to_string(),
Value::UintBig(value) => value.to_string(),
Value::Decimal(value) => value.to_string(),
Value::Float32(value) => value.to_string(),
Value::Float64(value) => value.to_string(),
Value::Bool(value) => value.to_string().to_uppercase(),
other => format!("{other:?}"),
}
}