Skip to content

Commit d7a0182

Browse files
committed
fixup
1 parent abf1347 commit d7a0182

4 files changed

Lines changed: 274 additions & 232 deletions

File tree

crates/lance-graph/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub mod error;
4343
pub mod lance_native_planner;
4444
pub mod lance_vector_search;
4545
pub mod logical_plan;
46+
pub mod namespace;
47+
pub mod parameter_substitution;
4648
pub mod parser;
4749
pub mod query;
4850
pub mod semantic;
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
use crate::ast::*;
2+
use crate::error::{GraphError, Result};
3+
use std::collections::HashMap;
4+
5+
/// Substitute parameters with literal values in the AST
6+
pub fn substitute_parameters(
7+
query: &mut CypherQuery,
8+
parameters: &HashMap<String, serde_json::Value>,
9+
) -> Result<()> {
10+
// Substitute in READING clauses
11+
for clause in &mut query.reading_clauses {
12+
substitute_in_reading_clause(clause, parameters)?;
13+
}
14+
15+
// Substitute in WHERE clause
16+
if let Some(where_clause) = &mut query.where_clause {
17+
substitute_in_where_clause(where_clause, parameters)?;
18+
}
19+
20+
// Substitute in WITH clause
21+
if let Some(with_clause) = &mut query.with_clause {
22+
substitute_in_with_clause(with_clause, parameters)?;
23+
}
24+
25+
// Substitute in post-WITH READING clauses
26+
for clause in &mut query.post_with_reading_clauses {
27+
substitute_in_reading_clause(clause, parameters)?;
28+
}
29+
30+
// Substitute in post-WITH WHERE clause
31+
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
36+
substitute_in_return_clause(&mut query.return_clause, parameters)?;
37+
38+
// Substitute in ORDER BY clause
39+
if let Some(order_by) = &mut query.order_by {
40+
substitute_in_order_by_clause(order_by, parameters)?;
41+
}
42+
43+
Ok(())
44+
}
45+
46+
fn 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+
63+
fn 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+
84+
fn 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+
94+
fn 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+
104+
fn 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 = parameters.get(name).ok_or_else(|| GraphError::PlanError {
110+
message: format!("Missing parameter: ${}", name),
111+
location: snafu::Location::new(file!(), line!(), column!()),
112+
})?;
113+
114+
*value = json_to_property_value(param_value)?;
115+
}
116+
Ok(())
117+
}
118+
119+
fn substitute_in_where_clause(
120+
where_clause: &mut WhereClause,
121+
parameters: &HashMap<String, serde_json::Value>,
122+
) -> Result<()> {
123+
substitute_in_boolean_expression(&mut where_clause.expression, parameters)
124+
}
125+
126+
fn substitute_in_with_clause(
127+
with_clause: &mut WithClause,
128+
parameters: &HashMap<String, serde_json::Value>,
129+
) -> Result<()> {
130+
for item in &mut with_clause.items {
131+
substitute_in_value_expression(&mut item.expression, parameters)?;
132+
}
133+
if let Some(order_by) = &mut with_clause.order_by {
134+
substitute_in_order_by_clause(order_by, parameters)?;
135+
}
136+
Ok(())
137+
}
138+
139+
fn substitute_in_return_clause(
140+
return_clause: &mut ReturnClause,
141+
parameters: &HashMap<String, serde_json::Value>,
142+
) -> Result<()> {
143+
for item in &mut return_clause.items {
144+
substitute_in_value_expression(&mut item.expression, parameters)?;
145+
}
146+
Ok(())
147+
}
148+
149+
fn substitute_in_order_by_clause(
150+
order_by: &mut OrderByClause,
151+
parameters: &HashMap<String, serde_json::Value>,
152+
) -> Result<()> {
153+
for item in &mut order_by.items {
154+
substitute_in_value_expression(&mut item.expression, parameters)?;
155+
}
156+
Ok(())
157+
}
158+
159+
fn substitute_in_boolean_expression(
160+
expr: &mut BooleanExpression,
161+
parameters: &HashMap<String, serde_json::Value>,
162+
) -> Result<()> {
163+
match expr {
164+
BooleanExpression::Comparison { left, right, .. } => {
165+
substitute_in_value_expression(left, parameters)?;
166+
substitute_in_value_expression(right, parameters)?;
167+
}
168+
BooleanExpression::And(left, right) | BooleanExpression::Or(left, right) => {
169+
substitute_in_boolean_expression(left, parameters)?;
170+
substitute_in_boolean_expression(right, parameters)?;
171+
}
172+
BooleanExpression::Not(inner) => {
173+
substitute_in_boolean_expression(inner, parameters)?;
174+
}
175+
BooleanExpression::Exists(_) => {}
176+
BooleanExpression::In { expression, list } => {
177+
substitute_in_value_expression(expression, parameters)?;
178+
for item in list {
179+
substitute_in_value_expression(item, parameters)?;
180+
}
181+
}
182+
BooleanExpression::Like { expression, .. }
183+
| BooleanExpression::ILike { expression, .. }
184+
| BooleanExpression::Contains { expression, .. }
185+
| BooleanExpression::StartsWith { expression, .. }
186+
| BooleanExpression::EndsWith { expression, .. }
187+
| BooleanExpression::IsNull(expression)
188+
| BooleanExpression::IsNotNull(expression) => {
189+
substitute_in_value_expression(expression, parameters)?;
190+
}
191+
}
192+
Ok(())
193+
}
194+
195+
fn substitute_in_value_expression(
196+
expr: &mut ValueExpression,
197+
parameters: &HashMap<String, serde_json::Value>,
198+
) -> Result<()> {
199+
match expr {
200+
ValueExpression::Parameter(name) => {
201+
let param_value = parameters.get(name).ok_or_else(|| GraphError::PlanError {
202+
message: format!("Missing parameter: ${}", name),
203+
location: snafu::Location::new(file!(), line!(), column!()),
204+
})?;
205+
206+
// Check for array to VectorLiteral conversion
207+
if let serde_json::Value::Array(arr) = param_value {
208+
let mut floats = Vec::new();
209+
for v in arr {
210+
if let Some(f) = v.as_f64() {
211+
floats.push(f as f32);
212+
} else {
213+
return Err(GraphError::PlanError {
214+
message: format!(
215+
"Parameter ${} is a list but contains non-numeric values. Only float vectors are supported as list parameters currently.",
216+
name
217+
),
218+
location: snafu::Location::new(file!(), line!(), column!()),
219+
});
220+
}
221+
}
222+
*expr = ValueExpression::VectorLiteral(floats);
223+
return Ok(());
224+
}
225+
226+
// Scalar conversion
227+
let prop_val = json_to_property_value(param_value)?;
228+
*expr = ValueExpression::Literal(prop_val);
229+
}
230+
ValueExpression::ScalarFunction { args, .. }
231+
| ValueExpression::AggregateFunction { args, .. } => {
232+
for arg in args {
233+
substitute_in_value_expression(arg, parameters)?;
234+
}
235+
}
236+
ValueExpression::Arithmetic { left, right, .. } => {
237+
substitute_in_value_expression(left, parameters)?;
238+
substitute_in_value_expression(right, parameters)?;
239+
}
240+
ValueExpression::VectorDistance { left, right, .. }
241+
| ValueExpression::VectorSimilarity { left, right, .. } => {
242+
substitute_in_value_expression(left, parameters)?;
243+
substitute_in_value_expression(right, parameters)?;
244+
}
245+
_ => {}
246+
}
247+
Ok(())
248+
}
249+
250+
fn json_to_property_value(value: &serde_json::Value) -> Result<PropertyValue> {
251+
match value {
252+
serde_json::Value::Null => Ok(PropertyValue::Null),
253+
serde_json::Value::Bool(b) => Ok(PropertyValue::Boolean(*b)),
254+
serde_json::Value::Number(n) => {
255+
if let Some(i) = n.as_i64() {
256+
Ok(PropertyValue::Integer(i))
257+
} else if let Some(f) = n.as_f64() {
258+
Ok(PropertyValue::Float(f))
259+
} else {
260+
Ok(PropertyValue::Null)
261+
}
262+
}
263+
serde_json::Value::String(s) => Ok(PropertyValue::String(s.clone())),
264+
serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
265+
Err(GraphError::PlanError {
266+
message: "Complex types (List, Map) are not fully supported as parameters yet (except float vectors).".to_string(),
267+
location: snafu::Location::new(file!(), line!(), column!()),
268+
})
269+
}
270+
}
271+
}

0 commit comments

Comments
 (0)