Skip to main content

marsdb_query/
params.rs

1use std::collections::HashMap;
2
3use marsdb_graph::PropertyValue;
4
5use crate::ast::{Expr, Literal, NodePattern, Pattern, QueryPart, ReturnExpr, Statement, Tail, WithClause};
6use crate::error::QueryError;
7
8/// Resolves every `$name` placeholder in `stmt` to a concrete `Literal`
9/// using `params`, in place. Called before execution so the executor never
10/// sees `Literal::Param` — see the `unreachable!` in
11/// `executor::literal_to_value`.
12pub fn substitute_params(stmt: &mut Statement, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
13    match stmt {
14        Statement::Create(patterns) => {
15            for pattern in patterns {
16                substitute_pattern(pattern, params)?;
17            }
18        }
19        Statement::Match {
20            parts,
21            tail,
22            order_by,
23            limit: _,
24        } => {
25            for part in parts {
26                substitute_query_part(part, params)?;
27            }
28            substitute_tail(tail, params)?;
29            if let Some(items) = order_by {
30                for (expr, _) in items {
31                    substitute_return_expr(expr, params)?;
32                }
33            }
34        }
35    }
36    Ok(())
37}
38
39fn substitute_query_part(part: &mut QueryPart, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
40    substitute_pattern(&mut part.pattern, params)?;
41    if let Some(expr) = &mut part.where_clause {
42        substitute_expr(expr, params)?;
43    }
44    if let Some(with) = &mut part.with {
45        substitute_with_clause(with, params)?;
46    }
47    Ok(())
48}
49
50fn substitute_with_clause(with: &mut WithClause, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
51    for item in &mut with.items {
52        substitute_return_expr(&mut item.expr, params)?;
53    }
54    if let Some(items) = &mut with.order_by {
55        for (expr, _) in items {
56            substitute_return_expr(expr, params)?;
57        }
58    }
59    Ok(())
60}
61
62fn substitute_pattern(pattern: &mut Pattern, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
63    substitute_node(&mut pattern.start, params)?;
64    for (rel, node) in &mut pattern.hops {
65        for (_, lit) in &mut rel.props {
66            substitute_literal(lit, params)?;
67        }
68        substitute_node(node, params)?;
69    }
70    Ok(())
71}
72
73fn substitute_node(node: &mut NodePattern, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
74    for (_, lit) in &mut node.props {
75        substitute_literal(lit, params)?;
76    }
77    Ok(())
78}
79
80fn substitute_expr(expr: &mut Expr, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
81    match expr {
82        Expr::And(l, r) | Expr::Or(l, r) => {
83            substitute_expr(l, params)?;
84            substitute_expr(r, params)?;
85        }
86        Expr::Not(e) => substitute_expr(e, params)?,
87        Expr::Compare(_, _, lit) => substitute_literal(lit, params)?,
88        Expr::HasLabel(_, _) => {}
89        Expr::VarEq(_, _) => {}
90    }
91    Ok(())
92}
93
94fn substitute_tail(tail: &mut Tail, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
95    match tail {
96        Tail::Return(items) => {
97            for item in items {
98                substitute_return_expr(&mut item.expr, params)?;
99            }
100        }
101        Tail::Delete(_) | Tail::DetachDelete(_) => {}
102        Tail::Set(items) => {
103            for (_, lit) in items {
104                substitute_literal(lit, params)?;
105            }
106        }
107    }
108    Ok(())
109}
110
111fn substitute_return_expr(expr: &mut ReturnExpr, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
112    match expr {
113        ReturnExpr::Var(_) | ReturnExpr::Prop(_) => {}
114        ReturnExpr::Lit(lit) => substitute_literal(lit, params)?,
115        ReturnExpr::Call(_, args) => {
116            for arg in args {
117                substitute_return_expr(arg, params)?;
118            }
119        }
120        ReturnExpr::Case { test, whens, else_ } => {
121            if let Some(t) = test {
122                substitute_return_expr(t, params)?;
123            }
124            for (when, then) in whens {
125                substitute_return_expr(when, params)?;
126                substitute_return_expr(then, params)?;
127            }
128            if let Some(e) = else_ {
129                substitute_return_expr(e, params)?;
130            }
131        }
132    }
133    Ok(())
134}
135
136fn substitute_literal(lit: &mut Literal, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
137    if let Literal::Param(name) = lit {
138        let value = params
139            .get(name)
140            .ok_or_else(|| QueryError::MissingParam(name.clone()))?;
141        *lit = property_value_to_literal(value);
142    }
143    Ok(())
144}
145
146fn property_value_to_literal(pv: &PropertyValue) -> Literal {
147    match pv {
148        PropertyValue::Null => Literal::Null,
149        PropertyValue::Bool(b) => Literal::Bool(*b),
150        PropertyValue::Int(i) => Literal::Int(*i),
151        PropertyValue::Float(f) => Literal::Float(*f),
152        PropertyValue::String(s) => Literal::String(s.clone()),
153    }
154}