diff --git a/benches/engine.rs b/benches/engine.rs index 527f32121bff22ab68056577f10a62ead16d4298..c612b65b3b3d8790690825fe39a5f4a647e3516b 100644 --- a/benches/engine.rs +++ b/benches/engine.rs @@ -6,8 +6,7 @@ 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::backend::sql::tree::{OrderedSyntaxNodes, 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, @@ -415,9 +414,10 @@ impl Coordinator for RouterRuntimeMock { buckets: &Buckets, ) -> Result<Box<dyn Any>, QueryPlannerError> { let result = ProducerResult::new(); - let mut sp = SyntaxPlan::new(plan, top_id)?; - let nodes = get_sql_order(&mut sp)?; - plan.syntax_nodes_as_sql(&nodes, buckets)?; + let sp = SyntaxPlan::new(plan, top_id)?; + let ordered = OrderedSyntaxNodes::try_from(sp)?; + let nodes = ordered.to_syntax_data()?; + plan.to_sql(&nodes, buckets)?; Ok(Box::new(result)) } diff --git a/benches/parse.rs b/benches/parse.rs index e4bbbad4df9d83383721312d126a153aeb39bdf0..e4565b377be891d489d59ba43ed6f2eb3a70cd9a 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -2,8 +2,7 @@ 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::engine::cartridge::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use sbroad::executor::Query; use sbroad::ir::value::Value; @@ -239,9 +238,10 @@ fn query1(pattern: &str, params: Vec<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 mut sp = SyntaxPlan::new(plan, top_id).unwrap(); - let nodes = get_sql_order(&mut sp).unwrap(); - plan.syntax_nodes_as_sql(&nodes, &buckets).unwrap(); + let sp = SyntaxPlan::new(plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + plan.to_sql(&nodes, &buckets).unwrap(); } fn bench_query1(c: &mut Criterion) { diff --git a/src/executor/engine/cartridge/backend/sql/ir.rs b/src/executor/engine/cartridge/backend/sql/ir.rs index 4f0bbb0479d0cea180ea63ed382ee9a7718bbc51..7262894e60be069920f742a083acf0166b1cde80 100644 --- a/src/executor/engine/cartridge/backend/sql/ir.rs +++ b/src/executor/engine/cartridge/backend/sql/ir.rs @@ -13,7 +13,7 @@ use crate::ir::operator::Relational; use crate::ir::value::Value; use crate::ir::Node; -use super::tree::{SyntaxData, SyntaxPlan}; +use super::tree::SyntaxData; #[derive(Debug, PartialEq, Serialize, tlua::Push)] pub struct PatternWithParams { @@ -74,7 +74,7 @@ impl ExecutionPlan { /// - plan is invalid and can't be transformed #[allow(dead_code)] #[allow(clippy::too_many_lines)] - pub fn syntax_nodes_as_sql( + pub fn to_sql( &self, nodes: &[&SyntaxData], buckets: &Buckets, @@ -292,48 +292,5 @@ 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 d4fa13b92acda3debd30b154bd4655f371197af6..be5cea5ba98c4adbfecd24034d818a7749e339a8 100644 --- a/src/executor/engine/cartridge/backend/sql/ir/tests.rs +++ b/src/executor/engine/cartridge/backend/sql/ir/tests.rs @@ -1,8 +1,7 @@ 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::cartridge::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::executor::engine::mock::RouterConfigurationMock; use crate::executor::ir::ExecutionPlan; use crate::frontend::sql::ast::AbstractSyntaxTree; @@ -24,9 +23,10 @@ fn one_table_projection() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -56,9 +56,10 @@ fn one_table_with_asterisk() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -94,9 +95,10 @@ fn union_all() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -129,9 +131,10 @@ fn from_sub_query() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -167,9 +170,10 @@ fn from_sub_query_with_union() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -202,9 +206,10 @@ fn inner_join() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -239,9 +244,10 @@ fn inner_join_with_sq() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -275,9 +281,10 @@ fn selection_with_sq() { plan.bind_params(vec![]).unwrap(); let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( @@ -313,9 +320,10 @@ fn except() { let ex_plan = ExecutionPlan::from(plan); let top_id = ex_plan.get_ir_plan().get_top().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(); + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + let sql = ex_plan.to_sql(&nodes, &Buckets::All).unwrap(); assert_eq!( PatternWithParams::new( diff --git a/src/executor/engine/cartridge/backend/sql/tree.rs b/src/executor/engine/cartridge/backend/sql/tree.rs index 8d20745e65fa3e6475509c412dcad34ebec37f04..937df293f36095a1adb23eba94c63d4d8008341f 100644 --- a/src/executor/engine/cartridge/backend/sql/tree.rs +++ b/src/executor/engine/cartridge/backend/sql/tree.rs @@ -1,5 +1,6 @@ use ahash::RandomState; use std::collections::HashMap; +use std::mem::take; use std::rc::Rc; use serde::{Deserialize, Serialize}; @@ -953,5 +954,65 @@ impl<'p> SyntaxPlan<'p> { } } +pub struct OrderedSyntaxNodes { + arena: Vec<SyntaxNode>, + positions: Vec<usize>, +} + +impl OrderedSyntaxNodes { + /// Constructs a vector of the syntax node pointers in an order, suitable for building + /// an SQL query (in-order traversal). + /// + /// # Errors + /// - internal error (positions point to invalid nodes in the arena) + pub fn to_syntax_data(&self) -> Result<Vec<&SyntaxData>, QueryPlannerError> { + let mut result: Vec<&SyntaxData> = Vec::with_capacity(self.positions.len()); + for id in &self.positions { + result.push( + &self + .arena + .get(*id) + .ok_or_else(|| { + QueryPlannerError::CustomError(format!("Invalid syntax node id: {}", id)) + })? + .data, + ); + } + Ok(result) + } +} + +impl TryFrom<SyntaxPlan<'_>> for OrderedSyntaxNodes { + type Error = QueryPlannerError; + + fn try_from(mut sp: SyntaxPlan) -> Result<Self, Self::Error> { + // 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 arena: Vec<SyntaxNode> = take(&mut sp.nodes.arena); + Ok(Self { arena, positions }) + } +} + #[cfg(test)] mod tests; diff --git a/src/executor/engine/cartridge/backend/sql/tree/tests.rs b/src/executor/engine/cartridge/backend/sql/tree/tests.rs index 38c1ba5c14ad757178d1c606cfaff890d6ed5e0c..07776e634c21c909371c0cad4ef25334f22a8fa0 100644 --- a/src/executor/engine/cartridge/backend/sql/tree/tests.rs +++ b/src/executor/engine/cartridge/backend/sql/tree/tests.rs @@ -3,8 +3,7 @@ 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::executor::engine::cartridge::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::ir::operator::Bool; use crate::ir::relation::{Column, ColumnRole, Table, Type}; use crate::ir::value::Value; @@ -66,8 +65,9 @@ fn sql_order_selection() { let top_id = exec_plan.get_ir_plan().get_top().unwrap(); // get nodes in the sql-convenient order - let mut sp = SyntaxPlan::new(&exec_plan, top_id).unwrap(); - let nodes = get_sql_order(&mut sp).unwrap(); + let sp = SyntaxPlan::new(&exec_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().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 diff --git a/src/executor/engine/cartridge/router.rs b/src/executor/engine/cartridge/router.rs index 01f4053f79d3eaaed8701481e5fae11495df1c4e..a21046cb33d47133845a87ecedca200844168c8f 100644 --- a/src/executor/engine/cartridge/router.rs +++ b/src/executor/engine/cartridge/router.rs @@ -13,8 +13,8 @@ use tarantool::tuple::Tuple; use crate::errors::QueryPlannerError; use crate::executor::bucket::Buckets; -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::backend::sql::ir::PatternWithParams; +use crate::executor::engine::cartridge::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::executor::engine::cartridge::config::RouterConfiguration; use crate::executor::engine::cartridge::hash::bucket_id_by_tuple; use crate::executor::engine::{ @@ -168,8 +168,9 @@ impl Coordinator for RouterRuntime { top_id: usize, buckets: &Buckets, ) -> Result<Box<dyn Any>, QueryPlannerError> { - let mut sp = SyntaxPlan::new(plan, top_id)?; - let nodes = get_sql_order(&mut sp)?; + let sp = SyntaxPlan::new(plan, top_id)?; + let ordered = OrderedSyntaxNodes::try_from(sp)?; + let nodes = ordered.to_syntax_data()?; let is_data_modifier = plan.subtree_modifies_data(top_id)?; let mut rs_query: HashMap<String, PatternWithParams> = HashMap::new(); @@ -197,7 +198,7 @@ impl Coordinator for RouterRuntime { .copied() .collect::<HashSet<u64, RepeatableState>>(); let pattern_with_params = - plan.syntax_nodes_as_sql(&nodes, &Buckets::new_filtered(bucket_set))?; + plan.to_sql(&nodes, &Buckets::new_filtered(bucket_set))?; rs_query.insert(rs.to_string(), pattern_with_params); } @@ -211,7 +212,7 @@ impl Coordinator for RouterRuntime { return self.exec_on_some(&rs_query, is_data_modifier); } - let pattern_with_params = plan.syntax_nodes_as_sql(&nodes, &Buckets::All)?; + let pattern_with_params = plan.to_sql(&nodes, &Buckets::All)?; self.exec_on_all(&pattern_with_params, is_data_modifier) } diff --git a/src/executor/engine/mock.rs b/src/executor/engine/mock.rs index 84617b0e12d0cb906d98f7403704d8c9aa729b39..2d48379f261aa101c836708d573c50a4e66218fb 100644 --- a/src/executor/engine/mock.rs +++ b/src/executor/engine/mock.rs @@ -5,8 +5,7 @@ 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::cartridge::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::executor::engine::{ normalize_name_from_sql, sharding_keys_from_map, sharding_keys_from_tuple, Configuration, Coordinator, @@ -277,18 +276,19 @@ impl Coordinator for RouterRuntimeMock { buckets: &Buckets, ) -> Result<Box<dyn Any>, QueryPlannerError> { let mut result = ProducerResult::new(); - let mut sp = SyntaxPlan::new(plan, top_id)?; - let nodes = get_sql_order(&mut sp)?; + let sp = SyntaxPlan::new(plan, top_id)?; + let ordered = OrderedSyntaxNodes::try_from(sp)?; + let nodes = ordered.to_syntax_data()?; match buckets { Buckets::All => { - let sql = plan.syntax_nodes_as_sql(&nodes, buckets)?; + let sql = plan.to_sql(&nodes, buckets)?; result.extend(exec_on_all(&String::from(sql).as_str()))?; } Buckets::Filtered(list) => { for bucket in list { let bucket_set: HashSet<u64, RepeatableState> = collection! { *bucket }; - let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::Filtered(bucket_set))?; + let sql = plan.to_sql(&nodes, &Buckets::Filtered(bucket_set))?; let temp_result = exec_on_some(*bucket, &String::from(sql).as_str()); result.extend(temp_result)?; } diff --git a/src/ir/transformation/helpers.rs b/src/ir/transformation/helpers.rs index 3c4039b033bb4aa16a477178f864c69c8ef352bb..89f101e9cdbb93079939f127e03b2455a90c15d2 100644 --- a/src/ir/transformation/helpers.rs +++ b/src/ir/transformation/helpers.rs @@ -1,8 +1,8 @@ //! IR test helpers. use crate::executor::bucket::Buckets; -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::backend::sql::ir::PatternWithParams; +use crate::executor::engine::cartridge::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::executor::engine::mock::RouterConfigurationMock; use crate::executor::ir::ExecutionPlan; use crate::frontend::sql::ast::AbstractSyntaxTree; @@ -31,7 +31,8 @@ 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 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() + let sp = SyntaxPlan::new(&ex_plan, top_id).unwrap(); + let ordered = OrderedSyntaxNodes::try_from(sp).unwrap(); + let nodes = ordered.to_syntax_data().unwrap(); + ex_plan.to_sql(&nodes, &Buckets::All).unwrap() }