From c0ca3420bb8f4fbdd503856770715a426ba8d608 Mon Sep 17 00:00:00 2001
From: Denis Smirnov <sd@picodata.io>
Date: Tue, 14 Jun 2022 12:50:47 +0700
Subject: [PATCH] feat: separate parameters from the query pattern.

Now coordinator dispatches SQL patterns with separated parameters.
It would help us to cache patterns on the segments as prepared
statements one day and skip SQL parsing.

Also we have faced a Tarantool bug

https://github.com/tarantool/tarantool/issues/7283

and made a hot fix in the Picodata fork. As a result we have also
migrated sbroad CI to our fork.
---
 .gitlab-ci.yml                                |   3 +-
 .gitmodules                                   |   3 +-
 Makefile                                      |   5 +-
 ci/Dockerfile                                 |   6 +-
 deps/tarantool                                |   2 +-
 src/executor/engine/cartridge.rs              |  34 +-
 .../engine/cartridge/backend/sql/ir.rs        |  47 +-
 .../engine/cartridge/backend/sql/ir/tests.rs  | 131 +++--
 .../engine/cartridge/backend/sql/tree.rs      |  35 +-
 .../cartridge/backend/sql/tree/tests.rs       |   2 +-
 src/executor/engine/mock.rs                   |   4 +-
 src/executor/result.rs                        |   2 +
 src/executor/tests.rs                         | 536 +++++++++++-------
 src/frontend/sql/ir/tests.rs                  | 226 +++++---
 src/ir/transformation/bool_in/tests.rs        |  42 +-
 src/ir/transformation/dnf/tests.rs            | 102 +++-
 .../equality_propagation/tests.rs             |  80 ++-
 src/ir/transformation/helpers.rs              |   9 +-
 src/ir/transformation/merge_tuples/tests.rs   |  61 +-
 src/ir/transformation/split_columns/tests.rs  |  40 +-
 src/ir/value.rs                               |  20 +-
 src/ir/value/double.rs                        |  10 +
 test_app/test/integration/api_test.lua        |   8 +-
 .../test/integration/target_queries_test.lua  |  48 +-
 .../sql_order_selection_syntax_nodes.yaml     |   4 +-
 25 files changed, 983 insertions(+), 477 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 22fa819627..4f8dd46baf 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,7 +14,7 @@ cache:
 default:
     tags:
         - picodata
-    image: registry.gitlab.com/picodata/picodata/sbroad/sbroad-builder:0.1.0
+    image: registry.gitlab.com/picodata/picodata/sbroad/sbroad-builder:0.2.0
 
 build:
     stage: build
@@ -36,6 +36,7 @@ test:
       - git submodule foreach --recursive git clean -xfd
       - git reset --hard
       - git submodule foreach --recursive git reset --hard
+      - git submodule sync --recursive
       - git submodule update --init --recursive
     stage: test
     script:
diff --git a/.gitmodules b/.gitmodules
index d61d03800d..073cdad4b5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "deps/tarantool"]
 	path = deps/tarantool
-	url = https://github.com/picodata/tarantool.git
+	url = https://sbroad-cargo:i8h8BZWfphEKz8hsZBnz@git.picodata.io/picodata/tarantool.git
+	branch = 2.10.0-picodata
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 6af69b6486..22fdd70781 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ bench_check:
 	MOCK_DEC_NUMBER=1 cargo bench --no-run
 
 build_debug:
-	MOCK_DEC_NUMBER=0 cargo build --debug
+	MOCK_DEC_NUMBER=0 cargo build
 
 build_release:
 	MOCK_DEC_NUMBER=0 cargo build --release
@@ -27,12 +27,13 @@ build_test_app:
 
 clean:
 	rm -rf target/release/libsbroad.*
+	rm -rf target/debug/libsbroad.*
 
 integration_test_app:
 	cd test_app && rm -rf tmp/tarantool.log && TARANTOOL_LOG_LEVEL=7 TARANTOOL_LOG=tmp/tarantool.log ./.rocks/bin/luatest --coverage -v test/
 
 init:
-	git submodule update --init --recursive
+	git submodule update --init --recursive --remote
 
 lint:
 	cargo fmt --all -- --check
diff --git a/ci/Dockerfile b/ci/Dockerfile
index e56129f3e0..15a361050b 100644
--- a/ci/Dockerfile
+++ b/ci/Dockerfile
@@ -1,9 +1,9 @@
-FROM tarantool/tarantool:2.x-centos7
+FROM docker-public.binary.picodata.io/tarantool:2.10.0
 
 ENV PATH=/root/.cargo/bin:${PATH}
 
-RUN curl -L https://tarantool.io/UaooCnt/release/2.8/installer.sh | bash; \
-    yum -y update && yum -y install -y wget openssl-devel cartridge-cli readline-devel; \
+RUN curl -L https://tarantool.io/UaooCnt/release/2/installer.sh | bash; \
+    yum -y update && yum -y install -y wget openssl-devel cartridge-cli readline-devel libicu-devel; \
     yum groupinstall -y "Development Tools"; \
     yum clean all; \
     curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile default; \
diff --git a/deps/tarantool b/deps/tarantool
index 47e6bd3626..a10f8af6e9 160000
--- a/deps/tarantool
+++ b/deps/tarantool
@@ -1 +1 @@
-Subproject commit 47e6bd362664e570a6c9d80c63b62b8d3d0bf6f8
+Subproject commit a10f8af6e932389163de9193a9d9f013214d800a
diff --git a/src/executor/engine/cartridge.rs b/src/executor/engine/cartridge.rs
index ff38be2418..e438ba55b5 100644
--- a/src/executor/engine/cartridge.rs
+++ b/src/executor/engine/cartridge.rs
@@ -12,6 +12,7 @@ use tarantool::tlua::LuaFunction;
 
 use crate::errors::QueryPlannerError;
 use crate::executor::bucket::Buckets;
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::executor::engine::cartridge::cache::lru::{LRUCache, DEFAULT_CAPACITY};
 use crate::executor::engine::cartridge::cache::ClusterAppConfig;
 use crate::executor::engine::{Engine, LocalMetadata};
@@ -26,7 +27,7 @@ use crate::ir::Plan;
 
 use self::hash::bucket_id_by_tuple;
 
-mod backend;
+pub mod backend;
 pub mod cache;
 pub mod hash;
 
@@ -171,7 +172,7 @@ impl Engine for Runtime {
         let nodes = plan.get_sql_order(top_id)?;
         let is_data_modifier = plan.subtree_modifies_data(top_id)?;
 
-        let mut rs_query: HashMap<String, String> = HashMap::new();
+        let mut rs_query: HashMap<String, PatternWithParams> = HashMap::new();
         if let Buckets::Filtered(bucket_set) = buckets {
             let random_bucket = self.get_random_bucket();
             let buckets = if bucket_set.is_empty() {
@@ -195,8 +196,9 @@ impl Engine for Runtime {
                     .iter()
                     .copied()
                     .collect::<HashSet<u64, RepeatableState>>();
-                let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::new_filtered(bucket_set))?;
-                rs_query.insert(rs.to_string(), sql);
+                let pattern_with_params =
+                    plan.syntax_nodes_as_sql(&nodes, &Buckets::new_filtered(bucket_set))?;
+                rs_query.insert(rs.to_string(), pattern_with_params);
             }
 
             if rs_query.is_empty() {
@@ -209,8 +211,8 @@ impl Engine for Runtime {
             return self.exec_on_some(&rs_query, is_data_modifier);
         }
 
-        let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::All)?;
-        self.exec_on_all(&sql, is_data_modifier)
+        let pattern_with_params = plan.syntax_nodes_as_sql(&nodes, &Buckets::All)?;
+        self.exec_on_all(&pattern_with_params, is_data_modifier)
     }
 
     /// Transform sub query results into a virtual table.
@@ -343,7 +345,7 @@ impl Runtime {
 
     fn read_on_some(
         &self,
-        rs_query: &HashMap<String, String>,
+        rs_query: &HashMap<String, PatternWithParams>,
     ) -> Result<Box<dyn Any>, QueryPlannerError> {
         let lua = tarantool::lua_state();
 
@@ -369,7 +371,7 @@ impl Runtime {
 
     fn write_on_some(
         &self,
-        rs_query: &HashMap<String, String>,
+        rs_query: &HashMap<String, PatternWithParams>,
     ) -> Result<Box<dyn Any>, QueryPlannerError> {
         let lua = tarantool::lua_state();
 
@@ -395,7 +397,7 @@ impl Runtime {
 
     fn exec_on_some(
         &self,
-        rs_query: &HashMap<String, String>,
+        rs_query: &HashMap<String, PatternWithParams>,
         is_data_modifier: bool,
     ) -> Result<Box<dyn Any>, QueryPlannerError> {
         if is_data_modifier {
@@ -405,7 +407,7 @@ impl Runtime {
         }
     }
 
-    fn read_on_all(&self, query: &str) -> Result<Box<dyn Any>, QueryPlannerError> {
+    fn read_on_all(&self, query: &PatternWithParams) -> Result<Box<dyn Any>, QueryPlannerError> {
         let lua = tarantool::lua_state();
 
         let exec_sql: LuaFunction<_> = lua.get("read_on_all").ok_or_else(|| {
@@ -428,7 +430,7 @@ impl Runtime {
         }
     }
 
-    fn write_on_all(&self, query: &str) -> Result<Box<dyn Any>, QueryPlannerError> {
+    fn write_on_all(&self, query: &PatternWithParams) -> Result<Box<dyn Any>, QueryPlannerError> {
         let lua = tarantool::lua_state();
 
         let exec_sql: LuaFunction<_> = lua.get("write_on_all").ok_or_else(|| {
@@ -453,7 +455,7 @@ impl Runtime {
 
     fn exec_on_all(
         &self,
-        query: &str,
+        query: &PatternWithParams,
         is_data_modifier: bool,
     ) -> Result<Box<dyn Any>, QueryPlannerError> {
         if is_data_modifier {
@@ -570,7 +572,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> {
 
         for rs_uuid, query in pairs(tbl_rs_query) do
             local replica = vshard.router.routeall()[rs_uuid]
-            local future, err = replica:callbre("box.execute", { query }, {is_async = true})
+            local future, err = replica:callbre("box.execute", { query['pattern'], query['params'] }, {is_async = true})
             if err ~= nil then
                 error(err)
             end
@@ -603,7 +605,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> {
 
         for rs_uuid, query in pairs(tbl_rs_query) do
             local replica = vshard.router.routeall()[rs_uuid]
-            local future, err = replica:callrw("box.execute", { query }, {is_async = true})
+            local future, err = replica:callrw("box.execute", { query['pattern'], query['params'] }, {is_async = true})
             if err ~= nil then
                 error(err)
             end
@@ -635,7 +637,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> {
         local futures = {}
 
         for _, replica in pairs(replicas) do
-            local future, err = replica:callbre("box.execute", { query }, {is_async = true})
+            local future, err = replica:callbre("box.execute", { query['pattern'], query['params'] }, {is_async = true})
             if err ~= nil then
                 error(err)
             end
@@ -669,7 +671,7 @@ pub fn load_extra_function() -> Result<(), QueryPlannerError> {
         local futures = {}
 
         for _, replica in pairs(replicas) do
-            local future, err = replica:callrw("box.execute", { query }, {is_async = true})
+            local future, err = replica:callrw("box.execute", { query['pattern'], query['params'] }, {is_async = true})
             if err ~= nil then
                 error(err)
             end
diff --git a/src/executor/engine/cartridge/backend/sql/ir.rs b/src/executor/engine/cartridge/backend/sql/ir.rs
index a8a56fef07..e648761286 100644
--- a/src/executor/engine/cartridge/backend/sql/ir.rs
+++ b/src/executor/engine/cartridge/backend/sql/ir.rs
@@ -1,4 +1,5 @@
 use itertools::Itertools;
+use tarantool::tlua;
 
 use crate::errors::QueryPlannerError;
 use crate::executor::bucket::Buckets;
@@ -6,10 +7,30 @@ use crate::executor::ir::ExecutionPlan;
 use crate::executor::vtable::VTableTuple;
 use crate::ir::expression::Expression;
 use crate::ir::operator::Relational;
+use crate::ir::value::Value;
 use crate::ir::Node;
 
 use super::tree::{SyntaxData, SyntaxPlan};
 
+#[derive(Debug, PartialEq, tlua::Push)]
+pub struct PatternWithParams {
+    pub pattern: String,
+    pub params: Vec<Value>,
+}
+
+impl PatternWithParams {
+    #[must_use]
+    pub fn new(pattern: String, params: Vec<Value>) -> Self {
+        PatternWithParams { pattern, params }
+    }
+}
+
+impl From<PatternWithParams> for String {
+    fn from(p: PatternWithParams) -> Self {
+        format!("pattern: {}, parameters: {:?}", p.pattern, p.params)
+    }
+}
+
 impl ExecutionPlan {
     /// Traverse plan sub-tree (pointed by top) in the order
     /// convenient for SQL serialization.
@@ -58,7 +79,9 @@ impl ExecutionPlan {
         &self,
         nodes: &[SyntaxData],
         buckets: &Buckets,
-    ) -> Result<String, QueryPlannerError> {
+    ) -> Result<PatternWithParams, QueryPlannerError> {
+        let mut params: Vec<Value> = Vec::new();
+
         let mut sql = String::new();
         let delim = " ";
 
@@ -166,6 +189,18 @@ impl ExecutionPlan {
                         },
                     }
                 }
+                SyntaxData::Parameter(id) => {
+                    sql.push('?');
+                    let value = ir_plan.get_expression_node(*id)?;
+                    if let Expression::Constant { value, .. } = value {
+                        params.push(value.clone());
+                    } else {
+                        return Err(QueryPlannerError::CustomError(format!(
+                            "Parameter {:?} is not a constant",
+                            value
+                        )));
+                    }
+                }
                 SyntaxData::VTable(vtable) => {
                     let cols_count = vtable.get_columns().len();
 
@@ -212,7 +247,7 @@ impl ExecutionPlan {
                     } else {
                         let values = tuples
                             .iter()
-                            .map(|t| format!("({})", (t.iter().map(ToString::to_string)).join(",")))
+                            .map(|t| format!("({})", (t.iter().map(|_| "?")).join(",")))
                             .collect::<Vec<String>>()
                             .join(",");
 
@@ -223,11 +258,17 @@ impl ExecutionPlan {
                             cols(anonymous_col_idx_base),
                             values
                         ));
+
+                        for t in tuples {
+                            for v in t {
+                                params.push(v.clone());
+                            }
+                        }
                     }
                 }
             }
         }
-        Ok(sql)
+        Ok(PatternWithParams::new(sql, params))
     }
 
     /// Checks if the given query subtree modifies data or not.
diff --git a/src/executor/engine/cartridge/backend/sql/ir/tests.rs b/src/executor/engine/cartridge/backend/sql/ir/tests.rs
index cf90041a4c..6a01182dcc 100644
--- a/src/executor/engine/cartridge/backend/sql/ir/tests.rs
+++ b/src/executor/engine/cartridge/backend/sql/ir/tests.rs
@@ -5,6 +5,9 @@ use crate::executor::engine::mock::MetadataMock;
 use crate::executor::ir::ExecutionPlan;
 use crate::frontend::sql::ast::AbstractSyntaxTree;
 use crate::frontend::Ast;
+use crate::ir::value::Value;
+
+use super::*;
 
 #[test]
 fn one_table_projection() {
@@ -23,11 +26,15 @@ fn one_table_projection() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {}",
-            r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
-            r#"FROM "hash_testing""#,
-            r#"WHERE ("hash_testing"."identification_number") = (1)"#
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {}",
+                r#"SELECT "hash_testing"."identification_number" as "identification_number","#,
+                r#""hash_testing"."product_code" as "product_code""#,
+                r#"FROM "hash_testing""#,
+                r#"WHERE ("hash_testing"."identification_number") = (?)"#,
+            ),
+            vec![Value::from(1_u64)]
         ),
         sql
     );
@@ -50,12 +57,16 @@ fn one_table_with_asterisk() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {} {}",
-            r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code","#,
-            r#""hash_testing"."product_units" as "product_units", "hash_testing"."sys_op" as "sys_op""#,
-            r#"FROM "hash_testing""#,
-            r#"WHERE ("hash_testing"."identification_number") = (1)"#
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {} {}",
+                r#"SELECT "hash_testing"."identification_number" as "identification_number","#,
+                r#""hash_testing"."product_code" as "product_code","#,
+                r#""hash_testing"."product_units" as "product_units", "hash_testing"."sys_op" as "sys_op""#,
+                r#"FROM "hash_testing""#,
+                r#"WHERE ("hash_testing"."identification_number") = (?)"#
+            ),
+            vec![Value::from(1_u64)]
         ),
         sql
     );
@@ -83,11 +94,16 @@ fn union_all() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {}",
-            r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (1)"#,
-            r#"UNION ALL"#,
-            r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."product_code") = ('a')"#
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {} {}",
+                r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
+                r#"WHERE ("hash_testing"."identification_number") = (?)"#,
+                r#"UNION ALL"#,
+                r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist""#,
+                r#"WHERE ("hash_testing_hist"."product_code") = (?)"#
+            ),
+            vec![Value::from(1_u64), Value::from("a")],
         ),
         sql
     );
@@ -112,12 +128,15 @@ fn from_sub_query() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {} {}",
-            r#"SELECT t1."product_code" as "product_code" FROM"#,
-            r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
-            r#"WHERE ("hash_testing"."identification_number") = (1)) as t1"#,
-            r#"WHERE (t1."product_code") = ('a')"#
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {}",
+                r#"SELECT t1."product_code" as "product_code" FROM"#,
+                r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
+                r#"WHERE ("hash_testing"."identification_number") = (?)) as t1"#,
+                r#"WHERE (t1."product_code") = (?)"#
+            ),
+            vec![Value::from(1_u64), Value::from("a")],
         ),
         sql
     );
@@ -146,13 +165,18 @@ fn from_sub_query_with_union() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {} {} {}",
-            r#"SELECT "t1"."product_code" as "product_code" FROM"#,
-            r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (1)"#,
-            r#"UNION ALL"#,
-            r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."product_code") = ('a')) as "t1""#,
-            r#"WHERE ("t1"."product_code") = ('a')"#,
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {} {} {} {}",
+                r#"SELECT "t1"."product_code" as "product_code" FROM"#,
+                r#"(SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
+                r#"WHERE ("hash_testing"."identification_number") = (?)"#,
+                r#"UNION ALL"#,
+                r#"SELECT "hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist""#,
+                r#"WHERE ("hash_testing_hist"."product_code") = (?)) as "t1""#,
+                r#"WHERE ("t1"."product_code") = (?)"#,
+            ),
+            vec![Value::from(1_u64), Value::from("a"), Value::from("a")],
         ),
         sql
     );
@@ -175,12 +199,15 @@ fn inner_join() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {} {}",
-            r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
-            r#"INNER JOIN (SELECT "history"."id" as "id" FROM "history") as "history""#,
-            r#"ON ("hash_testing"."identification_number") = ("history"."id")"#,
-            r#"WHERE ("hash_testing"."product_code") = ('a')"#,
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {}",
+                r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
+                r#"INNER JOIN (SELECT "history"."id" as "id" FROM "history") as "history""#,
+                r#"ON ("hash_testing"."identification_number") = ("history"."id")"#,
+                r#"WHERE ("hash_testing"."product_code") = (?)"#,
+            ),
+            vec![Value::from("a")],
         ),
         sql
     );
@@ -204,13 +231,16 @@ fn inner_join_with_sq() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {} {} {}",
-            r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
-            r#"INNER JOIN"#,
-            r#"(SELECT "history"."id" as "id" FROM "history" WHERE ("history"."id") = (1)) as "t""#,
-            r#"ON ("hash_testing"."identification_number") = ("t"."id")"#,
-            r#"WHERE ("hash_testing"."product_code") = ('a')"#,
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {} {}",
+                r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
+                r#"INNER JOIN"#,
+                r#"(SELECT "history"."id" as "id" FROM "history" WHERE ("history"."id") = (?)) as "t""#,
+                r#"ON ("hash_testing"."identification_number") = ("t"."id")"#,
+                r#"WHERE ("hash_testing"."product_code") = (?)"#,
+            ),
+            vec![Value::from(1_u64), Value::from("a")],
         ),
         sql
     );
@@ -232,13 +262,16 @@ fn selection_with_sq() {
     let sql = ex_plan.syntax_nodes_as_sql(&nodes, &Buckets::All).unwrap();
 
     assert_eq!(
-        format!(
-            "{} {} {} {} {}",
-            r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
-            r#"WHERE ("hash_testing"."identification_number") in"#,
-            r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#,
-            r#"WHERE ("hash_testing_hist"."product_code") = ('b'))"#,
-            r#"and ("hash_testing"."product_code") < ('a')"#,
+        PatternWithParams::new(
+            format!(
+                "{} {} {} {} {}",
+                r#"SELECT "hash_testing"."product_code" as "product_code" FROM "hash_testing""#,
+                r#"WHERE ("hash_testing"."identification_number") in"#,
+                r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#,
+                r#"WHERE ("hash_testing_hist"."product_code") = (?))"#,
+                r#"and ("hash_testing"."product_code") < (?)"#,
+            ),
+            vec![Value::from("b"), Value::from("a")],
         ),
         sql
     );
diff --git a/src/executor/engine/cartridge/backend/sql/tree.rs b/src/executor/engine/cartridge/backend/sql/tree.rs
index ef4ae73d00..9a4b725590 100644
--- a/src/executor/engine/cartridge/backend/sql/tree.rs
+++ b/src/executor/engine/cartridge/backend/sql/tree.rs
@@ -29,6 +29,8 @@ pub enum SyntaxData {
     Operator(String),
     /// plan node id
     PlanId(usize),
+    /// parameter (a wrapper over a plan constants)
+    Parameter(usize),
     /// virtual table
     VTable(VirtualTable),
 }
@@ -113,6 +115,14 @@ impl SyntaxNode {
         }
     }
 
+    fn new_parameter(id: usize) -> Self {
+        SyntaxNode {
+            data: SyntaxData::Parameter(id),
+            left: None,
+            right: Vec::new(),
+        }
+    }
+
     fn left_id_or_err(&self) -> Result<usize, QueryPlannerError> {
         match self.left {
             Some(id) => Ok(id),
@@ -210,19 +220,24 @@ impl SyntaxNodes {
     /// Push a new syntax node to arena
     pub fn push_syntax_node(&mut self, node: SyntaxNode) -> usize {
         let id = self.next_id();
-        if let SyntaxData::PlanId(plan_id) = node.data {
-            self.map.insert(plan_id, id);
+        match node.data {
+            SyntaxData::PlanId(plan_id) | SyntaxData::Parameter(plan_id) => {
+                self.map.insert(plan_id, id);
+            }
+            _ => {}
         }
         self.arena.push(node);
         id
     }
 
     /// Get next node id
+    #[must_use]
     pub fn next_id(&self) -> usize {
         self.arena.len()
     }
 
     /// Constructor with pre-allocated memory
+    #[must_use]
     pub fn with_capacity(capacity: usize) -> Self {
         SyntaxNodes {
             arena: Vec::with_capacity(capacity),
@@ -388,6 +403,10 @@ pub struct SyntaxPlan<'p> {
 
 #[allow(dead_code)]
 impl<'p> SyntaxPlan<'p> {
+    /// Add an IR plan node to the syntax tree.
+    ///
+    /// # Errors
+    /// - Failed to translate an IR plan node to a syntax node.
     #[allow(clippy::too_many_lines)]
     pub fn add_plan_node(&mut self, id: usize) -> Result<usize, QueryPlannerError> {
         let ir_plan = self.plan.get_ir_plan();
@@ -593,7 +612,11 @@ impl<'p> SyntaxPlan<'p> {
                 }
             },
             Node::Expression(expr) => match expr {
-                Expression::Constant { .. } | Expression::Reference { .. } => {
+                Expression::Constant { .. } => {
+                    let sn = SyntaxNode::new_parameter(id);
+                    Ok(self.nodes.push_syntax_node(sn))
+                }
+                Expression::Reference { .. } => {
                     let sn = SyntaxNode::new_pointer(id, None, &[]);
                     Ok(self.nodes.push_syntax_node(sn))
                 }
@@ -788,6 +811,12 @@ impl<'p> SyntaxPlan<'p> {
         }
     }
 
+    /// Build a new syntax tree from the execution plan.
+    ///
+    /// # Errors
+    /// - Failed to ad an IR plan to the syntax tree
+    /// - Failed to get to the top of the syntax tree
+    /// - Failed to move projection nodes under their scans
     pub fn new(plan: &'p ExecutionPlan, top: usize) -> Result<Self, QueryPlannerError> {
         let mut sp = SyntaxPlan::empty(plan);
         let ir_plan = plan.get_ir_plan();
diff --git a/src/executor/engine/cartridge/backend/sql/tree/tests.rs b/src/executor/engine/cartridge/backend/sql/tree/tests.rs
index 5f32269170..5955489b53 100644
--- a/src/executor/engine/cartridge/backend/sql/tree/tests.rs
+++ b/src/executor/engine/cartridge/backend/sql/tree/tests.rs
@@ -81,7 +81,7 @@ fn sql_order_selection() {
     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::PlanId(6)), nodes_iter.next()); // const 1
+    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/mock.rs b/src/executor/engine/mock.rs
index ff71c1dd41..26d99c2c00 100644
--- a/src/executor/engine/mock.rs
+++ b/src/executor/engine/mock.rs
@@ -261,13 +261,13 @@ impl Engine for EngineMock {
         match buckets {
             Buckets::All => {
                 let sql = plan.syntax_nodes_as_sql(&nodes, buckets)?;
-                result.extend(exec_on_all(&sql))?;
+                result.extend(exec_on_all(&String::from(sql).as_str()))?;
             }
             Buckets::Filtered(list) => {
                 for bucket in list {
                     let bucket_set: HashSet<u64, RepeatableState> = collection! { *bucket };
                     let sql = plan.syntax_nodes_as_sql(&nodes, &Buckets::Filtered(bucket_set))?;
-                    let temp_result = exec_on_some(*bucket, &sql);
+                    let temp_result = exec_on_some(*bucket, &String::from(sql).as_str());
                     result.extend(temp_result)?;
                 }
             }
diff --git a/src/executor/result.rs b/src/executor/result.rs
index c470966fa9..f6b978b5cf 100644
--- a/src/executor/result.rs
+++ b/src/executor/result.rs
@@ -45,6 +45,8 @@ impl TryInto<Column> for &MetadataColumn {
             "integer" => Ok(Column::new(&self.name, Type::Integer, ColumnRole::User)),
             "scalar" => Ok(Column::new(&self.name, Type::Scalar, ColumnRole::User)),
             "string" => Ok(Column::new(&self.name, Type::String, ColumnRole::User)),
+            "text" => Ok(Column::new(&self.name, Type::String, ColumnRole::User)),
+            "varchar" => Ok(Column::new(&self.name, Type::String, ColumnRole::User)),
             "unsigned" => Ok(Column::new(&self.name, Type::Unsigned, ColumnRole::User)),
             _ => Err(QueryPlannerError::CustomError(format!(
                 "unsupported column type: {}",
diff --git a/src/executor/tests.rs b/src/executor/tests.rs
index c9689d7fb4..9b140b9d87 100644
--- a/src/executor/tests.rs
+++ b/src/executor/tests.rs
@@ -1,5 +1,6 @@
 use pretty_assertions::assert_eq;
 
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::executor::engine::mock::EngineMock;
 use crate::executor::result::ProducerResult;
 use crate::executor::vtable::VirtualTable;
@@ -22,12 +23,17 @@ fn shard_query() {
 
     let param1 = Value::from(1_u64);
     let bucket = query.engine.determine_bucket_id(&[&param1]);
-    expected
-        .rows
-        .push(vec![
-            Value::from(format!("Execute query on a bucket [{}]", bucket)),
-            Value::from(r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space" WHERE ("test_space"."id") = (1)"#),
-        ]);
+    expected.rows.push(vec![
+        Value::from(format!("Execute query on a bucket [{}]", bucket)),
+        Value::from(String::from(PatternWithParams::new(
+            format!(
+                "{} {}",
+                r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space""#,
+                r#"WHERE ("test_space"."id") = (?)"#
+            ),
+            vec![param1],
+        ))),
+    ]);
     assert_eq!(expected, result);
 }
 
@@ -57,17 +63,19 @@ fn shard_union_query() {
         .push(vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket)),
             Value::String(
-                format!(
-                    "{} {}{} {} {}{} {}",
-                    r#"SELECT "t3"."id" as "id""#,
-                    r#"FROM ("#,
-                    r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") = (1)"#,
-                    r#"UNION ALL"#,
-                    r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") > (1)"#,
-                    r#") as "t3""#,
-                    r#"WHERE ("t3"."id") = (1)"#,
-                )
-            )
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                            "{} {}{} {} {}{} {}",
+                            r#"SELECT "t3"."id" as "id""#,
+                            r#"FROM ("#,
+                            r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") = (?)"#,
+                            r#"UNION ALL"#,
+                            r#"SELECT "test_space"."id" as "id" FROM "test_space" WHERE ("test_space"."sys_op") > (?)"#,
+                            r#") as "t3""#,
+                            r#"WHERE ("t3"."id") = (?)"#,
+                        ), vec![Value::from(1_u64), Value::from(1_u64), Value::from(1_u64)]),
+                ))
         ]);
 
     assert_eq!(expected, result);
@@ -84,18 +92,22 @@ fn map_reduce_query() {
     let mut expected = ProducerResult::new();
 
     let param1 = Value::from(1_u64);
-    let param457 = Value::from(457_u64);
+    let param457 = Value::from("457");
 
     let bucket = query.engine.determine_bucket_id(&[&param1, &param457]);
 
     expected.rows.push(vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
         Value::String(
-            format!(
-                "{} {} {}",
-                r#"SELECT "hash_testing"."product_code" as "product_code""#,
-                r#"FROM "hash_testing""#,
-                r#"WHERE ("hash_testing"."identification_number", "hash_testing"."product_code") = (1, '457')"#,
+            String::from(
+                PatternWithParams::new(
+                    format!(
+                        "{} {} {}",
+                        r#"SELECT "hash_testing"."product_code" as "product_code""#,
+                        r#"FROM "hash_testing""#,
+                        r#"WHERE ("hash_testing"."identification_number", "hash_testing"."product_code") = (?, ?)"#,
+                    ), vec![param1, param457],
+                )
             )
         )
     ]);
@@ -133,21 +145,33 @@ fn linker_test() {
     expected.rows.extend(vec![
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket3)),
-            Value::String(format!(
-                "{} {} {}",
-                r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#,
-                r#"FROM "test_space""#,
-                r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (3)))"#,
-            )),
+            Value::String(
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                        "{} {} {}",
+                        r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#,
+                        r#"FROM "test_space""#,
+                        r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#,
+                        ), vec![param3],
+                    )
+                )
+            ),
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
-            Value::String(format!(
-                "{} {} {}",
-                r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#,
-                r#"FROM "test_space""#,
-                r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (2)))"#,
-            )),
+            Value::String(
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                        "{} {} {}",
+                        r#"SELECT "test_space"."FIRST_NAME" as "FIRST_NAME""#,
+                        r#"FROM "test_space""#,
+                        r#"WHERE ("test_space"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#,
+                        ), vec![param2],
+                    )
+                )
+            ),
         ],
     ]);
 
@@ -195,39 +219,41 @@ fn union_linker_test() {
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket3)),
             Value::String(
+                String::from(PatternWithParams::new(
                 format!(
                     "{} {}{} {} {} {} {} {} {}{} {}",
                     r#"SELECT "t1"."id" as "id", "t1"."FIRST_NAME" as "FIRST_NAME""#,
                     r#"FROM ("#,
                     r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#,
                     r#"FROM "test_space""#,
-                    r#"WHERE ("test_space"."sys_op") < (0)"#,
+                    r#"WHERE ("test_space"."sys_op") < (?)"#,
                     r#"UNION ALL"#,
                     r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#,
                     r#"FROM "test_space_hist""#,
-                    r#"WHERE ("test_space_hist"."sys_op") > (0)"#,
+                    r#"WHERE ("test_space_hist"."sys_op") > (?)"#,
                     r#") as "t1""#,
-                    r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (3)))"#
-                )
-            )
+                    r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#,
+                ), vec![Value::from(0_u64), Value::from(0_u64), Value::from(3_u64)])
+            ))
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
             Value::String(
+                String::from(PatternWithParams::new(
                 format!(
                     "{} {}{} {} {} {} {} {} {}{} {}",
                     r#"SELECT "t1"."id" as "id", "t1"."FIRST_NAME" as "FIRST_NAME""#,
                     r#"FROM ("#,
                     r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#,
                     r#"FROM "test_space""#,
-                    r#"WHERE ("test_space"."sys_op") < (0)"#,
+                    r#"WHERE ("test_space"."sys_op") < (?)"#,
                     r#"UNION ALL"#,
                     r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#,
                     r#"FROM "test_space_hist""#,
-                    r#"WHERE ("test_space_hist"."sys_op") > (0)"#,
+                    r#"WHERE ("test_space_hist"."sys_op") > (?)"#,
                     r#") as "t1""#,
-                    r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (2)))"#
-                )
+                    r#"WHERE ("t1"."id") in (SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#,
+                ), vec![Value::from(0_u64), Value::from(0_u64), Value::from(2_u64)]))
             )
         ],
     ]);
@@ -283,25 +309,37 @@ WHERE "t3"."id" = 2 AND "t8"."identification_number" = 2"#;
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
             Value::String(
-                format!(
-                    "{}, {}, {} {}{} {} {} {} {} {} {}{} {} {}{} {} {}",
-                    r#"SELECT "t3"."id" as "id""#,
-                    r#""t3"."FIRST_NAME" as "FIRST_NAME""#,
-                    r#""t8"."identification_number" as "identification_number""#,
-                    r#"FROM ("#,
-                    r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#,
-                    r#"FROM "test_space""#,
-                    r#"WHERE (0) > ("test_space"."sys_op") and ("test_space"."sysFrom") >= (0)"#,
-                    r#"UNION ALL"#,
-                    r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#,
-                    r#"FROM "test_space_hist""#,
-                    r#"WHERE ("test_space_hist"."sysFrom") <= (0)"#,
-                    r#") as "t3""#,
-                    r#"INNER JOIN"#,
-                    r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (2))"#,
-                    r#") as "t8""#,
-                    r#"ON ("t3"."id") = ("t8"."identification_number")"#,
-                    r#"WHERE ("t3"."id", "t3"."id", "t8"."identification_number") = ("t8"."identification_number", 2, 2)"#
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                            "{}, {}, {} {}{} {} {} {} {} {} {}{} {} {}{} {} {}",
+                            r#"SELECT "t3"."id" as "id""#,
+                            r#""t3"."FIRST_NAME" as "FIRST_NAME""#,
+                            r#""t8"."identification_number" as "identification_number""#,
+                            r#"FROM ("#,
+                            r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#,
+                            r#"FROM "test_space""#,
+                            r#"WHERE (?) > ("test_space"."sys_op") and ("test_space"."sysFrom") >= (?)"#,
+                            r#"UNION ALL"#,
+                            r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#,
+                            r#"FROM "test_space_hist""#,
+                            r#"WHERE ("test_space_hist"."sysFrom") <= (?)"#,
+                            r#") as "t3""#,
+                            r#"INNER JOIN"#,
+                            r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (?))"#,
+                            r#") as "t8""#,
+                            r#"ON ("t3"."id") = ("t8"."identification_number")"#,
+                            r#"WHERE ("t3"."id", "t3"."id", "t8"."identification_number") = ("t8"."identification_number", ?, ?)"#
+                        ),
+                        vec![
+                            Value::from(0_u64),
+                            Value::from(0_u64),
+                            Value::from(0_u64),
+                            Value::from(2_u64),
+                            Value::from(2_u64),
+                            Value::from(2_u64)
+                        ]
+                    )
                 )
             )
         ],
@@ -353,13 +391,16 @@ fn join_linker2_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-        Value::String(format!(
-            "{} {} {} {}",
-            r#"SELECT "t1"."id" as "id" FROM "test_space" as "t1""#,
-            r#"INNER JOIN"#,
-            r#"(SELECT COLUMN_1 as "id1",COLUMN_2 as "id2" FROM (VALUES (1,1)))"#,
-            r#"as "t2" ON ("t1"."id") = (1)"#
-        )),
+        Value::String(String::from(PatternWithParams::new(
+            format!(
+                "{} {} {} {}",
+                r#"SELECT "t1"."id" as "id" FROM "test_space" as "t1""#,
+                r#"INNER JOIN"#,
+                r#"(SELECT COLUMN_1 as "id1",COLUMN_2 as "id2" FROM (VALUES (?,?)))"#,
+                r#"as "t2" ON ("t1"."id") = (?)"#
+            ),
+            vec![Value::from(1_u64), Value::from(1_u64), Value::from(1_u64)],
+        ))),
     ]]);
     assert_eq!(expected, result);
 }
@@ -409,14 +450,23 @@ fn join_linker3_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-        Value::String(format!(
-            "{} {} {} {} {}",
-            r#"SELECT "t2"."id1" as "id1" FROM"#,
-            r#"(SELECT "test_space"."id" as "id" FROM "test_space") as "t1""#,
-            r#"INNER JOIN"#,
-            r#"(SELECT COLUMN_3 as "id1",COLUMN_4 as "id2" FROM (VALUES (1,1),(2,2))) as "t2""#,
-            r#"ON ("t2"."id1") = (1)"#,
-        )),
+        Value::String(String::from(PatternWithParams::new(
+            format!(
+                "{} {} {} {} {}",
+                r#"SELECT "t2"."id1" as "id1" FROM"#,
+                r#"(SELECT "test_space"."id" as "id" FROM "test_space") as "t1""#,
+                r#"INNER JOIN"#,
+                r#"(SELECT COLUMN_3 as "id1",COLUMN_4 as "id2" FROM (VALUES (?,?),(?,?))) as "t2""#,
+                r#"ON ("t2"."id1") = (?)"#,
+            ),
+            vec![
+                Value::from(1_u64),
+                Value::from(1_u64),
+                Value::from(2_u64),
+                Value::from(2_u64),
+                Value::from(1_u64),
+            ],
+        ))),
     ]]);
     assert_eq!(expected, result);
 }
@@ -482,25 +532,31 @@ fn join_linker4_test() {
     expected.rows.extend(vec![
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
-            Value::String(format!(
-                "{} {} {} {} {}",
-                r#"SELECT t1."id" as "id" FROM "test_space" as t1"#,
-                r#"INNER JOIN"#,
-                r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (2))) as t2"#,
-                r#"ON (t1."id") = (t2."r_id")"#,
-                r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (2),(3)))"#,
-            )),
+            Value::String(String::from(PatternWithParams::new(
+                format!(
+                    "{} {} {} {} {}",
+                    r#"SELECT t1."id" as "id" FROM "test_space" as t1"#,
+                    r#"INNER JOIN"#,
+                    r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (?))) as t2"#,
+                    r#"ON (t1."id") = (t2."r_id")"#,
+                    r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (?),(?)))"#,
+                ),
+                vec![Value::from(2_u64), Value::from(2_u64), Value::from(3_u64)],
+            ))),
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-            Value::String(format!(
-                "{} {} {} {} {}",
-                r#"SELECT t1."id" as "id" FROM "test_space" as t1"#,
-                r#"INNER JOIN"#,
-                r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (1))) as t2"#,
-                r#"ON (t1."id") = (t2."r_id")"#,
-                r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (2),(3)))"#,
-            )),
+            Value::String(String::from(PatternWithParams::new(
+                format!(
+                    "{} {} {} {} {}",
+                    r#"SELECT t1."id" as "id" FROM "test_space" as t1"#,
+                    r#"INNER JOIN"#,
+                    r#"(SELECT COLUMN_1 as "r_id" FROM (VALUES (?))) as t2"#,
+                    r#"ON (t1."id") = (t2."r_id")"#,
+                    r#"and (t1."FIRST_NAME") = (SELECT COLUMN_3 as "fn" FROM (VALUES (?),(?)))"#,
+                ),
+                vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)],
+            ))),
         ],
     ]);
     assert_eq!(expected, result);
@@ -550,35 +606,41 @@ fn anonymous_col_index_test() {
     expected.rows.extend(vec![
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket3)),
-            Value::String(format!(
-                "{} {} {} {} {} {} {} {} {} {}",
-                "SELECT",
-                r#""test_space"."id" as "id","#,
-                r#""test_space"."sysFrom" as "sysFrom","#,
-                r#""test_space"."FIRST_NAME" as "FIRST_NAME","#,
-                r#""test_space"."sys_op" as "sys_op""#,
-                r#"FROM "test_space""#,
-                r#"WHERE (("test_space"."id") in"#,
-                r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (3)))"#,
-                r#"or ("test_space"."id") in"#,
-                r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (3))))"#,
-            )),
+            Value::String(String::from(PatternWithParams::new(
+                format!(
+                    "{} {} {} {} {} {} {} {} {} {}",
+                    "SELECT",
+                    r#""test_space"."id" as "id","#,
+                    r#""test_space"."sysFrom" as "sysFrom","#,
+                    r#""test_space"."FIRST_NAME" as "FIRST_NAME","#,
+                    r#""test_space"."sys_op" as "sys_op""#,
+                    r#"FROM "test_space""#,
+                    r#"WHERE (("test_space"."id") in"#,
+                    r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#,
+                    r#"or ("test_space"."id") in"#,
+                    r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (?))))"#,
+                ),
+                vec![Value::from(3_u64), Value::from(3_u64)],
+            ))),
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
-            Value::String(format!(
-                "{} {} {} {} {} {} {} {} {} {}",
-                "SELECT",
-                r#""test_space"."id" as "id","#,
-                r#""test_space"."sysFrom" as "sysFrom","#,
-                r#""test_space"."FIRST_NAME" as "FIRST_NAME","#,
-                r#""test_space"."sys_op" as "sys_op""#,
-                r#"FROM "test_space""#,
-                r#"WHERE (("test_space"."id") in"#,
-                r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (2)))"#,
-                r#"or ("test_space"."id") in"#,
-                r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (2))))"#,
-            )),
+            Value::String(String::from(PatternWithParams::new(
+                format!(
+                    "{} {} {} {} {} {} {} {} {} {}",
+                    "SELECT",
+                    r#""test_space"."id" as "id","#,
+                    r#""test_space"."sysFrom" as "sysFrom","#,
+                    r#""test_space"."FIRST_NAME" as "FIRST_NAME","#,
+                    r#""test_space"."sys_op" as "sys_op""#,
+                    r#"FROM "test_space""#,
+                    r#"WHERE (("test_space"."id") in"#,
+                    r#"(SELECT COLUMN_1 as "identification_number" FROM (VALUES (?)))"#,
+                    r#"or ("test_space"."id") in"#,
+                    r#"(SELECT COLUMN_2 as "identification_number" FROM (VALUES (?))))"#,
+                ),
+                vec![Value::from(2_u64), Value::from(2_u64)],
+            ))),
         ],
     ]);
 
@@ -599,12 +661,15 @@ fn sharding_column1_test() {
     let bucket = query.engine.determine_bucket_id(&[&param1]);
     expected.rows.push(vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
-        Value::String(format!(
-            "{} {} {}",
-            r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#,
-            r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op""#,
-            r#"FROM "test_space" WHERE ("test_space"."id") = (1)"#,
-        )),
+        Value::String(String::from(PatternWithParams::new(
+            format!(
+                "{} {} {}",
+                r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#,
+                r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op""#,
+                r#"FROM "test_space" WHERE ("test_space"."id") = (?)"#,
+            ),
+            vec![Value::from(1_u64)],
+        ))),
     ]);
     assert_eq!(expected, result);
 }
@@ -623,12 +688,18 @@ fn sharding_column2_test() {
     let bucket = query.engine.determine_bucket_id(&[&param1]);
     expected.rows.push(vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
-        Value::String(format!(
-            "{} {} {}",
-            r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#,
-            r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op","#,
-            r#""test_space"."bucket_id" as "bucket_id" FROM "test_space" WHERE ("test_space"."id") = (1)"#,
-        )),
+        Value::String(
+            String::from(
+                PatternWithParams::new(
+                    format!(
+                    "{} {} {}",
+                    r#"SELECT "test_space"."id" as "id", "test_space"."sysFrom" as "sysFrom","#,
+                    r#""test_space"."FIRST_NAME" as "FIRST_NAME", "test_space"."sys_op" as "sys_op","#,
+                    r#""test_space"."bucket_id" as "bucket_id" FROM "test_space" WHERE ("test_space"."id") = (?)"#,
+                    ), vec![Value::from(1_u64)],
+                )
+            )
+        ),
     ]);
     assert_eq!(expected, result);
 }
@@ -669,19 +740,25 @@ fn insert1_test() {
     expected.rows.extend(vec![
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-            Value::String(format!(
-                "{} {}",
-                r#"INSERT INTO "t" ("b", "bucket_id")"#,
-                r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (1,2156))"#,
-            )),
+            Value::String(String::from(PatternWithParams::new(
+                format!(
+                    "{} {}",
+                    r#"INSERT INTO "t" ("b", "bucket_id")"#,
+                    r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (?,?))"#,
+                ),
+                vec![Value::from(1_u64), Value::from(2156_u64)],
+            ))),
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
-            Value::String(format!(
-                "{} {}",
-                r#"INSERT INTO "t" ("b", "bucket_id")"#,
-                r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (2,3832))"#,
-            )),
+            Value::String(String::from(PatternWithParams::new(
+                format!(
+                    "{} {}",
+                    r#"INSERT INTO "t" ("b", "bucket_id")"#,
+                    r#"SELECT COLUMN_1 as "a",COLUMN_2 as "bucket_id" FROM (VALUES (?,?))"#,
+                ),
+                vec![Value::from(2_u64), Value::from(3832_u64)],
+            ))),
         ],
     ]);
     assert_eq!(expected, result);
@@ -725,11 +802,17 @@ fn insert2_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
-        Value::String(format!(
-            "{} {}",
-            r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
-            r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (1,2,550))"#,
-        )),
+        Value::String(
+            String::from(
+                PatternWithParams::new(
+                    format!(
+                    "{} {}",
+                    r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
+                    r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#,
+                    ), vec![Value::from(1_u64), Value::from(2_u64), Value::from(550_u64)],
+                )
+            )
+        ),
     ]]);
     assert_eq!(expected, result);
 }
@@ -775,19 +858,31 @@ fn insert3_test() {
     expected.rows.extend(vec![
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-            Value::String(format!(
-                "{} {}",
-                r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
-                r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (1,2,4427))"#,
-            )),
+            Value::String(
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                        "{} {}",
+                        r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
+                        r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#,
+                        ), vec![Value::from(1_u64), Value::from(2_u64), Value::from(4427_u64)],
+                    )
+                )
+            ),
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
-            Value::String(format!(
-                "{} {}",
-                r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
-                r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (3,4,7100))"#,
-            )),
+            Value::String(
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                        "{} {}",
+                        r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
+                        r#"SELECT COLUMN_1 as "a",COLUMN_2 as "b",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#,
+                        ), vec![Value::from(3_u64), Value::from(4_u64), Value::from(7100_u64)],
+                    )
+                )
+            ),
         ],
     ]);
     assert_eq!(expected, result);
@@ -830,11 +925,17 @@ fn insert4_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
-        Value::String(format!(
-            "{} {}",
-            r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
-            r#"SELECT COLUMN_1 as "b",COLUMN_2 as "a",COLUMN_3 as "bucket_id" FROM (VALUES (2,1,550))"#,
-        )),
+        Value::String(
+            String::from(
+                PatternWithParams::new(
+                    format!(
+                    "{} {}",
+                    r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
+                    r#"SELECT COLUMN_1 as "b",COLUMN_2 as "a",COLUMN_3 as "bucket_id" FROM (VALUES (?,?,?))"#,
+                    ), vec![Value::from(2_u64), Value::from(1_u64), Value::from(550_u64)],
+                )
+            )
+        ),
     ]]);
     assert_eq!(expected, result);
 }
@@ -875,11 +976,25 @@ fn insert5_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
-        Value::String(format!(
-            "{} {}",
-            r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
-            r#"SELECT COLUMN_4 as "a",COLUMN_5 as "b",COLUMN_6 as "bucket_id" FROM (VALUES (5,6,8788),(5,6,8788))"#,
-        )),
+        Value::String(
+            String::from(
+                PatternWithParams::new(
+                    format!(
+                    "{} {}",
+                    r#"INSERT INTO "t" ("b", "a", "bucket_id")"#,
+                    r#"SELECT COLUMN_4 as "a",COLUMN_5 as "b",COLUMN_6 as "bucket_id" FROM (VALUES (?,?,?),(?,?,?))"#,
+                    ),
+                    vec![
+                        Value::from(5_u64),
+                        Value::from(6_u64),
+                        Value::from(8788_u64),
+                        Value::from(5_u64),
+                        Value::from(6_u64),
+                        Value::from(8788_u64)
+                    ],
+                )
+            )
+        ),
     ]]);
     assert_eq!(expected, result);
 }
@@ -924,21 +1039,45 @@ fn insert6_test() {
     expected.rows.extend(vec![
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-            Value::String(format!(
-                "{} {} {}",
-                r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
-                r#"SELECT COLUMN_4 as "COLUMN_5",COLUMN_5 as "COLUMN_6",COLUMN_6 as "bucket_id""#,
-                r#"FROM (VALUES (1,2,550),(1,2,550))"#,
-            )),
+            Value::String(
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                        "{} {} {}",
+                        r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
+                        r#"SELECT COLUMN_4 as "COLUMN_5",COLUMN_5 as "COLUMN_6",COLUMN_6 as "bucket_id""#,
+                        r#"FROM (VALUES (?,?,?),(?,?,?))"#,
+                        ),
+                        vec![
+                            Value::from(1_u64),
+                            Value::from(2_u64),
+                            Value::from(550_u64),
+                            Value::from(1_u64),
+                            Value::from(2_u64),
+                            Value::from(550_u64)
+                        ],
+                    )
+                )
+            ),
         ],
         vec![
             Value::String(format!("Execute query on a bucket [{}]", bucket2)),
-            Value::String(format!(
-                "{} {} {}",
-                r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
-                r#"SELECT COLUMN_1 as "COLUMN_5",COLUMN_2 as "COLUMN_6",COLUMN_3 as "bucket_id""#,
-                r#"FROM (VALUES (3,4,8906))"#,
-            )),
+            Value::String(
+                String::from(
+                    PatternWithParams::new(
+                        format!(
+                        "{} {} {}",
+                        r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
+                        r#"SELECT COLUMN_1 as "COLUMN_5",COLUMN_2 as "COLUMN_6",COLUMN_3 as "bucket_id""#,
+                        r#"FROM (VALUES (?,?,?))"#,
+                        ), vec![
+                            Value::from(3_u64),
+                            Value::from(4_u64),
+                            Value::from(8906_u64),
+                        ],
+                    )
+                )
+            ),
         ],
     ]);
     assert_eq!(expected, result);
@@ -999,12 +1138,24 @@ fn insert8_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket)),
-        Value::String(format!(
-            "{} {}{}",
-            r#"INSERT INTO "hash_testing" ("identification_number", "product_code", "product_units", "sys_op", "bucket_id")"#,
-            r#"SELECT COLUMN_1 as "identification_number",COLUMN_2 as "product_code",COLUMN_3 as "product_units","#,
-            r#"COLUMN_4 as "sys_op",COLUMN_5 as "bucket_id" FROM (VALUES (1,'two',true,4,3016))"#,
-        )),
+        Value::String(
+            String::from(
+                PatternWithParams::new(
+                    format!(
+                    "{} {}{}",
+                    r#"INSERT INTO "hash_testing" ("identification_number", "product_code", "product_units", "sys_op", "bucket_id")"#,
+                    r#"SELECT COLUMN_1 as "identification_number",COLUMN_2 as "product_code",COLUMN_3 as "product_units","#,
+                    r#"COLUMN_4 as "sys_op",COLUMN_5 as "bucket_id" FROM (VALUES (?,?,?,?,?))"#,
+                    ), vec![
+                        Value::from(1_u64),
+                        Value::from("two"),
+                        Value::from(true),
+                        Value::from(4_u64),
+                        Value::from(3016_u64),
+                    ],
+                )
+            )
+        ),
     ]]);
     assert_eq!(expected, result);
 }
@@ -1042,12 +1193,15 @@ fn insert9_test() {
 
     expected.rows.extend(vec![vec![
         Value::String(format!("Execute query on a bucket [{}]", bucket1)),
-        Value::String(format!(
-            "{} {} {}",
-            r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
-            r#"SELECT COLUMN_1 as "COLUMN_1",COLUMN_2 as "COLUMN_2",COLUMN_3 as "bucket_id""#,
-            r#"FROM (VALUES (1,2,550))"#,
-        )),
+        Value::String(String::from(PatternWithParams::new(
+            format!(
+                "{} {} {}",
+                r#"INSERT INTO "t" ("a", "b", "bucket_id")"#,
+                r#"SELECT COLUMN_1 as "COLUMN_1",COLUMN_2 as "COLUMN_2",COLUMN_3 as "bucket_id""#,
+                r#"FROM (VALUES (?,?,?))"#,
+            ),
+            vec![Value::from(1_u64), Value::from(2_u64), Value::from(550_u64)],
+        ))),
     ]]);
     assert_eq!(expected, result);
 }
diff --git a/src/frontend/sql/ir/tests.rs b/src/frontend/sql/ir/tests.rs
index d6f739cebb..78c6ac0ccf 100644
--- a/src/frontend/sql/ir/tests.rs
+++ b/src/frontend/sql/ir/tests.rs
@@ -1,4 +1,5 @@
 use crate::errors::QueryPlannerError;
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::executor::engine::mock::MetadataMock;
 use crate::frontend::sql::ast::AbstractSyntaxTree;
 use crate::frontend::Ast;
@@ -13,10 +14,14 @@ fn no_transform(_plan: &mut Plan) {}
 fn front_sql1() {
     let input = r#"SELECT "identification_number", "product_code" FROM "hash_testing"
         WHERE "identification_number" = 1"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
-        r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (1)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "hash_testing"."identification_number" as "identification_number","#,
+            r#""hash_testing"."product_code" as "product_code""#,
+            r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") = (?)"#,
+        ),
+        vec![Value::from(1_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -28,11 +33,19 @@ fn front_sql2() {
         FROM "hash_testing"
         WHERE "identification_number" = 1 AND "product_code" = '1'
         OR "identification_number" = 2 AND "product_code" = '2'"#;
-    let expected = format!(
-        "{} {} {}",
-        r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
-        r#"FROM "hash_testing" WHERE (("hash_testing"."identification_number") = (1) and ("hash_testing"."product_code") = ('1')"#,
-        r#"or ("hash_testing"."identification_number") = (2) and ("hash_testing"."product_code") = ('2'))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
+            r#"FROM "hash_testing" WHERE (("hash_testing"."identification_number") = (?) and ("hash_testing"."product_code") = (?)"#,
+            r#"or ("hash_testing"."identification_number") = (?) and ("hash_testing"."product_code") = (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from("1"),
+            Value::from(2_u64),
+            Value::from("2"),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -50,15 +63,18 @@ fn front_sql3() {
             FROM "hash_testing_hist"
             WHERE "sys_op" > 1) AS "t3"
         WHERE "identification_number" = 1"#;
-    let expected = format!(
-        "{} {} {} {} {} {} {}",
-        r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#,
-        r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
-        r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (1)"#,
-        r#"UNION ALL"#,
-        r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#,
-        r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (1)) as "t3""#,
-        r#"WHERE ("t3"."identification_number") = (1)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {} {} {} {} {}",
+            r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#,
+            r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
+            r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (?)"#,
+            r#"UNION ALL"#,
+            r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#,
+            r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (?)) as "t3""#,
+            r#"WHERE ("t3"."identification_number") = (?)"#,
+        ),
+        vec![Value::from(1_u64), Value::from(1_u64), Value::from(1_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -80,16 +96,27 @@ fn front_sql4() {
             OR "identification_number" = 3))
             AND ("product_code" = '1'
             OR "product_code" = '2')"#;
-    let expected = format!(
-        "{} {} {} {} {} {} {} {}",
-        r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#,
-        r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
-        r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (1)"#,
-        r#"UNION ALL"#,
-        r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#,
-        r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (1)) as "t3""#,
-        r#"WHERE (("t3"."identification_number") = (1) or (("t3"."identification_number") = (2) or ("t3"."identification_number") = (3)))"#,
-        r#"and (("t3"."product_code") = ('1') or ("t3"."product_code") = ('2'))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {} {} {} {} {} {}",
+            r#"SELECT "t3"."identification_number" as "identification_number", "t3"."product_code" as "product_code" FROM"#,
+            r#"(SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
+            r#"FROM "hash_testing" WHERE ("hash_testing"."sys_op") = (?)"#,
+            r#"UNION ALL"#,
+            r#"SELECT "hash_testing_hist"."identification_number" as "identification_number", "hash_testing_hist"."product_code" as "product_code""#,
+            r#"FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (?)) as "t3""#,
+            r#"WHERE (("t3"."identification_number") = (?) or (("t3"."identification_number") = (?) or ("t3"."identification_number") = (?)))"#,
+            r#"and (("t3"."product_code") = (?) or ("t3"."product_code") = (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(1_u64),
+            Value::from(1_u64),
+            Value::from(2_u64),
+            Value::from(3_u64),
+            Value::from("1"),
+            Value::from("2"),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -100,12 +127,15 @@ fn front_sql5() {
     let input = r#"SELECT "identification_number", "product_code" FROM "hash_testing"
         WHERE "identification_number" in (
         SELECT "identification_number" FROM "hash_testing_hist" WHERE "product_code" = 'a')"#;
-    let expected = format!(
-        "{} {} {} {}",
-        r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
-        r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") in"#,
-        r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#,
-        r#"WHERE ("hash_testing_hist"."product_code") = ('a'))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {} {}",
+            r#"SELECT "hash_testing"."identification_number" as "identification_number", "hash_testing"."product_code" as "product_code""#,
+            r#"FROM "hash_testing" WHERE ("hash_testing"."identification_number") in"#,
+            r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number" FROM "hash_testing_hist""#,
+            r#"WHERE ("hash_testing_hist"."product_code") = (?))"#,
+        ),
+        vec![Value::from("a")],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -117,12 +147,15 @@ fn front_sql6() {
         INNER JOIN (SELECT "id" FROM "test_space") as t
         ON "hash_testing"."identification_number" = t."id"
         WHERE "hash_testing"."identification_number" = 5 and "hash_testing"."product_code" = '123'"#;
-    let expected = format!(
-        "{} {} {} {}",
-        r#"SELECT t."id" as "id", "hash_testing"."product_units" as "product_units""#,
-        r#"FROM "hash_testing" INNER JOIN (SELECT "test_space"."id" as "id" FROM "test_space") as t"#,
-        r#"ON ("hash_testing"."identification_number") = (t."id")"#,
-        r#"WHERE ("hash_testing"."identification_number") = (5) and ("hash_testing"."product_code") = ('123')"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {} {}",
+            r#"SELECT t."id" as "id", "hash_testing"."product_units" as "product_units""#,
+            r#"FROM "hash_testing" INNER JOIN (SELECT "test_space"."id" as "id" FROM "test_space") as t"#,
+            r#"ON ("hash_testing"."identification_number") = (t."id")"#,
+            r#"WHERE ("hash_testing"."identification_number") = (?) and ("hash_testing"."product_code") = (?)"#,
+        ),
+        vec![Value::from(5_u64), Value::from("123")],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -156,10 +189,13 @@ fn front_sql7() {
 fn front_sql8() {
     let input = r#"SELECT t."identification_number", "product_code" FROM "hash_testing" as t
         WHERE t."identification_number" = 1"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT t."identification_number" as "identification_number", t."product_code" as "product_code""#,
-        r#"FROM "hash_testing" as t WHERE (t."identification_number") = (1)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT t."identification_number" as "identification_number", t."product_code" as "product_code""#,
+            r#"FROM "hash_testing" as t WHERE (t."identification_number") = (?)"#,
+        ),
+        vec![Value::from(1_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -187,26 +223,38 @@ fn front_sql9() {
             WHERE "sys_op" <= 0) AS "t8"
             ON "t3"."id" = "t8"."identification_number"
         WHERE "id" = 1 AND "t8"."identification_number" = 1 AND "product_code" = '123'"#;
-    let expected = format!(
-        "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}",
-        r#"SELECT "t3"."id" as "id", "t3"."FIRST_NAME" as "FIRST_NAME","#,
-        r#""t8"."identification_number" as "identification_number","#,
-        r#""t8"."product_code" as "product_code" FROM"#,
-        r#"(SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#,
-        r#"FROM "test_space" WHERE ("test_space"."sys_op") < (0) and ("test_space"."sysFrom") >= (0)"#,
-        r#"UNION ALL"#,
-        r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#,
-        r#"FROM "test_space_hist" WHERE ("test_space_hist"."sysFrom") <= (0))"#,
-        r#"as "t3""#,
-        r#"INNER JOIN"#,
-        r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number","#,
-        r#""hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (0)"#,
-        r#"UNION ALL"#,
-        r#"SELECT "hash_single_testing_hist"."identification_number" as "identification_number","#,
-        r#""hash_single_testing_hist"."product_code" as "product_code" FROM "hash_single_testing_hist""#,
-        r#"WHERE ("hash_single_testing_hist"."sys_op") <= (0))"#,
-        r#"as "t8" ON ("t3"."id") = ("t8"."identification_number")"#,
-        r#"WHERE ("t3"."id") = (1) and ("t8"."identification_number") = (1) and ("t8"."product_code") = ('123')"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}",
+            r#"SELECT "t3"."id" as "id", "t3"."FIRST_NAME" as "FIRST_NAME","#,
+            r#""t8"."identification_number" as "identification_number","#,
+            r#""t8"."product_code" as "product_code" FROM"#,
+            r#"(SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME""#,
+            r#"FROM "test_space" WHERE ("test_space"."sys_op") < (?) and ("test_space"."sysFrom") >= (?)"#,
+            r#"UNION ALL"#,
+            r#"SELECT "test_space_hist"."id" as "id", "test_space_hist"."FIRST_NAME" as "FIRST_NAME""#,
+            r#"FROM "test_space_hist" WHERE ("test_space_hist"."sysFrom") <= (?))"#,
+            r#"as "t3""#,
+            r#"INNER JOIN"#,
+            r#"(SELECT "hash_testing_hist"."identification_number" as "identification_number","#,
+            r#""hash_testing_hist"."product_code" as "product_code" FROM "hash_testing_hist" WHERE ("hash_testing_hist"."sys_op") > (?)"#,
+            r#"UNION ALL"#,
+            r#"SELECT "hash_single_testing_hist"."identification_number" as "identification_number","#,
+            r#""hash_single_testing_hist"."product_code" as "product_code" FROM "hash_single_testing_hist""#,
+            r#"WHERE ("hash_single_testing_hist"."sys_op") <= (?))"#,
+            r#"as "t8" ON ("t3"."id") = ("t8"."identification_number")"#,
+            r#"WHERE ("t3"."id") = (?) and ("t8"."identification_number") = (?) and ("t8"."product_code") = (?)"#,
+        ),
+        vec![
+            Value::from(0_u64),
+            Value::from(0_u64),
+            Value::from(0_u64),
+            Value::from(0_u64),
+            Value::from(0_u64),
+            Value::from(1_u64),
+            Value::from(1_u64),
+            Value::from("123"),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -215,9 +263,17 @@ fn front_sql9() {
 #[test]
 fn front_sql10() {
     let input = r#"INSERT INTO "t" VALUES(1, 2, 3, 4)"#;
-    let expected = format!(
-        "{}",
-        r#"INSERT INTO "t" ("a", "b", "c", "d") VALUES (1, 2, 3, 4)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{}",
+            r#"INSERT INTO "t" ("a", "b", "c", "d") VALUES (?, ?, ?, ?)"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(2_u64),
+            Value::from(3_u64),
+            Value::from(4_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -226,7 +282,10 @@ fn front_sql10() {
 #[test]
 fn front_sql11() {
     let input = r#"INSERT INTO "t" ("a", "c") VALUES(1, 2)"#;
-    let expected = format!("{}", r#"INSERT INTO "t" ("a", "c") VALUES (1, 2)"#,);
+    let expected = PatternWithParams::new(
+        format!("{}", r#"INSERT INTO "t" ("a", "c") VALUES (?, ?)"#,),
+        vec![Value::from(1_u64), Value::from(2_u64)],
+    );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
 }
@@ -264,9 +323,12 @@ fn front_sql13() {
 #[test]
 fn front_sql14() {
     let input = r#"INSERT INTO "t" ("a", "c") SELECT "b", "d" FROM "t""#;
-    let expected = format!(
-        "{} {}",
-        r#"INSERT INTO "t" ("a", "c")"#, r#"SELECT "t"."b" as "b", "t"."d" as "d" FROM "t""#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"INSERT INTO "t" ("a", "c")"#, r#"SELECT "t"."b" as "b", "t"."d" as "d" FROM "t""#,
+        ),
+        vec![],
     );
 
     assert_eq!(sql_to_sql(input, &[], &no_transform), expected);
@@ -292,10 +354,13 @@ fn front_params1() {
     let pattern = r#"SELECT "id", "FIRST_NAME" FROM "test_space"
         WHERE "sys_op" = ? AND "sysFrom" > ?"#;
     let params = vec![Value::from(0_i64), Value::from(1_i64)];
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space""#,
-        r#"WHERE ("test_space"."sys_op") = (0) and ("test_space"."sysFrom") > (1)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "test_space"."id" as "id", "test_space"."FIRST_NAME" as "FIRST_NAME" FROM "test_space""#,
+            r#"WHERE ("test_space"."sys_op") = (?) and ("test_space"."sysFrom") > (?)"#,
+        ),
+        params.clone(),
     );
 
     assert_eq!(sql_to_sql(pattern, &params, &no_transform), expected);
@@ -306,10 +371,13 @@ fn front_params2() {
     let pattern = r#"SELECT "id" FROM "test_space"
         WHERE "sys_op" = ? AND "FIRST_NAME" = ?"#;
     let params = vec![Value::Null, Value::from("hello")];
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "test_space"."id" as "id" FROM "test_space""#,
-        r#"WHERE ("test_space"."sys_op") = (NULL) and ("test_space"."FIRST_NAME") = ('hello')"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "test_space"."id" as "id" FROM "test_space""#,
+            r#"WHERE ("test_space"."sys_op") = (?) and ("test_space"."FIRST_NAME") = (?)"#,
+        ),
+        params.clone(),
     );
 
     assert_eq!(sql_to_sql(pattern, &params, &no_transform), expected);
diff --git a/src/ir/transformation/bool_in/tests.rs b/src/ir/transformation/bool_in/tests.rs
index d50f26208c..e6034c73c9 100644
--- a/src/ir/transformation/bool_in/tests.rs
+++ b/src/ir/transformation/bool_in/tests.rs
@@ -1,4 +1,6 @@
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::ir::transformation::helpers::sql_to_sql;
+use crate::ir::value::Value;
 use crate::ir::Plan;
 use pretty_assertions::assert_eq;
 
@@ -9,10 +11,13 @@ fn replace_in_operator(plan: &mut Plan) {
 #[test]
 fn bool_in1() {
     let input = r#"SELECT "a" FROM "t" WHERE "a" IN (1, 2, 3)"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE ((("t"."a") = (1) or ("t"."a") = (2)) or ("t"."a") = (3))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE ((("t"."a") = (?) or ("t"."a") = (?)) or ("t"."a") = (?))"#,
+        ),
+        vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &replace_in_operator), expected);
@@ -22,10 +27,20 @@ fn bool_in1() {
 fn bool_in2() {
     let input = r#"SELECT "a" FROM "t"
     WHERE ("a", "b") IN ((1, 10), (2, 20), (3, 30))"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE ((("t"."a", "t"."b") = (1, 10) or ("t"."a", "t"."b") = (2, 20)) or ("t"."a", "t"."b") = (3, 30))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE ((("t"."a", "t"."b") = (?, ?) or ("t"."a", "t"."b") = (?, ?)) or ("t"."a", "t"."b") = (?, ?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(10_u64),
+            Value::from(2_u64),
+            Value::from(20_u64),
+            Value::from(3_u64),
+            Value::from(30_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &replace_in_operator), expected);
@@ -34,10 +49,13 @@ fn bool_in2() {
 #[test]
 fn bool_in3() {
     let input = r#"SELECT "a" FROM "t" WHERE "a" IN (1, 2) AND "b" IN (3)"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) or ("t"."a") = (2)) and ("t"."b") = (3)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) or ("t"."a") = (?)) and ("t"."b") = (?)"#,
+        ),
+        vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &replace_in_operator), expected);
diff --git a/src/ir/transformation/dnf/tests.rs b/src/ir/transformation/dnf/tests.rs
index d7c70e2100..d50cb50d0d 100644
--- a/src/ir/transformation/dnf/tests.rs
+++ b/src/ir/transformation/dnf/tests.rs
@@ -1,4 +1,6 @@
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::ir::transformation::helpers::sql_to_sql;
+use crate::ir::value::Value;
 use crate::ir::Plan;
 use pretty_assertions::assert_eq;
 
@@ -10,11 +12,20 @@ fn set_dnf(plan: &mut Plan) {
 fn dnf1() {
     let input = r#"SELECT "a" FROM "t"
     WHERE ("a" = 1 AND "b" = 2 OR "a" = 3) AND "c" = 4"#;
-    let expected = format!(
-        "{} {} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) and ("t"."b") = (2) and ("t"."c") = (4)"#,
-        r#"or ("t"."a") = (3) and ("t"."c") = (4))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) and ("t"."b") = (?) and ("t"."c") = (?)"#,
+            r#"or ("t"."a") = (?) and ("t"."c") = (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(2_u64),
+            Value::from(4_u64),
+            Value::from(3_u64),
+            Value::from(4_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &set_dnf), expected);
@@ -24,11 +35,23 @@ fn dnf1() {
 fn dnf2() {
     let input = r#"SELECT "a" FROM "t"
     WHERE ("a" = 1 OR "b" = 2) AND ("a" = 3 OR "c" = 4)"#;
-    let expected = format!(
-        "{} {} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (((("t"."a") = (3) and ("t"."a") = (1) or ("t"."c") = (4) and ("t"."a") = (1))"#,
-        r#"or ("t"."a") = (3) and ("t"."b") = (2)) or ("t"."c") = (4) and ("t"."b") = (2))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (((("t"."a") = (?) and ("t"."a") = (?) or ("t"."c") = (?) and ("t"."a") = (?))"#,
+            r#"or ("t"."a") = (?) and ("t"."b") = (?)) or ("t"."c") = (?) and ("t"."b") = (?))"#,
+        ),
+        vec![
+            Value::from(3_u64),
+            Value::from(1_u64),
+            Value::from(4_u64),
+            Value::from(1_u64),
+            Value::from(3_u64),
+            Value::from(2_u64),
+            Value::from(4_u64),
+            Value::from(2_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &set_dnf), expected);
@@ -38,10 +61,18 @@ fn dnf2() {
 fn dnf3() {
     let input = r#"SELECT "a" FROM "t"
     WHERE ("a" = 1 OR "b" = 2) AND NULL"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) and (NULL) or ("t"."b") = (2) and (NULL))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) and (?) or ("t"."b") = (?) and (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::Null,
+            Value::from(2_u64),
+            Value::Null,
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &set_dnf), expected);
@@ -51,10 +82,18 @@ fn dnf3() {
 fn dnf4() {
     let input = r#"SELECT "a" FROM "t"
     WHERE ("a" = 1 OR "b" = 2) AND true"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) and (true) or ("t"."b") = (2) and (true))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) and (?) or ("t"."b") = (?) and (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::Boolean(true),
+            Value::from(2_u64),
+            Value::Boolean(true),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &set_dnf), expected);
@@ -64,10 +103,18 @@ fn dnf4() {
 fn dnf5() {
     let input = r#"SELECT "a" FROM "t"
     WHERE ("a" = 1 OR "b" = 2) AND ((false))"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) and ((false)) or ("t"."b") = (2) and ((false)))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) and ((?)) or ("t"."b") = (?) and ((?)))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::Boolean(false),
+            Value::from(2_u64),
+            Value::Boolean(false),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &set_dnf), expected);
@@ -77,10 +124,13 @@ fn dnf5() {
 fn dnf6() {
     let input = r#"SELECT "a" FROM "t"
     WHERE "a" = 1 and "c" = 1 OR "b" = 2"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) and ("t"."c") = (1) or ("t"."b") = (2))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) and ("t"."c") = (?) or ("t"."b") = (?))"#,
+        ),
+        vec![Value::from(1_u64), Value::from(1_u64), Value::from(2_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &set_dnf), expected);
diff --git a/src/ir/transformation/equality_propagation/tests.rs b/src/ir/transformation/equality_propagation/tests.rs
index 013bcd142a..797712d576 100644
--- a/src/ir/transformation/equality_propagation/tests.rs
+++ b/src/ir/transformation/equality_propagation/tests.rs
@@ -1,4 +1,6 @@
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::ir::transformation::helpers::sql_to_sql;
+use crate::ir::value::Value;
 use crate::ir::Plan;
 use pretty_assertions::assert_eq;
 
@@ -11,11 +13,19 @@ fn equality_propagation1() {
     let input = r#"SELECT "a" FROM "t"
     WHERE "a" = 1 AND "b" = 2 AND "c" = 1 OR "d" = 1"#;
 
-    let expected = format!(
-        "{} {} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a") = (1) and ("t"."b") = (2) and ("t"."c") = (1)"#,
-        r#"and ("t"."c") = ("t"."a") or ("t"."d") = (1))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a") = (?) and ("t"."b") = (?) and ("t"."c") = (?)"#,
+            r#"and ("t"."c") = ("t"."a") or ("t"."d") = (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(2_u64),
+            Value::from(1_u64),
+            Value::from(1_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected);
@@ -26,9 +36,12 @@ fn equality_propagation2() {
     let input = r#"SELECT "a" FROM "t"
     WHERE "a" = NULL AND "b" = NULL"#;
 
-    let expected = format!(
-        "{}",
-        r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (NULL) and ("t"."b") = (NULL)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{}",
+            r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (?) and ("t"."b") = (?)"#,
+        ),
+        vec![Value::Null, Value::Null],
     );
 
     assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected);
@@ -39,10 +52,13 @@ fn equality_propagation3() {
     let input = r#"SELECT "a" FROM "t"
     WHERE "a" = 1 AND "b" = null AND "a" = null"#;
 
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE ("t"."a") = (1) and ("t"."b") = (NULL) and ("t"."a") = (NULL)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE ("t"."a") = (?) and ("t"."b") = (?) and ("t"."a") = (?)"#,
+        ),
+        vec![Value::from(1_u64), Value::Null, Value::Null],
     );
 
     assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected);
@@ -53,11 +69,19 @@ fn equality_propagation4() {
     let input = r#"SELECT "a" FROM "t"
     WHERE "a" = 1 AND "b" = null AND "a" = null AND "b" = 1"#;
 
-    let expected = format!(
-        "{} {} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE ("t"."a") = (1) and ("t"."b") = (NULL) and ("t"."a") = (NULL)"#,
-        r#"and ("t"."b") = (1) and ("t"."b") = ("t"."a")"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE ("t"."a") = (?) and ("t"."b") = (?) and ("t"."a") = (?)"#,
+            r#"and ("t"."b") = (?) and ("t"."b") = ("t"."a")"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::Null,
+            Value::Null,
+            Value::from(1_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected);
@@ -68,13 +92,21 @@ fn equality_propagation5() {
     let input = r#"SELECT "a" FROM "t"
     WHERE "a" = 1 AND "b" = 1 AND "c" = 1 AND "d" = 1"#;
 
-    let expected = format!(
-        "{} {} {} {} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE ("t"."a") = (1) and ("t"."b") = (1)"#,
-        r#"and ("t"."c") = (1) and ("t"."d") = (1)"#,
-        r#"and ("t"."c") = ("t"."b") and ("t"."b") = ("t"."a")"#,
-        r#"and ("t"."a") = ("t"."d")"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {} {} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE ("t"."a") = (?) and ("t"."b") = (?)"#,
+            r#"and ("t"."c") = (?) and ("t"."d") = (?)"#,
+            r#"and ("t"."c") = ("t"."b") and ("t"."b") = ("t"."a")"#,
+            r#"and ("t"."a") = ("t"."d")"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(1_u64),
+            Value::from(1_u64),
+            Value::from(1_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &derive_equalities), expected);
diff --git a/src/ir/transformation/helpers.rs b/src/ir/transformation/helpers.rs
index 6cd6305454..4d7a38caed 100644
--- a/src/ir/transformation/helpers.rs
+++ b/src/ir/transformation/helpers.rs
@@ -1,6 +1,7 @@
 //! IR test helpers.
 
 use crate::executor::bucket::Buckets;
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::executor::engine::mock::MetadataMock;
 use crate::executor::ir::ExecutionPlan;
 use crate::frontend::sql::ast::AbstractSyntaxTree;
@@ -18,9 +19,13 @@ pub fn sql_to_ir(query: &str, params: &[Value]) -> Plan {
     plan
 }
 
-/// Compiles and transforms an SQL query to a new SQL.
+/// Compiles and transforms an SQL query to a new parameterized SQL.
 #[allow(dead_code)]
-pub fn sql_to_sql(query: &str, params: &[Value], f_transform: &dyn Fn(&mut Plan)) -> String {
+pub fn sql_to_sql(
+    query: &str,
+    params: &[Value],
+    f_transform: &dyn Fn(&mut Plan),
+) -> PatternWithParams {
     let mut plan = sql_to_ir(query, params);
     f_transform(&mut plan);
     let ex_plan = ExecutionPlan::from(plan);
diff --git a/src/ir/transformation/merge_tuples/tests.rs b/src/ir/transformation/merge_tuples/tests.rs
index a3aa2415bc..479ea130fa 100644
--- a/src/ir/transformation/merge_tuples/tests.rs
+++ b/src/ir/transformation/merge_tuples/tests.rs
@@ -1,4 +1,6 @@
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::ir::transformation::helpers::sql_to_sql;
+use crate::ir::value::Value;
 use crate::ir::Plan;
 use pretty_assertions::assert_eq;
 
@@ -9,10 +11,18 @@ fn merge_tuples(plan: &mut Plan) {
 #[test]
 fn merge_tuples1() {
     let input = r#"SELECT "a" FROM "t" WHERE "a" = 1 and "b" = 2 and "c" < 3 and 4 < "a""#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE ("t"."a", "t"."b") = (1, 2) and (3, "t"."a") > ("t"."c", 4)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE ("t"."a", "t"."b") = (?, ?) and (?, "t"."a") > ("t"."c", ?)"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(2_u64),
+            Value::from(3_u64),
+            Value::from(4_u64),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected);
@@ -23,11 +33,21 @@ fn merge_tuples2() {
     let input = r#"SELECT "a" FROM "t"
         WHERE "a" = 1 and null and "b" = 2
         or true and "c" >= 3 and 4 <= "a""#;
-    let expected = format!(
-        "{} {} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#,
-        r#"WHERE (("t"."a", "t"."b") = (1, 2) and (NULL)"#,
-        r#"or ("t"."c", "t"."a") >= (3, 4) and (true))"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#,
+            r#"WHERE (("t"."a", "t"."b") = (?, ?) and (?)"#,
+            r#"or ("t"."c", "t"."a") >= (?, ?) and (?))"#,
+        ),
+        vec![
+            Value::from(1_u64),
+            Value::from(2_u64),
+            Value::Null,
+            Value::from(3_u64),
+            Value::from(4_u64),
+            Value::Boolean(true),
+        ],
     );
 
     assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected);
@@ -36,7 +56,10 @@ fn merge_tuples2() {
 #[test]
 fn merge_tuples3() {
     let input = r#"SELECT "a" FROM "t" WHERE true"#;
-    let expected = format!("{}", r#"SELECT "t"."a" as "a" FROM "t" WHERE true"#);
+    let expected = PatternWithParams::new(
+        format!("{}", r#"SELECT "t"."a" as "a" FROM "t" WHERE ?"#),
+        vec![Value::Boolean(true)],
+    );
 
     assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected);
 }
@@ -44,9 +67,12 @@ fn merge_tuples3() {
 #[test]
 fn merge_tuples4() {
     let input = r#"SELECT "a" FROM "t" WHERE ("a", "b") = (1, 2) and 3 = "c""#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."a", "t"."b", "t"."c") = (1, 2, 3)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."a", "t"."b", "t"."c") = (?, ?, ?)"#,
+        ),
+        vec![Value::from(1_u64), Value::from(2_u64), Value::from(3_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected);
@@ -55,9 +81,12 @@ fn merge_tuples4() {
 #[test]
 fn merge_tuples5() {
     let input = r#"SELECT "a" FROM "t" WHERE 3 < "c" and ("a", "b") > (1, 2)"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."c", "t"."a", "t"."b") > (3, 1, 2)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t""#, r#"WHERE ("t"."c", "t"."a", "t"."b") > (?, ?, ?)"#,
+        ),
+        vec![Value::from(3_u64), Value::from(1_u64), Value::from(2_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &merge_tuples), expected);
diff --git a/src/ir/transformation/split_columns/tests.rs b/src/ir/transformation/split_columns/tests.rs
index 77d587366c..2dfb5517d3 100644
--- a/src/ir/transformation/split_columns/tests.rs
+++ b/src/ir/transformation/split_columns/tests.rs
@@ -1,7 +1,9 @@
+use crate::executor::engine::cartridge::backend::sql::ir::PatternWithParams;
 use crate::executor::engine::mock::MetadataMock;
 use crate::frontend::sql::ast::AbstractSyntaxTree;
 use crate::frontend::Ast;
 use crate::ir::transformation::helpers::sql_to_sql;
+use crate::ir::value::Value;
 use crate::ir::Plan;
 use pretty_assertions::assert_eq;
 
@@ -12,9 +14,12 @@ fn split_columns(plan: &mut Plan) {
 #[test]
 fn split_columns1() {
     let input = r#"SELECT "a" FROM "t" WHERE ("a", 2) = (1, "b")"#;
-    let expected = format!(
-        "{}",
-        r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (1) and (2) = ("t"."b")"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{}",
+            r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (?) and (?) = ("t"."b")"#,
+        ),
+        vec![Value::from(1_u64), Value::from(2_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &split_columns), expected);
@@ -23,9 +28,12 @@ fn split_columns1() {
 #[test]
 fn split_columns2() {
     let input = r#"SELECT "a" FROM "t" WHERE "a" = 1"#;
-    let expected = format!(
-        "{}",
-        r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (1)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{}",
+            r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") = (?)"#,
+        ),
+        vec![Value::from(1_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &split_columns), expected);
@@ -54,9 +62,12 @@ fn split_columns3() {
 #[test]
 fn split_columns4() {
     let input = r#"SELECT "a" FROM "t" WHERE "a" in (1, 2)"#;
-    let expected = format!(
-        "{}",
-        r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") in (1, 2)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{}",
+            r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") in (?, ?)"#,
+        ),
+        vec![Value::from(1_u64), Value::from(2_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &split_columns), expected);
@@ -65,10 +76,13 @@ fn split_columns4() {
 #[test]
 fn split_columns5() {
     let input = r#"SELECT "a" FROM "t" WHERE ("a", 2) < (1, "b") and "a" > 2"#;
-    let expected = format!(
-        "{} {}",
-        r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") < (1) and (2) < ("t"."b")"#,
-        r#"and ("t"."a") > (2)"#,
+    let expected = PatternWithParams::new(
+        format!(
+            "{} {}",
+            r#"SELECT "t"."a" as "a" FROM "t" WHERE ("t"."a") < (?) and (?) < ("t"."b")"#,
+            r#"and ("t"."a") > (?)"#,
+        ),
+        vec![Value::from(1_u64), Value::from(2_u64), Value::from(2_u64)],
     );
 
     assert_eq!(sql_to_sql(input, &[], &split_columns), expected);
diff --git a/src/ir/value.rs b/src/ir/value.rs
index d708848f1c..9264eccbe0 100644
--- a/src/ir/value.rs
+++ b/src/ir/value.rs
@@ -63,8 +63,8 @@ impl fmt::Display for Value {
             Value::Null => write!(f, "NULL"),
             Value::Unsigned(v) => write!(f, "{}", v),
             Value::Integer(v) => write!(f, "{}", v),
-            Value::Double(v) => write!(f, "{}", v),
-            Value::Decimal(v) => write!(f, "{}", v),
+            Value::Double(v) => fmt::Display::fmt(&v, f),
+            Value::Decimal(v) => fmt::Display::fmt(v, f),
             Value::String(v) => write!(f, "'{}'", v),
         }
     }
@@ -337,6 +337,22 @@ impl From<Value> for String {
     }
 }
 
+impl<L: tlua::AsLua> tlua::Push<L> for Value {
+    type Err = tlua::Void;
+
+    fn push_to_lua(&self, lua: L) -> Result<tlua::PushGuard<L>, (Self::Err, L)> {
+        match self {
+            Value::Boolean(v) => v.push_to_lua(lua),
+            Value::Integer(v) => v.push_to_lua(lua),
+            Value::Decimal(v) => v.push_to_lua(lua),
+            Value::Double(v) => v.push_to_lua(lua),
+            Value::Unsigned(v) => v.push_to_lua(lua),
+            Value::String(v) => v.push_to_lua(lua),
+            Value::Null => tlua::Null.push_to_lua(lua),
+        }
+    }
+}
+
 impl<L> tlua::PushInto<L> for Value
 where
     L: tlua::AsLua,
diff --git a/src/ir/value/double.rs b/src/ir/value/double.rs
index bc1e554044..bf639848fb 100644
--- a/src/ir/value/double.rs
+++ b/src/ir/value/double.rs
@@ -62,6 +62,14 @@ impl FromStr for Double {
     }
 }
 
+impl<L: tlua::AsLua> tlua::Push<L> for Double {
+    type Err = tlua::Void;
+
+    fn push_to_lua(&self, lua: L) -> Result<tlua::PushGuard<L>, (Self::Err, L)> {
+        self.value.push_to_lua(lua)
+    }
+}
+
 impl<L> tlua::PushInto<L> for Double
 where
     L: tlua::AsLua,
@@ -71,7 +79,9 @@ where
         Ok(tlua::push_userdata(self.value, lua, |_| {}))
     }
 }
+
 impl<L> tlua::PushOneInto<L> for Double where L: tlua::AsLua {}
+
 impl<L> tlua::LuaRead<L> for Double
 where
     L: tlua::AsLua,
diff --git a/test_app/test/integration/api_test.lua b/test_app/test/integration/api_test.lua
index a4168fd247..9f25efd66e 100644
--- a/test_app/test/integration/api_test.lua
+++ b/test_app/test/integration/api_test.lua
@@ -63,14 +63,14 @@ g.test_bucket_id_calculation = function()
     t.assert_equals(r, 360)
 
     r, err = api:call("calculate_bucket_id_by_dict", { "testing_space", { id = 1 }})
-    t.assert_equals(err, "CustomError(\"The dict of args missed key/value to calculate bucket_id. Column: name\")")
+    t.assert_str_contains(tostring(err), [[dict of args missed key/value to calculate bucket_id]])
 end
 
 g.test_incorrect_query = function()
     local api = cluster:server("api-1").net_box
 
     local _, err = api:call("query", { [[SELECT * FROM "testing_space" INNER JOIN "testing_space"]], {} })
-    t.assert_equals(err, "CustomError(\"Parsing error: Error { variant: ParsingError { positives: [SubQuery], negatives: [] }, location: Pos(41), line_col: Pos((1, 42)), path: None, line: \\\"SELECT * FROM \\\\\\\"testing_space\\\\\\\" INNER JOIN \\\\\\\"testing_space\\\\\\\"\\\", continued_line: None }\")")
+    t.assert_str_contains(tostring(err), "Parsing error")
 end
 
 g.test_join_query_is_valid = function()
@@ -278,7 +278,7 @@ g.test_join_motion_query = function()
         metadata = {
             {name = "id", type = "integer"},
             {name = "name", type = "string"},
-            {name = "product_units", type = "integer"},
+            {name = "product_units", type = "any"},
         },
         rows = {
             { 1, "ok", 5 },
@@ -446,7 +446,7 @@ g.test_invalid_explain = function()
     local _, err = api:call("explain", { [[SELECT "id", "name" FROM "testing_space"
     WHERE "id" in (SELECT "id" FROM "space_simple_shard_key_hist" WHERE "sysOp" < 0)]] })
 
-    t.assert_equals(err, "Explain hasn't supported node Motion { children: [43], policy: Full, generation: None, output: 81 } yet")
+    t.assert_str_contains(tostring(err), "Explain hasn't supported node Motion")
 end
 
 g.test_valid_explain = function()
diff --git a/test_app/test/integration/target_queries_test.lua b/test_app/test/integration/target_queries_test.lua
index 76c6093304..19fce96154 100644
--- a/test_app/test/integration/target_queries_test.lua
+++ b/test_app/test/integration/target_queries_test.lua
@@ -534,9 +534,9 @@ WHERE "t3"."col1" = 1 AND "t8"."cola" = 1]], {} })
         { name = "col1", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -579,9 +579,9 @@ WHERE "t3"."col1" = ? AND ("t8"."cola" = ?
         { name = "col1", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -620,9 +620,9 @@ AND ("t8"."cola" = ? AND "t8"."colb" = ?)]], { 0, 0, 0, 0, 0, 0, 1, 2, 1, 2 } })
         { name = "col2", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -662,9 +662,9 @@ AND ("t8"."cola" = 1 AND ("t8"."colb" = 2 AND "t3"."amount" > 0))]], {} })
         { name = "col2", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -702,9 +702,9 @@ WHERE "t3"."col1" = ? AND "t8"."cola" = ?]], { 1, 2 } })
         { name = "col1", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -743,9 +743,9 @@ WHERE "t3"."col1" = ? AND "t3"."col2" = 2 AND ("t8"."cola" = 1 AND "t8"."colb" =
         { name = "col2", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -783,9 +783,9 @@ WHERE "t3"."col1" = 1 AND ("t3"."col2" = 1 AND "t8"."colb" = 2)]], {} })
         { name = "col2", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
@@ -822,9 +822,9 @@ WHERE "t3"."col1" = 1 AND "t3"."col2" = 1]], {} })
         { name = "col2", type = "integer" },
         { name = "account_id", type = "integer" },
         { name = "amount", type = "integer" },
-        { name = "id", type = "integer" },
-        { name = "cola", type = "integer" },
-        { name = "colb", type = "integer" },
+        { name = "id", type = "any" },
+        { name = "cola", type = "any" },
+        { name = "colb", type = "any" },
     })
 
     t.assert_items_equals(r.rows, {
diff --git a/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml b/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml
index 2784125792..8787c2e238 100644
--- a/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml
+++ b/tests/artifactory/backend/sql/tree/sql_order_selection_syntax_nodes.yaml
@@ -13,7 +13,7 @@
 #     - = 15
 #     - row 14
 #       - ( 12
-#       - const_1 11
+#       - parameter 11
 #       - ) 13
 ---
 arena:
@@ -77,7 +77,7 @@ arena:
       - 9
 #11
   - data:
-      PlanId: 6
+      Parameter: 6
     left: ~
     right: []
 #12
-- 
GitLab