diff --git a/benches/engine.rs b/benches/engine.rs index 1b16ed096552dd7cde93bdceef35c14e636b670c..527f32121bff22ab68056577f10a62ead16d4298 100644 --- a/benches/engine.rs +++ b/benches/engine.rs @@ -6,6 +6,8 @@ use std::collections::HashMap; use sbroad::errors::QueryPlannerError; use sbroad::executor::bucket::Buckets; +use sbroad::executor::engine::cartridge::backend::sql::ir::get_sql_order; +use sbroad::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use sbroad::executor::engine::cartridge::hash::bucket_id_by_tuple; use sbroad::executor::engine::{ normalize_name_from_sql, sharding_keys_from_map, sharding_keys_from_tuple, Configuration, @@ -413,7 +415,8 @@ impl Coordinator for RouterRuntimeMock { buckets: &Buckets, ) -> Result<Box<dyn Any>, QueryPlannerError> { let result = ProducerResult::new(); - let nodes = plan.get_sql_order(top_id)?; + let mut sp = SyntaxPlan::new(plan, top_id)?; + let nodes = get_sql_order(&mut sp)?; plan.syntax_nodes_as_sql(&nodes, buckets)?; Ok(Box::new(result)) diff --git a/benches/parse.rs b/benches/parse.rs index ff5f20adb211b589156d986ff82170abd5be881d..8f120baa75a573690ac70116fbf64722f1115225 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -2,6 +2,8 @@ extern crate sbroad; use criterion::{criterion_group, criterion_main, Criterion}; use engine::RouterRuntimeMock; +use sbroad::executor::engine::cartridge::backend::sql::ir::get_sql_order; +use sbroad::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use sbroad::executor::Query; use sbroad::ir::value::Value; @@ -237,7 +239,8 @@ fn query1(pattern: &str, params: &[Value], engine: &mut RouterRuntimeMock) { let top_id = query.get_exec_plan().get_ir_plan().get_top().unwrap(); let buckets = query.bucket_discovery(top_id).unwrap(); let plan = query.get_exec_plan(); - let nodes = plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); plan.syntax_nodes_as_sql(&nodes, &buckets).unwrap(); } diff --git a/src/executor/engine/cartridge/backend/sql/ir.rs b/src/executor/engine/cartridge/backend/sql/ir.rs index 88acb79ed586ff4d548fd8ca33de41e2c1a995fb..70d04d8b9da91e2bd270e07dca3f4b8ef401794c 100644 --- a/src/executor/engine/cartridge/backend/sql/ir.rs +++ b/src/executor/engine/cartridge/backend/sql/ir.rs @@ -68,43 +68,6 @@ impl From<PatternWithParams> for String { } impl ExecutionPlan { - /// Traverse plan sub-tree (pointed by top) in the order - /// convenient for SQL serialization. - /// - /// # Panics - /// - the amount of nodes exceeds `isize::MAX / usize` bytes - /// - /// # Errors - /// - top node is invalid - /// - plan is invalid - #[allow(dead_code)] - pub fn get_sql_order(&self, top: usize) -> Result<Vec<SyntaxData>, QueryPlannerError> { - let mut sp = SyntaxPlan::new(self, top)?; - // Result with plan node ids. - let mut result: Vec<SyntaxData> = Vec::with_capacity(sp.nodes.arena.len()); - // Stack to keep syntax node data. - let mut stack: Vec<usize> = Vec::with_capacity(sp.nodes.arena.len()); - - // Make a destructive in-order traversal over the syntax plan - // nodes (left and right pointers for any wrapped node become - // None or removed). It seems to be the fastest traversal - // approach in Rust (`take()` and `pop()`). - stack.push(sp.get_top()?); - while let Some(id) = stack.last() { - let sn = sp.nodes.get_mut_syntax_node(*id)?; - if let Some(left_id) = sn.left.take() { - stack.push(left_id); - } else if let Some(id) = stack.pop() { - let sn_next = sp.nodes.get_mut_syntax_node(id)?; - result.push(sn_next.data.clone()); - while let Some(right_id) = sn_next.right.pop() { - stack.push(right_id); - } - } - } - Ok(result) - } - /// Transform plan sub-tree (pointed by top) to sql string /// /// # Errors @@ -113,7 +76,7 @@ impl ExecutionPlan { #[allow(clippy::too_many_lines)] pub fn syntax_nodes_as_sql( &self, - nodes: &[SyntaxData], + nodes: &[&SyntaxData], buckets: &Buckets, ) -> Result<PatternWithParams, QueryPlannerError> { let mut params: Vec<Value> = Vec::new(); @@ -336,5 +299,48 @@ impl ExecutionPlan { } } +/// Traverse plan sub-tree (pointed by top) in the order +/// convenient for SQL serialization. +/// +/// # Panics +/// - the amount of nodes exceeds `isize::MAX / usize` bytes +/// +/// # Errors +/// - top node is invalid +/// - plan is invalid +pub fn get_sql_order<'sp>( + sp: &'sp mut SyntaxPlan, +) -> Result<Vec<&'sp SyntaxData>, QueryPlannerError> { + // Result with plan node ids. + let mut positions: Vec<usize> = Vec::with_capacity(sp.nodes.arena.len()); + // Stack to keep syntax node data. + let mut stack: Vec<usize> = Vec::with_capacity(sp.nodes.arena.len()); + + // Make a destructive in-order traversal over the syntax plan + // nodes (left and right pointers for any wrapped node become + // None or removed). It seems to be the fastest traversal + // approach in Rust (`take()` and `pop()`). + stack.push(sp.get_top()?); + while let Some(id) = stack.last() { + let sn = sp.nodes.get_mut_syntax_node(*id)?; + if let Some(left_id) = sn.left.take() { + stack.push(left_id); + } else if let Some(id) = stack.pop() { + positions.push(id); + let sn_next = sp.nodes.get_mut_syntax_node(id)?; + while let Some(right_id) = sn_next.right.pop() { + stack.push(right_id); + } + } + } + + let mut result: Vec<&SyntaxData> = Vec::with_capacity(positions.len()); + for id in positions { + result.push(&sp.nodes.get_syntax_node(id)?.data); + } + + Ok(result) +} + #[cfg(test)] mod tests; diff --git a/src/executor/engine/cartridge/backend/sql/ir/tests.rs b/src/executor/engine/cartridge/backend/sql/ir/tests.rs index 3ce07caad1da70cbeac88481f6f2615969c44d16..8249052bd7fdfde7a8052368fc7df7c83bc995d6 100644 --- a/src/executor/engine/cartridge/backend/sql/ir/tests.rs +++ b/src/executor/engine/cartridge/backend/sql/ir/tests.rs @@ -1,6 +1,8 @@ use pretty_assertions::assert_eq; use crate::executor::bucket::Buckets; +use crate::executor::engine::cartridge::backend::sql::ir::get_sql_order; +use crate::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use crate::executor::engine::mock::RouterConfigurationMock; use crate::executor::ir::ExecutionPlan; use crate::frontend::sql::ast::AbstractSyntaxTree; @@ -22,7 +24,8 @@ fn one_table_projection() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -53,7 +56,8 @@ fn one_table_with_asterisk() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -90,7 +94,8 @@ fn union_all() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -124,7 +129,8 @@ fn from_sub_query() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -161,7 +167,8 @@ fn from_sub_query_with_union() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -195,7 +202,8 @@ fn inner_join() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -231,7 +239,8 @@ fn inner_join_with_sq() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -266,7 +275,8 @@ fn selection_with_sq() { plan.bind_params(&[]).unwrap(); let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( @@ -303,7 +313,8 @@ fn except() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( diff --git a/src/executor/engine/cartridge/backend/sql/tree/tests.rs b/src/executor/engine/cartridge/backend/sql/tree/tests.rs index 92777984bacf46dfbf77be6b4c5acc6a42dfa3e6..38c1ba5c14ad757178d1c606cfaff890d6ed5e0c 100644 --- a/src/executor/engine/cartridge/backend/sql/tree/tests.rs +++ b/src/executor/engine/cartridge/backend/sql/tree/tests.rs @@ -3,6 +3,8 @@ use std::path::Path; use pretty_assertions::assert_eq; +use crate::executor::engine::cartridge::backend::sql::ir::get_sql_order; +use crate::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use crate::ir::operator::Bool; use crate::ir::relation::{Column, ColumnRole, Table, Type}; use crate::ir::value::Value; @@ -64,23 +66,24 @@ fn sql_order_selection() { let top_id = exec_plan.get_ir_plan().get_top().unwrap(); // get nodes in the sql-convenient order - let nodes = exec_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&exec_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); let mut nodes_iter = nodes.into_iter(); - assert_eq!(Some(SyntaxData::PlanId(16)), nodes_iter.next()); // projection - assert_eq!(Some(SyntaxData::PlanId(14)), nodes_iter.next()); // alias - assert_eq!(Some(SyntaxData::PlanId(13)), nodes_iter.next()); // ref - assert_eq!(Some(SyntaxData::From), nodes_iter.next()); // from - assert_eq!(Some(SyntaxData::PlanId(3)), nodes_iter.next()); // scan - assert_eq!(Some(SyntaxData::PlanId(12)), nodes_iter.next()); // selection - assert_eq!(Some(SyntaxData::PlanId(5)), nodes_iter.next()); // row - assert_eq!(Some(SyntaxData::OpenParenthesis), nodes_iter.next()); // ( - assert_eq!(Some(SyntaxData::PlanId(4)), nodes_iter.next()); // ref a - assert_eq!(Some(SyntaxData::CloseParenthesis), nodes_iter.next()); // ) - assert_eq!(Some(SyntaxData::PlanId(8)), nodes_iter.next()); // bool - assert_eq!(Some(SyntaxData::Operator("=".into())), nodes_iter.next()); // = - assert_eq!(Some(SyntaxData::PlanId(7)), nodes_iter.next()); // row - assert_eq!(Some(SyntaxData::OpenParenthesis), nodes_iter.next()); // ( - assert_eq!(Some(SyntaxData::Parameter(6)), nodes_iter.next()); // parameter - assert_eq!(Some(SyntaxData::CloseParenthesis), nodes_iter.next()); // ) + assert_eq!(Some(&SyntaxData::PlanId(16)), nodes_iter.next()); // projection + assert_eq!(Some(&SyntaxData::PlanId(14)), nodes_iter.next()); // alias + assert_eq!(Some(&SyntaxData::PlanId(13)), nodes_iter.next()); // ref + assert_eq!(Some(&SyntaxData::From), nodes_iter.next()); // from + assert_eq!(Some(&SyntaxData::PlanId(3)), nodes_iter.next()); // scan + assert_eq!(Some(&SyntaxData::PlanId(12)), nodes_iter.next()); // selection + assert_eq!(Some(&SyntaxData::PlanId(5)), nodes_iter.next()); // row + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); // ( + assert_eq!(Some(&SyntaxData::PlanId(4)), nodes_iter.next()); // ref a + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); // ) + assert_eq!(Some(&SyntaxData::PlanId(8)), nodes_iter.next()); // bool + assert_eq!(Some(&SyntaxData::Operator("=".into())), nodes_iter.next()); // = + assert_eq!(Some(&SyntaxData::PlanId(7)), nodes_iter.next()); // row + assert_eq!(Some(&SyntaxData::OpenParenthesis), nodes_iter.next()); // ( + assert_eq!(Some(&SyntaxData::Parameter(6)), nodes_iter.next()); // parameter + assert_eq!(Some(&SyntaxData::CloseParenthesis), nodes_iter.next()); // ) assert_eq!(None, nodes_iter.next()); } diff --git a/src/executor/engine/cartridge/router.rs b/src/executor/engine/cartridge/router.rs index 5cd8ebff9e1a6805d0b1c9d0093a7417a1cc5eb3..01f4053f79d3eaaed8701481e5fae11495df1c4e 100644 --- a/src/executor/engine/cartridge/router.rs +++ b/src/executor/engine/cartridge/router.rs @@ -13,7 +13,8 @@ use tarantool::tuple::Tuple; use crate::errors::QueryPlannerError; use crate::executor::bucket::Buckets; -use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; +use crate::executor::engine::cartridge::backend::sql::ir::{get_sql_order, PatternWithParams}; +use crate::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use crate::executor::engine::cartridge::config::RouterConfiguration; use crate::executor::engine::cartridge::hash::bucket_id_by_tuple; use crate::executor::engine::{ @@ -167,7 +168,8 @@ impl Coordinator for RouterRuntime { top_id: usize, buckets: &Buckets, ) -> Result<Box<dyn Any>, QueryPlannerError> { - let nodes = plan.get_sql_order(top_id)?; + let mut sp = SyntaxPlan::new(plan, top_id)?; + let nodes = get_sql_order(&mut sp)?; let is_data_modifier = plan.subtree_modifies_data(top_id)?; let mut rs_query: HashMap<String, PatternWithParams> = HashMap::new(); diff --git a/src/executor/engine/mock.rs b/src/executor/engine/mock.rs index acd19b0ddde63c264e1f79c719ff25c14fd4c1f4..84617b0e12d0cb906d98f7403704d8c9aa729b39 100644 --- a/src/executor/engine/mock.rs +++ b/src/executor/engine/mock.rs @@ -5,6 +5,8 @@ use std::collections::{HashMap, HashSet}; use crate::collection; use crate::errors::QueryPlannerError; use crate::executor::bucket::Buckets; +use crate::executor::engine::cartridge::backend::sql::ir::get_sql_order; +use crate::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use crate::executor::engine::{ normalize_name_from_sql, sharding_keys_from_map, sharding_keys_from_tuple, Configuration, Coordinator, @@ -275,7 +277,8 @@ impl Coordinator for RouterRuntimeMock { buckets: &Buckets, ) -> Result<Box<dyn Any>, QueryPlannerError> { let mut result = ProducerResult::new(); - let nodes = plan.get_sql_order(top_id)?; + let mut sp = SyntaxPlan::new(plan, top_id)?; + let nodes = get_sql_order(&mut sp)?; match buckets { Buckets::All => { diff --git a/src/ir.rs b/src/ir.rs index 67326f69255663519d07f22f13587dcc9fa74e38..c156abd89f73d40fd8be2c4ab2bda53a68d6565d 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -236,7 +236,7 @@ impl Plan { pub fn get_row_from_rel_node(&mut self, node: usize) -> Result<usize, QueryPlannerError> { if let Node::Relational(rel) = self.get_node(node)? { if let Node::Expression(Expression::Row { list, .. }) = self.get_node(rel.output())? { - let mut cols: Vec<usize> = Vec::new(); + let mut cols: Vec<usize> = Vec::with_capacity(list.len()); for alias in list { if let Node::Expression(Expression::Alias { child, .. }) = self.get_node(*alias)? @@ -458,14 +458,14 @@ impl Plan { let column_expr_node = self.get_expression_node(column_rel_node.output())?; let col_alias_idx = - *column_expr_node + column_expr_node .get_row_list()? .get(*position) .ok_or_else(|| { QueryPlannerError::CustomError("Invalid position in row list".into()) })?; - let col_alias_node = self.get_expression_node(col_alias_idx)?; + let col_alias_node = self.get_expression_node(*col_alias_idx)?; match col_alias_node { Expression::Alias { name, .. } => return Ok(name), _ => return Err(QueryPlannerError::CustomError("Expected alias node".into())), diff --git a/src/ir/transformation/helpers.rs b/src/ir/transformation/helpers.rs index dcd92b7d4345077ab62e405b197f2a9df053787c..f19b01fbce7c7b95b5704d8e2c1d102882858f8d 100644 --- a/src/ir/transformation/helpers.rs +++ b/src/ir/transformation/helpers.rs @@ -1,7 +1,8 @@ //! IR test helpers. use crate::executor::bucket::Buckets; -use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams; +use crate::executor::engine::cartridge::backend::sql::ir::{get_sql_order, PatternWithParams}; +use crate::executor::engine::cartridge::backend::sql::tree::SyntaxPlan; use crate::executor::engine::mock::RouterConfigurationMock; use crate::executor::ir::ExecutionPlan; use crate::frontend::sql::ast::AbstractSyntaxTree; @@ -30,6 +31,7 @@ pub fn sql_to_sql( f_transform(&mut plan); let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().unwrap(); - let nodes = ex_plan.get_sql_order(top_id).unwrap(); + let mut sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let nodes = get_sql_order(&mut sp).unwrap(); ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap() }