linkrs_query/planning/statements/
pattern_planner.rs1use std::sync::Arc;
11
12use crate::binder::validation::ValidationInfo;
13use crate::metadata::MetadataContext;
14use crate::parser::ast::pattern::{PathElement, Pattern, VariablePattern};
15use crate::parser::ast::stmt::{MatchDeleteClause, MatchStmt};
16use crate::planning::plan::core::next_node_id;
17use crate::planning::plan::core::nodes::base::plan_node_traits::PlanNode;
18use crate::planning::plan::core::nodes::data_modification::delete_nodes::{
19 PipeDeleteEdgesNode, PipeDeleteVerticesNode,
20};
21use crate::planning::plan::core::nodes::data_modification::info::{
22 EdgeDeleteInfo, VertexDeleteInfo,
23};
24use crate::planning::plan::core::nodes::ArgumentNode;
25use crate::planning::plan::SubPlan;
26use crate::planning::planner::PlannerError;
27use crate::QueryContext;
28use linkrs_core::types::expr::expression_context::ExpressionAnalysisContext;
29use linkrs_core::types::expr::ContextualExpression;
30
31mod edge;
32mod logical_mirror;
33mod node;
34mod path;
35
36pub use edge::{plan_pattern_edge, plan_pattern_edge_with_input};
37pub use node::{plan_node_pattern, plan_pattern_node};
38pub use path::{
39 plan_alternative_patterns, plan_optional_element, plan_path_pattern, plan_repeated_element,
40};
41
42use logical_mirror::logical_argument;
43
44pub struct PlanningContext<'a> {
45 pub space_id: u64,
46 pub space_name: &'a str,
47 pub validation_info: &'a ValidationInfo,
48 pub qctx: &'a Arc<QueryContext>,
49 pub enable_index_optimization: bool,
50 pub metadata_context: &'a Option<MetadataContext>,
51 pub expr_context: &'a Option<Arc<ExpressionAnalysisContext>>,
52 pub where_expression: Option<&'a ContextualExpression>,
53 pub optional: bool,
54}
55
56pub fn plan_match_delete(
57 input_plan: SubPlan,
58 delete_clause: &MatchDeleteClause,
59 space_name: &str,
60 match_stmt: &MatchStmt,
61) -> Result<SubPlan, PlannerError> {
62 let input_node = input_plan.root().as_ref().ok_or_else(|| {
63 PlannerError::PlanGenerationFailed("The input plan has no root node".to_string())
64 })?;
65
66 let delete_node = match &delete_clause.target {
67 crate::parser::ast::stmt::MatchDeleteTarget::Vertices(vertex_exprs) => {
68 let info = VertexDeleteInfo {
69 space_name: space_name.to_string(),
70 tag: None,
71 vertex_ids: vertex_exprs.clone(),
72 with_edge: delete_clause.with_edge,
73 cascade: delete_clause.with_edge,
74 };
75 PipeDeleteVerticesNode::new(next_node_id(), info, input_node.clone()).into_enum()
76 }
77 crate::parser::ast::stmt::MatchDeleteTarget::Edges(edge_exprs) => {
78 let edges: Vec<_> = edge_exprs
79 .iter()
80 .map(|e| (e.clone(), e.clone(), None))
81 .collect();
82 let edge_type = extract_edge_type_from_patterns(&match_stmt.patterns);
83
84 let info = EdgeDeleteInfo {
85 space_name: space_name.to_string(),
86 edges,
87 edge_type,
88 };
89 PipeDeleteEdgesNode::new(next_node_id(), info, input_node.clone()).into_enum()
90 }
91 crate::parser::ast::stmt::MatchDeleteTarget::EdgeRefs(edge_refs) => {
92 let edges = edge_refs.clone();
93 let edge_type = extract_edge_type_from_patterns(&match_stmt.patterns);
94
95 let info = EdgeDeleteInfo {
96 space_name: space_name.to_string(),
97 edges,
98 edge_type,
99 };
100 PipeDeleteEdgesNode::new(next_node_id(), info, input_node.clone()).into_enum()
101 }
102 };
103
104 Ok(SubPlan::new(Some(delete_node), input_plan.tail))
105}
106
107pub fn plan_match_delete_bound(
112 input_plan: SubPlan,
113 delete_clause: &crate::binder::bound::BoundMatchDeleteClause,
114 space_name: &str,
115 edge_type: Option<String>,
116) -> Result<SubPlan, PlannerError> {
117 let input_node = input_plan.root().as_ref().ok_or_else(|| {
118 PlannerError::PlanGenerationFailed("The input plan has no root node".to_string())
119 })?;
120
121 let expr_ctx = Arc::new(ExpressionAnalysisContext::new());
122 let convert = |bound_expr: &crate::binder::bound::BoundExpression| {
123 crate::binder::expr_converter::bound_expr_to_contextual(bound_expr, &expr_ctx)
124 .map_err(PlannerError::PlanGenerationFailed)
125 };
126
127 let delete_node = match &delete_clause.target {
128 crate::binder::bound::BoundMatchDeleteTarget::Vertices(exprs) => {
129 let vertex_ids = exprs
130 .iter()
131 .map(convert)
132 .collect::<Result<Vec<ContextualExpression>, _>>()?;
133 let info = VertexDeleteInfo {
134 space_name: space_name.to_string(),
135 tag: None,
136 vertex_ids,
137 with_edge: delete_clause.with_edge,
138 cascade: delete_clause.with_edge,
139 };
140 PipeDeleteVerticesNode::new(next_node_id(), info, input_node.clone()).into_enum()
141 }
142 crate::binder::bound::BoundMatchDeleteTarget::Edges(exprs) => {
143 let edges: Vec<ContextualExpression> =
144 exprs.iter().map(convert).collect::<Result<Vec<_>, _>>()?;
145 let edges = edges.iter().map(|e| (e.clone(), e.clone(), None)).collect();
146 let info = EdgeDeleteInfo {
147 space_name: space_name.to_string(),
148 edges,
149 edge_type,
150 };
151 PipeDeleteEdgesNode::new(next_node_id(), info, input_node.clone()).into_enum()
152 }
153 crate::binder::bound::BoundMatchDeleteTarget::EdgeRefs(refs) => {
154 let mut edges = Vec::with_capacity(refs.len());
155 for (src, dst, rank) in refs {
156 edges.push((
157 convert(src)?,
158 convert(dst)?,
159 rank.as_ref().map(convert).transpose()?,
160 ));
161 }
162 let info = EdgeDeleteInfo {
163 space_name: space_name.to_string(),
164 edges,
165 edge_type,
166 };
167 PipeDeleteEdgesNode::new(next_node_id(), info, input_node.clone()).into_enum()
168 }
169 };
170
171 Ok(SubPlan::new(Some(delete_node), input_plan.tail))
172}
173
174pub fn plan_pattern(pattern: &Pattern, ctx: &PlanningContext) -> Result<SubPlan, PlannerError> {
175 match pattern {
176 Pattern::Node(node) => plan_pattern_node(
177 node,
178 ctx.space_id,
179 ctx.space_name,
180 ctx.enable_index_optimization,
181 ctx.metadata_context,
182 ctx.expr_context,
183 ctx.where_expression,
184 ),
185 Pattern::Edge(edge) => plan_pattern_edge(
186 edge,
187 ctx.space_id,
188 ctx.space_name,
189 ctx.expr_context,
190 ctx.optional,
191 ),
192 Pattern::Path(_) => plan_path_pattern(pattern, ctx),
193 Pattern::Variable(var) => plan_variable_pattern(var, ctx.space_id, ctx.validation_info),
194 }
195}
196
197pub fn plan_variable_pattern(
198 var: &VariablePattern,
199 _space_id: u64,
200 validation_info: &ValidationInfo,
201) -> Result<SubPlan, PlannerError> {
202 if !validation_info.alias_map.contains_key(&var.name) {
203 return Err(PlannerError::PlanGenerationFailed(format!(
204 "Variable '{}' undefined",
205 var.name
206 )));
207 }
208
209 let argument_node = ArgumentNode::new(0, &var.name);
210 let arg_root = argument_node.into_enum();
211 let logical_root = logical_argument(&var.name);
212 let sub_plan = SubPlan {
213 root: Some(arg_root.clone()),
214 tail: Some(arg_root),
215 logical_root: Some(logical_root),
216 };
217 Ok(sub_plan)
218}
219
220pub(crate) fn extract_edge_type_from_patterns(patterns: &[Pattern]) -> Option<String> {
221 for pattern in patterns {
222 if let Pattern::Path(path_pattern) = pattern {
223 for element in &path_pattern.elements {
224 if let PathElement::Edge(edge_pattern) = element {
225 if let Some(edge_type) = edge_pattern.edge_types.first() {
226 return Some(edge_type.clone());
227 }
228 }
229 }
230 }
231 }
232 None
233}
234
235#[cfg(test)]
236mod tests;