From fa4aa1fef2cdfb82181640224cc13ef985b84aea Mon Sep 17 00:00:00 2001 From: Igor Kuznetsov <i.kuznetsov@picodata.io> Date: Mon, 5 Sep 2022 08:20:50 +0000 Subject: [PATCH] feat: add test `insert` operator to explain --- src/ir/explain.rs | 59 ++++++++++++++++++++++++---- src/ir/explain/tests.rs | 87 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 7 deletions(-) diff --git a/src/ir/explain.rs b/src/ir/explain.rs index f20fa8f539..98efad25fb 100644 --- a/src/ir/explain.rs +++ b/src/ir/explain.rs @@ -450,6 +450,9 @@ impl Display for InnerJoin { enum ExplainNode { Except, InnerJoin(InnerJoin), + ValueRow(Row), + Value, + Insert(String), Projection(Projection), Scan(Scan), Selection(Selection), @@ -463,6 +466,9 @@ impl Display for ExplainNode { let s = match &self { ExplainNode::Except => "except".to_string(), ExplainNode::InnerJoin(i) => i.to_string(), + ExplainNode::ValueRow(r) => format!("value row (data={})", r), + ExplainNode::Value => "values".to_string(), + ExplainNode::Insert(s) => format!("insert {}", s), ExplainNode::Projection(e) => e.to_string(), ExplainNode::Scan(s) => s.to_string(), ExplainNode::Selection(s) => format!("selection {}", s), @@ -732,13 +738,52 @@ impl FullExplain { let condition = Selection::new(ir, *condition, &sq_ref_map)?; Some(ExplainNode::InnerJoin(InnerJoin { condition })) } - Relational::Insert { .. } - | Relational::Values { .. } - | Relational::ValuesRow { .. } => { - return Err(QueryPlannerError::CustomError(format!( - "Explain hasn't supported node {:?} yet", - node - ))) + Relational::ValuesRow { data, children, .. } => { + let mut sq_ref_map: HashMap<usize, usize> = + HashMap::with_capacity(children.len()); + + for sq_id in children.iter().rev() { + let sq_node = stack.pop().ok_or_else(|| { + QueryPlannerError::CustomError( + "Insert node failed to get a sub-query.".into(), + ) + })?; + + result.subqueries.push(sq_node); + let offset = result.subqueries.len() - 1; + sq_ref_map.insert(*sq_id, offset); + } + + let values = ir.get_expression_node(*data)?.get_row_list()?; + let row = Row::from_ir_nodes(ir, values, &sq_ref_map)?; + + Some(ExplainNode::ValueRow(row)) + } + Relational::Values { children, .. } => { + let mut amount_values = children.len(); + + while amount_values > 0 { + let value_row = stack.pop().ok_or_else(|| { + QueryPlannerError::CustomError( + "Insert node failed to get a value row.".into(), + ) + })?; + + current_node.children.insert(0, value_row); + amount_values -= 1; + } + Some(ExplainNode::Value) + } + Relational::Insert { relation, .. } => { + let values = stack.pop().ok_or_else(|| { + QueryPlannerError::CustomError( + "Insert node failed to get a value row.".into(), + ) + })?; + + current_node.children.push(values); + + Some(ExplainNode::Insert(relation.into())) } }; stack.push(current_node); diff --git a/src/ir/explain/tests.rs b/src/ir/explain/tests.rs index 152049aa9f..d8a564bc48 100644 --- a/src/ir/explain/tests.rs +++ b/src/ir/explain/tests.rs @@ -302,3 +302,90 @@ fn unary_condition_plan() { assert_eq!(actual_explain, explain_tree.to_string()) } + +#[test] +fn insert_plan() { + let query = r#"INSERT INTO "test_space" ("id", "FIRST_NAME") VALUES (1, '123')"#; + + let plan = sql_to_optimized_ir(query, vec![]); + + let top = &plan.get_top().unwrap(); + let explain_tree = FullExplain::new(&plan, *top).unwrap(); + + let mut actual_explain = String::new(); + actual_explain.push_str( + r#"insert "test_space" + motion [policy: segment([ref(COLUMN_1)]), generation: sharding_column] + values + value row (data=ROW(1, '123')) +"#, + ); + + assert_eq!(actual_explain, explain_tree.to_string()) +} + +#[test] +fn multiply_insert_plan() { + let query = r#"INSERT INTO "test_space" ("id", "FIRST_NAME") VALUES (1, '123'), (2, '456'), (3, '789')"#; + + let plan = sql_to_optimized_ir(query, vec![]); + + let top = &plan.get_top().unwrap(); + let explain_tree = FullExplain::new(&plan, *top).unwrap(); + + let mut actual_explain = String::new(); + actual_explain.push_str( + r#"insert "test_space" + motion [policy: segment([ref(COLUMN_5)]), generation: sharding_column] + values + value row (data=ROW(1, '123')) + value row (data=ROW(2, '456')) + value row (data=ROW(3, '789')) +"#, + ); + + assert_eq!(actual_explain, explain_tree.to_string()) +} + +#[test] +fn insert_select_plan() { + let query = r#"INSERT INTO "test_space" ("id", "FIRST_NAME") +SELECT "identification_number", "product_code" FROM "hash_testing""#; + + let plan = sql_to_optimized_ir(query, vec![]); + + let top = &plan.get_top().unwrap(); + let explain_tree = FullExplain::new(&plan, *top).unwrap(); + + let mut actual_explain = String::new(); + actual_explain.push_str( + r#"insert "test_space" + motion [policy: segment([ref("identification_number")]), generation: sharding_column] + projection ("hash_testing"."identification_number" -> "identification_number", "hash_testing"."product_code" -> "product_code") + scan "hash_testing" +"#, + ); + + assert_eq!(actual_explain, explain_tree.to_string()) +} + +#[test] +fn select_value_plan() { + let query = r#"select * from (values (1))"#; + + let plan = sql_to_optimized_ir(query, vec![]); + + let top = &plan.get_top().unwrap(); + let explain_tree = FullExplain::new(&plan, *top).unwrap(); + + let mut actual_explain = String::new(); + actual_explain.push_str( + r#"projection (COLUMN_1 -> COLUMN_1) + scan + values + value row (data=ROW(1)) +"#, + ); + + assert_eq!(actual_explain, explain_tree.to_string()) +} -- GitLab