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