1use crate::parser::ast::{InsertStmt, InsertTarget, Stmt, VertexRow};
6use crate::planning::plan::core::{
7 node_id_generator::next_node_id,
8 nodes::{EdgeInsertInfo, TagInsertSpec, VertexInsertInfo},
9};
10use crate::planning::plan::logical::logical_nodes::dml::{
11 LogicalInsertEdgesNode, LogicalInsertVerticesNode,
12};
13use crate::planning::plan::logical::LogicalNodeEnum;
14use crate::planning::plan::SubPlan;
15use crate::planning::planner::{Planner, PlannerError, ValidatedStatement};
16use crate::planning::statements::clauses::exists_planner;
17use crate::QueryContext;
18use linkrs_core::types::expr::contextual::ContextualExpression;
19use std::sync::Arc;
20
21#[cfg(test)]
22use crate::parser::ast::utils::ExprFactory;
23#[cfg(test)]
24use linkrs_core::types::expr::expression_context::ExpressionAnalysisContext;
25#[cfg(test)]
26use linkrs_core::YieldColumn;
27
28#[derive(Debug, Clone)]
31pub struct InsertPlanner;
32
33impl InsertPlanner {
34 pub fn new() -> Self {
36 Self
37 }
38
39 pub fn match_stmt(stmt: &Stmt) -> bool {
41 matches!(stmt, Stmt::Insert(_))
42 }
43
44 fn extract_insert_stmt(&self, stmt: &Stmt) -> Result<InsertStmt, PlannerError> {
46 match stmt {
47 Stmt::Insert(insert_stmt) => Ok(insert_stmt.clone()),
48 _ => Err(PlannerError::PlanGenerationFailed(
49 "statement is not an INSERT statement".to_string(),
50 )),
51 }
52 }
53
54 fn build_vertex_insert_info(
59 &self,
60 space_name: String,
61 tag: crate::parser::ast::TagInsertSpec,
62 values: Vec<VertexRow>,
63 if_not_exists: bool,
64 ) -> Result<VertexInsertInfo, PlannerError> {
65 let tag_spec = TagInsertSpec {
66 tag_name: tag.tag_name,
67 prop_names: tag.prop_names,
68 };
69
70 let converted_values: Vec<(ContextualExpression, Vec<ContextualExpression>)> = values
72 .into_iter()
73 .map(|row| (row.vid, row.values))
74 .collect();
75
76 Ok(VertexInsertInfo {
77 space_name,
78 tag: tag_spec,
79 values: converted_values,
80 if_not_exists,
81 })
82 }
83
84 fn build_edge_insert_info(
86 &self,
87 space_name: String,
88 edge_name: String,
89 prop_names: Vec<String>,
90 edges: Vec<(
91 ContextualExpression,
92 ContextualExpression,
93 Option<ContextualExpression>,
94 Vec<ContextualExpression>,
95 )>,
96 if_not_exists: bool,
97 ) -> EdgeInsertInfo {
98 EdgeInsertInfo {
99 space_name,
100 edge_name,
101 prop_names,
102 edges,
103 if_not_exists,
104 }
105 }
106
107 #[cfg(test)]
109 fn create_yield_columns(
110 &self,
111 count: usize,
112 expr_context: &Arc<ExpressionAnalysisContext>,
113 ) -> Vec<YieldColumn> {
114 let expr = ExprFactory::constant(
115 linkrs_core::Value::BigInt(count as i64),
116 expr_context.clone(),
117 );
118 vec![YieldColumn::new(expr, "inserted_count".to_string())]
119 }
120}
121
122impl Planner for InsertPlanner {
123 fn plan_bound(
124 &mut self,
125 ctx: &crate::planning::context::PlanContext<'_>,
126 ) -> Result<SubPlan, PlannerError> {
127 use crate::binder::bound::{BoundExpression, BoundInsertTarget};
128
129 let bound = ctx.bound;
130 let qctx = ctx.qctx.clone();
131 let validated = ctx.validated;
132 let insert = match bound {
133 crate::binder::BoundStatement::Insert(i) => i,
134 _ => {
135 return Err(PlannerError::PlanGenerationFailed(
136 "statement is not an INSERT statement".to_string(),
137 ));
138 }
139 };
140
141 let space_name = qctx.space_name().unwrap_or_else(|| "default".to_string());
142 let expr_ctx = validated.expr_context().clone();
146 let convert = |bound_expr: &BoundExpression| {
147 crate::binder::expr_converter::bound_expr_to_contextual(bound_expr, &expr_ctx)
148 .map_err(PlannerError::PlanGenerationFailed)
149 };
150 let check_space_id = qctx.space_id().unwrap_or(1);
151 let check_space_name = qctx.space_name().unwrap_or_else(|| "default".to_string());
152 let outer_col_names: Vec<String> = Vec::new();
153 let reject_subquery = |expr: &ContextualExpression| -> Result<(), PlannerError> {
154 if let Some(expr_meta) = expr.expression() {
155 exists_planner::check_expression_subqueries(
156 expr_meta.inner(),
157 &qctx,
158 check_space_id,
159 &check_space_name,
160 &outer_col_names,
161 )?;
162 }
163 Ok(())
164 };
165
166 let (insert_node, _inserted_count) = match &insert.target {
167 BoundInsertTarget::Vertices { tag, values } => {
168 let mut rows = Vec::with_capacity(values.len());
169 for row in values {
170 let vid = convert(&row.vid)?;
171 reject_subquery(&vid)?;
172 let mut converted = Vec::with_capacity(row.values.len());
173 for value in &row.values {
174 let expr = convert(value)?;
175 reject_subquery(&expr)?;
176 converted.push(expr);
177 }
178 rows.push(VertexRow {
179 vid,
180 values: converted,
181 });
182 }
183 let count = rows.len();
184 let info = self.build_vertex_insert_info(
185 space_name,
186 tag.clone(),
187 rows,
188 insert.if_not_exists,
189 )?;
190 (
191 LogicalNodeEnum::InsertVertices(LogicalInsertVerticesNode {
192 id: next_node_id(),
193 info,
194 output_var: None,
195 col_names: vec!["inserted".to_string()],
196 column_types: vec![],
197 }),
198 count,
199 )
200 }
201 BoundInsertTarget::Edge {
202 edge_name,
203 prop_names,
204 edges,
205 } => {
206 let mut converted_edges = Vec::with_capacity(edges.len());
207 for (src_b, dst_b, rank_b, props_b) in edges {
208 let src = convert(src_b)?;
209 reject_subquery(&src)?;
210 let dst = convert(dst_b)?;
211 reject_subquery(&dst)?;
212 let rank = rank_b
213 .as_ref()
214 .map(|rank| -> Result<ContextualExpression, PlannerError> {
215 let expr = convert(rank)?;
216 reject_subquery(&expr)?;
217 Ok(expr)
218 })
219 .transpose()?;
220 let mut props = Vec::with_capacity(props_b.len());
221 for prop in props_b {
222 let expr = convert(prop)?;
223 reject_subquery(&expr)?;
224 props.push(expr);
225 }
226 converted_edges.push((src, dst, rank, props));
227 }
228 let count = converted_edges.len();
229 let info = self.build_edge_insert_info(
230 space_name,
231 edge_name.clone(),
232 prop_names.clone(),
233 converted_edges,
234 insert.if_not_exists,
235 );
236 (
237 LogicalNodeEnum::InsertEdges(LogicalInsertEdgesNode {
238 id: next_node_id(),
239 info,
240 output_var: None,
241 col_names: vec!["inserted".to_string()],
242 column_types: vec![],
243 }),
244 count,
245 )
246 }
247 };
248
249 Ok(SubPlan::from_logical_root(insert_node))
250 }
251
252 fn transform(
253 &mut self,
254 validated: &ValidatedStatement,
255 qctx: Arc<QueryContext>,
256 ) -> Result<SubPlan, PlannerError> {
257 let space_name = qctx.space_name().unwrap_or_else(|| "default".to_string());
259
260 let validation_info = &validated.validation_info;
262
263 let referenced_tags = &validation_info.semantic_info.referenced_tags;
265 if !referenced_tags.is_empty() {
266 log::debug!("INSERT references the tag: {:?}", referenced_tags);
267 }
268
269 let referenced_edges = &validation_info.semantic_info.referenced_edges;
270 if !referenced_edges.is_empty() {
271 log::debug!("INSERT referenced edge type: {:?}", referenced_edges);
272 }
273
274 let insert_stmt = self.extract_insert_stmt(validated.stmt())?;
276
277 let check_space_id = qctx.space_id().unwrap_or(1);
281 let check_space_name = qctx.space_name().unwrap_or_else(|| "default".to_string());
282 let outer_col_names: Vec<String> = Vec::new();
283 match &insert_stmt.target {
284 InsertTarget::Vertices { values, .. } => {
285 for row in values {
286 for expr in std::iter::once(&row.vid).chain(row.values.iter()) {
287 if let Some(expr_meta) = expr.expression() {
288 exists_planner::check_expression_subqueries(
289 expr_meta.inner(),
290 &qctx,
291 check_space_id,
292 &check_space_name,
293 &outer_col_names,
294 )?;
295 }
296 }
297 }
298 }
299 InsertTarget::Edge { edges, .. } => {
300 for (src, dst, rank, prop_values) in edges {
301 for expr in std::iter::once(src)
302 .chain(std::iter::once(dst))
303 .chain(rank.iter())
304 .chain(prop_values.iter())
305 {
306 if let Some(expr_meta) = expr.expression() {
307 exists_planner::check_expression_subqueries(
308 expr_meta.inner(),
309 &qctx,
310 check_space_id,
311 &check_space_name,
312 &outer_col_names,
313 )?;
314 }
315 }
316 }
317 }
318 }
319
320 let (insert_node, _inserted_count) = match &insert_stmt.target {
322 InsertTarget::Vertices { tag, values } => {
323 let count = values.len();
324 let info = self.build_vertex_insert_info(
325 space_name,
326 tag.clone(),
327 values.clone(),
328 insert_stmt.if_not_exists,
329 )?;
330 (
331 LogicalNodeEnum::InsertVertices(LogicalInsertVerticesNode {
332 id: next_node_id(),
333 info,
334 output_var: None,
335 col_names: vec!["inserted".to_string()],
336 column_types: vec![],
337 }),
338 count,
339 )
340 }
341 InsertTarget::Edge {
342 edge_name,
343 prop_names,
344 edges,
345 } => {
346 let count = edges.len();
347 let info = self.build_edge_insert_info(
348 space_name,
349 edge_name.clone(),
350 prop_names.clone(),
351 edges.clone(),
352 insert_stmt.if_not_exists,
353 );
354 (
355 LogicalNodeEnum::InsertEdges(LogicalInsertEdgesNode {
356 id: next_node_id(),
357 info,
358 output_var: None,
359 col_names: vec!["inserted".to_string()],
360 column_types: vec![],
361 }),
362 count,
363 )
364 }
365 };
366
367 Ok(SubPlan::from_logical_root(insert_node))
370 }
371
372 fn match_planner(&self, stmt: &Stmt) -> bool {
373 Self::match_stmt(stmt)
374 }
375}
376
377impl Default for InsertPlanner {
378 fn default() -> Self {
379 Self::new()
380 }
381}
382
383#[cfg(test)]
384#[allow(clippy::arc_with_non_send_sync)]
385mod tests {
386 use super::*;
387 use crate::binder::validation::ValidationInfo;
388 use crate::parser::ast::utils::ExprFactory;
389 use crate::parser::ast::{Ast, Span, Stmt};
390 use crate::parser::ast::{InsertStmt, InsertTarget, TagInsertSpec, VertexRow};
391 use crate::planning::planner::{Planner, ValidatedStatement};
392 use crate::QueryContext;
393 use linkrs_core::types::expr::contextual::ContextualExpression;
394 use linkrs_core::types::expr::expression_context::ExpressionAnalysisContext;
395 use linkrs_core::Value;
396 use std::sync::Arc;
397
398 fn create_test_span() -> Span {
399 use linkrs_core::types::span::Position;
400 Span::new(Position::new(1, 1), Position::new(1, 1))
401 }
402
403 fn create_test_stmt_with_insert(target: InsertTarget) -> Arc<Ast> {
404 let insert_stmt = InsertStmt {
405 span: create_test_span(),
406 target,
407 if_not_exists: false,
408 };
409 let ctx = Arc::new(ExpressionAnalysisContext::new());
410 Arc::new(Ast::new(Stmt::Insert(insert_stmt), ctx))
411 }
412
413 fn create_test_qctx() -> Arc<QueryContext> {
414 Arc::new(QueryContext::default())
415 }
416
417 fn lit(val: Value) -> ContextualExpression {
419 let ctx = Arc::new(ExpressionAnalysisContext::new());
420 ExprFactory::constant(val, ctx)
421 }
422
423 #[test]
424 fn test_insert_planner_new() {
425 let planner = InsertPlanner::new();
426 let ast = create_test_stmt_with_insert(InsertTarget::Vertices {
427 tag: TagInsertSpec {
428 tag_name: "person".to_string(),
429 prop_names: vec!["name".to_string(), "age".to_string()],
430 is_default_props: false,
431 },
432 values: vec![VertexRow {
433 vid: lit(Value::Int(1)),
434 values: vec![lit(Value::string("Alice")), lit(Value::Int(30))],
435 }],
436 });
437 assert!(planner.match_planner(&ast.stmt));
438 }
439
440 #[test]
441 fn test_match_stmt_with_insert() {
442 let ast = create_test_stmt_with_insert(InsertTarget::Vertices {
443 tag: TagInsertSpec {
444 tag_name: "person".to_string(),
445 prop_names: vec![],
446 is_default_props: true,
447 },
448 values: vec![],
449 });
450 assert!(InsertPlanner::match_stmt(&ast.stmt));
451 }
452
453 #[test]
454 fn test_match_stmt_without_insert() {
455 let stmt = Stmt::Use(crate::parser::ast::UseStmt {
456 span: create_test_span(),
457 space: "test_space".to_string(),
458 });
459 assert!(!InsertPlanner::match_stmt(&stmt));
460 }
461
462 #[test]
463 fn test_extract_insert_stmt_success() {
464 let planner = InsertPlanner::new();
465 let target = InsertTarget::Vertices {
466 tag: TagInsertSpec {
467 tag_name: "person".to_string(),
468 prop_names: vec!["name".to_string()],
469 is_default_props: false,
470 },
471 values: vec![],
472 };
473 let stmt = create_test_stmt_with_insert(target.clone());
474 let result = planner
475 .extract_insert_stmt(&stmt.as_ref().stmt)
476 .expect("Failed to extract insert statement");
477 assert_eq!(result.target, target);
478 }
479
480 #[test]
481 fn test_extract_insert_stmt_failure() {
482 let planner = InsertPlanner::new();
483 let stmt = Stmt::Use(crate::parser::ast::UseStmt {
484 span: create_test_span(),
485 space: "test_space".to_string(),
486 });
487 let result = planner.extract_insert_stmt(&stmt);
488 assert!(result.is_err());
489 assert!(result
490 .unwrap_err()
491 .to_string()
492 .contains("not an INSERT statement"));
493 }
494
495 #[test]
496 fn test_build_vertex_insert_info() {
497 let planner = InsertPlanner::new();
498 let info = planner
499 .build_vertex_insert_info(
500 "test_space".to_string(),
501 TagInsertSpec {
502 tag_name: "person".to_string(),
503 prop_names: vec!["name".to_string(), "age".to_string()],
504 is_default_props: false,
505 },
506 vec![VertexRow {
507 vid: lit(Value::Int(1)),
508 values: vec![lit(Value::string("Alice")), lit(Value::Int(30))],
509 }],
510 false,
511 )
512 .expect("Failed to build vertex insert info");
513 assert_eq!(info.space_name, "test_space");
514 assert_eq!(info.tag.tag_name, "person");
515 assert_eq!(info.tag.prop_names.len(), 2);
516 assert_eq!(info.values.len(), 1);
517 }
518
519 #[test]
520 fn test_build_edge_insert_info() {
521 let planner = InsertPlanner::new();
522 let info = planner.build_edge_insert_info(
523 "test_space".to_string(),
524 "follow".to_string(),
525 vec!["since".to_string()],
526 vec![(
527 lit(Value::Int(1)),
528 lit(Value::Int(2)),
529 Some(lit(Value::Int(0))),
530 vec![lit(Value::string("2023"))],
531 )],
532 false,
533 );
534 assert_eq!(info.space_name, "test_space");
535 assert_eq!(info.edge_name, "follow");
536 assert_eq!(info.prop_names.len(), 1);
537 assert_eq!(info.edges.len(), 1);
538 }
539
540 #[test]
541 fn test_create_yield_columns() {
542 let planner = InsertPlanner::new();
543 let expr_ctx = Arc::new(ExpressionAnalysisContext::new());
544 let columns = planner.create_yield_columns(5, &expr_ctx);
545 assert_eq!(columns.len(), 1);
546 assert_eq!(columns[0].alias, "inserted_count");
547 }
548
549 #[test]
550 fn test_transform_insert_vertices() {
551 let mut planner = InsertPlanner::new();
552 let target = InsertTarget::Vertices {
553 tag: TagInsertSpec {
554 tag_name: "person".to_string(),
555 prop_names: vec!["name".to_string()],
556 is_default_props: false,
557 },
558 values: vec![
559 VertexRow {
560 vid: lit(Value::Int(1)),
561 values: vec![lit(Value::string("Alice"))],
562 },
563 VertexRow {
564 vid: lit(Value::Int(2)),
565 values: vec![lit(Value::string("Bob"))],
566 },
567 ],
568 };
569 let ast = create_test_stmt_with_insert(target);
570 let qctx = create_test_qctx();
571
572 let validation_info = ValidationInfo::new();
574 let validated = ValidatedStatement::new(ast, validation_info);
575
576 let result = planner.transform(&validated, qctx);
577 assert!(result.is_ok());
578 let sub_plan = result.expect("Failed to transform insert statement");
579 assert!(sub_plan.root.is_some());
580 }
581
582 #[test]
583 fn test_transform_insert_edge() {
584 let mut planner = InsertPlanner::new();
585 let target = InsertTarget::Edge {
586 edge_name: "follow".to_string(),
587 prop_names: vec!["since".to_string()],
588 edges: vec![(
589 lit(Value::Int(1)),
590 lit(Value::Int(2)),
591 Some(lit(Value::Int(0))),
592 vec![lit(Value::string("2023"))],
593 )],
594 };
595 let ast = create_test_stmt_with_insert(target);
596 let qctx = create_test_qctx();
597
598 let validation_info = ValidationInfo::new();
600 let validated = ValidatedStatement::new(ast, validation_info);
601
602 let result = planner.transform(&validated, qctx);
603 assert!(result.is_ok());
604 let sub_plan = result.expect("Failed to transform insert statement");
605 assert!(sub_plan.root.is_some());
606 }
607
608 #[test]
609 fn test_transform_without_insert_stmt() {
610 let mut planner = InsertPlanner::new();
611 let stmt = Stmt::Use(crate::parser::ast::UseStmt {
612 span: create_test_span(),
613 space: "test_space".to_string(),
614 });
615 let qctx = create_test_qctx();
616 let ctx = Arc::new(ExpressionAnalysisContext::new());
617 let ast = Arc::new(Ast::new(stmt, ctx));
618
619 let validation_info = ValidationInfo::new();
621 let validated = ValidatedStatement::new(ast, validation_info);
622
623 let result = planner.transform(&validated, qctx);
624 assert!(result.is_err());
625 }
626
627 #[test]
628 fn test_default_impl() {
629 let planner: InsertPlanner = Default::default();
630 let ast = create_test_stmt_with_insert(InsertTarget::Vertices {
631 tag: TagInsertSpec {
632 tag_name: "test".to_string(),
633 prop_names: vec![],
634 is_default_props: true,
635 },
636 values: vec![],
637 });
638 assert!(planner.match_planner(&ast.stmt));
639 }
640}