From 6cd70cee350f43c4f8cc9443061259f1923f12c9 Mon Sep 17 00:00:00 2001 From: Denis Smirnov <sd@picodata.io> Date: Fri, 28 Oct 2022 14:05:36 +0700 Subject: [PATCH] feat: implement transformation undo log As we use an append-only arena for the plan, we can easily implement an UNDO log for each transformation we make. It can be helpful when we would like to undo any of the transformations. The main goal of it now - to rewind transformed selection to the original state, as it can produce a logically equivalent, but too long SQL string that breaks tarantool parser on the storages. --- sbroad-core/src/ir.rs | 34 +++++++++++++++++++- sbroad-core/src/ir/transformation.rs | 7 ++-- sbroad-core/src/ir/transformation/bool_in.rs | 2 +- sbroad-core/src/otm/statistics/table.rs | 2 +- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs index 80d8ff0648..abdab500c9 100644 --- a/sbroad-core/src/ir.rs +++ b/sbroad-core/src/ir.rs @@ -11,6 +11,7 @@ use operator::Relational; use relation::Table; use crate::errors::QueryPlannerError; +use crate::ir::helpers::RepeatableState; use crate::ir::value::Value; pub mod distribution; @@ -23,6 +24,32 @@ pub mod transformation; pub mod tree; pub mod value; +/// A tree map structure where the value points to the parent key. +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub struct TreeMap(HashMap<usize, usize, RepeatableState>); + +impl TreeMap { + pub fn new() -> Self { + Self(HashMap::with_hasher(RepeatableState)) + } + + pub fn add(&mut self, old_id: usize, new_id: usize) { + self.0.insert(new_id, old_id); + } + + pub fn get(&self, new_id: usize) -> Option<usize> { + self.0.get(&new_id).copied() + } + + pub fn get_oldest(&self, new_id: usize) -> usize { + let mut id = new_id; + while let Some(old_id) = self.get(id) { + id = old_id; + } + id + } +} + /// Plan tree node. /// /// There are two kinds of node variants: expressions and relational @@ -109,8 +136,12 @@ pub struct Plan { /// be added last. The plan without a top should be treated as invalid. top: Option<usize>, /// The flag is enabled if user wants to get a query plan only. - /// In this case we don't need to execute query + /// In this case we don't need to execute query. is_explain: bool, + /// The undo log is used to track the changes in the plan tree + /// during the optimization process. It is used to restore the + /// original subtree after the optimization (if it is needed). + undo: TreeMap, } impl Default for Plan { @@ -165,6 +196,7 @@ impl Plan { slices: None, top: None, is_explain: false, + undo: TreeMap::new(), } } diff --git a/sbroad-core/src/ir/transformation.rs b/sbroad-core/src/ir/transformation.rs index 8bee0f4b87..c5ffa20462 100644 --- a/sbroad-core/src/ir/transformation.rs +++ b/sbroad-core/src/ir/transformation.rs @@ -82,7 +82,7 @@ impl Plan { let nodes: Vec<usize> = ir_tree.map(|(_, id)| *id).collect(); for id in &nodes { let rel = self.get_relation_node(*id)?; - let new_tree_id = match rel { + let (new_tree_id, old_tree_id) = match rel { Relational::Selection { filter: tree_id, .. } @@ -90,10 +90,13 @@ impl Plan { condition: tree_id, .. } => { let expr_id = *tree_id; - f(self, expr_id)? + (f(self, expr_id)?, expr_id) } _ => continue, }; + if old_tree_id != new_tree_id { + self.undo.add(new_tree_id, old_tree_id); + } let rel = self.get_mut_relation_node(*id)?; match rel { Relational::Selection { diff --git a/sbroad-core/src/ir/transformation/bool_in.rs b/sbroad-core/src/ir/transformation/bool_in.rs index c87250809a..4ca5fc8ad0 100644 --- a/sbroad-core/src/ir/transformation/bool_in.rs +++ b/sbroad-core/src/ir/transformation/bool_in.rs @@ -45,7 +45,7 @@ impl Plan { } }; - // To not apply current transformation to motion and sub-query nodes. + // Do not apply current transformation to motion and sub-query nodes. if self.get_motion_from_row(right_id)?.is_some() || self.get_sub_query_from_row_node(right_id)?.is_some() { diff --git a/sbroad-core/src/otm/statistics/table.rs b/sbroad-core/src/otm/statistics/table.rs index 47a68533bb..308d0ea382 100644 --- a/sbroad-core/src/otm/statistics/table.rs +++ b/sbroad-core/src/otm/statistics/table.rs @@ -6,7 +6,7 @@ //! - `__sbroad_query` space - stores the queries that are currently being executed. //! Its query id is used as a key for the `__sbroad_stat` space. //! - `__sbroad_stat` space - stores the statistics for the query spans. The spans -//! are stored as a flat tree for each query. +//! are stored as a tree map for each query. //! - `SpanMap` hash table - stores the mapping between the span id and the span name. //! The reason is that the span context contains only the span id, so we use //! this table to save the span name when create the span (and remove it when -- GitLab