From 95edcbdc2f9b8cbb3404072e5891bc8aeba5b816 Mon Sep 17 00:00:00 2001 From: Denis Smirnov <sd@picodata.io> Date: Tue, 10 Jan 2023 15:59:40 +0700 Subject: [PATCH] refactoring(perf): replace tree traversal with a custom one Reduce the amount of the heap allocations (use recursion instead of the heap stack). --- Cargo.lock | 7 - sbroad-benches/src/engine.rs | 2 +- sbroad-core/Cargo.toml | 1 - sbroad-core/src/backend/sql/tree.rs | 21 +-- sbroad-core/src/executor/bucket.rs | 13 +- sbroad-core/src/executor/engine.rs | 2 +- sbroad-core/src/executor/engine/mock.rs | 2 +- sbroad-core/src/executor/ir.rs | 14 +- sbroad-core/src/executor/shard.rs | 20 ++- sbroad-core/src/frontend/sql.rs | 73 ++++----- sbroad-core/src/frontend/sql/ast.rs | 44 +++--- sbroad-core/src/frontend/sql/ast/tests.rs | 58 +++---- sbroad-core/src/frontend/sql/ir.rs | 30 ++-- sbroad-core/src/frontend/sql/tree.rs | 10 +- sbroad-core/src/ir.rs | 2 +- sbroad-core/src/ir/api/parameter.rs | 14 +- sbroad-core/src/ir/distribution.rs | 5 +- sbroad-core/src/ir/explain.rs | 28 ++-- sbroad-core/src/ir/expression.rs | 36 +++-- sbroad-core/src/ir/operator.rs | 38 ++--- sbroad-core/src/ir/operator/tests.rs | 2 +- sbroad-core/src/ir/transformation.rs | 20 ++- .../src/ir/transformation/merge_tuples.rs | 19 ++- .../src/ir/transformation/redistribution.rs | 47 +++--- sbroad-core/src/ir/tree.rs | 4 +- sbroad-core/src/ir/tree/and.rs | 12 +- sbroad-core/src/ir/tree/eq_class.rs | 77 --------- sbroad-core/src/ir/tree/expression.rs | 12 +- sbroad-core/src/ir/tree/relation.rs | 12 +- sbroad-core/src/ir/tree/subtree.rs | 40 ++--- sbroad-core/src/ir/tree/tests.rs | 148 ++++++++---------- sbroad-core/src/ir/tree/traversal.rs | 106 +++++++++++++ 32 files changed, 476 insertions(+), 443 deletions(-) delete mode 100644 sbroad-core/src/ir/tree/eq_class.rs create mode 100644 sbroad-core/src/ir/tree/traversal.rs diff --git a/Cargo.lock b/Cargo.lock index 8ed6693f1a..0abfbe4285 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1161,7 +1161,6 @@ dependencies = [ "serde", "serde_yaml", "tarantool", - "traversal", "uuid 1.2.2", ] @@ -1439,12 +1438,6 @@ dependencies = [ "syn 1.0.107", ] -[[package]] -name = "traversal" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ec9745d7517c8b8e8c0a65cba2d84e42f95fd348a01693c5e4da1bc6d00c99" - [[package]] name = "typenum" version = "1.16.0" diff --git a/sbroad-benches/src/engine.rs b/sbroad-benches/src/engine.rs index f768da0a7e..c48e8010b7 100644 --- a/sbroad-benches/src/engine.rs +++ b/sbroad-benches/src/engine.rs @@ -415,7 +415,7 @@ impl Coordinator for RouterRuntimeMock { } else { Err(SbroadError::NotFound( Entity::VirtualTable, - format!("for motion node {}", motion_node_id), + format!("for motion node {motion_node_id}"), )) } } diff --git a/sbroad-core/Cargo.toml b/sbroad-core/Cargo.toml index 6d3c537ad5..0f9e568d44 100644 --- a/sbroad-core/Cargo.toml +++ b/sbroad-core/Cargo.toml @@ -25,7 +25,6 @@ sbroad-proc = { path = "../sbroad-proc", version = "0.1" } serde = { version = "1.0", features = ["derive", "rc"] } serde_yaml = "0.8" tarantool = { git = "https://git.picodata.io/picodata/picodata/tarantool-module.git", features = ["picodata", "schema"] } -traversal = "0.1" uuid = { version = "1.1", features = ["v4", "fast-rng", "macro-diagnostics"] } [dev-dependencies] diff --git a/sbroad-core/src/backend/sql/tree.rs b/sbroad-core/src/backend/sql/tree.rs index e16e65bb4a..73a2966fc6 100644 --- a/sbroad-core/src/backend/sql/tree.rs +++ b/sbroad-core/src/backend/sql/tree.rs @@ -3,12 +3,12 @@ use std::collections::HashMap; use std::mem::take; use serde::{Deserialize, Serialize}; -use traversal::DftPost; use crate::errors::{Action, Entity, SbroadError}; use crate::executor::ir::ExecutionPlan; use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Relational}; +use crate::ir::tree::traversal::PostOrder; use crate::ir::tree::Snapshot; use crate::ir::Node; use crate::otm::child_span; @@ -975,23 +975,26 @@ impl<'p> SyntaxPlan<'p> { let ir_plan = plan.get_ir_plan(); // Wrap plan's nodes and preserve their ids. + let capacity = ir_plan.next_id(); match snapshot { Snapshot::Latest => { - let dft_post = DftPost::new(&top, |node| ir_plan.subtree_iter(node)); - for (_, id) in dft_post { + let mut dft_post = + PostOrder::with_capacity(|node| ir_plan.subtree_iter(node), capacity); + for (_, id) in dft_post.iter(top) { // it works only for post-order traversal - let sn_id = sp.add_plan_node(*id)?; - if *id == top { + let sn_id = sp.add_plan_node(id)?; + if id == top { sp.set_top(sn_id)?; } } } Snapshot::Oldest => { - let dft_post = DftPost::new(&top, |node| ir_plan.flashback_subtree_iter(node)); - for (_, id) in dft_post { + let mut dft_post = + PostOrder::with_capacity(|node| ir_plan.flashback_subtree_iter(node), capacity); + for (_, id) in dft_post.iter(top) { // it works only for post-order traversal - let sn_id = sp.add_plan_node(*id)?; - if *id == top { + let sn_id = sp.add_plan_node(id)?; + if id == top { sp.set_top(sn_id)?; } } diff --git a/sbroad-core/src/executor/bucket.rs b/sbroad-core/src/executor/bucket.rs index 45ddb92f4a..30d07c9f59 100644 --- a/sbroad-core/src/executor/bucket.rs +++ b/sbroad-core/src/executor/bucket.rs @@ -1,7 +1,5 @@ use std::collections::HashSet; -use traversal::DftPost; - use crate::errors::{Action, Entity, SbroadError}; use crate::executor::engine::Coordinator; use crate::executor::Query; @@ -10,6 +8,7 @@ use crate::ir::expression::Expression; use crate::ir::helpers::RepeatableState; use crate::ir::operator::{Bool, Relational}; use crate::ir::transformation::redistribution::MotionPolicy; +use crate::ir::tree::traversal::PostOrder; use crate::ir::value::Value; use crate::otm::child_span; use sbroad_proc::otm_child_span; @@ -134,7 +133,7 @@ where *right_columns.get(*position).ok_or_else(|| { SbroadError::NotFound( Entity::Column, - format!("at position {} for right row", position), + format!("at position {position} for right row"), ) })?; let right_column_expr = ir_plan.get_expression_node(right_column_id)?; @@ -204,11 +203,13 @@ where let ir_plan = self.exec_plan.get_ir_plan(); // We use a `subtree_iter()` because we need DNF version of the filter/condition // expressions to determine buckets. - let tree = DftPost::new(&top_id, |node| ir_plan.subtree_iter(node)); + let capacity = ir_plan.next_id(); + let mut tree = PostOrder::with_capacity(|node| ir_plan.subtree_iter(node), capacity); let nodes: Vec<usize> = tree + .iter(top_id) .filter_map(|(_, id)| { - if ir_plan.get_relation_node(*id).is_ok() { - Some(*id) + if ir_plan.get_relation_node(id).is_ok() { + Some(id) } else { None } diff --git a/sbroad-core/src/executor/engine.rs b/sbroad-core/src/executor/engine.rs index ec7a9fc2f8..46412227cf 100644 --- a/sbroad-core/src/executor/engine.rs +++ b/sbroad-core/src/executor/engine.rs @@ -169,7 +169,7 @@ pub fn sharding_keys_from_tuple<'rec>( let value = tuple.get(*position).ok_or_else(|| { SbroadError::NotFound( Entity::ShardingKey, - format!("position {:?} in the tuple {:?}", position, tuple), + format!("position {position:?} in the tuple {tuple:?}"), ) })?; sharding_tuple.push(value); diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs index 39e6993aea..72be683814 100644 --- a/sbroad-core/src/executor/engine/mock.rs +++ b/sbroad-core/src/executor/engine/mock.rs @@ -300,7 +300,7 @@ impl Coordinator for RouterRuntimeMock { } else { Err(SbroadError::NotFound( Entity::VirtualTable, - format!("for motion node {}", motion_node_id), + format!("for motion node {motion_node_id}"), )) } } diff --git a/sbroad-core/src/executor/ir.rs b/sbroad-core/src/executor/ir.rs index d92ad547d7..6469ce8c89 100644 --- a/sbroad-core/src/executor/ir.rs +++ b/sbroad-core/src/executor/ir.rs @@ -3,13 +3,13 @@ use std::rc::Rc; use ahash::AHashMap; use serde::{Deserialize, Serialize}; -use traversal::DftPost; use crate::errors::{Action, Entity, SbroadError}; use crate::executor::vtable::{VirtualTable, VirtualTableMap}; use crate::ir::expression::Expression; use crate::ir::operator::Relational; use crate::ir::transformation::redistribution::MotionPolicy; +use crate::ir::tree::traversal::PostOrder; use crate::ir::{Node, Plan}; /// Query type (used to parse the returned results). @@ -243,11 +243,13 @@ impl ExecutionPlan { HashMap::with_capacity(vtables_capacity); let mut new_plan = Plan::new(); new_plan.nodes.reserve(nodes_capacity); - let subtree = DftPost::new(&top_id, |node| { - self.get_ir_plan().exec_plan_subtree_iter(node) - }); - let nodes: Vec<usize> = subtree.map(|(_, id)| *id).collect(); - for node_id in nodes { + let mut subtree = PostOrder::with_capacity( + |node| self.get_ir_plan().exec_plan_subtree_iter(node), + self.get_ir_plan().next_id(), + ); + subtree.populate_nodes(top_id); + let nodes = subtree.take_nodes(); + for (_, node_id) in nodes { // We have already processed this node (sub-queries in BETWEEN can be referred twice). if translation.contains_key(&node_id) { continue; diff --git a/sbroad-core/src/executor/shard.rs b/sbroad-core/src/executor/shard.rs index 6f80b84485..aec54bab27 100644 --- a/sbroad-core/src/executor/shard.rs +++ b/sbroad-core/src/executor/shard.rs @@ -1,7 +1,5 @@ use std::collections::HashSet; -use traversal::DftPost; - use crate::executor::ir::ExecutionPlan; use crate::{ errors::{SbroadError, Entity}, @@ -14,11 +12,11 @@ impl<'e> ExecutionPlan<'e> { fn get_bool_eq_with_rows(&self, top_node_id: usize) -> Vec<usize> { let mut nodes: Vec<usize> = Vec::new(); let ir_plan = self.get_ir_plan(); - - let post_tree = DftPost::new(&top_node_id, |node| ir_plan.subtree_iter(node)); - for (_, node_id) in post_tree { - if ir_plan.is_bool_eq_with_rows(*node_id) { - nodes.push(*node_id); + let capacity = ir_plan.next_id(); + let post_tree = PostOrder::with_capacity(|node| ir_plan.subtree_iter(node), capacity); + for (_, node_id) in post_tree.iter(top_node_id) { + if ir_plan.is_bool_eq_with_rows(node_id) { + nodes.push(node_id); } } nodes @@ -36,10 +34,10 @@ impl<'e> ExecutionPlan<'e> { let mut result = HashSet::new(); let ir_plan = self.get_ir_plan(); - let post_tree = DftPost::new(&top_node_id, |node| ir_plan.nodes.rel_iter(node)); - for (_, node) in post_tree { - if ir_plan.get_relation_node(*node)?.is_motion() { - let vtable = self.get_motion_vtable(*node)?; + let mut post_tree = PostOrder::with_capacity(|node| ir_plan.subtree_iter(node), ir_plan.next_id()); + for (_, id) in post_tree.iter(top_node_id) { + if ir_plan.get_relation_node(id)?.is_motion() { + let vtable = self.get_motion_vtable(id)?; result.extend(&vtable.get_sharding_keys()?); } } diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index 710e9ce079..ec22742793 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -5,7 +5,6 @@ use pest::Parser; use std::collections::{HashMap, HashSet}; -use traversal::DftPost; use crate::errors::{Entity, SbroadError}; use crate::executor::engine::{normalize_name_from_sql, CoordinatorMetadata}; @@ -17,6 +16,7 @@ use crate::frontend::Ast; use crate::ir::expression::cast::Type as CastType; use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Unary}; +use crate::ir::tree::traversal::PostOrder; use crate::ir::value::Value; use crate::ir::{Node, Plan}; use crate::otm::child_span; @@ -123,15 +123,16 @@ impl Ast for AbstractSyntaxTree { Some(t) => t, None => return Err(SbroadError::Invalid(Entity::AST, None)), }; - let dft_post = DftPost::new(&top, |node| self.nodes.ast_iter(node)); + let capacity = self.nodes.arena.len(); + let mut dft_post = PostOrder::with_capacity(|node| self.nodes.ast_iter(node), capacity); let mut map = Translation::with_capacity(self.nodes.next_id()); let mut rows: HashSet<usize> = HashSet::with_capacity(self.nodes.next_id()); let mut col_idx: usize = 0; let mut betweens: Vec<Between> = Vec::new(); - for (_, id) in dft_post { - let node = self.nodes.get_node(*id)?; + for (_, id) in dft_post.iter(top) { + let node = self.nodes.get_node(id)?; match &node.rule { Type::Scan => { let ast_child_id = node.children.first().ok_or_else(|| { @@ -140,7 +141,7 @@ impl Ast for AbstractSyntaxTree { ) })?; let plan_child_id = map.get(*ast_child_id)?; - map.add(*id, plan_child_id); + map.add(id, plan_child_id); if let Some(ast_scan_id) = node.children.get(1) { let ast_scan = self.nodes.get_node(*ast_scan_id)?; if let Type::ScanName = ast_scan.rule { @@ -163,7 +164,7 @@ impl Ast for AbstractSyntaxTree { let t = metadata.get_table_segment(table)?; plan.add_rel(t); let scan_id = plan.add_scan(&normalize_name_from_sql(table), None)?; - map.add(*id, scan_id); + map.add(id, scan_id); } else { return Err(SbroadError::Invalid( Entity::Type, @@ -196,10 +197,10 @@ impl Ast for AbstractSyntaxTree { None }; let plan_sq_id = plan.add_sub_query(plan_child_id, alias_name.as_deref())?; - map.add(*id, plan_sq_id); + map.add(id, plan_sq_id); } Type::Reference => { - let ast_rel_list = self.get_referred_relational_nodes(*id)?; + let ast_rel_list = self.get_referred_relational_nodes(id)?; let mut plan_rel_list = Vec::new(); for ast_id in ast_rel_list { let plan_id = map.get(ast_id)?; @@ -269,7 +270,7 @@ impl Ast for AbstractSyntaxTree { &[&col_name], )?; rows.insert(ref_id); - map.add(*id, ref_id); + map.add(id, ref_id); } else { return Err(SbroadError::NotFound( Entity::Column, @@ -290,7 +291,7 @@ impl Ast for AbstractSyntaxTree { &[&col_name], )?; rows.insert(ref_id); - map.add(*id, ref_id); + map.add(id, ref_id); } else { return Err(SbroadError::NotFound( Entity::Column, @@ -327,7 +328,7 @@ impl Ast for AbstractSyntaxTree { &[&col_name], )?; rows.insert(ref_id); - map.add(*id, ref_id); + map.add(id, ref_id); } let right_col_map = plan .get_relation_node(*plan_right_id)? @@ -339,11 +340,11 @@ impl Ast for AbstractSyntaxTree { &[&col_name], )?; rows.insert(ref_id); - map.add(*id, ref_id); + map.add(id, ref_id); } return Err(SbroadError::NotFound( Entity::Column, - format!("'{}' for the join left or right children", col_name), + format!("'{col_name}' for the join left or right children"), )); } else { return Err(SbroadError::UnexpectedNumberOfValues( @@ -408,7 +409,7 @@ impl Ast for AbstractSyntaxTree { "Referred column is not found.".into(), ) })?; - map.add(*id, ref_id); + map.add(id, ref_id); } else { return Err(SbroadError::UnexpectedNumberOfValues( "expected one or two referred relational nodes, got less or more." @@ -425,14 +426,14 @@ impl Ast for AbstractSyntaxTree { | Type::True | Type::False => { let val = Value::from_node(node)?; - map.add(*id, plan.add_const(val)); + map.add(id, plan.add_const(val)); } Type::Parameter => { - map.add(*id, plan.add_param()); + map.add(id, plan.add_param()); } Type::Asterisk => { // We can get an asterisk only in projection. - let ast_rel_list = self.get_referred_relational_nodes(*id)?; + let ast_rel_list = self.get_referred_relational_nodes(id)?; let mut plan_rel_list = Vec::new(); for ast_id in ast_rel_list { let plan_id = map.get(ast_id)?; @@ -450,7 +451,7 @@ impl Ast for AbstractSyntaxTree { ) })?; let plan_asterisk_id = plan.add_row_for_output(plan_rel_id, &[], false)?; - map.add(*id, plan_asterisk_id); + map.add(id, plan_asterisk_id); } Type::Alias => { let ast_ref_id = node.children.first().ok_or_else(|| { @@ -472,14 +473,14 @@ impl Ast for AbstractSyntaxTree { let plan_alias_id = plan .nodes .add_alias(&normalize_name_from_sql(name), plan_ref_id)?; - map.add(*id, plan_alias_id); + map.add(id, plan_alias_id); } Type::Column => { let ast_child_id = node.children.first().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues("Column has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; - map.add(*id, plan_child_id); + map.add(id, plan_child_id); } Type::Row => { let mut plan_col_list = Vec::new(); @@ -498,7 +499,7 @@ impl Ast for AbstractSyntaxTree { plan_col_list.push(plan_id); } let plan_row_id = plan.nodes.add_row(plan_col_list, None); - map.add(*id, plan_row_id); + map.add(id, plan_row_id); } Type::And | Type::Or @@ -523,7 +524,7 @@ impl Ast for AbstractSyntaxTree { let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; let op = Bool::from_node_type(&node.rule)?; let cond_id = plan.add_cond(plan_left_id, op, plan_right_id)?; - map.add(*id, cond_id); + map.add(id, cond_id); } Type::IsNull | Type::IsNotNull => { let ast_child_id = node.children.first().ok_or_else(|| { @@ -535,7 +536,7 @@ impl Ast for AbstractSyntaxTree { let plan_child_id = plan.as_row(map.get(*ast_child_id)?, &mut rows)?; let op = Unary::from_node_type(&node.rule)?; let unary_id = plan.add_unary(op, plan_child_id)?; - map.add(*id, unary_id); + map.add(id, unary_id); } Type::Between => { // left BETWEEN center AND right @@ -558,7 +559,7 @@ impl Ast for AbstractSyntaxTree { let greater_eq_id = plan.add_cond(plan_left_id, Bool::GtEq, plan_center_id)?; let less_eq_id = plan.add_cond(plan_left_id, Bool::LtEq, plan_right_id)?; let and_id = plan.add_cond(greater_eq_id, Bool::And, less_eq_id)?; - map.add(*id, and_id); + map.add(id, and_id); betweens.push(Between::new(plan_left_id, less_eq_id)); } Type::Cast => { @@ -602,7 +603,7 @@ impl Ast for AbstractSyntaxTree { CastType::try_from(&ast_type.rule) }?; let cast_id = plan.add_cast(plan_child_id, cast_type)?; - map.add(*id, cast_id); + map.add(id, cast_id); } Type::Concat => { let ast_left_id = node.children.first().ok_or_else(|| { @@ -614,14 +615,14 @@ impl Ast for AbstractSyntaxTree { })?; let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; let concat_id = plan.add_concat(plan_left_id, plan_right_id)?; - map.add(*id, concat_id); + map.add(id, concat_id); } Type::Condition => { let ast_child_id = node.children.first().ok_or_else(|| { SbroadError::UnexpectedNumberOfValues("Condition has no children.".into()) })?; let plan_child_id = map.get(*ast_child_id)?; - map.add(*id, plan_child_id); + map.add(id, plan_child_id); } Type::Function => { if let Some((first, other)) = node.children.split_first() { @@ -637,7 +638,7 @@ impl Ast for AbstractSyntaxTree { let func = metadata.get_function(function_name)?; if func.is_stable() { let plan_func_id = plan.add_stable_function(func, plan_arg_list)?; - map.add(*id, plan_func_id); + map.add(id, plan_func_id); } else { // At the moment we don't support any non-stable functions. // Later this code block should handle other function behaviors. @@ -669,7 +670,7 @@ impl Ast for AbstractSyntaxTree { })?; let plan_cond_id = map.get(*ast_cond_id)?; let plan_join_id = plan.add_join(plan_left_id, plan_right_id, plan_cond_id)?; - map.add(*id, plan_join_id); + map.add(id, plan_join_id); } Type::Selection => { let ast_child_id = node.children.first().ok_or_else(|| { @@ -684,7 +685,7 @@ impl Ast for AbstractSyntaxTree { })?; let plan_filter_id = map.get(*ast_filter_id)?; let plan_selection_id = plan.add_select(&[plan_child_id], plan_filter_id)?; - map.add(*id, plan_selection_id); + map.add(id, plan_selection_id); } Type::Projection => { let ast_child_id = node.children.first().ok_or_else(|| { @@ -735,7 +736,7 @@ impl Ast for AbstractSyntaxTree { } } let projection_id = plan.add_proj_internal(plan_child_id, &columns)?; - map.add(*id, projection_id); + map.add(id, projection_id); } Type::Except => { let ast_left_id = node.children.first().ok_or_else(|| { @@ -747,7 +748,7 @@ impl Ast for AbstractSyntaxTree { })?; let plan_right_id = map.get(*ast_right_id)?; let plan_except_id = plan.add_except(plan_left_id, plan_right_id)?; - map.add(*id, plan_except_id); + map.add(id, plan_except_id); } Type::UnionAll => { let ast_left_id = node.children.first().ok_or_else(|| { @@ -762,7 +763,7 @@ impl Ast for AbstractSyntaxTree { })?; let plan_right_id = map.get(*ast_right_id)?; let plan_union_all_id = plan.add_union_all(plan_left_id, plan_right_id)?; - map.add(*id, plan_union_all_id); + map.add(id, plan_union_all_id); } Type::ValuesRow => { let ast_child_id = node.children.first().ok_or_else(|| { @@ -770,7 +771,7 @@ impl Ast for AbstractSyntaxTree { })?; let plan_child_id = map.get(*ast_child_id)?; let values_row_id = plan.add_values_row(plan_child_id, &mut col_idx)?; - map.add(*id, values_row_id); + map.add(id, values_row_id); } Type::Values => { let mut plan_children_ids: Vec<usize> = Vec::with_capacity(node.children.len()); @@ -779,7 +780,7 @@ impl Ast for AbstractSyntaxTree { plan_children_ids.push(plan_child_id); } let plan_values_id = plan.add_values(plan_children_ids)?; - map.add(*id, plan_values_id); + map.add(id, plan_values_id); } Type::Insert => { let ast_table_id = node.children.first().ok_or_else(|| { @@ -836,7 +837,7 @@ impl Ast for AbstractSyntaxTree { let plan_child_id = map.get(*ast_child_id)?; plan.add_insert(relation, plan_child_id, &[])? }; - map.add(*id, plan_insert_id); + map.add(id, plan_insert_id); } Type::Explain => { plan.mark_as_explain(); diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs index 98fad9d621..0427f9def2 100644 --- a/sbroad-core/src/frontend/sql/ast.rs +++ b/sbroad-core/src/frontend/sql/ast.rs @@ -10,9 +10,9 @@ use std::mem::swap; use pest::iterators::Pair; use serde::{Deserialize, Serialize}; -use traversal::DftPost; use crate::errors::{Action, Entity, SbroadError}; +use crate::ir::tree::traversal::{PostOrder, EXPR_CAPACITY}; /// Parse tree #[derive(Parser)] @@ -382,10 +382,7 @@ impl AbstractSyntaxTree { for (parent_id, children_pos) in parents { let parent = self.nodes.get_node(parent_id)?; let child_id = *parent.children.get(children_pos).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - format!("at expected position {}", children_pos), - ) + SbroadError::NotFound(Entity::Node, format!("at expected position {children_pos}")) })?; let child = self.nodes.get_node(child_id)?; let mut node_id = *child.children.first().ok_or_else(|| { @@ -841,9 +838,10 @@ impl AbstractSyntaxTree { // Traverse relational nodes in Post Order and then enter their subtrees // and map expressions to relational nodes. let top = self.get_top()?; - let tree = DftPost::new(&top, |node| self.nodes.ast_iter(node)); - for (_, node_id) in tree { - let rel_node = self.nodes.get_node(*node_id)?; + let capacity = self.nodes.arena.len(); + let mut tree = PostOrder::with_capacity(|node| self.nodes.ast_iter(node), capacity); + for (_, node_id) in tree.iter(top) { + let rel_node = self.nodes.get_node(node_id)?; match rel_node.rule { Type::Projection => { let rel_id = rel_node.children.first().ok_or_else(|| { @@ -852,11 +850,13 @@ impl AbstractSyntaxTree { ) })?; for top in rel_node.children.iter().skip(1) { - let subtree = DftPost::new(top, |node| self.nodes.ast_iter(node)); - for (_, id) in subtree { - let node = self.nodes.get_node(*id)?; + let capacity = EXPR_CAPACITY * 3; + let mut subtree = + PostOrder::with_capacity(|node| self.nodes.ast_iter(node), capacity); + for (_, id) in subtree.iter(*top) { + let node = self.nodes.get_node(id)?; if let Type::Reference | Type::Asterisk = node.rule { - if let Entry::Vacant(entry) = map.entry(*id) { + if let Entry::Vacant(entry) = map.entry(id) { entry.insert(vec![*rel_id]); } } @@ -875,11 +875,13 @@ impl AbstractSyntaxTree { "that is AST selection filter child with index 1".into(), ) })?; - let subtree = DftPost::new(filter, |node| self.nodes.ast_iter(node)); - for (_, id) in subtree { - let node = self.nodes.get_node(*id)?; + let capacity = EXPR_CAPACITY * 2; + let mut subtree = + PostOrder::with_capacity(|node| self.nodes.ast_iter(node), capacity); + for (_, id) in subtree.iter(*filter) { + let node = self.nodes.get_node(id)?; if node.rule == Type::Reference { - if let Entry::Vacant(entry) = map.entry(*id) { + if let Entry::Vacant(entry) = map.entry(id) { entry.insert(vec![*rel_id]); } } @@ -904,11 +906,13 @@ impl AbstractSyntaxTree { ) })?; // ast_iter is not working here - we have to ignore sub-queries in the join condition. - let subtree = DftPost::new(cond_id, |node| self.nodes.ast_iter(node)); - for (_, id) in subtree { - let node = self.nodes.get_node(*id)?; + let capacity = EXPR_CAPACITY * 4; + let mut subtree = + PostOrder::with_capacity(|node| self.nodes.ast_iter(node), capacity); + for (_, id) in subtree.iter(*cond_id) { + let node = self.nodes.get_node(id)?; if node.rule == Type::Reference { - if let Entry::Vacant(entry) = map.entry(*id) { + if let Entry::Vacant(entry) = map.entry(id) { entry.insert(vec![*left_id, *right_id]); } } diff --git a/sbroad-core/src/frontend/sql/ast/tests.rs b/sbroad-core/src/frontend/sql/ast/tests.rs index caf4329ade..7440a8469b 100644 --- a/sbroad-core/src/frontend/sql/ast/tests.rs +++ b/sbroad-core/src/frontend/sql/ast/tests.rs @@ -3,7 +3,6 @@ use crate::frontend::Ast; use pretty_assertions::assert_eq; use std::fs; use std::path::Path; -use traversal::DftPost; #[test] fn ast() { @@ -91,61 +90,62 @@ fn traversal() { let query = r#"select a from t where a = 1"#; let ast = AbstractSyntaxTree::new(query).unwrap(); let top = ast.top.unwrap(); - let mut dft_post = DftPost::new(&top, |node| ast.nodes.ast_iter(node)); + let mut dft_post = PostOrder::with_capacity(|node| ast.nodes.ast_iter(node), 64); + let mut iter = dft_post.iter(top); - let (_, table_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*table_id).unwrap(); + let (_, table_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(table_id).unwrap(); assert_eq!(node.rule, Type::Table); - let (_, scan_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*scan_id).unwrap(); + let (_, scan_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(scan_id).unwrap(); assert_eq!(node.rule, Type::Scan); - let (_, sel_name_a_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*sel_name_a_id).unwrap(); + let (_, sel_name_a_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(sel_name_a_id).unwrap(); assert_eq!(node.rule, Type::ColumnName); - let (_, a_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*a_id).unwrap(); + let (_, a_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(a_id).unwrap(); assert_eq!(node.rule, Type::Reference); - let (_, num_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*num_id).unwrap(); + let (_, num_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(num_id).unwrap(); assert_eq!(node.rule, Type::Unsigned); - let (_, eq_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*eq_id).unwrap(); + let (_, eq_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(eq_id).unwrap(); assert_eq!(node.rule, Type::Eq); - let (_, selection_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*selection_id).unwrap(); + let (_, selection_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(selection_id).unwrap(); assert_eq!(node.rule, Type::Selection); - let (_, prj_name_a_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*prj_name_a_id).unwrap(); + let (_, prj_name_a_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(prj_name_a_id).unwrap(); assert_eq!(node.rule, Type::ColumnName); - let (_, str_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*str_id).unwrap(); + let (_, str_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(str_id).unwrap(); assert_eq!(node.rule, Type::Reference); - let (_, alias_name_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*alias_name_id).unwrap(); + let (_, alias_name_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(alias_name_id).unwrap(); assert_eq!(node.rule, Type::AliasName); - let (_, alias_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*alias_id).unwrap(); + let (_, alias_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(alias_id).unwrap(); assert_eq!(node.rule, Type::Alias); - let (_, col_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*col_id).unwrap(); + let (_, col_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(col_id).unwrap(); assert_eq!(node.rule, Type::Column); - let (_, projection_id) = dft_post.next().unwrap(); - let node = ast.nodes.get_node(*projection_id).unwrap(); + let (_, projection_id) = iter.next().unwrap(); + let node = ast.nodes.get_node(projection_id).unwrap(); assert_eq!(node.rule, Type::Projection); - assert_eq!(None, dft_post.next()); + assert_eq!(None, iter.next()); } #[test] diff --git a/sbroad-core/src/frontend/sql/ir.rs b/sbroad-core/src/frontend/sql/ir.rs index 5791ae48b3..3da97977b4 100644 --- a/sbroad-core/src/frontend/sql/ir.rs +++ b/sbroad-core/src/frontend/sql/ir.rs @@ -2,13 +2,13 @@ use std::collections::{HashMap, HashSet}; use ahash::AHashMap; use tarantool::decimal::Decimal; -use traversal::DftPost; use crate::errors::{Action, Entity, SbroadError}; use crate::frontend::sql::ast::{ParseNode, Type}; use crate::ir::expression::Expression; use crate::ir::helpers::RepeatableState; use crate::ir::operator::{Bool, Relational, Unary}; +use crate::ir::tree::traversal::{PostOrder, EXPR_CAPACITY, REL_CAPACITY}; use crate::ir::value::double::Double; use crate::ir::value::Value; use crate::ir::{Node, Plan}; @@ -128,7 +128,7 @@ impl Translation { self.map.get(&old).copied().ok_or_else(|| { SbroadError::NotFound( Entity::Node, - format!("(parse node) [{}] in translation map", old), + format!("(parse node) [{old}] in translation map"), ) }) } @@ -156,28 +156,32 @@ impl Plan { fn gather_sq_for_replacement(&self) -> Result<HashSet<SubQuery, RepeatableState>, SbroadError> { let mut set: HashSet<SubQuery, RepeatableState> = HashSet::with_hasher(RepeatableState); let top = self.get_top()?; - let rel_post = DftPost::new(&top, |node| self.nodes.rel_iter(node)); + let mut rel_post = PostOrder::with_capacity(|node| self.nodes.rel_iter(node), REL_CAPACITY); // Traverse expression trees of the selection and join nodes. // Gather all sub-queries in the boolean expressions there. - for (_, rel_id) in rel_post { - match self.get_node(*rel_id)? { + for (_, rel_id) in rel_post.iter(top) { + match self.get_node(rel_id)? { Node::Relational( Relational::Selection { filter: tree, .. } | Relational::InnerJoin { condition: tree, .. }, ) => { - let expr_post = DftPost::new(tree, |node| self.nodes.expr_iter(node, false)); - for (_, id) in expr_post { + let capacity = self.nodes.len(); + let mut expr_post = PostOrder::with_capacity( + |node| self.nodes.expr_iter(node, false), + capacity, + ); + for (_, id) in expr_post.iter(*tree) { if let Node::Expression(Expression::Bool { left, right, .. }) = - self.get_node(*id)? + self.get_node(id)? { let children = &[*left, *right]; for child in children { if let Node::Relational(Relational::ScanSubQuery { .. }) = self.get_node(*child)? { - set.insert(SubQuery::new(*rel_id, *id, *child)); + set.insert(SubQuery::new(rel_id, id, *child)); } } } @@ -319,10 +323,12 @@ impl Plan { } fn clone_expr_subtree(&mut self, top_id: usize) -> Result<usize, SbroadError> { - let subtree = DftPost::new(&top_id, |node| self.nodes.expr_iter(node, false)); - let nodes = subtree.map(|(_, node_id)| *node_id).collect::<Vec<_>>(); let mut map = HashMap::new(); - for id in nodes { + let mut subtree = + PostOrder::with_capacity(|node| self.nodes.expr_iter(node, false), EXPR_CAPACITY); + subtree.populate_nodes(top_id); + let nodes = subtree.take_nodes(); + for (_, id) in nodes { let next_id = self.nodes.next_id(); let mut expr = self.get_expression_node(id)?.clone(); match expr { diff --git a/sbroad-core/src/frontend/sql/tree.rs b/sbroad-core/src/frontend/sql/tree.rs index 86ee344720..eb93621f40 100644 --- a/sbroad-core/src/frontend/sql/tree.rs +++ b/sbroad-core/src/frontend/sql/tree.rs @@ -6,20 +6,20 @@ use std::cell::RefCell; /// AST traversal iterator. #[derive(Debug)] pub struct AstIterator<'n> { - current: &'n usize, + current: usize, child: RefCell<usize>, nodes: &'n ParseNodes, } impl<'n> Iterator for AstIterator<'n> { - type Item = &'n usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - if let Some(node) = self.nodes.arena.get(*self.current) { + if let Some(node) = self.nodes.arena.get(self.current) { let step = *self.child.borrow(); if step < node.children.len() { *self.child.borrow_mut() += 1; - return node.children.get(step); + return node.children.get(step).copied(); } None } else { @@ -32,7 +32,7 @@ impl<'n> ParseNodes { /// Returns an iterator over the children of the node. #[allow(dead_code)] #[must_use] - pub fn ast_iter(&'n self, current: &'n usize) -> AstIterator<'n> { + pub fn ast_iter(&'n self, current: usize) -> AstIterator<'n> { AstIterator { current, child: RefCell::new(0), diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs index 5d3e9c11eb..6c92043236 100644 --- a/sbroad-core/src/ir.rs +++ b/sbroad-core/src/ir.rs @@ -563,7 +563,7 @@ impl Plan { .ok_or_else(|| { SbroadError::NotFound( Entity::Column, - format!("at position {} in row list", position), + format!("at position {position} in row list"), ) })?; diff --git a/sbroad-core/src/ir/api/parameter.rs b/sbroad-core/src/ir/api/parameter.rs index 6921c38d23..91ae115bdd 100644 --- a/sbroad-core/src/ir/api/parameter.rs +++ b/sbroad-core/src/ir/api/parameter.rs @@ -1,6 +1,7 @@ use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::operator::Relational; +use crate::ir::tree::traversal::PostOrder; use crate::ir::value::Value; use crate::ir::{Node, Plan}; use crate::otm::child_span; @@ -8,7 +9,6 @@ use sbroad_proc::otm_child_span; use ahash::RandomState; use std::collections::{HashMap, HashSet}; -use traversal::DftPost; impl Plan { pub fn add_param(&mut self) -> usize { @@ -47,9 +47,11 @@ impl Plan { return Ok(()); } + let capacity = self.next_id(); + let mut tree = PostOrder::with_capacity(|node| self.subtree_iter(node), capacity); let top_id = self.get_top()?; - let tree = DftPost::new(&top_id, |node| self.subtree_iter(node)); - let nodes: Vec<usize> = tree.map(|(_, id)| *id).collect(); + tree.populate_nodes(top_id); + let nodes = tree.take_nodes(); // Transform parameters to values. The result values are stored in the // opposite to parameters order. @@ -77,7 +79,7 @@ impl Plan { // Populate rows. let mut idx = value_ids.len(); - for id in &nodes { + for (_, id) in &nodes { let node = self.get_node(*id)?; match node { Node::Relational(rel) => match rel { @@ -160,7 +162,7 @@ impl Plan { // Replace parameters in the plan. idx = value_ids.len(); - for id in &nodes { + for (_, id) in &nodes { let node = self.get_mut_node(*id)?; match node { Node::Relational(rel) => match rel { @@ -240,7 +242,7 @@ impl Plan { } // Update values row output. - for id in nodes { + for (_, id) in nodes { if let Ok(Relational::ValuesRow { .. }) = self.get_relation_node(id) { self.update_values_row(id)?; } diff --git a/sbroad-core/src/ir/distribution.rs b/sbroad-core/src/ir/distribution.rs index 0cbfbc0f26..ae7c909c3b 100644 --- a/sbroad-core/src/ir/distribution.rs +++ b/sbroad-core/src/ir/distribution.rs @@ -454,10 +454,7 @@ impl Plan { row_id: usize, ) -> Result<(), SbroadError> { let table: &Table = self.relations.get(table_name).ok_or_else(|| { - SbroadError::NotFound( - Entity::Table, - format!("{} among plan relations", table_name), - ) + SbroadError::NotFound(Entity::Table, format!("{table_name} among plan relations")) })?; let mut new_key: Key = Key::new(Vec::new()); let all_found = table.key.positions.iter().all(|pos| { diff --git a/sbroad-core/src/ir/explain.rs b/sbroad-core/src/ir/explain.rs index b3312ab4f4..3dc0bdd2f0 100644 --- a/sbroad-core/src/ir/explain.rs +++ b/sbroad-core/src/ir/explain.rs @@ -3,7 +3,6 @@ use std::fmt::{Display, Formatter, Write as _}; use itertools::Itertools; use serde::Serialize; -use traversal::DftPost; use crate::errors::{Entity, SbroadError}; use crate::ir::expression::cast::Type as CastType; @@ -15,6 +14,7 @@ use crate::ir::transformation::redistribution::{ use crate::ir::Plan; use super::operator::{Bool, Unary}; +use super::tree::traversal::{PostOrder, EXPR_CAPACITY, REL_CAPACITY}; use super::value::Value; #[derive(Debug, Serialize)] @@ -57,11 +57,12 @@ impl Default for ColExpr { impl ColExpr { #[allow(dead_code)] fn new(plan: &Plan, subtree_top: usize) -> Result<Self, SbroadError> { - let dft_post = DftPost::new(&subtree_top, |node| plan.nodes.expr_iter(node, false)); let mut stack: Vec<ColExpr> = Vec::new(); + let mut dft_post = + PostOrder::with_capacity(|node| plan.nodes.expr_iter(node, false), EXPR_CAPACITY); - for (_, id) in dft_post { - let current_node = plan.get_expression_node(*id)?; + for (_, id) in dft_post.iter(subtree_top) { + let current_node = plan.get_expression_node(id)?; match ¤t_node { Expression::Cast { to, .. } => { @@ -76,7 +77,7 @@ impl ColExpr { Expression::Reference { position, .. } => { let mut col_name = String::new(); - let rel_id: usize = *plan.get_relational_from_reference_node(*id)?; + let rel_id: usize = *plan.get_relational_from_reference_node(id)?; let rel_node = plan.get_relation_node(rel_id)?; if let Some(name) = rel_node.scan_name(plan, *position)? { @@ -163,14 +164,15 @@ impl Col { fn new(plan: &Plan, subtree_top: usize) -> Result<Self, SbroadError> { let mut column = Col::default(); - let dft_post = DftPost::new(&subtree_top, |node| plan.nodes.expr_iter(node, true)); - for (_, id) in dft_post { - let current_node = plan.get_expression_node(*id)?; + let mut dft_post = + PostOrder::with_capacity(|node| plan.nodes.expr_iter(node, true), EXPR_CAPACITY); + for (_, id) in dft_post.iter(subtree_top) { + let current_node = plan.get_expression_node(id)?; if let Expression::Alias { name, .. } = ¤t_node { column.alias = Some(name.to_string()); } else { - column.col = ColExpr::new(plan, *id)?; + column.col = ColExpr::new(plan, id)?; } } @@ -669,10 +671,10 @@ impl FullExplain { let mut stack: Vec<ExplainTreePart> = Vec::with_capacity(ir.nodes.relation_node_amount()); let mut result = FullExplain::default(); - let dft_post = DftPost::new(&top_id, |node| ir.nodes.rel_iter(node)); - for (level, id) in dft_post { + let mut dft_post = PostOrder::with_capacity(|node| ir.nodes.rel_iter(node), REL_CAPACITY); + for (level, id) in dft_post.iter(top_id) { let mut current_node = ExplainTreePart::with_level(level); - let node = ir.get_relation_node(*id)?; + let node = ir.get_relation_node(id)?; current_node.current = match &node { Relational::Except { .. } => { if let (Some(right), Some(left)) = (stack.pop(), stack.pop()) { @@ -789,7 +791,7 @@ impl FullExplain { let col_id = *col_list.get(*pos).ok_or_else(|| { SbroadError::NotFound( Entity::Target, - format!("reference with position {}", pos), + format!("reference with position {pos}"), ) })?; let col_name = ir diff --git a/sbroad-core/src/ir/expression.rs b/sbroad-core/src/ir/expression.rs index ef8db278af..242cb8f801 100644 --- a/sbroad-core/src/ir/expression.rs +++ b/sbroad-core/src/ir/expression.rs @@ -9,12 +9,12 @@ use ahash::RandomState; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; -use traversal::DftPost; use crate::errors::{Entity, SbroadError}; use crate::ir::operator::{Bool, Relational}; use super::distribution::Distribution; +use super::tree::traversal::{PostOrder, EXPR_CAPACITY}; use super::value::Value; use super::{operator, Node, Nodes, Plan}; @@ -247,7 +247,7 @@ impl Nodes { /// - name is empty pub fn add_alias(&mut self, name: &str, child: usize) -> Result<usize, SbroadError> { self.arena.get(child).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, format!("from arena with index {}", child)) + SbroadError::NotFound(Entity::Node, format!("from arena with index {child}")) })?; if name.is_empty() { return Err(SbroadError::Invalid( @@ -332,7 +332,7 @@ impl Nodes { self.arena.get(*alias_node).ok_or_else(|| { SbroadError::NotFound( Entity::Node, - format!("(Alias) from arena with index {}", alias_node), + format!("(Alias) from arena with index {alias_node}"), ) })? { @@ -362,7 +362,7 @@ impl Nodes { child: usize, ) -> Result<usize, SbroadError> { self.arena.get(child).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, format!("from arena with index {}", child)) + SbroadError::NotFound(Entity::Node, format!("from arena with index {child}")) })?; Ok(self.push(Node::Expression(Expression::Unary { op, child }))) } @@ -431,7 +431,7 @@ impl Plan { } else { return Err(SbroadError::NotFound( Entity::Node, - format!("pointed by target child {}", target_child), + format!("pointed by target child {target_child}"), )); }; let relational_op = self.get_relation_node(child_node)?; @@ -479,7 +479,7 @@ impl Plan { let table = self.get_relation(relation).ok_or_else(|| { SbroadError::NotFound( Entity::Table, - format!("{} among the plan relations", relation), + format!("{relation} among the plan relations"), ) })?; let sharding_column_pos = table.get_bucket_id_position()?; @@ -599,7 +599,7 @@ impl Plan { if !all_found { return Err(SbroadError::NotFound( Entity::Column, - format!("with name {:?}", col_names), + format!("with name {col_names:?}"), )); } @@ -803,18 +803,21 @@ impl Plan { row_id: usize, ) -> Result<HashSet<usize, RandomState>, SbroadError> { let row = self.get_expression_node(row_id)?; - if let Expression::Row { .. } = row { + let capacity = if let Expression::Row { list, .. } = row { + list.len() * 2 } else { return Err(SbroadError::Invalid( Entity::Node, Some("Node is not a row".into()), )); - } - let post_tree = DftPost::new(&row_id, |node| self.nodes.expr_iter(node, false)); - let nodes: Vec<usize> = post_tree.map(|(_, id)| *id).collect(); + }; + let mut post_tree = + PostOrder::with_capacity(|node| self.nodes.expr_iter(node, false), capacity); + post_tree.populate_nodes(row_id); + let nodes = post_tree.take_nodes(); let mut rel_nodes: HashSet<usize, RandomState> = HashSet::with_capacity_and_hasher(nodes.len(), RandomState::new()); - for id in nodes { + for (_, id) in nodes { let reference = self.get_expression_node(id)?; if let Expression::Reference { targets, parent, .. @@ -951,10 +954,11 @@ impl Plan { to_id: Option<usize>, ) -> Result<(), SbroadError> { let mut references: Vec<usize> = Vec::new(); - let subtree = DftPost::new(&node_id, |node| self.nodes.expr_iter(node, false)); - for (_, id) in subtree { - if let Node::Expression(Expression::Reference { .. }) = self.get_node(*id)? { - references.push(*id); + let mut subtree = + PostOrder::with_capacity(|node| self.nodes.expr_iter(node, false), EXPR_CAPACITY); + for (_, id) in subtree.iter(node_id) { + if let Node::Expression(Expression::Reference { .. }) = self.get_node(id)? { + references.push(id); } } for id in references { diff --git a/sbroad-core/src/ir/operator.rs b/sbroad-core/src/ir/operator.rs index 91538ca33b..d5b43d94b6 100644 --- a/sbroad-core/src/ir/operator.rs +++ b/sbroad-core/src/ir/operator.rs @@ -4,7 +4,6 @@ use ahash::RandomState; use serde::{Deserialize, Serialize}; -use serde_yaml::mapping::Entry; use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; @@ -12,11 +11,11 @@ use crate::errors::{Action, Entity, SbroadError}; use super::expression::Expression; use super::transformation::redistribution::{DataGeneration, MotionPolicy}; +use super::tree::traversal::{BreadthFirst, EXPR_CAPACITY, REL_CAPACITY}; use super::{Node, Nodes, Plan}; use crate::collection; use crate::ir::distribution::{Distribution, KeySet}; use crate::ir::relation::ColumnRole; -use traversal::Bft; /// Binary operator returning Bool expression. #[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Hash, Clone)] @@ -467,10 +466,7 @@ impl Relational { let output_row = plan.get_expression_node(self.output())?; let list = output_row.get_row_list()?; let col_id = *list.get(position).ok_or_else(|| { - SbroadError::NotFound( - Entity::Column, - format!("at position {} of Row", position), - ) + SbroadError::NotFound(Entity::Column, format!("at position {position} of Row")) })?; let col_node = plan.get_expression_node(col_id)?; if let Expression::Alias { child, .. } = col_node { @@ -670,7 +666,7 @@ impl Plan { } Err(SbroadError::NotFound( Entity::Table, - format!("{} among the plan relations", table), + format!("{table} among the plan relations"), )) } @@ -709,10 +705,7 @@ impl Plan { { // We'll need it later to update the condition expression (borrow checker). let table = self.get_relation(relation).ok_or_else(|| { - SbroadError::NotFound( - Entity::Table, - format!("{} among plan relations", relation), - ) + SbroadError::NotFound(Entity::Table, format!("{relation} among plan relations")) })?; let sharding_column_pos = table.get_bucket_id_position()?; @@ -727,12 +720,17 @@ impl Plan { children.push(sq_id); // Update references to the sub-query's output in the condition. - let condition_iter = Bft::new(&condition, |node| self.nodes.expr_iter(node, false)); - let refs = condition_iter + let mut condition_tree = BreadthFirst::with_capacity( + |node| self.nodes.expr_iter(node, false), + EXPR_CAPACITY, + EXPR_CAPACITY, + ); + let refs = condition_tree + .iter(condition) .filter_map(|(_, id)| { - let expr = self.get_expression_node(*id).ok(); + let expr = self.get_expression_node(id).ok(); if let Some(Expression::Reference { .. }) = expr { - Some(*id) + Some(id) } else { None } @@ -1141,9 +1139,13 @@ impl Plan { /// - Node returned by the relational iterator is not relational (bug) pub fn is_additional_child(&self, node_id: usize) -> Result<bool, SbroadError> { let top_id = self.get_top()?; - let rel_tree = Bft::new(&top_id, |node| self.nodes.rel_iter(node)); - for (_, id) in rel_tree { - let rel = self.get_relation_node(*id)?; + let mut rel_tree = BreadthFirst::with_capacity( + |node| self.nodes.rel_iter(node), + REL_CAPACITY, + REL_CAPACITY, + ); + for (_, id) in rel_tree.iter(top_id) { + let rel = self.get_relation_node(id)?; match rel { Relational::Selection { children, .. } => { if children.iter().skip(1).any(|&c| c == node_id) { diff --git a/sbroad-core/src/ir/operator/tests.rs b/sbroad-core/src/ir/operator/tests.rs index e3c12e58fc..5c126cda51 100644 --- a/sbroad-core/src/ir/operator/tests.rs +++ b/sbroad-core/src/ir/operator/tests.rs @@ -115,7 +115,7 @@ fn projection() { // Try to build projection from the non-existing node assert_eq!( - SbroadError::NotFound(Entity::Node, format!("from arena with index 42")), + SbroadError::NotFound(Entity::Node, "from arena with index 42".to_string()), plan.add_proj(42, &["a"]).unwrap_err() ); } diff --git a/sbroad-core/src/ir/transformation.rs b/sbroad-core/src/ir/transformation.rs index 654333e688..875ea634f2 100644 --- a/sbroad-core/src/ir/transformation.rs +++ b/sbroad-core/src/ir/transformation.rs @@ -14,7 +14,8 @@ use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Relational}; use crate::ir::Plan; use std::collections::HashMap; -use traversal::DftPost; + +use super::tree::traversal::{PostOrder, EXPR_CAPACITY}; impl Plan { /// Concatenates trivalents (boolean or NULL expressions) to the AND node. @@ -90,9 +91,10 @@ impl Plan { f: &dyn Fn(&mut Plan, usize) -> Result<usize, SbroadError>, ) -> Result<(), SbroadError> { let top_id = self.get_top()?; - let ir_tree = DftPost::new(&top_id, |node| self.nodes.rel_iter(node)); - let nodes: Vec<usize> = ir_tree.map(|(_, id)| *id).collect(); - for id in &nodes { + let mut ir_tree = PostOrder::with_capacity(|node| self.nodes.rel_iter(node), EXPR_CAPACITY); + ir_tree.populate_nodes(top_id); + let nodes = ir_tree.take_nodes(); + for (_, id) in &nodes { let rel = self.get_relation_node(*id)?; let (new_tree_id, old_tree_id) = match rel { Relational::Selection { @@ -140,9 +142,11 @@ impl Plan { ops: &[Bool], ) -> Result<usize, SbroadError> { let mut map: HashMap<usize, usize> = HashMap::new(); - let subtree = DftPost::new(&top_id, |node| self.nodes.expr_iter(node, false)); - let nodes: Vec<usize> = subtree.map(|(_, id)| *id).collect(); - for id in &nodes { + let mut subtree = + PostOrder::with_capacity(|node| self.nodes.expr_iter(node, false), EXPR_CAPACITY); + subtree.populate_nodes(top_id); + let nodes = subtree.take_nodes(); + for (_, id) in &nodes { let expr = self.get_expression_node(*id)?; if let Expression::Bool { op, .. } = expr { if ops.contains(op) || ops.is_empty() { @@ -152,7 +156,7 @@ impl Plan { } } let mut new_top_id = top_id; - for id in &nodes { + for (_, id) in &nodes { let expr = self.get_mut_expression_node(*id)?; // For all expressions in the subtree tries to replace their children // with the new nodes from the map. diff --git a/sbroad-core/src/ir/transformation/merge_tuples.rs b/sbroad-core/src/ir/transformation/merge_tuples.rs index 9bbd1b1e4e..b4371fde04 100644 --- a/sbroad-core/src/ir/transformation/merge_tuples.rs +++ b/sbroad-core/src/ir/transformation/merge_tuples.rs @@ -14,11 +14,12 @@ use crate::errors::{Entity, SbroadError}; use crate::ir::expression::Expression; use crate::ir::helpers::RepeatableState; use crate::ir::operator::Bool; +use crate::ir::tree::traversal::BreadthFirst; +use crate::ir::tree::traversal::EXPR_CAPACITY; use crate::ir::Plan; use crate::otm::child_span; use sbroad_proc::otm_child_span; use std::collections::{hash_map::Entry, HashMap, HashSet}; -use traversal::Bft; fn call_expr_tree_merge_tuples(plan: &mut Plan, top_id: usize) -> Result<usize, SbroadError> { plan.expr_tree_modify_and_chains(top_id, &call_build_and_chains, &call_as_plan) @@ -216,8 +217,12 @@ impl Plan { } visited.insert(*id); - let tree_and = Bft::new(id, |node| self.nodes.and_iter(node)); - let nodes_and: Vec<usize> = tree_and.map(|(_, id)| *id).collect(); + let mut tree_and = BreadthFirst::with_capacity( + |node| self.nodes.and_iter(node), + EXPR_CAPACITY, + EXPR_CAPACITY, + ); + let nodes_and: Vec<usize> = tree_and.iter(*id).map(|(_, id)| id).collect(); let mut nodes_for_chain: Vec<usize> = Vec::with_capacity(nodes_and.len()); for and_id in nodes_and { let expr = self.get_expression_node(and_id)?; @@ -273,8 +278,12 @@ impl Plan { -> Result<HashMap<usize, Chain, RepeatableState>, SbroadError>, f_to_plan: &dyn Fn(&Chain, &mut Plan) -> Result<usize, SbroadError>, ) -> Result<usize, SbroadError> { - let tree = Bft::new(&expr_id, |node| self.nodes.expr_iter(node, false)); - let nodes: Vec<usize> = tree.map(|(_, id)| *id).collect(); + let mut tree = BreadthFirst::with_capacity( + |node| self.nodes.expr_iter(node, false), + EXPR_CAPACITY, + EXPR_CAPACITY, + ); + let nodes: Vec<usize> = tree.iter(expr_id).map(|(_, id)| id).collect(); let chains = f_build_chains(self, &nodes)?; // Replace nodes' children with the merged tuples. diff --git a/sbroad-core/src/ir/transformation/redistribution.rs b/sbroad-core/src/ir/transformation/redistribution.rs index 537de38813..5246b61224 100644 --- a/sbroad-core/src/ir/transformation/redistribution.rs +++ b/sbroad-core/src/ir/transformation/redistribution.rs @@ -5,13 +5,13 @@ use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::{hash_map::Entry, HashMap, HashSet}; use std::fmt::{Display, Formatter}; -use traversal::{Bft, DftPost}; use crate::errors::{Action, Entity, SbroadError}; use crate::ir::distribution::{Distribution, Key, KeySet}; use crate::ir::expression::Expression; use crate::ir::operator::{Bool, Relational}; use crate::ir::relation::Column; +use crate::ir::tree::traversal::{BreadthFirst, PostOrder, EXPR_CAPACITY, REL_CAPACITY}; use crate::ir::value::Value; use crate::ir::{Node, Plan}; use crate::otm::child_span; @@ -157,8 +157,9 @@ impl Plan { /// - plan doesn't contain the top node fn get_relational_nodes_dfs_post(&self) -> Result<Vec<usize>, SbroadError> { let top = self.get_top()?; - let post_tree = DftPost::new(&top, |node| self.nodes.rel_iter(node)); - let nodes: Vec<usize> = post_tree.map(|(_, id)| *id).collect(); + let mut post_tree = + PostOrder::with_capacity(|node| self.nodes.rel_iter(node), REL_CAPACITY); + let nodes: Vec<usize> = post_tree.iter(top).map(|(_, id)| id).collect(); Ok(nodes) } @@ -172,10 +173,11 @@ impl Plan { ) -> Result<Vec<usize>, SbroadError> { let mut nodes: Vec<usize> = Vec::new(); - let post_tree = DftPost::new(&top, |node| self.nodes.expr_iter(node, false)); - for (_, node) in post_tree { + let mut post_tree = + PostOrder::with_capacity(|node| self.nodes.expr_iter(node, false), EXPR_CAPACITY); + for (_, id) in post_tree.iter(top) { // Append only booleans with row children. - if let Node::Expression(Expression::Bool { left, right, .. }) = self.get_node(*node)? { + if let Node::Expression(Expression::Bool { left, right, .. }) = self.get_node(id)? { let left_is_row = matches!( self.get_node(*left)?, Node::Expression(Expression::Row { .. }) @@ -185,7 +187,7 @@ impl Plan { Node::Expression(Expression::Row { .. }) ); if left_is_row && right_is_row { - nodes.push(*node); + nodes.push(id); } } } @@ -480,7 +482,7 @@ impl Plan { let mut children_set: HashSet<usize> = HashSet::new(); for pos in &key.positions { let column_id = *row_map.get(pos).ok_or_else(|| { - SbroadError::NotFound(Entity::Column, format!("{} in row map {:?}", pos, row_map)) + SbroadError::NotFound(Entity::Column, format!("{pos} in row map {row_map:?}")) })?; if let Expression::Reference { targets, .. } = self.get_expression_node(column_id)? { if let Some(targets) = targets { @@ -488,7 +490,7 @@ impl Plan { let child_id = *join_children.get(*target).ok_or_else(|| { SbroadError::NotFound( Entity::Target, - format!("{} in join children {:?}", target, join_children), + format!("{target} in join children {join_children:?}"), ) })?; children_set.insert(child_id); @@ -690,11 +692,12 @@ impl Plan { })?; let mut inner_map: HashMap<usize, MotionPolicy> = HashMap::new(); let mut new_inner_policy = MotionPolicy::Full; - let expr_tree = DftPost::new(&expr_id, |node| self.nodes.expr_iter(node, true)); - for (_, node_id) in expr_tree { - let expr = self.get_expression_node(*node_id)?; + let mut expr_tree = + PostOrder::with_capacity(|node| self.nodes.expr_iter(node, true), EXPR_CAPACITY); + for (_, node_id) in expr_tree.iter(expr_id) { + let expr = self.get_expression_node(node_id)?; let bool_op = if let Expression::Bool { .. } = expr { - BoolOp::from_expr(self, *node_id)? + BoolOp::from_expr(self, node_id)? } else { continue; }; @@ -702,7 +705,7 @@ impl Plan { // Try to improve full motion policy in the sub-queries. // We don't influence the inner child here, so the inner map is empty // for the current node id. - let sq_strategies = self.get_sq_node_strategies(rel_id, *node_id)?; + let sq_strategies = self.get_sq_node_strategies(rel_id, node_id)?; let sq_strategies_len = sq_strategies.len(); for (id, policy) in sq_strategies { strategy.insert(id, (policy, DataGeneration::None)); @@ -766,7 +769,7 @@ impl Plan { )) } }; - inner_map.insert(*node_id, new_inner_policy.clone()); + inner_map.insert(node_id, new_inner_policy.clone()); } strategy.insert(inner_child, (new_inner_policy, DataGeneration::None)); Ok(strategy) @@ -1014,11 +1017,15 @@ impl Plan { // Gather motions (revert levels in bft) let mut motions: Vec<Vec<usize>> = Vec::new(); let top = self.get_top()?; - let bft_tree = Bft::new(&top, |node| self.nodes.rel_iter(node)); + let mut bft_tree = BreadthFirst::with_capacity( + |node| self.nodes.rel_iter(node), + REL_CAPACITY, + REL_CAPACITY, + ); let mut map: HashMap<usize, usize> = HashMap::new(); let mut max_level: usize = 0; - for (level, node) in bft_tree { - if let Node::Relational(Relational::Motion { .. }) = self.get_node(*node)? { + for (level, id) in bft_tree.iter(top) { + if let Node::Relational(Relational::Motion { .. }) = self.get_node(id)? { let key: usize = match map.entry(level) { Entry::Occupied(o) => *o.into_mut(), Entry::Vacant(v) => { @@ -1029,8 +1036,8 @@ impl Plan { } }; match motions.get_mut(key) { - Some(list) => list.push(*node), - None => motions.push(vec![*node]), + Some(list) => list.push(id), + None => motions.push(vec![id]), } } } diff --git a/sbroad-core/src/ir/tree.rs b/sbroad-core/src/ir/tree.rs index d0df786dc3..d2ffd50990 100644 --- a/sbroad-core/src/ir/tree.rs +++ b/sbroad-core/src/ir/tree.rs @@ -4,7 +4,7 @@ use super::{Nodes, Plan}; use std::cell::RefCell; trait TreeIterator<'nodes> { - fn get_current(&self) -> &'nodes usize; + fn get_current(&self) -> usize; fn get_child(&self) -> &RefCell<usize>; fn get_nodes(&self) -> &'nodes Nodes; } @@ -22,10 +22,10 @@ pub enum Snapshot { } pub mod and; -pub mod eq_class; pub mod expression; pub mod relation; pub mod subtree; +pub mod traversal; #[cfg(test)] mod tests; diff --git a/sbroad-core/src/ir/tree/and.rs b/sbroad-core/src/ir/tree/and.rs index 1c6c8377f9..3b4a080c14 100644 --- a/sbroad-core/src/ir/tree/and.rs +++ b/sbroad-core/src/ir/tree/and.rs @@ -13,13 +13,13 @@ trait AndTreeIterator<'nodes>: TreeIterator<'nodes> {} #[allow(clippy::module_name_repetitions)] #[derive(Debug)] pub struct AndIterator<'n> { - current: &'n usize, + current: usize, child: RefCell<usize>, nodes: &'n Nodes, } impl<'nodes> TreeIterator<'nodes> for AndIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { + fn get_current(&self) -> usize { self.current } @@ -36,7 +36,7 @@ impl<'nodes> AndTreeIterator<'nodes> for AndIterator<'nodes> {} impl<'n> Nodes { #[must_use] - pub fn and_iter(&'n self, current: &'n usize) -> AndIterator<'n> { + pub fn and_iter(&'n self, current: usize) -> AndIterator<'n> { AndIterator { current, child: RefCell::new(0), @@ -46,15 +46,15 @@ impl<'n> Nodes { } impl<'n> Iterator for AndIterator<'n> { - type Item = &'n usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - and_next(self) + and_next(self).copied() } } fn and_next<'nodes>(iter: &mut impl AndTreeIterator<'nodes>) -> Option<&'nodes usize> { - let node = iter.get_nodes().arena.get(*iter.get_current()); + let node = iter.get_nodes().arena.get(iter.get_current()); if let Some(Node::Expression(Expression::Bool { left, op, right, .. })) = node diff --git a/sbroad-core/src/ir/tree/eq_class.rs b/sbroad-core/src/ir/tree/eq_class.rs deleted file mode 100644 index c8febae73d..0000000000 --- a/sbroad-core/src/ir/tree/eq_class.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::cell::RefCell; - -use super::TreeIterator; -use crate::ir::expression::Expression; -use crate::ir::operator::Bool; -use crate::ir::{Node, Nodes}; - -trait EqClassTreeIterator<'nodes>: TreeIterator<'nodes> {} - -/// Children iterator for "and"-ed equivalent expressions. -/// -/// The iterator returns the next child for the chained `Bool::And` -/// and `Bool::Eq` nodes. -#[allow(clippy::module_name_repetitions)] -#[derive(Debug)] -pub struct EqClassIterator<'n> { - current: &'n usize, - child: RefCell<usize>, - nodes: &'n Nodes, -} - -impl<'nodes> TreeIterator<'nodes> for EqClassIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { - self.current - } - - fn get_child(&self) -> &RefCell<usize> { - &self.child - } - - fn get_nodes(&self) -> &'nodes Nodes { - self.nodes - } -} - -impl<'nodes> EqClassTreeIterator<'nodes> for EqClassIterator<'nodes> {} - -impl<'n> Nodes { - #[must_use] - pub fn eq_iter(&'n self, current: &'n usize) -> EqClassIterator<'n> { - EqClassIterator { - current, - child: RefCell::new(0), - nodes: self, - } - } -} - -impl<'n> Iterator for EqClassIterator<'n> { - type Item = &'n usize; - - fn next(&mut self) -> Option<Self::Item> { - eq_class_next(self) - } -} - -fn eq_class_next<'nodes>(iter: &mut impl EqClassTreeIterator<'nodes>) -> Option<&'nodes usize> { - if let Some(Node::Expression(Expression::Bool { - left, op, right, .. - })) = iter.get_nodes().arena.get(*iter.get_current()) - { - if (*op != Bool::And) && (*op != Bool::Eq) { - return None; - } - let child_step = *iter.get_child().borrow(); - if child_step == 0 { - *iter.get_child().borrow_mut() += 1; - return Some(left); - } else if child_step == 1 { - *iter.get_child().borrow_mut() += 1; - return Some(right); - } - None - } else { - None - } -} diff --git a/sbroad-core/src/ir/tree/expression.rs b/sbroad-core/src/ir/tree/expression.rs index f46e4ebb69..e37b87155e 100644 --- a/sbroad-core/src/ir/tree/expression.rs +++ b/sbroad-core/src/ir/tree/expression.rs @@ -15,7 +15,7 @@ trait ExpressionTreeIterator<'nodes>: TreeIterator<'nodes> { #[allow(clippy::module_name_repetitions)] #[derive(Debug)] pub struct ExpressionIterator<'n> { - current: &'n usize, + current: usize, child: RefCell<usize>, nodes: &'n Nodes, make_row_leaf: bool, @@ -23,7 +23,7 @@ pub struct ExpressionIterator<'n> { impl<'n> Nodes { #[must_use] - pub fn expr_iter(&'n self, current: &'n usize, make_row_leaf: bool) -> ExpressionIterator<'n> { + pub fn expr_iter(&'n self, current: usize, make_row_leaf: bool) -> ExpressionIterator<'n> { ExpressionIterator { current, child: RefCell::new(0), @@ -34,7 +34,7 @@ impl<'n> Nodes { } impl<'nodes> TreeIterator<'nodes> for ExpressionIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { + fn get_current(&self) -> usize { self.current } @@ -54,17 +54,17 @@ impl<'nodes> ExpressionTreeIterator<'nodes> for ExpressionIterator<'nodes> { } impl<'n> Iterator for ExpressionIterator<'n> { - type Item = &'n usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - expression_next(self) + expression_next(self).copied() } } fn expression_next<'nodes>( iter: &mut impl ExpressionTreeIterator<'nodes>, ) -> Option<&'nodes usize> { - match iter.get_nodes().arena.get(*iter.get_current()) { + match iter.get_nodes().arena.get(iter.get_current()) { Some(Node::Expression( Expression::Alias { child, .. } | Expression::Cast { child, .. } diff --git a/sbroad-core/src/ir/tree/relation.rs b/sbroad-core/src/ir/tree/relation.rs index 8bb0ff454c..543118d817 100644 --- a/sbroad-core/src/ir/tree/relation.rs +++ b/sbroad-core/src/ir/tree/relation.rs @@ -11,14 +11,14 @@ trait RelationalTreeIterator<'nodes>: TreeIterator<'nodes> {} /// The iterator returns the next relational node in the plan tree. #[derive(Debug)] pub struct RelationalIterator<'n> { - current: &'n usize, + current: usize, child: RefCell<usize>, nodes: &'n Nodes, } impl<'n> Nodes { #[must_use] - pub fn rel_iter(&'n self, current: &'n usize) -> RelationalIterator<'n> { + pub fn rel_iter(&'n self, current: usize) -> RelationalIterator<'n> { RelationalIterator { current, child: RefCell::new(0), @@ -28,7 +28,7 @@ impl<'n> Nodes { } impl<'nodes> TreeIterator<'nodes> for RelationalIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { + fn get_current(&self) -> usize { self.current } @@ -44,17 +44,17 @@ impl<'nodes> TreeIterator<'nodes> for RelationalIterator<'nodes> { impl<'nodes> RelationalTreeIterator<'nodes> for RelationalIterator<'nodes> {} impl<'n> Iterator for RelationalIterator<'n> { - type Item = &'n usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - relational_next(self) + relational_next(self).copied() } } fn relational_next<'nodes>( iter: &mut impl RelationalTreeIterator<'nodes>, ) -> Option<&'nodes usize> { - match iter.get_nodes().arena.get(*iter.get_current()) { + match iter.get_nodes().arena.get(iter.get_current()) { Some(Node::Relational( Relational::Except { children, .. } | Relational::InnerJoin { children, .. } diff --git a/sbroad-core/src/ir/tree/subtree.rs b/sbroad-core/src/ir/tree/subtree.rs index 05b79438d8..672bf05be0 100644 --- a/sbroad-core/src/ir/tree/subtree.rs +++ b/sbroad-core/src/ir/tree/subtree.rs @@ -15,13 +15,13 @@ trait SubtreePlanIterator<'plan>: PlanTreeIterator<'plan> { #[allow(clippy::module_name_repetitions)] #[derive(Debug)] pub struct SubtreeIterator<'plan> { - current: &'plan usize, + current: usize, child: RefCell<usize>, plan: &'plan Plan, } impl<'nodes> TreeIterator<'nodes> for SubtreeIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { + fn get_current(&self) -> usize { self.current } @@ -51,16 +51,16 @@ impl<'plan> SubtreePlanIterator<'plan> for SubtreeIterator<'plan> { } impl<'plan> Iterator for SubtreeIterator<'plan> { - type Item = &'plan usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - subtree_next(self, &Snapshot::Latest) + subtree_next(self, &Snapshot::Latest).copied() } } impl<'plan> Plan { #[must_use] - pub fn subtree_iter(&'plan self, current: &'plan usize) -> SubtreeIterator<'plan> { + pub fn subtree_iter(&'plan self, current: usize) -> SubtreeIterator<'plan> { SubtreeIterator { current, child: RefCell::new(0), @@ -75,13 +75,13 @@ impl<'plan> Plan { /// at the moment). #[derive(Debug)] pub struct FlashbackSubtreeIterator<'plan> { - current: &'plan usize, + current: usize, child: RefCell<usize>, plan: &'plan Plan, } impl<'nodes> TreeIterator<'nodes> for FlashbackSubtreeIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { + fn get_current(&self) -> usize { self.current } @@ -111,19 +111,16 @@ impl<'plan> SubtreePlanIterator<'plan> for FlashbackSubtreeIterator<'plan> { } impl<'plan> Iterator for FlashbackSubtreeIterator<'plan> { - type Item = &'plan usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - subtree_next(self, &Snapshot::Oldest) + subtree_next(self, &Snapshot::Oldest).copied() } } impl<'plan> Plan { #[must_use] - pub fn flashback_subtree_iter( - &'plan self, - current: &'plan usize, - ) -> FlashbackSubtreeIterator<'plan> { + pub fn flashback_subtree_iter(&'plan self, current: usize) -> FlashbackSubtreeIterator<'plan> { FlashbackSubtreeIterator { current, child: RefCell::new(0), @@ -135,13 +132,13 @@ impl<'plan> Plan { /// An iterator used while copying and execution plan subtree. #[derive(Debug)] pub struct ExecPlanSubtreeIterator<'plan> { - current: &'plan usize, + current: usize, child: RefCell<usize>, plan: &'plan Plan, } impl<'nodes> TreeIterator<'nodes> for ExecPlanSubtreeIterator<'nodes> { - fn get_current(&self) -> &'nodes usize { + fn get_current(&self) -> usize { self.current } @@ -171,19 +168,16 @@ impl<'plan> SubtreePlanIterator<'plan> for ExecPlanSubtreeIterator<'plan> { } impl<'plan> Iterator for ExecPlanSubtreeIterator<'plan> { - type Item = &'plan usize; + type Item = usize; fn next(&mut self) -> Option<Self::Item> { - subtree_next(self, &Snapshot::Oldest) + subtree_next(self, &Snapshot::Oldest).copied() } } impl<'plan> Plan { #[must_use] - pub fn exec_plan_subtree_iter( - &'plan self, - current: &'plan usize, - ) -> ExecPlanSubtreeIterator<'plan> { + pub fn exec_plan_subtree_iter(&'plan self, current: usize) -> ExecPlanSubtreeIterator<'plan> { ExecPlanSubtreeIterator { current, child: RefCell::new(0), @@ -197,7 +191,7 @@ fn subtree_next<'plan>( iter: &mut impl SubtreePlanIterator<'plan>, snapshot: &Snapshot, ) -> Option<&'plan usize> { - if let Some(child) = iter.get_nodes().arena.get(*iter.get_current()) { + if let Some(child) = iter.get_nodes().arena.get(iter.get_current()) { return match child { Node::Parameter => None, Node::Expression(exp) => match exp { @@ -249,7 +243,7 @@ fn subtree_next<'plan>( }; if let Ok(rel_id) = iter .get_plan() - .get_relational_from_reference_node(*iter.get_current()) + .get_relational_from_reference_node(iter.get_current()) { match iter.get_plan().get_relation_node(*rel_id) { Ok(rel_node) if rel_node.is_subquery() || rel_node.is_motion() => { diff --git a/sbroad-core/src/ir/tree/tests.rs b/sbroad-core/src/ir/tree/tests.rs index f8ea53c900..00295a4de0 100644 --- a/sbroad-core/src/ir/tree/tests.rs +++ b/sbroad-core/src/ir/tree/tests.rs @@ -1,9 +1,9 @@ use crate::ir::operator::Bool; use crate::ir::relation::{Column, ColumnRole, Table, Type}; +use crate::ir::tree::traversal::{BreadthFirst, PostOrder, EXPR_CAPACITY, REL_CAPACITY}; use crate::ir::value::Value; use crate::ir::{Expression, Plan}; use pretty_assertions::assert_eq; -use traversal::{Bft, DftPost, DftPre}; #[test] fn expression_bft() { @@ -25,55 +25,24 @@ fn expression_bft() { .add_bool(c1c2_and_c2c3, Bool::Or, c4_eq_c5) .unwrap(); - let mut bft_tree = Bft::new(&top, |node| plan.nodes.expr_iter(node, true)); - assert_eq!(bft_tree.next(), Some((0, &top))); - assert_eq!(bft_tree.next(), Some((1, &c1c2_and_c2c3))); - assert_eq!(bft_tree.next(), Some((1, &c4_eq_c5))); - assert_eq!(bft_tree.next(), Some((2, &c1_eq_c2))); - assert_eq!(bft_tree.next(), Some((2, &c2_eq_c3))); - assert_eq!(bft_tree.next(), Some((2, &c4))); - assert_eq!(bft_tree.next(), Some((2, &c5))); - assert_eq!(bft_tree.next(), Some((3, &c1))); - assert_eq!(bft_tree.next(), Some((3, &c2))); - assert_eq!(bft_tree.next(), Some((3, &c2))); - assert_eq!(bft_tree.next(), Some((3, &c3))); - assert_eq!(bft_tree.next(), None); -} - -#[test] -#[allow(clippy::similar_names)] -fn and_chain_pre() { - // (((b1 or b2) and b3) and b4) and (b5 = (b6 = b7)) - - let mut plan = Plan::default(); - let b1 = plan.nodes.add_const(Value::Boolean(true)); - let b2 = plan.nodes.add_const(Value::Boolean(true)); - let b3 = plan.nodes.add_const(Value::Boolean(true)); - let b4 = plan.nodes.add_const(Value::Boolean(true)); - let b5 = plan.nodes.add_const(Value::Boolean(true)); - let b6 = plan.nodes.add_const(Value::Boolean(true)); - let b7 = plan.nodes.add_const(Value::Boolean(true)); - - let b1_2 = plan.nodes.add_bool(b1, Bool::Or, b2).unwrap(); - let b1_23 = plan.nodes.add_bool(b1_2, Bool::And, b3).unwrap(); - let b1_234 = plan.nodes.add_bool(b1_23, Bool::And, b4).unwrap(); - let b6b7 = plan.nodes.add_bool(b6, Bool::Eq, b7).unwrap(); - let b5b6b7 = plan.nodes.add_bool(b5, Bool::Eq, b6b7).unwrap(); - let top = plan.nodes.add_bool(b1_234, Bool::And, b5b6b7).unwrap(); - - let mut dft_pre = DftPre::new(&top, |node| plan.nodes.eq_iter(node)); - assert_eq!(dft_pre.next(), Some((0, &top))); - assert_eq!(dft_pre.next(), Some((1, &b1_234))); - assert_eq!(dft_pre.next(), Some((2, &b1_23))); - assert_eq!(dft_pre.next(), Some((3, &b1_2))); - assert_eq!(dft_pre.next(), Some((3, &b3))); - assert_eq!(dft_pre.next(), Some((2, &b4))); - assert_eq!(dft_pre.next(), Some((1, &b5b6b7))); - assert_eq!(dft_pre.next(), Some((2, &b5))); - assert_eq!(dft_pre.next(), Some((2, &b6b7))); - assert_eq!(dft_pre.next(), Some((3, &b6))); - assert_eq!(dft_pre.next(), Some((3, &b7))); - assert_eq!(dft_pre.next(), None); + let mut bft_tree = BreadthFirst::with_capacity( + |node| plan.nodes.expr_iter(node, true), + EXPR_CAPACITY, + EXPR_CAPACITY, + ); + let mut iter = bft_tree.iter(top); + assert_eq!(iter.next(), Some((0, top))); + assert_eq!(iter.next(), Some((1, c1c2_and_c2c3))); + assert_eq!(iter.next(), Some((1, c4_eq_c5))); + assert_eq!(iter.next(), Some((2, c1_eq_c2))); + assert_eq!(iter.next(), Some((2, c2_eq_c3))); + assert_eq!(iter.next(), Some((2, c4))); + assert_eq!(iter.next(), Some((2, c5))); + assert_eq!(iter.next(), Some((3, c1))); + assert_eq!(iter.next(), Some((3, c2))); + assert_eq!(iter.next(), Some((3, c2))); + assert_eq!(iter.next(), Some((3, c3))); + assert_eq!(iter.next(), None); } #[test] @@ -112,12 +81,13 @@ fn relational_post() { let top = plan.get_top().unwrap(); // Traverse the tree - let mut dft_post = DftPost::new(&top, |node| plan.nodes.rel_iter(node)); - assert_eq!(dft_post.next(), Some((1, &scan_t1_id))); - assert_eq!(dft_post.next(), Some((2, &scan_t2_id))); - assert_eq!(dft_post.next(), Some((1, &selection_id))); - assert_eq!(dft_post.next(), Some((0, &union_id))); - assert_eq!(dft_post.next(), None); + let mut dft_post = PostOrder::with_capacity(|node| plan.nodes.rel_iter(node), REL_CAPACITY); + let mut iter = dft_post.iter(top); + assert_eq!(iter.next(), Some((1, scan_t1_id))); + assert_eq!(iter.next(), Some((2, scan_t2_id))); + assert_eq!(iter.next(), Some((1, selection_id))); + assert_eq!(iter.next(), Some((0, union_id))); + assert_eq!(iter.next(), None); } #[test] @@ -180,28 +150,33 @@ fn selection_subquery_dfs_post() { let top = plan.get_top().unwrap(); // Traverse relational nodes in the plan tree - let mut dft_post = DftPost::new(&top, |node| plan.nodes.rel_iter(node)); - assert_eq!(dft_post.next(), Some((1, &scan_t1_id))); - assert_eq!(dft_post.next(), Some((4, &scan_t2_id))); - assert_eq!(dft_post.next(), Some((3, &selection_t2_id))); - assert_eq!(dft_post.next(), Some((2, &proj_id))); - assert_eq!(dft_post.next(), Some((1, &sq_id))); - assert_eq!(dft_post.next(), Some((0, &selection_t1_id))); - assert_eq!(dft_post.next(), None); + let mut dft_post = PostOrder::with_capacity(|node| plan.nodes.rel_iter(node), REL_CAPACITY); + let mut iter = dft_post.iter(top); + assert_eq!(iter.next(), Some((1, scan_t1_id))); + assert_eq!(iter.next(), Some((4, scan_t2_id))); + assert_eq!(iter.next(), Some((3, selection_t2_id))); + assert_eq!(iter.next(), Some((2, proj_id))); + assert_eq!(iter.next(), Some((1, sq_id))); + assert_eq!(iter.next(), Some((0, selection_t1_id))); + assert_eq!(iter.next(), None); // Traverse expression nodes in the selection t2 filter - let mut dft_post = DftPost::new(&eq_op, |node| plan.nodes.expr_iter(node, true)); - assert_eq!(dft_post.next(), Some((1, &b))); - assert_eq!(dft_post.next(), Some((1, &const1))); - assert_eq!(dft_post.next(), Some((0, &eq_op))); - assert_eq!(dft_post.next(), None); + let mut dft_post = + PostOrder::with_capacity(|node| plan.nodes.expr_iter(node, true), EXPR_CAPACITY); + let mut iter = dft_post.iter(eq_op); + assert_eq!(iter.next(), Some((1, b))); + assert_eq!(iter.next(), Some((1, const1))); + assert_eq!(iter.next(), Some((0, eq_op))); + assert_eq!(iter.next(), None); // Traverse expression nodes in the selection t1 filter - let mut dft_post = DftPost::new(&in_op, |node| plan.nodes.expr_iter(node, true)); - assert_eq!(dft_post.next(), Some((1, &a))); - assert_eq!(dft_post.next(), Some((1, &c))); - assert_eq!(dft_post.next(), Some((0, &in_op))); - assert_eq!(dft_post.next(), None); + let mut dft_post = + PostOrder::with_capacity(|node| plan.nodes.expr_iter(node, true), EXPR_CAPACITY); + let mut iter = dft_post.iter(in_op); + assert_eq!(iter.next(), Some((1, a))); + assert_eq!(iter.next(), Some((1, c))); + assert_eq!(iter.next(), Some((0, in_op))); + assert_eq!(iter.next(), None); } #[test] @@ -257,16 +232,17 @@ fn subtree_dfs_post() { }; // Traverse relational nodes in the plan tree - let mut dft_post = DftPost::new(&top, |node| plan.subtree_iter(node)); - assert_eq!(dft_post.next(), Some((3, c_ref_id))); - assert_eq!(dft_post.next(), Some((2, alias_id))); - assert_eq!(dft_post.next(), Some((1, &proj_row_id))); - assert_eq!(dft_post.next(), Some((2, &scan_t1_id))); - assert_eq!(dft_post.next(), Some((4, &a_ref))); - assert_eq!(dft_post.next(), Some((3, &a))); - assert_eq!(dft_post.next(), Some((3, &const1))); - assert_eq!(dft_post.next(), Some((2, &eq_op))); - assert_eq!(dft_post.next(), Some((1, &selection_t1_id))); - assert_eq!(dft_post.next(), Some((0, &proj_id))); - assert_eq!(dft_post.next(), None); + let mut dft_post = PostOrder::with_capacity(|node| plan.subtree_iter(node), plan.next_id()); + let mut iter = dft_post.iter(top); + assert_eq!(iter.next(), Some((3, *c_ref_id))); + assert_eq!(iter.next(), Some((2, *alias_id))); + assert_eq!(iter.next(), Some((1, proj_row_id))); + assert_eq!(iter.next(), Some((2, scan_t1_id))); + assert_eq!(iter.next(), Some((4, a_ref))); + assert_eq!(iter.next(), Some((3, a))); + assert_eq!(iter.next(), Some((3, const1))); + assert_eq!(iter.next(), Some((2, eq_op))); + assert_eq!(iter.next(), Some((1, selection_t1_id))); + assert_eq!(iter.next(), Some((0, proj_id))); + assert_eq!(iter.next(), None); } diff --git a/sbroad-core/src/ir/tree/traversal.rs b/sbroad-core/src/ir/tree/traversal.rs new file mode 100644 index 0000000000..19e67b9609 --- /dev/null +++ b/sbroad-core/src/ir/tree/traversal.rs @@ -0,0 +1,106 @@ +use std::collections::VecDeque; + +pub const EXPR_CAPACITY: usize = 64; +pub const REL_CAPACITY: usize = 32; + +pub type LevelNode = (usize, usize); + +pub struct PostOrder<F, I> +where + F: FnMut(usize) -> I, + I: Iterator<Item = usize>, +{ + iter_children: F, + nodes: Vec<LevelNode>, +} + +impl<F, I> PostOrder<F, I> +where + F: FnMut(usize) -> I, + I: Iterator<Item = usize>, +{ + pub fn iter(&mut self, root: usize) -> impl Iterator<Item = LevelNode> { + self.populate_nodes(root); + self.take_nodes().into_iter() + } + + pub fn new(iter_children: F, nodes: Vec<LevelNode>) -> Self { + Self { + iter_children, + nodes, + } + } + + pub fn populate_nodes(&mut self, root: usize) { + self.traverse(root, 0); + } + + pub fn take_nodes(&mut self) -> Vec<LevelNode> { + std::mem::take(&mut self.nodes) + } + + fn traverse(&mut self, root: usize, level: usize) { + for child in (self.iter_children)(root) { + self.traverse(child, level + 1); + } + self.nodes.push((level, root)); + } + + pub fn with_capacity(iter_children: F, capacity: usize) -> Self { + Self { + iter_children, + nodes: Vec::with_capacity(capacity), + } + } +} + +pub struct BreadthFirst<F, I> +where + F: FnMut(usize) -> I, + I: Iterator<Item = usize>, +{ + iter_children: F, + queue: VecDeque<LevelNode>, + nodes: Vec<LevelNode>, +} + +impl<F, I> BreadthFirst<F, I> +where + F: FnMut(usize) -> I, + I: Iterator<Item = usize>, +{ + pub fn iter(&mut self, root: usize) -> impl Iterator<Item = LevelNode> { + self.populate_nodes(root); + self.take_nodes().into_iter() + } + + pub fn new(iter_children: F, queue: VecDeque<LevelNode>, nodes: Vec<LevelNode>) -> Self { + Self { + iter_children, + queue, + nodes, + } + } + + pub fn populate_nodes(&mut self, root: usize) { + self.queue.push_back((0, root)); + while let Some((level, node)) = self.queue.pop_front() { + self.nodes.push((level, node)); + for child in (self.iter_children)(node) { + self.queue.push_back((level + 1, child)); + } + } + } + + pub fn take_nodes(&mut self) -> Vec<LevelNode> { + std::mem::take(&mut self.nodes) + } + + pub fn with_capacity(iter_children: F, node_capacity: usize, queue_capacity: usize) -> Self { + Self { + iter_children, + queue: VecDeque::with_capacity(queue_capacity), + nodes: Vec::with_capacity(node_capacity), + } + } +} -- GitLab