1use crate::ast::*;
2use crate::error::{GraphError, Result};
3use std::collections::HashMap;
4
5pub fn substitute_parameters(
7 query: &mut CypherQuery,
8 parameters: &HashMap<String, serde_json::Value>,
9) -> Result<()> {
10 for reading_clause in &mut query.reading_clauses {
12 substitute_in_reading_clause(reading_clause, parameters)?;
13 }
14
15 if let Some(where_clause) = &mut query.where_clause {
17 substitute_in_where_clause(where_clause, parameters)?;
18 }
19
20 if let Some(with_clause) = &mut query.with_clause {
22 substitute_in_with_clause(with_clause, parameters)?;
23 }
24
25 for reading_clause in &mut query.post_with_reading_clauses {
27 substitute_in_reading_clause(reading_clause, parameters)?;
28 }
29
30 if let Some(post_where) = &mut query.post_with_where_clause {
32 substitute_in_where_clause(post_where, parameters)?;
33 }
34
35 substitute_in_return_clause(&mut query.return_clause, parameters)?;
37
38 if let Some(order_by) = &mut query.order_by {
40 substitute_in_order_by_clause(order_by, parameters)?;
41 }
42
43 Ok(())
44}
45
46fn substitute_in_reading_clause(
47 clause: &mut ReadingClause,
48 parameters: &HashMap<String, serde_json::Value>,
49) -> Result<()> {
50 match clause {
51 ReadingClause::Match(match_clause) => {
52 for pattern in &mut match_clause.patterns {
53 substitute_in_graph_pattern(pattern, parameters)?;
54 }
55 }
56 ReadingClause::Unwind(unwind_clause) => {
57 substitute_in_value_expression(&mut unwind_clause.expression, parameters)?;
58 }
59 }
60 Ok(())
61}
62
63fn substitute_in_graph_pattern(
64 pattern: &mut GraphPattern,
65 parameters: &HashMap<String, serde_json::Value>,
66) -> Result<()> {
67 match pattern {
68 GraphPattern::Node(node) => {
69 for value in node.properties.values_mut() {
70 substitute_in_property_value(value, parameters)?;
71 }
72 }
73 GraphPattern::Path(path) => {
74 substitute_in_node_pattern(&mut path.start_node, parameters)?;
75 for segment in &mut path.segments {
76 substitute_in_relationship_pattern(&mut segment.relationship, parameters)?;
77 substitute_in_node_pattern(&mut segment.end_node, parameters)?;
78 }
79 }
80 }
81 Ok(())
82}
83
84fn substitute_in_node_pattern(
85 node: &mut NodePattern,
86 parameters: &HashMap<String, serde_json::Value>,
87) -> Result<()> {
88 for value in node.properties.values_mut() {
89 substitute_in_property_value(value, parameters)?;
90 }
91 Ok(())
92}
93
94fn substitute_in_relationship_pattern(
95 rel: &mut RelationshipPattern,
96 parameters: &HashMap<String, serde_json::Value>,
97) -> Result<()> {
98 for value in rel.properties.values_mut() {
99 substitute_in_property_value(value, parameters)?;
100 }
101 Ok(())
102}
103
104fn substitute_in_property_value(
105 value: &mut PropertyValue,
106 parameters: &HashMap<String, serde_json::Value>,
107) -> Result<()> {
108 if let PropertyValue::Parameter(name) = value {
109 let param_value =
110 parameters
111 .get(&name.to_lowercase())
112 .ok_or_else(|| GraphError::PlanError {
113 message: format!("Missing parameter: ${}", name),
114 location: snafu::Location::new(file!(), line!(), column!()),
115 })?;
116
117 *value = json_to_property_value(param_value)?;
118 }
119 Ok(())
120}
121
122fn substitute_in_where_clause(
123 where_clause: &mut WhereClause,
124 parameters: &HashMap<String, serde_json::Value>,
125) -> Result<()> {
126 substitute_in_boolean_expression(&mut where_clause.expression, parameters)
127}
128
129fn substitute_in_with_clause(
130 with_clause: &mut WithClause,
131 parameters: &HashMap<String, serde_json::Value>,
132) -> Result<()> {
133 for item in &mut with_clause.items {
134 substitute_in_value_expression(&mut item.expression, parameters)?;
135 }
136 if let Some(order_by) = &mut with_clause.order_by {
137 substitute_in_order_by_clause(order_by, parameters)?;
138 }
139 Ok(())
140}
141
142fn substitute_in_return_clause(
143 return_clause: &mut ReturnClause,
144 parameters: &HashMap<String, serde_json::Value>,
145) -> Result<()> {
146 for item in &mut return_clause.items {
147 substitute_in_value_expression(&mut item.expression, parameters)?;
148 }
149 Ok(())
150}
151
152fn substitute_in_order_by_clause(
153 order_by: &mut OrderByClause,
154 parameters: &HashMap<String, serde_json::Value>,
155) -> Result<()> {
156 for item in &mut order_by.items {
157 substitute_in_value_expression(&mut item.expression, parameters)?;
158 }
159 Ok(())
160}
161
162fn substitute_in_boolean_expression(
163 expr: &mut BooleanExpression,
164 parameters: &HashMap<String, serde_json::Value>,
165) -> Result<()> {
166 match expr {
167 BooleanExpression::Comparison { left, right, .. } => {
168 substitute_in_value_expression(left, parameters)?;
169 substitute_in_value_expression(right, parameters)?;
170 }
171 BooleanExpression::And(left, right) | BooleanExpression::Or(left, right) => {
172 substitute_in_boolean_expression(left, parameters)?;
173 substitute_in_boolean_expression(right, parameters)?;
174 }
175 BooleanExpression::Not(inner) => {
176 substitute_in_boolean_expression(inner, parameters)?;
177 }
178 BooleanExpression::Exists(_) => {}
179 BooleanExpression::In { expression, list } => {
180 substitute_in_value_expression(expression, parameters)?;
181 for item in list {
182 substitute_in_value_expression(item, parameters)?;
183 }
184 }
185 BooleanExpression::Like { expression, .. }
186 | BooleanExpression::ILike { expression, .. }
187 | BooleanExpression::Contains { expression, .. }
188 | BooleanExpression::StartsWith { expression, .. }
189 | BooleanExpression::EndsWith { expression, .. }
190 | BooleanExpression::IsNull(expression)
191 | BooleanExpression::IsNotNull(expression) => {
192 substitute_in_value_expression(expression, parameters)?;
193 }
194 }
195 Ok(())
196}
197
198fn substitute_in_value_expression(
199 expr: &mut ValueExpression,
200 parameters: &HashMap<String, serde_json::Value>,
201) -> Result<()> {
202 match expr {
203 ValueExpression::Parameter(name) => {
204 let param_value =
205 parameters
206 .get(&name.to_lowercase())
207 .ok_or_else(|| GraphError::PlanError {
208 message: format!("Missing parameter: ${}", name),
209 location: snafu::Location::new(file!(), line!(), column!()),
210 })?;
211
212 if let serde_json::Value::Array(arr) = param_value {
214 let mut floats = Vec::new();
215 for v in arr {
216 if let Some(f) = v.as_f64() {
217 floats.push(f as f32);
218 } else {
219 return Err(GraphError::PlanError {
220 message: format!(
221 "Parameter ${} is a list but contains non-numeric values. Only float vectors are supported as list parameters currently.",
222 name
223 ),
224 location: snafu::Location::new(file!(), line!(), column!()),
225 });
226 }
227 }
228 *expr = ValueExpression::VectorLiteral(floats);
229 return Ok(());
230 }
231
232 let prop_val = json_to_property_value(param_value)?;
234 *expr = ValueExpression::Literal(prop_val);
235 }
236 ValueExpression::ScalarFunction { args, .. }
237 | ValueExpression::AggregateFunction { args, .. } => {
238 for arg in args {
239 substitute_in_value_expression(arg, parameters)?;
240 }
241 }
242 ValueExpression::Arithmetic { left, right, .. } => {
243 substitute_in_value_expression(left, parameters)?;
244 substitute_in_value_expression(right, parameters)?;
245 }
246 ValueExpression::VectorDistance { left, right, .. }
247 | ValueExpression::VectorSimilarity { left, right, .. } => {
248 substitute_in_value_expression(left, parameters)?;
249 substitute_in_value_expression(right, parameters)?;
250 }
251 _ => {}
252 }
253 Ok(())
254}
255
256fn json_to_property_value(value: &serde_json::Value) -> Result<PropertyValue> {
257 match value {
258 serde_json::Value::Null => Ok(PropertyValue::Null),
259 serde_json::Value::Bool(b) => Ok(PropertyValue::Boolean(*b)),
260 serde_json::Value::Number(n) => {
261 if let Some(i) = n.as_i64() {
262 Ok(PropertyValue::Integer(i))
263 } else if let Some(f) = n.as_f64() {
264 Ok(PropertyValue::Float(f))
265 } else {
266 Err(GraphError::PlanError {
267 message: format!("Number parameter could not be converted to i64 or f64: {}", n),
268 location: snafu::Location::new(file!(), line!(), column!()),
269 })
270 }
271 }
272 serde_json::Value::String(s) => Ok(PropertyValue::String(s.clone())),
273 serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
274 Err(GraphError::PlanError {
275 message: "Complex types (List, Map) are not fully supported as parameters yet (except float vectors).".to_string(),
276 location: snafu::Location::new(file!(), line!(), column!()),
277 })
278 }
279 }
280}