1use std::collections::HashMap;
2
3use marsdb_graph::PropertyValue;
4
5use crate::ast::{Expr, Literal, NodePattern, Pattern, QueryPart, ReturnExpr, Statement, Tail, WithClause, WithExpr};
6use crate::error::QueryError;
7
8pub 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(where_clause) = &mut with.where_clause {
55 substitute_with_expr(where_clause, params)?;
56 }
57 if let Some(items) = &mut with.order_by {
58 for (expr, _) in items {
59 substitute_return_expr(expr, params)?;
60 }
61 }
62 Ok(())
63}
64
65fn substitute_with_expr(expr: &mut WithExpr, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
66 match expr {
67 WithExpr::And(l, r) | WithExpr::Or(l, r) => {
68 substitute_with_expr(l, params)?;
69 substitute_with_expr(r, params)?;
70 }
71 WithExpr::Not(e) => substitute_with_expr(e, params)?,
72 WithExpr::Compare(lhs, _, lit) => {
73 substitute_return_expr(lhs, params)?;
74 substitute_literal(lit, params)?;
75 }
76 }
77 Ok(())
78}
79
80fn substitute_pattern(pattern: &mut Pattern, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
81 substitute_node(&mut pattern.start, params)?;
82 for (rel, node) in &mut pattern.hops {
83 for (_, lit) in &mut rel.props {
84 substitute_literal(lit, params)?;
85 }
86 substitute_node(node, params)?;
87 }
88 Ok(())
89}
90
91fn substitute_node(node: &mut NodePattern, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
92 for (_, lit) in &mut node.props {
93 substitute_literal(lit, params)?;
94 }
95 Ok(())
96}
97
98fn substitute_expr(expr: &mut Expr, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
99 match expr {
100 Expr::And(l, r) | Expr::Or(l, r) => {
101 substitute_expr(l, params)?;
102 substitute_expr(r, params)?;
103 }
104 Expr::Not(e) => substitute_expr(e, params)?,
105 Expr::Compare(_, _, lit) => substitute_literal(lit, params)?,
106 Expr::HasLabel(_, _) => {}
107 Expr::VarEq(_, _) => {}
108 }
109 Ok(())
110}
111
112fn substitute_tail(tail: &mut Tail, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
113 match tail {
114 Tail::Return(items) => {
115 for item in items {
116 substitute_return_expr(&mut item.expr, params)?;
117 }
118 }
119 Tail::Delete(_) | Tail::DetachDelete(_) => {}
120 Tail::Set(items) => {
121 for (_, lit) in items {
122 substitute_literal(lit, params)?;
123 }
124 }
125 }
126 Ok(())
127}
128
129fn substitute_return_expr(expr: &mut ReturnExpr, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
130 match expr {
131 ReturnExpr::Var(_) | ReturnExpr::Prop(_) | ReturnExpr::CountStar => {}
132 ReturnExpr::Lit(lit) => substitute_literal(lit, params)?,
133 ReturnExpr::Call { args, .. } => {
134 for arg in args {
135 substitute_return_expr(arg, params)?;
136 }
137 }
138 ReturnExpr::Case { test, whens, else_ } => {
139 if let Some(t) = test {
140 substitute_return_expr(t, params)?;
141 }
142 for (when, then) in whens {
143 substitute_return_expr(when, params)?;
144 substitute_return_expr(then, params)?;
145 }
146 if let Some(e) = else_ {
147 substitute_return_expr(e, params)?;
148 }
149 }
150 }
151 Ok(())
152}
153
154fn substitute_literal(lit: &mut Literal, params: &HashMap<String, PropertyValue>) -> Result<(), QueryError> {
155 if let Literal::Param(name) = lit {
156 let value = params
157 .get(name)
158 .ok_or_else(|| QueryError::MissingParam(name.clone()))?;
159 *lit = property_value_to_literal(value);
160 }
161 Ok(())
162}
163
164fn property_value_to_literal(pv: &PropertyValue) -> Literal {
165 match pv {
166 PropertyValue::Null => Literal::Null,
167 PropertyValue::Bool(b) => Literal::Bool(*b),
168 PropertyValue::Int(i) => Literal::Int(*i),
169 PropertyValue::Float(f) => Literal::Float(*f),
170 PropertyValue::String(s) => Literal::String(s.clone()),
171 }
172}