hawdb_expression/sql/
numeric.rs1use super::{Expr, ExprKind, SqlValue};
8use hawdb_core::numeric::{cast_numeric, evaluate_numeric, negate_numeric};
9use hawdb_core::{HawDBError, Result, Value};
10
11const MAX_DEPTH: usize = 64;
12const MAX_NODES: usize = 4096;
13
14pub fn bind_sql_value(value: &SqlValue, parameters: &[Value]) -> Result<Value> {
15 Binder {
16 parameters,
17 remaining: MAX_NODES,
18 }
19 .value(value, 0)
20}
21
22pub fn bind_constant_numeric(expression: &Expr, parameters: &[Value]) -> Result<Value> {
23 Binder {
24 parameters,
25 remaining: MAX_NODES,
26 }
27 .expression(expression, 0)
28}
29
30struct Binder<'a> {
31 parameters: &'a [Value],
32 remaining: usize,
33}
34
35impl Binder<'_> {
36 fn admit(&mut self, depth: usize) -> Result<()> {
37 if depth > MAX_DEPTH || self.remaining == 0 {
38 return Err(HawDBError::Semantic(
39 "numeric expression exceeds 64 levels or 4096 nodes".into(),
40 ));
41 }
42 self.remaining -= 1;
43 Ok(())
44 }
45
46 fn value(&mut self, value: &SqlValue, depth: usize) -> Result<Value> {
47 self.admit(depth)?;
48 match value {
49 SqlValue::Literal(value) => Ok(value.clone()),
50 SqlValue::Parameter(position) => position
51 .checked_sub(1)
52 .and_then(|index| self.parameters.get(index))
53 .cloned()
54 .ok_or_else(|| {
55 HawDBError::Semantic(format!("missing PostgreSQL parameter ${position}"))
56 }),
57 SqlValue::Expression(expression) => self.expression(expression, depth + 1),
58 }
59 }
60
61 fn expression(&mut self, expression: &Expr, depth: usize) -> Result<Value> {
62 self.admit(depth)?;
63 match &expression.kind {
64 ExprKind::Value(value) => self.value(value, depth + 1),
65 ExprKind::Arithmetic { left, op, right } => {
66 let left = self.expression(left, depth + 1)?;
67 let right = self.expression(right, depth + 1)?;
68 evaluate_numeric(left.as_ref(), *op, right.as_ref())
69 }
70 ExprKind::Negate(expression) => {
71 negate_numeric(self.expression(expression, depth + 1)?.as_ref())
72 }
73 ExprKind::Cast { expression, target } => {
74 cast_numeric(self.expression(expression, depth + 1)?.as_ref(), *target)
75 }
76 _ => Err(HawDBError::Semantic(
77 "expected a constant numeric expression".into(),
78 )),
79 }
80 }
81}