diff --git a/sbroad/sbroad-cartridge/src/cartridge/config.rs b/sbroad/sbroad-cartridge/src/cartridge/config.rs
index 23b80e2a587bccec9c02179f65993210d794af92..44acd34476f09e3d92cde18b5f9e8231fb45b6d9 100644
--- a/sbroad/sbroad-cartridge/src/cartridge/config.rs
+++ b/sbroad/sbroad-cartridge/src/cartridge/config.rs
@@ -11,6 +11,7 @@ use sbroad::executor::engine::helpers::normalize_name_from_sql;
 use sbroad::executor::engine::{get_builtin_functions, Metadata};
 use sbroad::executor::lru::DEFAULT_CAPACITY;
 use sbroad::ir::function::Function;
+use sbroad::ir::relation::DerivedType;
 use sbroad::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type};
 use sbroad::{debug, warn};
 
@@ -138,7 +139,7 @@ impl RouterConfiguration {
                         } else {
                             ColumnRole::User
                         };
-                        let col = Column::new(name, t, role, is_nullable);
+                        let col = Column::new(name, DerivedType::new(t), role, is_nullable);
                         result.push(col);
                     }
                     result
diff --git a/sbroad/sbroad-cartridge/src/cartridge/config/tests.rs b/sbroad/sbroad-cartridge/src/cartridge/config/tests.rs
index ba4c6c8294d0e89801b83506ae88e0b7c45eae46..3f7149f4f807a95ab6323a6aae2dfb03bdec8966 100644
--- a/sbroad/sbroad-cartridge/src/cartridge/config/tests.rs
+++ b/sbroad/sbroad-cartridge/src/cartridge/config/tests.rs
@@ -1,5 +1,6 @@
 use super::*;
 use pretty_assertions::assert_eq;
+use sbroad::ir::relation::DerivedType;
 
 #[test]
 fn test_yaml_schema_parser() {
@@ -145,15 +146,40 @@ fn test_getting_table_segment() {
         vec![
             Column::new(
                 "identification_number",
-                Type::Integer,
+                DerivedType::new(Type::Integer),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("product_code", Type::String, ColumnRole::User, false),
-            Column::new("product_units", Type::Boolean, ColumnRole::User, false),
-            Column::new("sys_op", Type::Integer, ColumnRole::User, false),
-            Column::new("detail", Type::Array, ColumnRole::User, false),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "product_code",
+                DerivedType::new(Type::String),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "product_units",
+                DerivedType::new(Type::Boolean),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "sys_op",
+                DerivedType::new(Type::Integer),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "detail",
+                DerivedType::new(Type::Array),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ],
         &["identification_number", "product_code"],
         &["identification_number"],
diff --git a/sbroad/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua b/sbroad/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua
index 98fd6fd6fc106a74ca9e3cec936edeb8fc045ef5..23cdfffd76e9225d90ace635c733b4bf7cc47d3a 100644
--- a/sbroad/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua
+++ b/sbroad/sbroad-cartridge/test_app/test/integration/arbitrary_expressions_test.lua
@@ -60,6 +60,16 @@ arbitrary_projection.test_arbitrary_invalid = function()
         select "a" + "b" and true from "arithmetic_space"
     ]], {} })
     t.assert_str_contains(tostring(err), "Type mismatch: can not convert integer(3) to boolean")
+
+    local _, err = api:call("sbroad.execute", { [[
+        SELECT
+            CASE "id"
+                WHEN 1 THEN 'first'
+                ELSE 42
+            END "case_result"
+        FROM "arithmetic_space"
+    ]], {} })
+    t.assert_str_contains(tostring(err), "expected string type, but got unsigned")
 end
 
 arbitrary_projection.test_arbitrary_valid = function()
@@ -196,16 +206,16 @@ arbitrary_projection.test_arbitrary_valid = function()
             CASE "id"
                 WHEN 1 THEN 'first'
                 WHEN 2 THEN 'second'
-                ELSE 42
+                ELSE '42'
             END "case_result"
         FROM "arithmetic_space"
     ]], {} })
     t.assert_equals(err, nil)
     t.assert_equals(r.metadata, {
-        {name = "case_result", type = "integer"},
+        {name = "case_result", type = "string"},
     })
     t.assert_items_equals(r.rows, {
-        {'first'}, {42}, {42}, {42}, {42}, {'second'}, {42}, {42}, {42}, {42}
+        {'first'}, {'42'}, {'42'}, {'42'}, {'42'}, {'second'}, {'42'}, {'42'}, {'42'}, {'42'}
     })
 
     local r, err = api:call("sbroad.execute", { [[
@@ -281,14 +291,14 @@ arbitrary_projection.test_arbitrary_valid = function()
         SELECT
             "id",
             CASE
-                WHEN false THEN 'never'
+                WHEN false THEN 0
                 WHEN "id" < 3 THEN 1
                 WHEN "id" > 3 AND "id" < 8 THEN 2
                 ELSE
                     CASE
                         WHEN "id" = 8 THEN 3
                         WHEN "id" = 9 THEN 4
-                        ELSE 0.42
+                        ELSE 0
                     END
             END
         FROM "arithmetic_space"
@@ -296,18 +306,70 @@ arbitrary_projection.test_arbitrary_valid = function()
     t.assert_equals(err, nil)
     t.assert_equals(r.metadata, {
         {name = "id", type = "integer"},
-        {name = "col_1", type = "double"},
+        {name = "col_1", type = "unsigned"},
     })
     t.assert_items_equals(r.rows, {
         {1, 1},
         {5, 2},
         {8, 3},
         {9, 4},
-        {10, 0.42},
+        {10, 0},
         {2, 1},
-        {3, 0.42},
+        {3, 0},
         {4, 2},
         {6, 2},
         {7, 2},
     })
 end
+
+arbitrary_projection.test_values = function()
+    local api = cluster:server("api-1").net_box
+
+    local r, err = api:call("sbroad.execute", { [[VALUES (?, ?, ?), (?, ?, ?)]], { 8, 8, box.NULL, 9, 9, 'hello' } })
+    t.assert_equals(err, nil)
+    t.assert_items_equals(r["metadata"], {
+        {name = "COLUMN_4", type = "integer"},
+        {name = "COLUMN_5", type = "integer"},
+        {name = "COLUMN_6", type = "string"},
+    })
+    t.assert_items_equals(r["rows"], { { 8, 8, box.NULL }, { 9, 9, 'hello' } })
+
+    local r, err = api:call("sbroad.execute", { [[VALUES (?, ?, ?), (?, ?, ?)]], { 8, 8, box.NULL, 9, 9, 'hello' } })
+    t.assert_equals(err, nil)
+    t.assert_items_equals(r["metadata"], {
+        {name = "COLUMN_4", type = "integer"},
+        {name = "COLUMN_5", type = "integer"},
+        {name = "COLUMN_6", type = "string"},
+    })
+    t.assert_items_equals(r["rows"], { { 8, 8, box.NULL }, { 9, 9, 'hello' } })
+
+    r, err = api:call(
+        "sbroad.execute",
+        { [[VALUES (?, ?, ?), (?, ?, ?)]], { 9, 9, 'hello', 8, 8, box.NULL } }
+    )
+    t.assert_equals(err, nil)
+    t.assert_items_equals(r["metadata"], {
+        {name = "COLUMN_4", type = "integer"},
+        {name = "COLUMN_5", type = "integer"},
+        {name = "COLUMN_6", type = "string"},
+    })
+    t.assert_items_equals(r["rows"], { { 9, 9, 'hello' }, { 8, 8, box.NULL } })
+
+    r, err = api:call("sbroad.execute", { [[VALUES (8, 8, null), (9, 9, 'hello')]], {} })
+    t.assert_equals(err, nil)
+    t.assert_items_equals(r["metadata"], {
+        {name = "COLUMN_4", type = "unsigned"},
+        {name = "COLUMN_5", type = "unsigned"},
+        {name = "COLUMN_6", type = "string"},
+    })
+    t.assert_items_equals(r["rows"], { { 8, 8, box.NULL }, { 9, 9, 'hello' } })
+
+    r, err = api:call("sbroad.execute", { [[VALUES (9, 9, 'hello'), (8, 8, null)]], {} })
+    t.assert_equals(err, nil)
+    t.assert_items_equals(r["metadata"], {
+        {name = "COLUMN_4", type = "unsigned"},
+        {name = "COLUMN_5", type = "unsigned"},
+        {name = "COLUMN_6", type = "string"},
+    })
+    t.assert_items_equals(r["rows"], { { 9, 9, 'hello' }, { 8, 8, box.NULL } })
+end
\ No newline at end of file
diff --git a/sbroad/sbroad-cartridge/test_app/test/integration/insert_test.lua b/sbroad/sbroad-cartridge/test_app/test/integration/insert_test.lua
index d2d2bb149c008df3373f70c1b0f75adf0fe0a850..c4fc74fd30e57f8fdae9ac4f64c87e3107d2c7a1 100644
--- a/sbroad/sbroad-cartridge/test_app/test/integration/insert_test.lua
+++ b/sbroad/sbroad-cartridge/test_app/test/integration/insert_test.lua
@@ -235,51 +235,10 @@ g.test_insert_6 = function()
     })
 end
 
--- TODO(ars): this test fails. What also fails is a simple query like: values (1, 'hello')...
--- check type derivation for null column in the first row of the VALUES operator
 g.test_insert_7 = function()
     local api = cluster:server("api-1").net_box
 
-    local r, err = api:call("sbroad.execute", { [[VALUES (?, ?, ?), (?, ?, ?)]], { 8, 8, box.NULL, 9, 9, 'hello' } })
-    t.assert_equals(err, nil)
-    t.assert_items_equals(r["metadata"], {
-        {name = "COLUMN_4", type = "integer"},
-        {name = "COLUMN_5", type = "integer"},
-        {name = "COLUMN_6", type = "integer"},
-    })
-    t.assert_items_equals(r["rows"], { { 8, 8, box.NULL }, { 9, 9, 'hello' } })
-
-    r, err = api:call(
-        "sbroad.execute",
-        { [[VALUES (?, ?, ?), (?, ?, ?)]], { 9, 9, 'hello', 8, 8, box.NULL } }
-    )
-    t.assert_equals(err, nil)
-    t.assert_items_equals(r["metadata"], {
-        {name = "COLUMN_4", type = "integer"},
-        {name = "COLUMN_5", type = "integer"},
-        {name = "COLUMN_6", type = "integer"},
-    })
-    t.assert_items_equals(r["rows"], { { 9, 9, 'hello' }, { 8, 8, box.NULL } })
-
-    r, err = api:call("sbroad.execute", { [[VALUES (8, 8, null), (9, 9, 'hello')]], {} })
-    t.assert_equals(err, nil)
-    t.assert_items_equals(r["metadata"], {
-        {name = "COLUMN_4", type = "unsigned"},
-        {name = "COLUMN_5", type = "unsigned"},
-        {name = "COLUMN_6", type = "string"},
-    })
-    t.assert_items_equals(r["rows"], { { 8, 8, box.NULL }, { 9, 9, 'hello' } })
-
-    r, err = api:call("sbroad.execute", { [[VALUES (9, 9, 'hello'), (8, 8, null)]], {} })
-    t.assert_equals(err, nil)
-    t.assert_items_equals(r["metadata"], {
-        {name = "COLUMN_4", type = "unsigned"},
-        {name = "COLUMN_5", type = "unsigned"},
-        {name = "COLUMN_6", type = "integer"},
-    })
-    t.assert_items_equals(r["rows"], { { 9, 9, 'hello' }, { 8, 8, box.NULL } })
-
-    r, err = api:call("sbroad.execute", { [[INSERT INTO "space_simple_shard_key"
+    local r, err = api:call("sbroad.execute", { [[INSERT INTO "space_simple_shard_key"
     ("sysOp", "id", "name") VALUES (?, ?, ?), (?, ?, ?)]], { 8, 8, box.NULL, 9, 9, 'hello' } })
     t.assert_equals(err, nil)
     t.assert_equals(r, {row_count = 2})
diff --git a/sbroad/sbroad-core/src/backend/sql/ir/tests/selection.rs b/sbroad/sbroad-core/src/backend/sql/ir/tests/selection.rs
index 7e1b97e2c9082fcfb770d0259626b48940f9f206..9b34bb870fae1328853087b2cee220ad130c5712 100644
--- a/sbroad/sbroad-core/src/backend/sql/ir/tests/selection.rs
+++ b/sbroad/sbroad-core/src/backend/sql/ir/tests/selection.rs
@@ -1,4 +1,5 @@
 use super::*;
+use crate::executor::tests::f_sql;
 use crate::ir::tree::Snapshot;
 use crate::ir::value::Value;
 
@@ -65,14 +66,21 @@ fn selection2_latest() {
         AND ("product_units" <> "sys_op" OR "product_units" IS NULL)"#;
 
     let expected = PatternWithParams::new(
-        [
-            r#"SELECT "hash_testing"."product_code" FROM "hash_testing""#,
-            r#"WHERE ("hash_testing"."product_units", "hash_testing"."product_units", "hash_testing"."identification_number") = ("hash_testing"."identification_number", ?, ?)"#,
-            r#"and ("hash_testing"."product_units") <> ("hash_testing"."sys_op")"#,
-            r#"or ("hash_testing"."product_units", "hash_testing"."product_units", "hash_testing"."identification_number") = ("hash_testing"."identification_number", ?, ?)"#,
-            r#"and ("hash_testing"."product_units") is null"#
-        ].join(" "),
-        vec![Value::Unsigned(1), Value::Unsigned(1), Value::Unsigned(1), Value::Unsigned(1)],
+        f_sql(
+            r#"SELECT "hash_testing"."product_code" FROM "hash_testing"
+WHERE ("hash_testing"."identification_number", "hash_testing"."product_units", "hash_testing"."identification_number") =
+("hash_testing"."product_units", ?, ?)
+and ("hash_testing"."product_units") <> ("hash_testing"."sys_op")
+or ("hash_testing"."identification_number", "hash_testing"."product_units", "hash_testing"."identification_number")
+= ("hash_testing"."product_units", ?, ?)
+and ("hash_testing"."product_units") is null"#,
+        ),
+        vec![
+            Value::Unsigned(1),
+            Value::Unsigned(1),
+            Value::Unsigned(1),
+            Value::Unsigned(1),
+        ],
     );
     check_sql_with_snapshot(query, vec![], expected, Snapshot::Latest);
 }
diff --git a/sbroad/sbroad-core/src/backend/sql/tree.rs b/sbroad/sbroad-core/src/backend/sql/tree.rs
index ca934e274201eae12540ff99c919b04387b1d8ba..c0306e0c75a89c19ac9ea2d8a890b628ad622060 100644
--- a/sbroad/sbroad-core/src/backend/sql/tree.rs
+++ b/sbroad/sbroad-core/src/backend/sql/tree.rs
@@ -1010,7 +1010,13 @@ impl<'p> SyntaxPlan<'p> {
                     let Expression::Reference(Reference { col_type, .. }) = ref_expr else {
                         panic!("expected Reference under Alias in Motion output");
                     };
-                    select_columns.push(format_smolstr!("cast(null as {col_type})"));
+                    let casted_null_str = if let Some(col_type) = col_type.get() {
+                        format_smolstr!("cast(null as {col_type})")
+                    } else {
+                        // No need to cast as there are no type.
+                        SmolStr::from("null")
+                    };
+                    select_columns.push(casted_null_str);
                 }
                 let empty_select =
                     format_smolstr!("select {} where false", select_columns.join(","));
diff --git a/sbroad/sbroad-core/src/cbo/selectivity.rs b/sbroad/sbroad-core/src/cbo/selectivity.rs
index bc031401054b2df4bb6e5cf8eef68bac69f85725..f6cc2ba748181a3a32b0534d84ec0488e6c72fdc 100644
--- a/sbroad/sbroad-core/src/cbo/selectivity.rs
+++ b/sbroad/sbroad-core/src/cbo/selectivity.rs
@@ -264,6 +264,10 @@ pub fn calculate_filter_selectivity(
         )),
     ));
 
+    let Some(column_type) = column_type.get() else {
+        return types_mismatch_error;
+    };
+
     match column_type {
         Type::Boolean => match constant {
             Value::Boolean(b) => {
@@ -406,8 +410,8 @@ pub fn calculate_condition_selectivity(
         )),
     ));
 
-    match (left_column_type, right_column_type) {
-        (Type::Boolean, Type::Boolean) => {
+    match (left_column_type.get(), right_column_type.get()) {
+        (Some(Type::Boolean), Some(Type::Boolean)) => {
             let left_downcasted_stats =
                 downcast_column_stats::<bool>(&left_column_stats, left_column)?;
             let right_downcasted_stats =
diff --git a/sbroad/sbroad-core/src/executor.rs b/sbroad/sbroad-core/src/executor.rs
index 039a197479a666023fd6a64fbb0e3243f7ac5a4a..7520a2a1adb3f8eaeca76fa19e92c72dfbdbedab 100644
--- a/sbroad/sbroad-core/src/executor.rs
+++ b/sbroad/sbroad-core/src/executor.rs
@@ -427,4 +427,4 @@ where
 }
 
 #[cfg(test)]
-mod tests;
+pub mod tests;
diff --git a/sbroad/sbroad-core/src/executor/engine.rs b/sbroad/sbroad-core/src/executor/engine.rs
index f75e0f0bf4a28198ffc12473f5432b9802189f38..e1faafc98fc8718e4c5f7c7b745e1a7184ae33cf 100644
--- a/sbroad/sbroad-core/src/executor/engine.rs
+++ b/sbroad/sbroad-core/src/executor/engine.rs
@@ -22,8 +22,8 @@ use crate::executor::ir::ExecutionPlan;
 use crate::executor::protocol::SchemaInfo;
 use crate::executor::vtable::VirtualTable;
 use crate::ir::function::Function;
-use crate::ir::relation::Table;
 use crate::ir::relation::Type;
+use crate::ir::relation::{DerivedType, Table};
 use crate::ir::value::Value;
 
 use super::result::ProducerResult;
@@ -78,12 +78,12 @@ pub fn get_builtin_functions() -> &'static [Function] {
     unsafe {
         BUILTINS.get_or_init(|| {
             vec![
-                Function::new_stable("to_date".into(), Type::Datetime, false),
-                Function::new_stable("to_char".into(), Type::String, false),
-                Function::new_stable("substr".into(), Type::String, true),
-                Function::new_stable("lower".into(), Type::String, true),
-                Function::new_stable("upper".into(), Type::String, true),
-                Function::new_stable("coalesce".into(), Type::Any, true),
+                Function::new_stable("to_date".into(), DerivedType::new(Type::Datetime), false),
+                Function::new_stable("to_char".into(), DerivedType::new(Type::String), false),
+                Function::new_stable("substr".into(), DerivedType::new(Type::String), true),
+                Function::new_stable("lower".into(), DerivedType::new(Type::String), true),
+                Function::new_stable("upper".into(), DerivedType::new(Type::String), true),
+                Function::new_stable("coalesce".into(), DerivedType::new(Type::Any), true),
             ]
         })
     }
diff --git a/sbroad/sbroad-core/src/executor/engine/helpers.rs b/sbroad/sbroad-core/src/executor/engine/helpers.rs
index 5c08b36e81eabc66991e222252603610588a9c77..4e9ef8bc3c4b6b325505e481eceb092fedb2019f 100644
--- a/sbroad/sbroad-core/src/executor/engine/helpers.rs
+++ b/sbroad/sbroad-core/src/executor/engine/helpers.rs
@@ -3,9 +3,12 @@ use ahash::AHashMap;
 use crate::{
     error,
     executor::vtable::vtable_indexed_column_name,
-    ir::node::{
-        expression::Expression, relational::Relational, Alias, Constant, Limit, Motion, NodeId,
-        Update, Values, ValuesRow,
+    ir::{
+        node::{
+            expression::Expression, relational::Relational, Alias, Constant, Limit, Motion, NodeId,
+            Update, Values, ValuesRow,
+        },
+        relation::DerivedType,
     },
     utils::MutexLike,
 };
@@ -45,10 +48,10 @@ use crate::{
         ir::{ExecutionPlan, QueryType},
         protocol::{Binary, EncodedOptionalData, OptionalData, RequiredData},
         result::{ConsumerResult, MetadataColumn, ProducerResult},
-        vtable::{calculate_vtable_unified_types, VTableTuple, VirtualTable},
+        vtable::{calculate_unified_types, VTableTuple, VirtualTable},
     },
     ir::{
-        relation::{Column, ColumnRole, Type},
+        relation::{Column, ColumnRole},
         transformation::redistribution::{MotionKey, MotionPolicy},
         tree::Snapshot,
         value::Value,
@@ -429,7 +432,7 @@ pub enum TupleBuilderCommand {
     TakePosition(usize),
     /// Take a value from the original tuple and cast
     /// it into specified type.
-    TakeAndCastPosition(usize, Type),
+    TakeAndCastPosition(usize, DerivedType),
     /// Set a specified value.
     /// Related only to the tuple we are currently constructing and not to the original tuple.
     SetValue(Value),
@@ -442,7 +445,7 @@ pub enum TupleBuilderCommand {
     /// Update table column to the value in original tuple on specified position and cast it
     /// into specifeid type.
     /// Needed only for `Update`.
-    UpdateColToCastedPos(usize, usize, Type),
+    UpdateColToCastedPos(usize, usize, DerivedType),
 }
 
 /// Vec of commands that helps us transforming `VTableTuple` into a tuple suitable to be passed
@@ -1046,7 +1049,8 @@ pub fn materialize_values(
             .as_virtual_table(columns)?
     };
 
-    let unified_types = calculate_vtable_unified_types(&vtable)?;
+    let vtable_types = vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types)?;
     vtable.cast_values(&unified_types)?;
 
     let _ = exec_plan.get_mut_ir_plan().replace_with_stub(values_id);
diff --git a/sbroad/sbroad-core/src/executor/engine/mock.rs b/sbroad/sbroad-core/src/executor/engine/mock.rs
index 96cd81f6982f5b4fd2addf077bfef7ffb5a2035a..f40d6b1f2fb377cb82c6c32645a444d4488a3c29 100644
--- a/sbroad/sbroad-core/src/executor/engine/mock.rs
+++ b/sbroad/sbroad-core/src/executor/engine/mock.rs
@@ -32,6 +32,7 @@ use crate::executor::Cache;
 use crate::frontend::sql::ast::AbstractSyntaxTree;
 use crate::ir::function::Function;
 use crate::ir::node::NodeId;
+use crate::ir::relation::DerivedType;
 use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type};
 use crate::ir::tree::Snapshot;
 use crate::ir::value::{LuaValue, Value};
@@ -106,9 +107,11 @@ impl RouterConfigurationMock {
     #[must_use]
     pub fn new() -> Self {
         let name_func = normalize_name_from_sql("func");
-        let fn_func = Function::new_stable(name_func.clone(), Type::Integer, false);
+        let fn_func =
+            Function::new_stable(name_func.clone(), DerivedType::new(Type::Integer), false);
         let name_trim = normalize_name_from_sql("trim");
-        let trim_func = Function::new_stable(name_trim.clone(), Type::String, false);
+        let trim_func =
+            Function::new_stable(name_trim.clone(), DerivedType::new(Type::String), false);
         let mut functions = HashMap::new();
         functions.insert(name_func, fn_func);
         functions.insert(name_trim, trim_func);
@@ -121,14 +124,34 @@ impl RouterConfigurationMock {
         let columns = vec![
             Column::new(
                 "identification_number",
-                Type::Integer,
+                DerivedType::new(Type::Integer),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("product_code", Type::String, ColumnRole::User, false),
-            Column::new("product_units", Type::Boolean, ColumnRole::User, true),
-            Column::new("sys_op", Type::Unsigned, ColumnRole::User, true),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "product_code",
+                DerivedType::new(Type::String),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "product_units",
+                DerivedType::new(Type::Boolean),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "sys_op",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key = &["identification_number", "product_code"];
         let primary_key = &["product_code", "identification_number"];
@@ -182,11 +205,36 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("id", Type::Unsigned, ColumnRole::User, false),
-            Column::new("sysFrom", Type::Unsigned, ColumnRole::User, true),
-            Column::new("FIRST_NAME", Type::String, ColumnRole::User, true),
-            Column::new("sys_op", Type::Unsigned, ColumnRole::User, true),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "sysFrom",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "FIRST_NAME",
+                DerivedType::new(Type::String),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "sys_op",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key = &["id"];
         let primary_key = &["id"];
@@ -216,8 +264,18 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("id", Type::Unsigned, ColumnRole::User, false),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key: &[&str] = &["id"];
         let primary_key: &[&str] = &["id"];
@@ -234,9 +292,24 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("A", Type::Unsigned, ColumnRole::User, true),
-            Column::new("B", Type::Unsigned, ColumnRole::User, false),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "A",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "B",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key: &[&str] = &["A", "B"];
         let primary_key: &[&str] = &["B"];
@@ -253,11 +326,36 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("a", Type::Unsigned, ColumnRole::User, true),
-            Column::new("b", Type::Unsigned, ColumnRole::User, false),
-            Column::new("c", Type::Unsigned, ColumnRole::User, true),
-            Column::new("d", Type::Unsigned, ColumnRole::User, true),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "a",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "b",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "c",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "d",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                true,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key: &[&str] = &["a", "b"];
         let primary_key: &[&str] = &["b"];
@@ -268,9 +366,19 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("a", Type::String, ColumnRole::User, false),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
-            Column::new("b", Type::Integer, ColumnRole::User, false),
+            Column::new("a", DerivedType::new(Type::String), ColumnRole::User, false),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
+            Column::new(
+                "b",
+                DerivedType::new(Type::Integer),
+                ColumnRole::User,
+                false,
+            ),
         ];
         let sharding_key: &[&str] = &["a", "b"];
         let primary_key: &[&str] = &["a", "b"];
@@ -281,11 +389,36 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("e", Type::Unsigned, ColumnRole::User, false),
-            Column::new("f", Type::Unsigned, ColumnRole::User, false),
-            Column::new("g", Type::Unsigned, ColumnRole::User, false),
-            Column::new("h", Type::Unsigned, ColumnRole::User, false),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "e",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "f",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "g",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "h",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key: &[&str] = &["e", "f"];
         let primary_key: &[&str] = &["g", "h"];
@@ -296,9 +429,19 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
-            Column::new("a", Type::String, ColumnRole::User, false),
-            Column::new("b", Type::Integer, ColumnRole::User, false),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
+            Column::new("a", DerivedType::new(Type::String), ColumnRole::User, false),
+            Column::new(
+                "b",
+                DerivedType::new(Type::Integer),
+                ColumnRole::User,
+                false,
+            ),
         ];
         let sharding_key: &[&str] = &["a"];
         let primary_key: &[&str] = &["a"];
@@ -309,9 +452,19 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
-            Column::new("c", Type::String, ColumnRole::User, false),
-            Column::new("d", Type::Integer, ColumnRole::User, false),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
+            Column::new("c", DerivedType::new(Type::String), ColumnRole::User, false),
+            Column::new(
+                "d",
+                DerivedType::new(Type::Integer),
+                ColumnRole::User,
+                false,
+            ),
         ];
         let sharding_key: &[&str] = &["c"];
         let primary_key: &[&str] = &["d"];
@@ -322,8 +475,18 @@ impl RouterConfigurationMock {
         );
 
         let columns = vec![
-            Column::new("a", Type::Integer, ColumnRole::User, false),
-            Column::new("b", Type::Integer, ColumnRole::User, false),
+            Column::new(
+                "a",
+                DerivedType::new(Type::Integer),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "b",
+                DerivedType::new(Type::Integer),
+                ColumnRole::User,
+                false,
+            ),
         ];
         let primary_key: &[&str] = &["a"];
         tables.insert(
@@ -333,365 +496,630 @@ impl RouterConfigurationMock {
 
         // Table for sbroad-benches
         let columns = vec![
-            Column::new("vehicleguid", Type::Unsigned, ColumnRole::User, false),
-            Column::new("reestrid", Type::Unsigned, ColumnRole::User, false),
-            Column::new("reestrstatus", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehicleregno", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclevin", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclevin2", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclechassisnum", Type::Unsigned, ColumnRole::User, false),
+            Column::new(
+                "vehicleguid",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "reestrid",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "reestrstatus",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicleregno",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclevin",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclevin2",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclechassisnum",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
             Column::new(
                 "vehiclereleaseyear",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "operationregdoctypename",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "operationregdoc",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("operationregdoc", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "operationregdocissuedate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "operationregdoccomments",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "vehicleptstypename",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicleptsnum",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehicleptsnum", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehicleptsissuedate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicleptsissuer",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehicleptsissuer", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehicleptscomments",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclebodycolor",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclebrand",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclemodel",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclebrandmodel",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclebodynum",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclecost",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclegasequip",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehiclebodycolor", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclebrand", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclemodel", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclebrandmodel", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclebodynum", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclecost", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclegasequip", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehicleproducername",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclegrossmass",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclemass",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehiclegrossmass", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclemass", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehiclesteeringwheeltypeid",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclekpptype",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehiclekpptype", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehicletransmissiontype",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicletypename",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehiclecategory",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicletypeunit",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicleecoclass",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehicletypename", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehiclecategory", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehicletypeunit", Type::Unsigned, ColumnRole::User, false),
-            Column::new("vehicleecoclass", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehiclespecfuncname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "vehicleenclosedvolume",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "vehicleenginemodel",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicleenginenum",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehicleenginenum", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "vehicleenginepower",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "vehicleenginepowerkw",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "vehicleenginetype",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("vehicleenginetype", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "holdrestrictiondate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "approvalnum",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "approvaldate",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "approvaltype",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("approvalnum", Type::Unsigned, ColumnRole::User, false),
-            Column::new("approvaldate", Type::Unsigned, ColumnRole::User, false),
-            Column::new("approvaltype", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "utilizationfeename",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "customsdoc",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "customsdocdate",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "customsdocissue",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("customsdoc", Type::Unsigned, ColumnRole::User, false),
-            Column::new("customsdocdate", Type::Unsigned, ColumnRole::User, false),
-            Column::new("customsdocissue", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "customsdocrestriction",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "customscountryremovalid",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "customscountryremovalname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerorgname",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerinn",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerogrn",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerkpp",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("ownerorgname", Type::Unsigned, ColumnRole::User, false),
-            Column::new("ownerinn", Type::Unsigned, ColumnRole::User, false),
-            Column::new("ownerogrn", Type::Unsigned, ColumnRole::User, false),
-            Column::new("ownerkpp", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "ownerpersonlastname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "ownerpersonfirstname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "ownerpersonmiddlename",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "ownerpersonbirthdate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerbirthplace",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerpersonogrnip",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "owneraddressindex",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("ownerbirthplace", Type::Unsigned, ColumnRole::User, false),
-            Column::new("ownerpersonogrnip", Type::Unsigned, ColumnRole::User, false),
-            Column::new("owneraddressindex", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "owneraddressmundistrict",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "owneraddresssettlement",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "owneraddressstreet",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerpersoninn",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("ownerpersoninn", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "ownerpersondoccode",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "ownerpersondocnum",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("ownerpersondocnum", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "ownerpersondocdate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "operationname",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "operationdate",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("operationname", Type::Unsigned, ColumnRole::User, false),
-            Column::new("operationdate", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "operationdepartmentname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "operationattorney",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "operationlising",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "holdertypeid",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("operationattorney", Type::Unsigned, ColumnRole::User, false),
-            Column::new("operationlising", Type::Unsigned, ColumnRole::User, false),
-            Column::new("holdertypeid", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "holderpersondoccode",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersondocnum",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersondocdate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersondocissuer",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersonlastname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersonfirstname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersonmiddlename",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersonbirthdate",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderpersonbirthregionid",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "holderpersonsex",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("holderpersonsex", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "holderpersonbirthplace",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "holderpersoninn",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "holderpersonsnils",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("holderpersoninn", Type::Unsigned, ColumnRole::User, false),
-            Column::new("holderpersonsnils", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "holderpersonogrnip",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "holderaddressguid",
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("holderaddressguid", Type::Unsigned, ColumnRole::User, false),
             Column::new(
                 "holderaddressregionid",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressregionname",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressdistrict",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressmundistrict",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddresssettlement",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressstreet",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressbuilding",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressstructureid",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressstructurename",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
             Column::new(
                 "holderaddressstructure",
-                Type::Unsigned,
+                DerivedType::new(Type::Unsigned),
                 ColumnRole::User,
                 false,
             ),
-            Column::new("sys_from", Type::Unsigned, ColumnRole::User, false),
-            Column::new("sys_to", Type::Unsigned, ColumnRole::User, false),
-            Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "sys_from",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "sys_to",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::User,
+                false,
+            ),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ];
         let sharding_key: &[&str] = &["reestrid"];
         let primary_key: &[&str] = &["reestrid"];
diff --git a/sbroad/sbroad-core/src/executor/result.rs b/sbroad/sbroad-core/src/executor/result.rs
index 5a7180c1327c51fbeacf78fcdeb1c0d111bc4b30..8c919989259b9df51a8cc27fdb32c8c03d31c808 100644
--- a/sbroad/sbroad-core/src/executor/result.rs
+++ b/sbroad/sbroad-core/src/executor/result.rs
@@ -20,7 +20,7 @@ use crate::errors::SbroadError;
 use crate::executor::vtable::{VTableTuple, VirtualTable};
 use crate::ir::node::relational::Relational;
 use crate::ir::node::{Node, NodeId};
-use crate::ir::relation::{Column, ColumnRole, Type};
+use crate::ir::relation::{Column, ColumnRole, DerivedType, Type};
 use crate::ir::tree::traversal::{PostOrderWithFilter, REL_CAPACITY};
 use crate::ir::value::{LuaValue, Value};
 use crate::ir::Plan;
@@ -72,7 +72,12 @@ impl TryInto<Column> for &MetadataColumn {
 
     fn try_into(self) -> Result<Column, Self::Error> {
         let col_type = Type::new(&self.r#type)?;
-        Ok(Column::new(&self.name, col_type, ColumnRole::User, true))
+        Ok(Column::new(
+            &self.name,
+            DerivedType::new(col_type),
+            ColumnRole::User,
+            true,
+        ))
     }
 }
 
@@ -151,11 +156,22 @@ impl ProducerResult {
                 let column = &columns[i];
                 let value = Value::from(value);
 
-                if value.get_type() == column.r#type {
+                // TODO: Seems like logic of casting may be removed and replaced with
+                //       `cast_values` call after vtable is built.
+                let Some(column_ty) = column.r#type.get() else {
+                    // No need to cast Null.
                     tuple.push(value);
+                    continue;
+                };
+
+                let types_equal = *value.get_type().get() == Some(*column_ty);
+
+                let casted_value = if types_equal {
+                    value
                 } else {
-                    tuple.push(value.cast(column.r#type)?);
-                }
+                    value.cast(*column_ty)?
+                };
+                tuple.push(casted_value);
             }
             data.push(tuple);
         }
diff --git a/sbroad/sbroad-core/src/executor/result/tests.rs b/sbroad/sbroad-core/src/executor/result/tests.rs
index 4ed11d0908d7187d65122cebdc8fe1312b70aaae..ef79f2e56d591208d740597e259824ee176747d3 100644
--- a/sbroad/sbroad-core/src/executor/result/tests.rs
+++ b/sbroad/sbroad-core/src/executor/result/tests.rs
@@ -1,8 +1,6 @@
 use pretty_assertions::{assert_eq, assert_ne};
-use tarantool::decimal;
 
 use super::*;
-use crate::ir::relation::Type;
 
 #[test]
 fn box_execute_result_serialize() {
diff --git a/sbroad/sbroad-core/src/executor/tests.rs b/sbroad/sbroad-core/src/executor/tests.rs
index bc83cfb5dd154d27e10f884695df16950b24e98b..71d765cbff6f29b9805589337ccf5914188c4523 100644
--- a/sbroad/sbroad-core/src/executor/tests.rs
+++ b/sbroad/sbroad-core/src/executor/tests.rs
@@ -13,6 +13,16 @@ use crate::ir::value::{LuaValue, Value};
 
 use super::*;
 
+// Helper function to format back sql.
+// The local sql we produce doesn't contain line breaks,
+// but in code it's hard to read such long string, so
+// we insert line breaks and remove them back for
+// string comparison with expected pattern.
+#[cfg(test)]
+pub fn f_sql(s: &str) -> String {
+    s.replace("\n", " ")
+}
+
 #[test]
 fn shard_query() {
     let sql = r#"SELECT "FIRST_NAME" FROM "test_space" where "id" = 1"#;
diff --git a/sbroad/sbroad-core/src/executor/tests/exec_plan.rs b/sbroad/sbroad-core/src/executor/tests/exec_plan.rs
index 6a02039790702b531cd39fe3c2a4887a4480d157..99b321c93b8cbb8ce3b468346276f2f645fc175f 100644
--- a/sbroad/sbroad-core/src/executor/tests/exec_plan.rs
+++ b/sbroad/sbroad-core/src/executor/tests/exec_plan.rs
@@ -28,15 +28,6 @@ fn reshard_vtable(
     }
 }
 
-// Helper function to format back sql.
-// The local sql we produce doesn't contain line breaks,
-// but in code it's hard to read such long string, so
-// we insert line breaks and remove them back for
-// string comparison with expected pattern.
-fn f_sql(s: &str) -> String {
-    s.replace("\n", " ")
-}
-
 /// Helper function to generate sql from `exec_plan` from given `top_id` node.
 /// Used for testing.
 fn get_sql_from_execution_plan(
@@ -231,17 +222,19 @@ fn exec_plan_subtree_aggregates() {
     } else {
         panic!("Expected MotionPolicy::Segment for local aggregation stage");
     };
+
     assert_eq!(
         sql,
         PatternWithParams::new(
             f_sql(
                 r#"SELECT "T1"."sys_op" as "column_596",
-("T1"."id") * ("T1"."sys_op") as "column_1632", "T1"."id" as "column_2096",
-count ("T1"."id") as "count_2696", group_concat ("T1"."FIRST_NAME", ?) as "group_concat_2496",
-total ("T1"."id") as "total_2896", min ("T1"."id") as "min_3096", max ("T1"."id") as "max_3296",
-count ("T1"."sysFrom") as "count_1596", sum ("T1"."id") as "sum_1796"
-FROM "test_space" as "T1"
-GROUP BY "T1"."sys_op", ("T1"."id") * ("T1"."sys_op"), "T1"."id""#
+("T1"."id") * ("T1"."sys_op") as "column_1632",
+"T1"."id" as "column_2096", count ("T1"."sysFrom") as "count_1596",
+sum ("T1"."id") as "sum_1796", count ("T1"."id") as "count_2696",
+min ("T1"."id") as "min_3096", group_concat ("T1"."FIRST_NAME", ?) as "group_concat_2496",
+total ("T1"."id") as "total_2896",
+max ("T1"."id") as "max_3296" FROM "test_space" as "T1" GROUP BY "T1"."sys_op",
+("T1"."id") * ("T1"."sys_op"), "T1"."id""#
             ),
             vec![Value::from("o")]
         )
@@ -252,16 +245,14 @@ GROUP BY "T1"."sys_op", ("T1"."id") * ("T1"."sys_op"), "T1"."id""#
     assert_eq!(
         sql,
         PatternWithParams::new(
-            format!(
-                "{} {} {} {} {} {} {} {}",
-                r#"SELECT ("COL_1") || ("COL_1") as "col_1","#,
-                r#"("COL_1") * (?) + (sum ("COL_9")) as "col_2", sum ("COL_10") as "col_3","#,
-                r#"(sum (DISTINCT "COL_2")) / (count (DISTINCT "COL_3")) as "col_4","#,
-                r#"group_concat ("COL_5", ?) as "col_5","#,
-                r#"sum (CAST ("COL_10" as double)) / sum (CAST ("COL_4" as double)) as "col_6","#,
-                r#"total ("COL_6") as "col_7", min ("COL_7") as "col_8", max ("COL_8") as "col_9""#,
-                r#"FROM (SELECT "COL_1","COL_2","COL_3","COL_4","COL_5","COL_6","COL_7","COL_8","COL_9","COL_10" FROM "TMP_test_0136")"#,
-                r#"GROUP BY "COL_1""#
+            f_sql(
+                r#"SELECT ("COL_1") || ("COL_1") as "col_1",
+("COL_1") * (?) + (sum ("COL_4")) as "col_2",
+sum ("COL_5") as "col_3", (sum (DISTINCT "COL_2")) / (count (DISTINCT "COL_3")) as "col_4",
+group_concat ("COL_8", ?) as "col_5", sum (CAST ("COL_5" as double)) / sum (CAST ("COL_6" as double)) as "col_6",
+total ("COL_9") as "col_7", min ("COL_7") as "col_8",
+max ("COL_10") as "col_9" FROM (SELECT "COL_1","COL_2","COL_3","COL_4","COL_5","COL_6","COL_7","COL_8","COL_9","COL_10" FROM "TMP_test_0136")
+GROUP BY "COL_1""#
             ),
             vec![Value::Unsigned(2), Value::from("o")]
         )
diff --git a/sbroad/sbroad-core/src/executor/vtable.rs b/sbroad/sbroad-core/src/executor/vtable.rs
index 61fc3309cba77d46513e26ef68f2c824b5ba6edc..62f1fb0eae3076c93b68bf9e2edad8f8eddb4cd4 100644
--- a/sbroad/sbroad-core/src/executor/vtable.rs
+++ b/sbroad/sbroad-core/src/executor/vtable.rs
@@ -16,7 +16,7 @@ use crate::executor::protocol::{Binary, EncodedRows, EncodedTables};
 use crate::executor::{bucket::Buckets, Vshard};
 use crate::ir::helpers::RepeatableState;
 use crate::ir::node::NodeId;
-use crate::ir::relation::{Column, ColumnRole, Type};
+use crate::ir::relation::{Column, ColumnRole, DerivedType, Type};
 use crate::ir::transformation::redistribution::{ColumnPosition, MotionKey, Target};
 use crate::ir::value::{EncodedValue, LuaValue, MsgPackValue, Value};
 use crate::utils::{write_u32_array_len, ByteCounter};
@@ -100,7 +100,7 @@ pub struct VirtualTable {
 /// (as soon as it's generated automatically).
 #[derive(PartialEq, Debug, Eq, Clone)]
 pub struct VTableColumn {
-    pub r#type: Type,
+    pub r#type: DerivedType,
     pub role: ColumnRole,
     pub is_nullable: bool,
 }
@@ -187,6 +187,15 @@ impl VirtualTable {
         &self.tuples
     }
 
+    /// Retrieve value types of virtual table tuples.
+    #[must_use]
+    pub fn get_types(&self) -> Vec<Vec<DerivedType>> {
+        self.get_tuples()
+            .iter()
+            .map(|tuple| tuple.iter().map(|v| v.get_type()).collect::<Vec<_>>())
+            .collect()
+    }
+
     /// Gets a mutable virtual table tuples list
     #[must_use]
     pub fn get_mut_tuples(&mut self) -> &mut Vec<VTableTuple> {
@@ -194,13 +203,7 @@ impl VirtualTable {
     }
 
     /// Given a vec of [(`is_nullable`, `correct_type`)], fix metadata of each value in the tuples.
-    ///
-    /// # Errors
-    /// - Unable to apply values cast.
-    ///
-    /// # Panics
-    /// - Unacceptable type met.
-    pub fn cast_values(&mut self, fixed_types: &[(bool, Type)]) -> Result<(), SbroadError> {
+    pub fn cast_values(&mut self, fixed_types: &[(bool, DerivedType)]) -> Result<(), SbroadError> {
         for tuple in self.get_mut_tuples() {
             for (i, v) in tuple.iter_mut().enumerate() {
                 let (_, ty) = fixed_types.get(i).expect("Type expected.");
@@ -753,22 +756,18 @@ impl ExecutionPlan {
     }
 }
 
-/// In case Vtable tuples have values of different types, we try to
+/// In case passed types are different, we try to
 /// unify them:
 /// * Some types (like String or Boolean) support only values of the same type. In case we met inconsistency,
 ///   we throw an error.
 /// * Numerical values can be cast according to the following order:
 ///   Decimal > Double > Integer > Unsigned > Null.
 ///
-/// # Errors
-/// - Contains values of inconsistent types.
-///
-/// # Panics
-/// - Internal error.
+/// Each pair in the returned vec is (is_type_nullable, unified_type).
 #[allow(clippy::too_many_lines)]
-pub fn calculate_vtable_unified_types(
-    vtable: &VirtualTable,
-) -> Result<Vec<(bool, Type)>, SbroadError> {
+pub fn calculate_unified_types(
+    types: &Vec<Vec<DerivedType>>,
+) -> Result<Vec<(bool, DerivedType)>, SbroadError> {
     // Map of { type -> types_which_can_be_upcasted_to_given_one }.
     let get_types_less = |ty: &Type| -> &[Type] {
         match ty {
@@ -786,10 +785,10 @@ pub fn calculate_vtable_unified_types(
         }
     };
 
-    let columns_len = vtable.columns.len();
+    let columns_len = types.first().expect("Types vec should not be empty").len();
     let mut nullable_column_indices = HashSet::with_capacity(columns_len);
-    let fix_type = |current_type_unified: &mut Option<Type>, given_type: &Type| {
-        if let Some(current_type_unified) = current_type_unified {
+    let fix_type = |current_type_unified: &mut DerivedType, given_type: &Type| {
+        if let Some(current_type_unified) = current_type_unified.get_mut() {
             if get_types_less(given_type).contains(current_type_unified) {
                 *current_type_unified = *given_type;
             } else if *given_type != *current_type_unified
@@ -797,80 +796,36 @@ pub fn calculate_vtable_unified_types(
             {
                 return Err(SbroadError::Invalid(
                     Entity::Type,
-                    Some(format_smolstr!("Virtual table contains values of inconsistent types: {current_type_unified:?} and {given_type:?}.")),
+                    Some(format_smolstr!("Unable to unify inconsistent types: {current_type_unified:?} and {given_type:?}.")),
                 ));
             }
         } else {
-            *current_type_unified = Some(*given_type);
+            current_type_unified.set(*given_type);
         }
         Ok(())
     };
 
-    let mut unified_types: Vec<Option<Type>> = iter::repeat(None).take(columns_len).collect();
-
-    for tuple in vtable.get_tuples() {
-        for (i, value) in tuple.iter().enumerate() {
-            let current_type_unified = unified_types
-                .get_mut(i)
-                .expect("Unified types vec isn't initialized.");
-            match value {
-                Value::Boolean(_) => {
-                    fix_type(current_type_unified, &Type::Boolean)?;
-                }
-                Value::String(_) => {
-                    fix_type(current_type_unified, &Type::String)?;
-                }
-                Value::Uuid(_) => {
-                    fix_type(current_type_unified, &Type::Uuid)?;
-                }
-                Value::Datetime(_) => {
-                    fix_type(current_type_unified, &Type::Datetime)?;
-                }
-                Value::Null => {
-                    nullable_column_indices.insert(i);
-                }
-                Value::Unsigned(_) => {
-                    fix_type(current_type_unified, &Type::Unsigned)?;
-                }
-                Value::Integer(_) => {
-                    fix_type(current_type_unified, &Type::Integer)?;
-                }
-                Value::Double(_) => {
-                    fix_type(current_type_unified, &Type::Double)?;
-                }
-                Value::Decimal(_) => {
-                    fix_type(current_type_unified, &Type::Decimal)?;
-                }
-                Value::Tuple(_) => panic!("Unexpected tuple under values."),
+    let mut unified_types: Vec<DerivedType> = vec![DerivedType::unknown(); columns_len];
+
+    for type_tuple in types {
+        for (i, ty) in type_tuple.iter().enumerate() {
+            let current_type_unified = unified_types.get_mut(i).unwrap_or_else(|| {
+                panic!("Unified types vec isn't initialized to retrieve index {i}.")
+            });
+            if let Some(ty) = ty.get() {
+                fix_type(current_type_unified, ty)?;
+            } else {
+                nullable_column_indices.insert(i);
             }
         }
     }
 
-    if unified_types
-        .first()
-        .expect("Unified types vec is empty.")
-        .is_none()
-    {
-        // This is possible in case we deal with VALUES like
-        // `values ((select a from t where false), (select a from t where false), ...)`
-        // where there are no tuples to traverse so that we just infer types returned to us from
-        // local SQL execution.
-        Ok(vtable
-            .get_columns()
-            .iter()
-            .map(|c| (c.is_nullable, c.r#type))
-            .collect())
-    } else {
-        Ok(unified_types
-            .into_iter()
-            .zip(vtable.get_columns().iter())
-            .enumerate()
-            .map(|(i, (t, c))| {
-                let t = if let Some(t) = t { t } else { c.r#type };
-                (nullable_column_indices.contains(&i), t)
-            })
-            .collect())
-    }
+    let res = unified_types
+        .into_iter()
+        .enumerate()
+        .map(|(i, t)| (nullable_column_indices.contains(&i), t))
+        .collect();
+    Ok(res)
 }
 
 #[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
diff --git a/sbroad/sbroad-core/src/executor/vtable/tests.rs b/sbroad/sbroad-core/src/executor/vtable/tests.rs
index a104f6f6786f4dc07616a35ee33d1dc74133cd85..9aa006b9acc8c47ac703ecababef715c75b292ad 100644
--- a/sbroad/sbroad-core/src/executor/vtable/tests.rs
+++ b/sbroad/sbroad-core/src/executor/vtable/tests.rs
@@ -337,12 +337,13 @@ fn vtable_values_types_casting_single_tuple() {
 
     actual_vtable.add_column(vcolumn_integer_user_non_null());
     actual_vtable.add_tuple(vec![Value::Unsigned(1)]);
-    let unified_types = calculate_vtable_unified_types(&actual_vtable).unwrap();
+    let vtable_types = actual_vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types).unwrap();
     actual_vtable.cast_values(&unified_types).unwrap();
 
     let mut expected_vtable = VirtualTable::new();
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Unsigned,
+        r#type: DerivedType::new(Type::Unsigned),
         role: ColumnRole::User,
         is_nullable: false,
     });
@@ -358,12 +359,13 @@ fn vtable_values_types_casting_two_tuples() {
     actual_vtable.add_column(vcolumn_integer_user_non_null());
     actual_vtable.add_tuple(vec![Value::Unsigned(1)]);
     actual_vtable.add_tuple(vec![Value::Integer(1)]);
-    let unified_types = calculate_vtable_unified_types(&actual_vtable).unwrap();
+    let vtable_types = actual_vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types).unwrap();
     actual_vtable.cast_values(&unified_types).unwrap();
 
     let mut expected_vtable = VirtualTable::new();
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Integer,
+        r#type: DerivedType::new(Type::Integer),
         role: ColumnRole::User,
         is_nullable: false,
     });
@@ -380,12 +382,12 @@ fn vtable_values_types_casting_two_tuples_err() {
     actual_vtable.add_column(vcolumn_integer_user_non_null());
     actual_vtable.add_tuple(vec![Value::Unsigned(1)]);
     actual_vtable.add_tuple(vec![Value::String("name".into())]);
-    let err = calculate_vtable_unified_types(&actual_vtable).unwrap_err();
-    println!("{}", err);
+    let vtable_types = actual_vtable.get_types();
+    let err = calculate_unified_types(&vtable_types).unwrap_err();
     assert_eq!(
         true,
         err.to_string()
-            .contains("Virtual table contains values of inconsistent types: Unsigned and String.")
+            .contains("Unable to unify inconsistent types: Unsigned and String.")
     );
 }
 
@@ -396,17 +398,18 @@ fn vtable_values_types_casting_two_columns() {
     actual_vtable.add_column(vcolumn_integer_user_non_null());
     actual_vtable.add_column(vcolumn_integer_user_non_null());
     actual_vtable.add_tuple(vec![Value::Unsigned(1), Value::Integer(1)]);
-    let unified_types = calculate_vtable_unified_types(&actual_vtable).unwrap();
+    let vtable_types = actual_vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types).unwrap();
     actual_vtable.cast_values(&unified_types).unwrap();
 
     let mut expected_vtable = VirtualTable::new();
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Unsigned,
+        r#type: DerivedType::new(Type::Unsigned),
         role: ColumnRole::User,
         is_nullable: false,
     });
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Integer,
+        r#type: DerivedType::new(Type::Integer),
         role: ColumnRole::User,
         is_nullable: false,
     });
@@ -423,17 +426,18 @@ fn vtable_values_types_casting_two_columns_two_tuples() {
     actual_vtable.add_column(vcolumn_integer_user_non_null());
     actual_vtable.add_tuple(vec![Value::Unsigned(1), Value::Integer(1)]);
     actual_vtable.add_tuple(vec![Value::Decimal(Decimal::from(2)), Value::Integer(1)]);
-    let unified_types = calculate_vtable_unified_types(&actual_vtable).unwrap();
+    let vtable_types = actual_vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types).unwrap();
     actual_vtable.cast_values(&unified_types).unwrap();
 
     let mut expected_vtable = VirtualTable::new();
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Decimal,
+        r#type: DerivedType::new(Type::Decimal),
         role: ColumnRole::User,
         is_nullable: false,
     });
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Integer,
+        r#type: DerivedType::new(Type::Integer),
         role: ColumnRole::User,
         is_nullable: false,
     });
@@ -452,17 +456,18 @@ fn vtable_values_types_casting_two_columns_with_nulls() {
     actual_vtable.add_tuple(vec![Value::Unsigned(1), Value::Null]);
     actual_vtable.add_tuple(vec![Value::Null, Value::Null]);
     actual_vtable.add_tuple(vec![Value::Decimal(Decimal::from(2)), Value::Null]);
-    let unified_types = calculate_vtable_unified_types(&actual_vtable).unwrap();
+    let vtable_types = actual_vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types).unwrap();
     actual_vtable.cast_values(&unified_types).unwrap();
 
     let mut expected_vtable = VirtualTable::new();
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Decimal,
+        r#type: DerivedType::new(Type::Decimal),
         role: ColumnRole::User,
         is_nullable: true,
     });
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Integer,
+        r#type: DerivedType::unknown(),
         role: ColumnRole::User,
         is_nullable: true,
     });
@@ -490,17 +495,18 @@ fn vtable_values_types_casting_two_columns_numerical() {
         Value::Decimal(Decimal::from(2)),
         Value::Double(0.5_f64.into()),
     ]);
-    let unified_types = calculate_vtable_unified_types(&actual_vtable).unwrap();
+    let vtable_types = actual_vtable.get_types();
+    let unified_types = calculate_unified_types(&vtable_types).unwrap();
     actual_vtable.cast_values(&unified_types).unwrap();
 
     let mut expected_vtable = VirtualTable::new();
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Decimal,
+        r#type: DerivedType::new(Type::Decimal),
         role: ColumnRole::User,
         is_nullable: true,
     });
     expected_vtable.add_column(VTableColumn {
-        r#type: Type::Decimal,
+        r#type: DerivedType::new(Type::Decimal),
         role: ColumnRole::User,
         is_nullable: true,
     });
diff --git a/sbroad/sbroad-core/src/frontend/sql.rs b/sbroad/sbroad-core/src/frontend/sql.rs
index 5fd2ec9ae1a40cce3d4781f376b3b62125befb56..c477a914dd39a54fdae4c2129919a1390a9ab490 100644
--- a/sbroad/sbroad-core/src/frontend/sql.rs
+++ b/sbroad/sbroad-core/src/frontend/sql.rs
@@ -3604,6 +3604,15 @@ impl AbstractSyntaxTree {
 
             let entity = match expr {
                 Node::Expression(expr) => {
+                    if let Expression::Reference(Reference {col_type, ..}) = expr {
+                        if matches!(col_type.get(), Some(Type::Array)) {
+                            return Err(SbroadError::Invalid(
+                                Entity::Expression,
+                                Some(format_smolstr!("Array is not supported as a sort type for ORDER BY"))
+                            ));
+                        }
+                    }
+
                     if let Expression::Constant(Constant {value: Value::Unsigned(index)}) = expr {
                         let index_usize = usize::try_from(*index).map_err(|_| {
                             SbroadError::Invalid(
@@ -3618,11 +3627,13 @@ impl AbstractSyntaxTree {
                         if let Some(alias_node_id) = sq_output.get(index_usize - 1) {
                             let alias_node = plan.get_expression_node(*alias_node_id)?;
                             if let Expression::Alias(Alias { child, .. }) = alias_node {
-                                if let Expression::Reference(Reference { col_type: Type::Array, .. }) = plan.get_expression_node(*child)? {
-                                    return Err(SbroadError::Invalid(
-                                        Entity::Expression,
-                                        Some(format_smolstr!("Array is not supported as a sort type for ORDER BY"))
-                                    ));
+                                if let Expression::Reference(Reference { col_type, .. }) = plan.get_expression_node(*child)? {
+                                    if matches!(col_type.get(), Some(Type::Array)) {
+                                        return Err(SbroadError::Invalid(
+                                            Entity::Expression,
+                                            Some(format_smolstr!("Array is not supported as a sort type for ORDER BY"))
+                                        ));
+                                    }
                                 }
                             }
                         } else {
@@ -3632,11 +3643,6 @@ impl AbstractSyntaxTree {
                             ));
                         }
                         OrderByEntity::Index { value: index_usize }
-                    } else if let Expression::Reference(Reference {col_type: Type::Array, ..}) = expr {
-                        return Err(SbroadError::Invalid(
-                            Entity::Expression,
-                            Some(format_smolstr!("Array is not supported as a sort type for ORDER BY"))
-                        ));
                     } else {
                         // Check that at least one reference is met in expression tree.
                         // Otherwise, ordering expression has no sense.
diff --git a/sbroad/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad/sbroad-core/src/frontend/sql/ir/tests.rs
index 83409f0e4567071e9825e12ccd519a43da6842fc..f11529268463e88a4077a96ac4fc82fbfa41d11b 100644
--- a/sbroad/sbroad-core/src/frontend/sql/ir/tests.rs
+++ b/sbroad/sbroad-core/src/frontend/sql/ir/tests.rs
@@ -1457,7 +1457,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1483,7 +1482,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1546,7 +1544,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1580,7 +1577,6 @@ execution options:
 "#,
     );
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -1662,7 +1658,6 @@ execution options:
 "#,
     );
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -1687,7 +1682,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -1725,9 +1719,9 @@ fn front_sql_aggregates() {
 
     let expected_explain = String::from(
         r#"projection ("column_596"::unsigned -> "b", ROW(sum(("count_1496"::unsigned))::unsigned) + ROW(sum(("count_1596"::unsigned))::unsigned) -> "col_1")
-    group by ("column_596"::unsigned) output: ("column_596"::unsigned -> "column_596", "count_1496"::unsigned -> "count_1496", "count_1596"::unsigned -> "count_1596")
+    group by ("column_596"::unsigned) output: ("column_596"::unsigned -> "column_596", "count_1596"::unsigned -> "count_1596", "count_1496"::unsigned -> "count_1496")
         motion [policy: segment([ref("column_596")])]
-            projection ("t"."b"::unsigned -> "column_596", count(("t"."a"::unsigned))::unsigned -> "count_1496", count(("t"."b"::unsigned))::unsigned -> "count_1596")
+            projection ("t"."b"::unsigned -> "column_596", count(("t"."b"::unsigned))::unsigned -> "count_1596", count(("t"."a"::unsigned))::unsigned -> "count_1496")
                 group by ("t"."b"::unsigned) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id")
                     scan "t"
 execution options:
@@ -1804,7 +1798,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1826,7 +1819,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1848,7 +1840,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1870,7 +1861,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1892,7 +1882,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1946,7 +1935,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1969,7 +1957,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -1997,9 +1984,9 @@ fn front_sql_aggregates_with_subexpressions() {
 
     let expected_explain = String::from(
         r#"projection ("column_596"::unsigned -> "b", sum(("count_1496"::unsigned))::unsigned -> "col_1", sum(("count_1796"::unsigned))::unsigned -> "col_2")
-    group by ("column_596"::unsigned) output: ("column_596"::unsigned -> "column_596", "count_1796"::unsigned -> "count_1796", "count_1496"::unsigned -> "count_1496")
+    group by ("column_596"::unsigned) output: ("column_596"::unsigned -> "column_596", "count_1496"::unsigned -> "count_1496", "count_1796"::unsigned -> "count_1796")
         motion [policy: segment([ref("column_596")])]
-            projection ("t"."b"::unsigned -> "column_596", count(("func"(("t"."a"::unsigned))::integer))::unsigned -> "count_1796", count((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned) + ROW(1::unsigned)))::unsigned -> "count_1496")
+            projection ("t"."b"::unsigned -> "column_596", count((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned) + ROW(1::unsigned)))::unsigned -> "count_1496", count(("func"(("t"."a"::unsigned))::integer))::unsigned -> "count_1796")
                 group by ("t"."b"::unsigned) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id")
                     scan "t"
 execution options:
@@ -2030,7 +2017,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2054,7 +2040,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2076,7 +2061,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2205,7 +2189,7 @@ execution options:
     vtable_max_rows = 42
 "#,
     );
-    println!("{}", plan.as_explain().unwrap());
+
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2357,7 +2341,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2386,7 +2369,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2418,7 +2400,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2433,7 +2414,7 @@ fn front_sql_insert_single() {
     motion [policy: segment([value(NULL), ref("col_2")])]
         projection (sum(("sum_696"::decimal))::decimal -> "col_1", sum(("count_896"::unsigned))::unsigned -> "col_2")
             motion [policy: full]
-                projection (count(("t"."d"::unsigned))::unsigned -> "count_896", sum(("t"."b"::unsigned))::decimal -> "sum_696")
+                projection (sum(("t"."b"::unsigned))::decimal -> "sum_696", count(("t"."d"::unsigned))::unsigned -> "count_896")
                     scan "t"
 execution options:
     vdbe_max_steps = 45000
@@ -2441,7 +2422,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2460,7 +2440,7 @@ fn front_sql_except_single_right() {
     motion [policy: segment([ref("col_1"), ref("col_2")])]
         projection (sum(("sum_1396"::decimal))::decimal -> "col_1", sum(("count_1596"::unsigned))::unsigned -> "col_2")
             motion [policy: full]
-                projection (sum(("t"."a"::unsigned))::decimal -> "sum_1396", count(("t"."b"::unsigned))::unsigned -> "count_1596")
+                projection (count(("t"."b"::unsigned))::unsigned -> "count_1596", sum(("t"."a"::unsigned))::decimal -> "sum_1396")
                     scan "t"
 execution options:
     vdbe_max_steps = 45000
@@ -2468,7 +2448,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 
     let input = r#"SELECT "b", "a" from "t"
@@ -2485,7 +2464,7 @@ execution options:
     motion [policy: segment([ref("col_2"), ref("col_1")])]
         projection (sum(("sum_1396"::decimal))::decimal -> "col_1", sum(("count_1596"::unsigned))::unsigned -> "col_2")
             motion [policy: full]
-                projection (sum(("t"."a"::unsigned))::decimal -> "sum_1396", count(("t"."b"::unsigned))::unsigned -> "count_1596")
+                projection (count(("t"."b"::unsigned))::unsigned -> "count_1596", sum(("t"."a"::unsigned))::decimal -> "sum_1396")
                     scan "t"
 execution options:
     vdbe_max_steps = 45000
@@ -2493,7 +2472,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2510,7 +2488,7 @@ fn front_sql_except_single_left() {
     motion [policy: segment([ref("col_1"), ref("col_2")])]
         projection (sum(("sum_696"::decimal))::decimal -> "col_1", sum(("count_896"::unsigned))::unsigned -> "col_2")
             motion [policy: full]
-                projection (sum(("t"."a"::unsigned))::decimal -> "sum_696", count(("t"."b"::unsigned))::unsigned -> "count_896")
+                projection (count(("t"."b"::unsigned))::unsigned -> "count_896", sum(("t"."a"::unsigned))::decimal -> "sum_696")
                     scan "t"
     projection ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b")
         scan "t"
@@ -2520,7 +2498,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2537,12 +2514,12 @@ fn front_sql_except_single_both() {
     motion [policy: segment([ref("col_1")])]
         projection (sum(("sum_696"::decimal))::decimal -> "col_1", sum(("count_896"::unsigned))::unsigned -> "col_2")
             motion [policy: full]
-                projection (sum(("t"."a"::unsigned))::decimal -> "sum_696", count(("t"."b"::unsigned))::unsigned -> "count_896")
+                projection (count(("t"."b"::unsigned))::unsigned -> "count_896", sum(("t"."a"::unsigned))::decimal -> "sum_696")
                     scan "t"
     motion [policy: segment([ref("col_1")])]
         projection (sum(("sum_1596"::decimal))::decimal -> "col_1", sum(("sum_1796"::decimal))::decimal -> "col_2")
             motion [policy: full]
-                projection (sum(("t"."a"::unsigned))::decimal -> "sum_1596", sum(("t"."b"::unsigned))::decimal -> "sum_1796")
+                projection (sum(("t"."b"::unsigned))::decimal -> "sum_1796", sum(("t"."a"::unsigned))::decimal -> "sum_1596")
                     scan "t"
 execution options:
     vdbe_max_steps = 45000
@@ -2573,7 +2550,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2597,7 +2573,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2609,9 +2584,9 @@ fn front_sql_groupby_expression3() {
     let plan = sql_to_optimized_ir(input, vec![]);
     let expected_explain = String::from(
         r#"projection ("column_532"::unsigned -> "col_1", "column_832"::unsigned * ROW(sum(("sum_2496"::decimal))::decimal) / ROW(sum(("count_2596"::unsigned))::unsigned) -> "col_2")
-    group by ("column_532"::unsigned, "column_832"::unsigned) output: ("column_532"::unsigned -> "column_532", "column_832"::unsigned -> "column_832", "sum_2496"::decimal -> "sum_2496", "count_2596"::unsigned -> "count_2596")
+    group by ("column_532"::unsigned, "column_832"::unsigned) output: ("column_532"::unsigned -> "column_532", "column_832"::unsigned -> "column_832", "count_2596"::unsigned -> "count_2596", "sum_2496"::decimal -> "sum_2496")
         motion [policy: segment([ref("column_532"), ref("column_832")])]
-            projection (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> "column_532", (ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned)) -> "column_832", sum((ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned)))::decimal -> "sum_2496", count((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned)))::unsigned -> "count_2596")
+            projection (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned) -> "column_532", (ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned)) -> "column_832", count((ROW("t"."a"::unsigned) * ROW("t"."b"::unsigned)))::unsigned -> "count_2596", sum((ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned)))::decimal -> "sum_2496")
                 group by (ROW("t"."a"::unsigned) + ROW("t"."b"::unsigned), (ROW("t"."c"::unsigned) * ROW("t"."d"::unsigned))) output: ("t"."a"::unsigned -> "a", "t"."b"::unsigned -> "b", "t"."c"::unsigned -> "c", "t"."d"::unsigned -> "d", "t"."bucket_id"::unsigned -> "bucket_id")
                     scan "t"
 execution options:
@@ -2621,7 +2596,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -2644,7 +2618,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -2740,7 +2713,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -2774,7 +2746,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -2808,7 +2779,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -2864,7 +2834,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2888,7 +2857,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2911,7 +2879,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -2960,7 +2927,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -3011,7 +2977,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -3044,7 +3009,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -3058,7 +3022,7 @@ fn front_sql_unique_local_aggregates() {
     let expected_explain = String::from(
         r#"projection (sum(("sum_696"::decimal))::decimal -> "col_1", sum(("count_896"::unsigned))::unsigned -> "col_2", ROW(sum(("sum_696"::decimal))::decimal) + ROW(sum(("count_896"::unsigned))::unsigned) -> "col_3")
     motion [policy: full]
-        projection (count(("t"."a"::unsigned))::unsigned -> "count_896", sum(("t"."a"::unsigned))::decimal -> "sum_696")
+        projection (sum(("t"."a"::unsigned))::decimal -> "sum_696", count(("t"."a"::unsigned))::unsigned -> "count_896")
             scan "t"
 execution options:
     vdbe_max_steps = 45000
@@ -3067,7 +3031,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -3092,7 +3055,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -3157,7 +3119,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -3179,7 +3140,6 @@ execution options:
     );
 
     assert_eq!(expected_explain, plan.as_explain().unwrap());
-    println!("{}", plan.as_explain().unwrap());
 }
 
 #[test]
@@ -3217,7 +3177,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -3237,7 +3196,6 @@ execution options:
 "#,
     );
 
-    println!("{}", plan.as_explain().unwrap());
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -3595,7 +3553,7 @@ execution options:
     vtable_max_rows = 5000
 "#,
     );
-    println!("{}", plan.as_explain().unwrap());
+
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
@@ -4151,7 +4109,7 @@ execution options:
     vtable_max_rows = 5000
 "#,
     );
-    println!("{}", plan.as_explain().unwrap());
+
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
diff --git a/sbroad/sbroad-core/src/frontend/sql/ir/tests/coalesce.rs b/sbroad/sbroad-core/src/frontend/sql/ir/tests/coalesce.rs
index adbf99452155e020bf82da01dcc273d1081eb88b..5fa0e7ec0ed6dd0c218e2debd575168fbcb22350 100644
--- a/sbroad/sbroad-core/src/frontend/sql/ir/tests/coalesce.rs
+++ b/sbroad/sbroad-core/src/frontend/sql/ir/tests/coalesce.rs
@@ -7,7 +7,7 @@ fn coalesce_in_projection() {
     let plan = sql_to_optimized_ir(sql, vec![]);
 
     let expected_explain = String::from(
-        r#"projection (coalesce((NULL::integer, "test_space"."FIRST_NAME"::string))::any -> "col_1")
+        r#"projection (coalesce((NULL::unknown, "test_space"."FIRST_NAME"::string))::any -> "col_1")
     scan "test_space"
 execution options:
     vdbe_max_steps = 45000
diff --git a/sbroad/sbroad-core/src/frontend/sql/ir/tests/funcs.rs b/sbroad/sbroad-core/src/frontend/sql/ir/tests/funcs.rs
index 6445f3597dc9e4d5dd78e6b4dbd7856bfbabee53..cd5487c63f5d5ec3387e208e4f40502ea481db60 100644
--- a/sbroad/sbroad-core/src/frontend/sql/ir/tests/funcs.rs
+++ b/sbroad/sbroad-core/src/frontend/sql/ir/tests/funcs.rs
@@ -6,7 +6,6 @@ fn lower_upper() {
     let input = r#"select upper(lower('a' || 'B')), upper(a) from t1"#;
 
     let plan = sql_to_optimized_ir(input, vec![]);
-    println!("{}", plan.as_explain().unwrap());
 
     let expected_explain = String::from(
         r#"projection (upper((lower((ROW('a'::string) || ROW('B'::string)))::string))::string -> "col_1", upper(("t1"."a"::string))::string -> "col_2")
diff --git a/sbroad/sbroad-core/src/frontend/sql/ir/tests/global.rs b/sbroad/sbroad-core/src/frontend/sql/ir/tests/global.rs
index 117a922ae18049418ead1afdb920d71a7f81f28e..674a5edd57b0c5d0457a8ef802faa5c75976c754 100644
--- a/sbroad/sbroad-core/src/frontend/sql/ir/tests/global.rs
+++ b/sbroad/sbroad-core/src/frontend/sql/ir/tests/global.rs
@@ -811,9 +811,9 @@ fn front_sql_global_aggregate5() {
     let expected_explain = String::from(
         r#"projection ("column_1432"::integer -> "col_1", sum(("sum_2896"::decimal))::decimal -> "col_2")
     having ROW(sum(("sum_2296"::decimal::double))::decimal / sum(("count_2296"::decimal::double))::decimal) > ROW(3::unsigned)
-        group by ("column_1432"::integer) output: ("column_1432"::integer -> "column_1432", "sum_2896"::decimal -> "sum_2896", "sum_2296"::decimal -> "sum_2296", "count_2296"::unsigned -> "count_2296")
+        group by ("column_1432"::integer) output: ("column_1432"::integer -> "column_1432", "sum_2296"::decimal -> "sum_2296", "count_2296"::unsigned -> "count_2296", "sum_2896"::decimal -> "sum_2896")
             motion [policy: segment([ref("column_1432")])]
-                projection (ROW("global_t"."b"::integer) + ROW("global_t"."a"::integer) -> "column_1432", sum(("global_t"."a"::integer))::decimal -> "sum_2896", sum(("global_t"."b"::integer))::decimal -> "sum_2296", count(("global_t"."b"::integer))::unsigned -> "count_2296")
+                projection (ROW("global_t"."b"::integer) + ROW("global_t"."a"::integer) -> "column_1432", sum(("global_t"."b"::integer))::decimal -> "sum_2296", count(("global_t"."b"::integer))::unsigned -> "count_2296", sum(("global_t"."a"::integer))::decimal -> "sum_2896")
                     group by (ROW("global_t"."b"::integer) + ROW("global_t"."a"::integer)) output: ("global_t"."a"::integer -> "a", "global_t"."b"::integer -> "b")
                         selection ROW("global_t"."a"::integer, "global_t"."b"::integer) in ROW($0, $0)
                             scan "global_t"
@@ -826,6 +826,7 @@ execution options:
     vtable_max_rows = 5000
 "#,
     );
+
     assert_eq!(expected_explain, plan.as_explain().unwrap());
 }
 
diff --git a/sbroad/sbroad-core/src/frontend/sql/ir/tests/params.rs b/sbroad/sbroad-core/src/frontend/sql/ir/tests/params.rs
index f395bb7ca48635ff6414725cfa8e49120c029a05..dba4134759f0c37df3dd5bcd616fbe7e790f3ae0 100644
--- a/sbroad/sbroad-core/src/frontend/sql/ir/tests/params.rs
+++ b/sbroad/sbroad-core/src/frontend/sql/ir/tests/params.rs
@@ -186,7 +186,7 @@ fn front_params2() {
 
     let expected_explain = String::from(
         r#"projection ("test_space"."id"::unsigned -> "id")
-    selection ROW("test_space"."sys_op"::unsigned) = ROW(NULL::integer) and ROW("test_space"."FIRST_NAME"::string) = ROW('hello'::string)
+    selection ROW("test_space"."sys_op"::unsigned) = ROW(NULL::unknown) and ROW("test_space"."FIRST_NAME"::string) = ROW('hello'::string)
         scan "test_space"
 execution options:
     vdbe_max_steps = 45000
@@ -207,7 +207,7 @@ fn front_params3() {
 
     let expected_explain = String::from(
         r#"projection ("test_space"."id"::unsigned -> "id")
-    selection ROW("test_space"."sys_op"::unsigned) = ROW(NULL::integer) and ROW("test_space"."FIRST_NAME"::string) = ROW('кириллица'::string)
+    selection ROW("test_space"."sys_op"::unsigned) = ROW(NULL::unknown) and ROW("test_space"."FIRST_NAME"::string) = ROW('кириллица'::string)
         scan "test_space"
 execution options:
     vdbe_max_steps = 45000
diff --git a/sbroad/sbroad-core/src/ir.rs b/sbroad/sbroad-core/src/ir.rs
index 130d84934ebb679d5a382b5c18d24754f205380d..ff86c5e37bd56d5b2c32f127eafbbac96b3da95c 100644
--- a/sbroad/sbroad-core/src/ir.rs
+++ b/sbroad/sbroad-core/src/ir.rs
@@ -34,7 +34,7 @@ use crate::ir::node::{
     StableFunction, Trim, UnaryExpr, Values,
 };
 use crate::ir::operator::Bool;
-use crate::ir::relation::Column;
+use crate::ir::relation::{Column, DerivedType};
 use crate::ir::tree::traversal::{
     BreadthFirst, PostOrder, PostOrderWithFilter, EXPR_CAPACITY, REL_CAPACITY,
 };
@@ -1824,7 +1824,7 @@ impl Plan {
 }
 
 impl Plan {
-    fn get_param_type(&self, param_id: NodeId) -> Result<Option<Type>, SbroadError> {
+    fn get_param_type(&self, param_id: NodeId) -> Result<DerivedType, SbroadError> {
         let node = self.get_node(param_id)?;
         if let Node::Parameter(ty) = node {
             return Ok(ty.param_type);
@@ -1838,7 +1838,7 @@ impl Plan {
     fn set_param_type(&mut self, param_id: NodeId, ty: Type) -> Result<(), SbroadError> {
         let node = self.get_mut_node(param_id)?;
         if let MutNode::Parameter(param) = node {
-            param.param_type = Some(ty);
+            param.param_type.set(ty);
             Ok(())
         } else {
             Err(SbroadError::Invalid(
@@ -1876,7 +1876,7 @@ impl Plan {
         let mut inferred_types = vec![None; params_count];
 
         for (node_id, param_idx) in &self.pg_params_map {
-            let param_type = self.get_param_type(*node_id)?;
+            let param_type = *self.get_param_type(*node_id)?.get();
             let inferred_type = inferred_types.get(*param_idx).unwrap_or_else(|| {
                 panic!("param idx {param_idx} exceeds params count {params_count}")
             });
diff --git a/sbroad/sbroad-core/src/ir/aggregates.rs b/sbroad/sbroad-core/src/ir/aggregates.rs
index d7f55f513a05a17c2bc68c8d910640bd0cd76298..924e5dd0d3f4069a780952488fdde23a46205e0b 100644
--- a/sbroad/sbroad-core/src/ir/aggregates.rs
+++ b/sbroad/sbroad-core/src/ir/aggregates.rs
@@ -12,6 +12,7 @@ use std::rc::Rc;
 
 use super::expression::{ColumnPositionMap, FunctionFeature, Position};
 use super::node::expression::Expression;
+use super::relation::DerivedType;
 use crate::frontend::sql::ir::SubtreeCloner;
 
 /// The kind of aggregate function
@@ -60,22 +61,22 @@ impl AggregateKind {
     }
 
     #[inline(always)]
-    pub fn to_type(self, plan: &Plan, args: &[NodeId]) -> Result<RelType, SbroadError> {
-        match self {
-            AggregateKind::COUNT => Ok(RelType::Unsigned),
-            AggregateKind::TOTAL => Ok(RelType::Double),
-            AggregateKind::GRCONCAT => Ok(RelType::String),
-            AggregateKind::SUM | AggregateKind::AVG => Ok(RelType::Decimal),
-            AggregateKind::MIN | AggregateKind::MAX => {
-                let child_node =
-                    args.first()
-                        .ok_or(SbroadError::UnexpectedNumberOfValues(format_smolstr!(
-                            "expected at least 1 argument, got 0"
-                        )))?;
-                let expr_node = plan.get_expression_node(*child_node)?;
-                expr_node.calculate_type(plan)
-            }
-        }
+    pub fn to_type(self, plan: &Plan, args: &[NodeId]) -> Result<DerivedType, SbroadError> {
+        let ty =
+            match self {
+                AggregateKind::COUNT => RelType::Unsigned,
+                AggregateKind::TOTAL => RelType::Double,
+                AggregateKind::GRCONCAT => RelType::String,
+                AggregateKind::SUM | AggregateKind::AVG => RelType::Decimal,
+                AggregateKind::MIN | AggregateKind::MAX => {
+                    let child_node = args.first().ok_or(SbroadError::UnexpectedNumberOfValues(
+                        format_smolstr!("expected at least 1 argument, got 0"),
+                    ))?;
+                    let expr_node = plan.get_expression_node(*child_node)?;
+                    return expr_node.calculate_type(plan);
+                }
+            };
+        Ok(DerivedType::new(ty))
     }
 
     #[must_use]
@@ -97,7 +98,11 @@ impl AggregateKind {
     /// - Invalid index
     /// - Node doesn't exist in the plan
     /// - Node is not an expression type
-    pub fn get_arg_type(idx: usize, plan: &Plan, args: &[NodeId]) -> Result<RelType, SbroadError> {
+    pub fn get_arg_type(
+        idx: usize,
+        plan: &Plan,
+        args: &[NodeId],
+    ) -> Result<DerivedType, SbroadError> {
         let arg_id = *args.get(idx).ok_or(SbroadError::NotFound(
             Entity::Index,
             format_smolstr!("no element at index {idx} in args {args:?}"),
@@ -127,23 +132,35 @@ impl AggregateKind {
         match self {
             AggregateKind::SUM | AggregateKind::AVG | AggregateKind::TOTAL => {
                 let arg_type = Self::get_arg_type(0, plan, args)?;
+                let Some(arg_type) = arg_type.get() else {
+                    return Ok(());
+                };
                 if !matches!(
                     arg_type,
                     RelType::Decimal | RelType::Double | RelType::Unsigned | RelType::Integer
                 ) {
-                    err(&arg_type)?;
+                    err(arg_type)?;
                 }
             }
             AggregateKind::MIN | AggregateKind::MAX => {
                 let arg_type = Self::get_arg_type(0, plan, args)?;
+                let Some(arg_type) = arg_type.get() else {
+                    return Ok(());
+                };
                 if !arg_type.is_scalar() {
-                    err(&arg_type)?;
+                    err(arg_type)?;
                 }
             }
             AggregateKind::GRCONCAT => {
-                let first_type = Self::get_arg_type(0, plan, args)?;
+                let arg_type_first = Self::get_arg_type(0, plan, args)?;
+                let Some(first_type) = arg_type_first.get() else {
+                    return Ok(());
+                };
                 if args.len() == 2 {
-                    let second_type = Self::get_arg_type(1, plan, args)?;
+                    let arg_type_second = Self::get_arg_type(1, plan, args)?;
+                    let Some(second_type) = arg_type_second.get() else {
+                        return Ok(());
+                    };
                     if first_type != second_type {
                         return Err(SbroadError::Invalid(
                             Entity::Query,
@@ -155,7 +172,7 @@ impl AggregateKind {
                     }
                 }
                 if !matches!(first_type, RelType::String) {
-                    err(&first_type)?;
+                    err(first_type)?;
                 }
             }
             AggregateKind::COUNT => {}
@@ -317,7 +334,7 @@ impl SimpleAggregate {
         &self,
         parent: NodeId,
         plan: &mut Plan,
-        fun_type: RelType,
+        fun_type: DerivedType,
         mut position_kinds: Vec<PositionKind>,
         is_distinct: bool,
     ) -> Result<NodeId, SbroadError> {
diff --git a/sbroad/sbroad-core/src/ir/api/constant.rs b/sbroad/sbroad-core/src/ir/api/constant.rs
index 4fd7c574629323f81f770f410b96ea510c898258..10f3a5e253655116c88b80e1bc5f7a101966231f 100644
--- a/sbroad/sbroad-core/src/ir/api/constant.rs
+++ b/sbroad/sbroad-core/src/ir/api/constant.rs
@@ -4,6 +4,7 @@ use crate::errors::{Entity, SbroadError};
 use crate::ir::node::expression::Expression;
 use crate::ir::node::{Constant, Node64, NodeId, Parameter};
 use crate::ir::value::Value;
+use crate::ir::DerivedType;
 use crate::ir::{ArenaType, Nodes, Plan};
 
 impl Expression<'_> {
@@ -105,9 +106,12 @@ impl Plan {
     pub fn stash_constants(&mut self) -> Result<(), SbroadError> {
         let constants = self.get_const_list();
         for const_id in constants {
-            let const_node = self
-                .nodes
-                .replace(const_id, Node64::Parameter(Parameter { param_type: None }))?;
+            let const_node = self.nodes.replace(
+                const_id,
+                Node64::Parameter(Parameter {
+                    param_type: DerivedType::unknown(),
+                }),
+            )?;
             self.constants.insert(const_id, const_node);
         }
         Ok(())
diff --git a/sbroad/sbroad-core/src/ir/api/parameter.rs b/sbroad/sbroad-core/src/ir/api/parameter.rs
index 440372b2e94a44b9e386677f68ac685ee16846f4..17bfd45237d8f4110858848800deeeb5cf95f433 100644
--- a/sbroad/sbroad-core/src/ir/api/parameter.rs
+++ b/sbroad/sbroad-core/src/ir/api/parameter.rs
@@ -7,12 +7,12 @@ use crate::ir::node::{
     MutNode, Node64, NodeId, Parameter, Procedure, Row, Selection, StableFunction, Trim, UnaryExpr,
     ValuesRow,
 };
+use crate::ir::relation::DerivedType;
 use crate::ir::tree::traversal::{LevelNode, PostOrder, PostOrderWithFilter};
 use crate::ir::value::Value;
 use crate::ir::{ArenaType, Node, OptionParamValue, Plan, ValueIdx};
 use smol_str::format_smolstr;
 
-use crate::ir::relation::Type;
 use ahash::{AHashMap, AHashSet, RandomState};
 use std::collections::HashMap;
 
@@ -66,6 +66,9 @@ fn get_param_value(
 impl<'binder> ParamsBinder<'binder> {
     fn new(plan: &'binder mut Plan, mut values: Vec<Value>) -> Result<Self, SbroadError> {
         let capacity = plan.nodes.len();
+
+        // Note: `need_output` is set to false for `subtree_iter` specially to avoid traversing
+        //       the same nodes twice. See `update_values_row` for more info.
         let mut tree = PostOrder::with_capacity(|node| plan.subtree_iter(node, false), capacity);
         let top_id = plan.get_top()?;
         tree.populate_nodes(top_id);
@@ -402,24 +405,6 @@ impl<'binder> ParamsBinder<'binder> {
     /// Replace parameters in the plan.
     #[allow(clippy::too_many_lines)]
     fn bind_params(&mut self) -> Result<(), SbroadError> {
-        let mut exprs_to_set_ref_type: HashMap<NodeId, Type> = HashMap::new();
-
-        for LevelNode(_, id) in &self.nodes {
-            // Before binding, references that referred to
-            // parameters had scalar type (by default),
-            // but in fact they may refer to different stuff.
-            if let Node::Expression(expr) = self.plan.get_node(*id)? {
-                if let Expression::Reference { .. } = expr {
-                    exprs_to_set_ref_type.insert(*id, expr.recalculate_type(self.plan)?);
-                    continue;
-                }
-            }
-        }
-        for (id, new_type) in exprs_to_set_ref_type {
-            let mut expr = self.plan.get_mut_expression_node(id)?;
-            expr.set_ref_type(new_type);
-        }
-
         // Len of `value_ids` - `param_index` = param index we are currently binding.
         let mut param_index = self.value_ids.len();
 
@@ -586,21 +571,45 @@ impl<'binder> ParamsBinder<'binder> {
         }
         Ok(())
     }
+
+    fn recalculate_ref_types(&mut self) -> Result<(), SbroadError> {
+        for LevelNode(_, id) in &self.nodes {
+            // Before binding, references that referred to
+            // parameters had an unknown types,
+            // but in fact they should have the types of given parameters.
+            let new_type = if let Node::Expression(ref mut expr @ Expression::Reference(_)) =
+                self.plan.get_node(*id)?
+            {
+                Some(expr.recalculate_ref_type(self.plan)?)
+            } else {
+                None
+            };
+
+            if let Some(new_type) = new_type {
+                let MutNode::Expression(ref mut expr @ MutExpression::Reference { .. }) =
+                    self.plan.get_mut_node(*id)?
+                else {
+                    panic!("Reference expected to set recalculated type")
+                };
+                expr.set_ref_type(new_type);
+            }
+        }
+        Ok(())
+    }
 }
 
 impl Plan {
     pub fn add_param(&mut self) -> NodeId {
-        self.nodes.push(Parameter { param_type: None }.into())
+        self.nodes.push(
+            Parameter {
+                param_type: DerivedType::unknown(),
+            }
+            .into(),
+        )
     }
 
     /// Bind params related to `Option` clause.
     /// Returns the number of params binded to options.
-    ///
-    /// # Errors
-    /// - User didn't provide parameter value for corresponding option parameter
-    ///
-    /// # Panics
-    /// - Plan is inconsistent state
     pub fn bind_option_params(&mut self, values: &mut Vec<Value>) -> usize {
         // Bind parameters in options to values.
         // Because the Option clause is the last clause in the
@@ -629,9 +638,7 @@ impl Plan {
     }
 
     // Gather all parameter nodes from the tree to a hash set.
-    /// # Panics
     #[must_use]
-    /// # Panics
     pub fn get_param_set(&self) -> AHashSet<NodeId> {
         let param_set: AHashSet<NodeId> = self
             .nodes
@@ -654,13 +661,9 @@ impl Plan {
 
     /// Synchronize values row output with the data tuple after parameter binding.
     ///
-    /// # Errors
-    /// - Node is not values row
-    /// - Output and data tuples have different number of columns
-    /// - Output is not a row of aliases
-    ///
-    /// # Panics
-    /// - Plan is inconsistent state
+    /// ValuesRow fields `data` and `output` are referencing the same nodes. We exclude
+    /// their output from nodes traversing. And in case parameters are met under ValuesRow, we don't update
+    /// references in its output. That why we have to traverse the tree one more time fixing output.
     pub fn update_values_row(&mut self, id: NodeId) -> Result<(), SbroadError> {
         let values_row = self.get_node(id)?;
         let (output_id, data_id) =
@@ -692,10 +695,6 @@ impl Plan {
     /// Substitute parameters to the plan.
     /// The purpose of this function is to find every `Parameter` node and replace it
     /// with `Expression::Constant` (under the row).
-    ///
-    /// # Errors
-    /// - Invalid amount of parameters.
-    /// - Internal errors.
     #[allow(clippy::too_many_lines)]
     pub fn bind_params(&mut self, values: Vec<Value>) -> Result<(), SbroadError> {
         if values.is_empty() {
@@ -734,6 +733,7 @@ impl Plan {
         binder.cover_params_with_rows()?;
         binder.bind_params()?;
         binder.update_value_rows()?;
+        binder.recalculate_ref_types()?;
 
         Ok(())
     }
diff --git a/sbroad/sbroad-core/src/ir/ddl.rs b/sbroad/sbroad-core/src/ir/ddl.rs
index b7b78eda7dcf40a7fde02095876f2d2e2447aa8a..39091dcdd1b782bef2a756f28013fc276d2791eb 100644
--- a/sbroad/sbroad-core/src/ir/ddl.rs
+++ b/sbroad/sbroad-core/src/ir/ddl.rs
@@ -20,17 +20,27 @@ impl Default for ColumnDef {
     fn default() -> Self {
         Self {
             name: SmolStr::default(),
-            data_type: RelationType::default(),
+            // TODO: Fix to Option.
+            data_type: RelationType::Any,
             is_nullable: true,
         }
     }
 }
 
-#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
 pub struct ParamDef {
     pub data_type: RelationType,
 }
 
+impl Default for ParamDef {
+    fn default() -> Self {
+        Self {
+            // TODO: Remove later.
+            data_type: RelationType::Any,
+        }
+    }
+}
+
 #[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
 pub enum Language {
     #[default]
diff --git a/sbroad/sbroad-core/src/ir/distribution.rs b/sbroad/sbroad-core/src/ir/distribution.rs
index 473a96aa4c039f8139796a1eb4b34fccb99c3596..d3091484e4dfd62c0f8f1cfee8d7e9b63e8c7004 100644
--- a/sbroad/sbroad-core/src/ir/distribution.rs
+++ b/sbroad/sbroad-core/src/ir/distribution.rs
@@ -60,13 +60,15 @@ impl Key {
                             format_smolstr!("column {name} not found at position {pos}"),
                         )
                     })?;
-                    if !column.r#type.is_scalar() {
-                        return Err(SbroadError::Invalid(
-                            Entity::Column,
-                            Some(format_smolstr!(
-                                "column {name} at position {pos} is not scalar"
-                            )),
-                        ));
+                    if let Some(ty) = column.r#type.get() {
+                        if !ty.is_scalar() {
+                            return Err(SbroadError::Invalid(
+                                Entity::Column,
+                                Some(format_smolstr!(
+                                    "column {name} at position {pos} is not scalar"
+                                )),
+                            ));
+                        }
                     }
                     Ok(pos)
                 }
diff --git a/sbroad/sbroad-core/src/ir/explain.rs b/sbroad/sbroad-core/src/ir/explain.rs
index 07deb80938ccc72716f8a1a10b8567b59dda291d..0fc4e21a08155486bea140fd7e91f85ca9cc86a6 100644
--- a/sbroad/sbroad-core/src/ir/explain.rs
+++ b/sbroad/sbroad-core/src/ir/explain.rs
@@ -20,7 +20,6 @@ use crate::ir::node::{
     Selection, StableFunction, Trim, UnaryExpr, Update as UpdateRel, Values, ValuesRow,
 };
 use crate::ir::operator::{ConflictStrategy, JoinKind, OrderByElement, OrderByEntity, OrderByType};
-use crate::ir::relation::Type;
 use crate::ir::transformation::redistribution::{
     MotionKey as IrMotionKey, MotionPolicy as IrMotionPolicy, Target as IrTarget,
 };
@@ -31,6 +30,7 @@ use super::node::expression::Expression;
 use super::node::relational::Relational;
 use super::node::Limit;
 use super::operator::{Arithmetic, Bool, Unary};
+use super::relation::DerivedType;
 use super::tree::traversal::{LevelNode, PostOrder, EXPR_CAPACITY, REL_CAPACITY};
 use super::value::Value;
 
@@ -41,7 +41,7 @@ enum ColExpr {
     Arithmetic(Box<ColExpr>, Arithmetic, Box<ColExpr>),
     Bool(Box<ColExpr>, Bool, Box<ColExpr>),
     Unary(Unary, Box<ColExpr>),
-    Column(String, Type),
+    Column(String, DerivedType),
     Cast(Box<ColExpr>, CastType),
     Case(
         Option<Box<ColExpr>>,
@@ -50,7 +50,13 @@ enum ColExpr {
     ),
     Concat(Box<ColExpr>, Box<ColExpr>),
     Like(Box<ColExpr>, Box<ColExpr>, Option<Box<ColExpr>>),
-    StableFunction(SmolStr, Vec<ColExpr>, Option<FunctionFeature>, Type, bool),
+    StableFunction(
+        SmolStr,
+        Vec<ColExpr>,
+        Option<FunctionFeature>,
+        DerivedType,
+        bool,
+    ),
     Trim(Option<TrimKind>, Option<Box<ColExpr>>, Box<ColExpr>),
     Row(Row),
     None,
@@ -99,8 +105,9 @@ impl Display for ColExpr {
                 }
                 let is_distinct = matches!(feature, Some(FunctionFeature::Distinct));
                 let formatted_args = format!("({})", args.iter().format(", "));
+                let func_type_name = func_type.to_string();
                 format!(
-                    "{name}({}{formatted_args})::{func_type}",
+                    "{name}({}{formatted_args})::{func_type_name}",
                     if is_distinct { "distinct " } else { "" }
                 )
             }
diff --git a/sbroad/sbroad-core/src/ir/expression.rs b/sbroad/sbroad-core/src/ir/expression.rs
index e457a672bdde406ad4d7eb6a3eca6acdedcd82a2..4130242389fa5cd9be673d433a62ea4aa5069541 100644
--- a/sbroad/sbroad-core/src/ir/expression.rs
+++ b/sbroad/sbroad-core/src/ir/expression.rs
@@ -24,7 +24,7 @@ use crate::errors::{Entity, SbroadError};
 use crate::executor::engine::helpers::to_user;
 use crate::ir::node::ReferenceAsteriskSource;
 use crate::ir::operator::Bool;
-use crate::ir::relation::Type;
+use crate::ir::relation::{DerivedType, Type};
 use crate::ir::tree::traversal::{PostOrderWithFilter, EXPR_CAPACITY};
 use crate::ir::{Nodes, Plan, Positions as Targets};
 
@@ -140,7 +140,7 @@ impl Nodes {
         parent: Option<NodeId>,
         targets: Option<Vec<usize>>,
         position: usize,
-        col_type: Type,
+        col_type: DerivedType,
         asterisk_source: Option<ReferenceAsteriskSource>,
     ) -> NodeId {
         let r = Reference {
@@ -1482,7 +1482,8 @@ impl Plan {
                 }
             }
             Expression::Reference(Reference { col_type, .. }) => {
-                return Ok(matches!(col_type, Type::Boolean))
+                let col_type_inner = col_type.get();
+                return Ok(col_type_inner.map_or(true, |t| matches!(t, Type::Boolean)));
             }
             _ => {}
         }
diff --git a/sbroad/sbroad-core/src/ir/expression/cast.rs b/sbroad/sbroad-core/src/ir/expression/cast.rs
index b94bb007e4deaa91733ae6d7d5f76a7409ad4dd6..bc41b172dc80cc1f567865643a2d212f35e19982 100644
--- a/sbroad/sbroad-core/src/ir/expression/cast.rs
+++ b/sbroad/sbroad-core/src/ir/expression/cast.rs
@@ -107,7 +107,7 @@ impl Type {
     #[must_use]
     pub fn as_relation_type(&self) -> RelationType {
         match self {
-            Type::Any => RelationType::default(),
+            Type::Any => RelationType::Any,
             Type::Map => RelationType::Map,
             Type::Boolean => RelationType::Boolean,
             Type::Datetime => RelationType::Datetime,
@@ -135,7 +135,7 @@ impl Plan {
 
         let child_plan_node = self.get_mut_node(expr_id)?;
         if let MutNode::Parameter(ty) = child_plan_node {
-            ty.param_type = Some(to_type.as_relation_type());
+            ty.param_type.set(to_type.as_relation_type());
         }
 
         Ok(cast_id)
diff --git a/sbroad/sbroad-core/src/ir/expression/tests.rs b/sbroad/sbroad-core/src/ir/expression/tests.rs
index f8bbd0b7952e8897f63a8e820534530921ba4042..e9431184e3071109590159cda5821cc4abce8030 100644
--- a/sbroad/sbroad-core/src/ir/expression/tests.rs
+++ b/sbroad/sbroad-core/src/ir/expression/tests.rs
@@ -3,7 +3,7 @@ use crate::ir::tests::{column_integer_user_non_null, sharding_column};
 use pretty_assertions::assert_eq;
 use smol_str::SmolStr;
 
-use crate::ir::relation::{Column, SpaceEngine, Table, Type};
+use crate::ir::relation::{Column, DerivedType, SpaceEngine, Table, Type};
 use crate::ir::value::Value;
 use crate::ir::Plan;
 
@@ -69,7 +69,7 @@ fn derive_expr_type() {
     fn column(name: SmolStr, ty: Type) -> Column {
         Column {
             name,
-            r#type: ty,
+            r#type: DerivedType::new(ty),
             role: Default::default(),
             is_nullable: false,
         }
@@ -106,40 +106,58 @@ fn derive_expr_type() {
         .add_arithmetic_to_plan(b_id, Arithmetic::Divide, c_id)
         .unwrap();
     let expr = plan.get_expression_node(arith_divide_id).unwrap();
-    assert_eq!(expr.calculate_type(&plan).unwrap(), Type::Integer);
+    assert_eq!(
+        expr.calculate_type(&plan).unwrap().get().unwrap(),
+        Type::Integer
+    );
 
     // d*e
     let arith_multiply_id = plan
         .add_arithmetic_to_plan(d_id, Arithmetic::Multiply, e_id)
         .unwrap();
     let expr = plan.get_expression_node(arith_multiply_id).unwrap();
-    assert_eq!(expr.calculate_type(&plan).unwrap(), Type::Decimal);
+    assert_eq!(
+        expr.calculate_type(&plan).unwrap().get().unwrap(),
+        Type::Decimal
+    );
 
     // (b/c + d*e)
     let arith_addition_id = plan
         .add_arithmetic_to_plan(arith_divide_id, Arithmetic::Add, arith_multiply_id)
         .unwrap();
     let expr = plan.get_expression_node(arith_addition_id).unwrap();
-    assert_eq!(expr.calculate_type(&plan).unwrap(), Type::Decimal);
+    assert_eq!(
+        expr.calculate_type(&plan).unwrap().get().unwrap(),
+        Type::Decimal
+    );
 
     // (b/c + d*e) * f
     let arith_multiply_id2 = plan
         .add_arithmetic_to_plan(arith_addition_id, Arithmetic::Multiply, f_id)
         .unwrap();
     let expr = plan.get_expression_node(arith_multiply_id2).unwrap();
-    assert_eq!(expr.calculate_type(&plan).unwrap(), Type::Double);
+    assert_eq!(
+        expr.calculate_type(&plan).unwrap().get().unwrap(),
+        Type::Double
+    );
 
     // a + (b/c + d*e) * f
     let arith_addition_id2 = plan
         .add_arithmetic_to_plan(a_id, Arithmetic::Add, arith_multiply_id2)
         .unwrap();
     let expr = plan.get_expression_node(arith_addition_id2).unwrap();
-    assert_eq!(expr.calculate_type(&plan).unwrap(), Type::Double);
+    assert_eq!(
+        expr.calculate_type(&plan).unwrap().get().unwrap(),
+        Type::Double
+    );
 
     // a + (b/c + d*e) * f - b
     let arith_subract_id = plan
         .add_arithmetic_to_plan(arith_addition_id2, Arithmetic::Subtract, b_id)
         .unwrap();
     let expr = plan.get_expression_node(arith_subract_id).unwrap();
-    assert_eq!(expr.calculate_type(&plan).unwrap(), Type::Double);
+    assert_eq!(
+        expr.calculate_type(&plan).unwrap().get().unwrap(),
+        Type::Double
+    );
 }
diff --git a/sbroad/sbroad-core/src/ir/expression/types.rs b/sbroad/sbroad-core/src/ir/expression/types.rs
index 0ee9a9d180d0ae00ca2c3dccb92e98fe82f35f0b..e2d8475511879bc9208c86e7cda696a6d8a76161 100644
--- a/sbroad/sbroad-core/src/ir/expression/types.rs
+++ b/sbroad/sbroad-core/src/ir/expression/types.rs
@@ -2,7 +2,11 @@ use smol_str::{format_smolstr, ToSmolStr};
 
 use crate::{
     errors::{Entity, SbroadError, TypeError},
-    ir::{relation::Type, Plan},
+    executor::vtable::calculate_unified_types,
+    ir::{
+        relation::{DerivedType, Type},
+        Plan,
+    },
 };
 
 use super::{
@@ -11,7 +15,7 @@ use super::{
 };
 
 impl Plan {
-    fn get_node_type(&self, node_id: NodeId) -> Result<Type, SbroadError> {
+    fn get_node_type(&self, node_id: NodeId) -> Result<DerivedType, SbroadError> {
         match self.get_node(node_id)? {
             Node::Expression(expr) => expr.calculate_type(self),
             Node::Relational(relational) => Err(SbroadError::Invalid(
@@ -22,7 +26,7 @@ impl Plan {
             )),
             // Parameter nodes must recalculate their type during
             // binding (see `bind_params` function).
-            Node::Parameter(ty) => Ok(ty.param_type.unwrap_or(Type::default())),
+            Node::Parameter(ty) => Ok(ty.param_type),
             Node::Ddl(ddl) => Err(SbroadError::Invalid(
                 Entity::Node,
                 Some(format_smolstr!("DDL node {ddl:?} has no type")),
@@ -57,93 +61,92 @@ impl Plan {
 
 impl Expression<'_> {
     /// Calculate the type of the expression.
-    ///
-    /// # Errors
-    /// - the row list contains non-expression nodes;
-    ///
-    /// # Panics
-    /// - Plan is in inconsistent state
-    pub fn calculate_type(&self, plan: &Plan) -> Result<Type, SbroadError> {
-        match self {
+    pub fn calculate_type(&self, plan: &Plan) -> Result<DerivedType, SbroadError> {
+        let ty = match self {
             Expression::Case(Case {
                 when_blocks,
                 else_expr,
                 ..
             }) => {
-                let mut case_type = None;
-                let check_types_corresponds = |case_type: &Type, ret_expr_type: &Type| {
-                    if case_type != ret_expr_type {
-                        return if matches!(ret_expr_type, Type::Array)
-                            || matches!(ret_expr_type, Type::Map)
-                        {
-                            Some(Type::Any)
-                        } else {
-                            Some(Type::default())
-                        };
+                // Outer option -- for uninitialized type.
+                // Inner option -- for the case of Null met.
+                let mut case_ty_general: Option<DerivedType> = None;
+                let mut check_types_corresponds = |another_ty: &DerivedType| {
+                    if let Some(case_ty) = case_ty_general {
+                        match (case_ty.get(), another_ty.get()) {
+                            (Some(case_ty), Some(another_ty)) => {
+                                if *case_ty != *another_ty {
+                                    return Err(SbroadError::TypeError(TypeError::TypeMismatch(
+                                        *case_ty,
+                                        *another_ty,
+                                    )));
+                                }
+                            }
+                            (None, Some(_)) => case_ty_general = Some(*another_ty),
+                            (_, _) => {}
+                        }
+                    } else {
+                        case_ty_general = Some(*another_ty)
                     }
-                    None
+                    Ok(())
                 };
 
                 for (_, ret_expr) in when_blocks {
                     let ret_expr_type = plan.get_node_type(*ret_expr)?;
-                    if let Some(case_type) = &case_type {
-                        if let Some(ret_type) = check_types_corresponds(case_type, &ret_expr_type) {
-                            return Ok(ret_type);
-                        }
-                    } else {
-                        case_type = Some(ret_expr_type);
-                    }
+                    check_types_corresponds(&ret_expr_type)?
                 }
-                let case_type_unwrapped = case_type.expect("Case WHEN type must be known");
                 if let Some(else_expr) = else_expr {
                     let else_expr_type = plan.get_node_type(*else_expr)?;
-                    if let Some(ret_type) =
-                        check_types_corresponds(&case_type_unwrapped, &else_expr_type)
-                    {
-                        return Ok(ret_type);
-                    }
+                    check_types_corresponds(&else_expr_type)?
                 }
-                Ok(case_type_unwrapped)
+                case_ty_general.expect("Case type must be known")
             }
             Expression::Alias(Alias { child, .. })
             | Expression::ExprInParentheses(ExprInParentheses { child }) => {
-                plan.get_node_type(*child)
+                plan.get_node_type(*child)?
             }
             Expression::Bool(_) | Expression::Unary(_) | Expression::Like { .. } => {
-                Ok(Type::Boolean)
+                DerivedType::new(Type::Boolean)
             }
             Expression::Arithmetic(ArithmeticExpr {
                 left, right, op, ..
             }) => {
                 let left_type = plan.get_node_type(*left)?;
                 let right_type = plan.get_node_type(*right)?;
-                match (&left_type, &right_type) {
+
+                let (left_type, right_type) = match (left_type.get(), right_type.get()) {
+                    (Some(l_t), Some(r_t)) => (l_t, r_t),
+                    _ => return Ok(DerivedType::unknown()),
+                };
+
+                let res = match (left_type, right_type) {
                     (Type::Double, Type::Double | Type::Unsigned | Type::Integer | Type::Decimal)
                     | (Type::Unsigned | Type::Integer | Type::Decimal, Type::Double) => {
-                        Ok(Type::Double)
+                        Type::Double
                     }
                     (Type::Decimal, Type::Decimal | Type::Unsigned | Type::Integer)
-                    | (Type::Unsigned | Type::Integer, Type::Decimal) => Ok(Type::Decimal),
+                    | (Type::Unsigned | Type::Integer, Type::Decimal) => Type::Decimal,
                     (Type::Integer, Type::Unsigned | Type::Integer)
-                    | (Type::Unsigned, Type::Integer) => Ok(Type::Integer),
-                    (Type::Unsigned, Type::Unsigned) => Ok(Type::Unsigned),
-                    _ => Err(SbroadError::Invalid(
+                    | (Type::Unsigned, Type::Integer) => Type::Integer,
+                    (Type::Unsigned, Type::Unsigned) => Type::Unsigned,
+                    _ => return Err(SbroadError::Invalid(
                         Entity::Expression,
                         Some(format_smolstr!("types {left_type} and {right_type} are not supported for arithmetic expression ({:?} {op:?} {:?})",
                         plan.get_node(*left)?, plan.get_node(*right)?)),
                     )),
-                }
+                };
+                DerivedType::new(res)
             }
-            Expression::Cast(Cast { to, .. }) => Ok(to.as_relation_type()),
-            Expression::Trim(_) | Expression::Concat(_) => Ok(Type::String),
-            Expression::Constant(Constant { value, .. }) => Ok(value.get_type()),
-            Expression::Reference(Reference { col_type, .. }) => Ok(*col_type),
+            Expression::Cast(Cast { to, .. }) => DerivedType::new(to.as_relation_type()),
+            Expression::Trim(_) | Expression::Concat(_) => DerivedType::new(Type::String),
+            Expression::Constant(Constant { value, .. }) => value.get_type(),
+            Expression::Reference(Reference { col_type, .. }) => *col_type,
             Expression::Row(Row { list, .. }) => {
                 if let (Some(expr_id), None) = (list.first(), list.get(1)) {
                     let expr = plan.get_expression_node(*expr_id)?;
-                    expr.calculate_type(plan)
+                    expr.calculate_type(plan)?
                 } else {
-                    Ok(Type::Array)
+                    DerivedType::new(Type::Array)
                 }
             }
             Expression::StableFunction(StableFunction {
@@ -160,27 +163,31 @@ impl Expression<'_> {
                             .first()
                             .expect("min/max functions must have an argument");
                         let expr = plan.get_expression_node(*expr_id)?;
-                        expr.calculate_type(plan)
+                        expr.calculate_type(plan)?
                     }
                     "coalesce" => {
-                        let first_id = children.first().expect("coalesce must have children");
-                        let child = plan.get_expression_node(*first_id)?;
-                        let ty = child.calculate_type(plan)?;
-                        // ensure all the types are the same
+                        let mut last_ty = DerivedType::unknown();
                         for child_id in children {
                             let child = plan.get_expression_node(*child_id)?;
-                            let child_ty = child.calculate_type(plan)?;
-                            if child_ty != ty {
-                                return Err(TypeError::TypeMismatch(ty, child_ty).into());
+                            let ty = child.calculate_type(plan)?;
+                            if let Some(ty) = ty.get() {
+                                if let Some(last_ty) = last_ty.get() {
+                                    if ty != last_ty {
+                                        return Err(TypeError::TypeMismatch(*last_ty, *ty).into());
+                                    }
+                                } else {
+                                    last_ty.set(*ty)
+                                }
                             }
                         }
-                        Ok(ty)
+                        last_ty
                     }
-                    _ => Ok(*func_type),
+                    _ => *func_type,
                 }
             }
-            Expression::CountAsterisk(_) => Ok(Type::Integer),
-        }
+            Expression::CountAsterisk(_) => DerivedType::new(Type::Integer),
+        };
+        Ok(ty)
     }
 
     /// Returns the recalculated type of the expression.
@@ -194,59 +201,60 @@ impl Expression<'_> {
     /// where we can't calculate type of
     /// upper reference, because we don't know what value will be
     /// passed as an argument.
-    /// When `resolve_metadata` is called references are typed with `Scalar`.
+    /// When `resolve_metadata` is called references have unknown type.
     /// When `bind_params` is called references types are refined.
-    ///
-    /// # Errors
-    /// - if the reference is invalid;
-    pub fn recalculate_type(&self, plan: &Plan) -> Result<Type, SbroadError> {
-        if let Expression::Reference(Reference {
+    pub fn recalculate_ref_type(&self, plan: &Plan) -> Result<DerivedType, SbroadError> {
+        let prev_type = self.calculate_type(plan)?;
+        let Expression::Reference(Reference {
             parent,
             targets,
             position,
             ..
         }) = self
-        {
-            let parent_id = parent.ok_or_else(|| {
-                SbroadError::Invalid(
-                    Entity::Expression,
-                    Some("reference expression has no parent".to_smolstr()),
-                )
-            })?;
-            let parent_rel = plan.get_relation_node(parent_id)?;
-            // We are interested only in the first target, because:
-            // - union all relies on the first child type;
-            // - scan has no children (and the space column type can't change anyway);
-            if let Some(Some(target)) = targets.as_ref().map(|targets| targets.first()) {
-                let target_children = parent_rel.children();
-                let target_rel_id = *target_children.get(*target).ok_or_else(|| {
-                    SbroadError::Invalid(
-                        Entity::Expression,
-                        Some(format_smolstr!(
-                            "reference expression has no target relation at position {target}"
-                        )),
-                    )
-                })?;
-                let target_rel = plan.get_relation_node(target_rel_id)?;
-                let columns = plan.get_row_list(target_rel.output())?;
-                let column_id = *columns.get(*position).ok_or_else(|| {
-                    SbroadError::Invalid(
-                        Entity::Expression,
-                        Some(format_smolstr!(
-                            "reference expression has no target column at position {position}"
-                        )),
-                    )
-                })?;
-                let col_expr = plan.get_expression_node(column_id)?;
-                return col_expr.calculate_type(plan);
-            }
+        else {
+            return Ok(prev_type);
+        };
+
+        let Some(targets) = targets else {
+            // No need to recalculate types for Scan node references.
+            return Ok(prev_type);
+        };
+
+        let parent_id = parent.ok_or_else(|| {
+            SbroadError::Invalid(
+                Entity::Expression,
+                Some("reference expression has no parent".to_smolstr()),
+            )
+        })?;
+        let parent_rel = plan.get_relation_node(parent_id)?;
+        let rel_children: crate::ir::api::children::Children<'_> = parent_rel.children();
+
+        let mut types = Vec::new();
+        for target_index in targets {
+            let target_rel_id = *rel_children.get(*target_index).unwrap_or_else(|| {
+                panic!("reference expression has no target relation at position {target_index}")
+            });
+
+            let target_rel = plan.get_relation_node(target_rel_id)?;
+            let columns = plan.get_row_list(target_rel.output())?;
+            let column_id = *columns.get(*position).unwrap_or_else(|| {
+                panic!("reference expression has no target column at position {position}")
+            });
+            let col_expr = plan.get_expression_node(column_id)?;
+            let ty: DerivedType = col_expr.calculate_type(plan)?;
+            types.push(vec![ty])
         }
-        self.calculate_type(plan)
+
+        let unified_res = calculate_unified_types(&types)?;
+        let (_, unified_type) = unified_res.first().expect(
+            "Vec for reference unified type recalculation should consists of a single column.",
+        );
+        Ok(*unified_type)
     }
 }
 
 impl MutExpression<'_> {
-    pub fn set_ref_type(&mut self, new_type: Type) {
+    pub fn set_ref_type(&mut self, new_type: DerivedType) {
         if let MutExpression::Reference(Reference { col_type, .. }) = self {
             *col_type = new_type;
         }
diff --git a/sbroad/sbroad-core/src/ir/function.rs b/sbroad/sbroad-core/src/ir/function.rs
index f0d433b46e4243a8d2557bfd91d75b3d4b7e1219..79509a861de6a6cc9e697e5232fc683fa1d8ab17 100644
--- a/sbroad/sbroad-core/src/ir/function.rs
+++ b/sbroad/sbroad-core/src/ir/function.rs
@@ -2,12 +2,12 @@ use crate::errors::{Entity, SbroadError};
 use crate::executor::engine::helpers::to_user;
 use crate::ir::aggregates::AggregateKind;
 use crate::ir::node::{NodeId, StableFunction};
-use crate::ir::relation::Type;
 use crate::ir::Plan;
 use serde::{Deserialize, Serialize};
 use smol_str::{format_smolstr, SmolStr, ToSmolStr};
 
 use super::expression::FunctionFeature;
+use super::relation::DerivedType;
 
 #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
 pub enum Behavior {
@@ -23,7 +23,7 @@ pub enum Behavior {
 pub struct Function {
     pub name: SmolStr,
     pub behavior: Behavior,
-    pub func_type: Type,
+    pub func_type: DerivedType,
     /// True if this function is provided by tarantool,
     /// when referencing this func in local sql, we must
     /// not use quotes
@@ -32,7 +32,7 @@ pub struct Function {
 
 impl Function {
     #[must_use]
-    pub fn new(name: SmolStr, behavior: Behavior, func_type: Type, is_system: bool) -> Self {
+    pub fn new(name: SmolStr, behavior: Behavior, func_type: DerivedType, is_system: bool) -> Self {
         Self {
             name,
             behavior,
@@ -42,7 +42,7 @@ impl Function {
     }
 
     #[must_use]
-    pub fn new_stable(name: SmolStr, func_type: Type, is_system: bool) -> Self {
+    pub fn new_stable(name: SmolStr, func_type: DerivedType, is_system: bool) -> Self {
         Self::new(name, Behavior::Stable, func_type, is_system)
     }
 
diff --git a/sbroad/sbroad-core/src/ir/helpers.rs b/sbroad/sbroad-core/src/ir/helpers.rs
index ff0aa44a971b0e47dfca0b8f4e8baee1e992ee4e..90e1ac92aa4e609476d3e7fa3a2d1317b291f43c 100644
--- a/sbroad/sbroad-core/src/ir/helpers.rs
+++ b/sbroad/sbroad-core/src/ir/helpers.rs
@@ -186,10 +186,11 @@ impl Plan {
                         writeln!(buf, "NO TARGETS")?;
                     }
 
+                    let col_type_str = col_type.to_string();
                     writeln_with_tabulation(
                         buf,
                         tabulation_number + 1,
-                        format!("Column type: {col_type}").as_str(),
+                        format!("Column type: {col_type_str}").as_str(),
                     )?;
                 }
                 Expression::Row(Row { list, distribution }) => {
diff --git a/sbroad/sbroad-core/src/ir/helpers/tests.rs b/sbroad/sbroad-core/src/ir/helpers/tests.rs
index f13bf7a13eecae293e546ab1f159701303207db9..daa4424840245b7a5269a143219a544f5f084982 100644
--- a/sbroad/sbroad-core/src/ir/helpers/tests.rs
+++ b/sbroad/sbroad-core/src/ir/helpers/tests.rs
@@ -20,11 +20,11 @@ fn simple_select() {
 	Output_id: 064
 		[id: 064] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0, 1] }}) })]
 			List:
-				[id: 032] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 0, col_type: Integer, asterisk_source: None })]
-				[id: 132] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 1, col_type: String, asterisk_source: None })]
-				[id: 232] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 2, col_type: Boolean, asterisk_source: None })]
-				[id: 332] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 3, col_type: Unsigned, asterisk_source: None })]
-				[id: 432] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 4, col_type: Unsigned, asterisk_source: None })]
+				[id: 032] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
+				[id: 132] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
+				[id: 232] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 2, col_type: DerivedType(Some(Boolean)), asterisk_source: None })]
+				[id: 332] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 3, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 432] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 4, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 364] relation: Projection
@@ -33,7 +33,7 @@ fn simple_select() {
 	Output_id: 264
 		[id: 264] expression: Row [distribution = Some(Any)]
 			List:
-				[id: 532] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: String, asterisk_source: None })]
+				[id: 532] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
 ---------------------------------------------
 "#);
 
@@ -59,11 +59,11 @@ fn simple_join() {
 	Output_id: 064
 		[id: 064] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 032] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 0, col_type: Unsigned, asterisk_source: None })]
-				[id: 132] expression: Alias [name = sysFrom, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 1, col_type: Unsigned, asterisk_source: None })]
-				[id: 232] expression: Alias [name = FIRST_NAME, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 2, col_type: String, asterisk_source: None })]
-				[id: 332] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 3, col_type: Unsigned, asterisk_source: None })]
-				[id: 432] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 4, col_type: Unsigned, asterisk_source: None })]
+				[id: 032] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 0, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 132] expression: Alias [name = sysFrom, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 1, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 232] expression: Alias [name = FIRST_NAME, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 2, col_type: DerivedType(Some(String)), asterisk_source: None })]
+				[id: 332] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 3, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 432] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 4, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 364] relation: Projection
@@ -72,7 +72,7 @@ fn simple_join() {
 	Output_id: 264
 		[id: 264] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 532] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Unsigned, asterisk_source: None })]
+				[id: 532] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 564] relation: ScanSubQuery
@@ -82,7 +82,7 @@ fn simple_join() {
 	Output_id: 464
 		[id: 464] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 632] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 5, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Unsigned, asterisk_source: None })]
+				[id: 632] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 5, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 764] relation: ScanRelation
@@ -91,11 +91,11 @@ fn simple_join() {
 	Output_id: 664
 		[id: 664] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0, 1] }}) })]
 			List:
-				[id: 732] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 0, col_type: Integer, asterisk_source: None })]
-				[id: 832] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 1, col_type: String, asterisk_source: None })]
-				[id: 932] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 2, col_type: Boolean, asterisk_source: None })]
-				[id: 1032] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 3, col_type: Unsigned, asterisk_source: None })]
-				[id: 1132] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 4, col_type: Unsigned, asterisk_source: None })]
+				[id: 732] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
+				[id: 832] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
+				[id: 932] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 2, col_type: DerivedType(Some(Boolean)), asterisk_source: None })]
+				[id: 1032] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 3, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 1132] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 4, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 964] relation: Projection
@@ -104,7 +104,7 @@ fn simple_join() {
 	Output_id: 864
 		[id: 864] expression: Row [distribution = Some(Any)]
 			List:
-				[id: 1232] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1232] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 1164] relation: ScanSubQuery
@@ -114,7 +114,7 @@ fn simple_join() {
 	Output_id: 1064
 		[id: 1064] expression: Row [distribution = Some(Any)]
 			List:
-				[id: 1332] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 11, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1332] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 11, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 0136] relation: Motion [policy = Segment(MotionKey { targets: [Reference(0)] }), alias = t2]
@@ -123,7 +123,7 @@ fn simple_join() {
 	Output_id: 2064
 		[id: 2064] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 1932] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 0, arena_type: Arena136 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1932] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 0, arena_type: Arena136 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 1564] relation: InnerJoin
@@ -153,8 +153,8 @@ fn simple_join() {
 	Output_id: 1464
 		[id: 1464] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [1] }, Key { positions: [0] }}) })]
 			List:
-				[id: 1532] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 15, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Unsigned, asterisk_source: None })]
-				[id: 1632] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 15, arena_type: Arena64 }), targets: Some([1]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1532] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 15, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 1632] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 15, arena_type: Arena64 }), targets: Some([1]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 1764] relation: Projection
@@ -163,7 +163,7 @@ fn simple_join() {
 	Output_id: 1664
 		[id: 1664] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 1732] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 17, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Unsigned, asterisk_source: None })]
+				[id: 1732] expression: Alias [name = id, child = Reference(Reference { parent: Some(NodeId { offset: 17, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 "#);
 
@@ -197,11 +197,11 @@ fn simple_join_subtree() {
 	Output_id: 664
 		[id: 664] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0, 1] }}) })]
 			List:
-				[id: 732] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 0, col_type: Integer, asterisk_source: None })]
-				[id: 832] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 1, col_type: String, asterisk_source: None })]
-				[id: 932] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 2, col_type: Boolean, asterisk_source: None })]
-				[id: 1032] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 3, col_type: Unsigned, asterisk_source: None })]
-				[id: 1132] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 4, col_type: Unsigned, asterisk_source: None })]
+				[id: 732] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
+				[id: 832] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
+				[id: 932] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 2, col_type: DerivedType(Some(Boolean)), asterisk_source: None })]
+				[id: 1032] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 3, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 1132] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 7, arena_type: Arena64 }), targets: None, position: 4, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 964] relation: Projection
@@ -210,7 +210,7 @@ fn simple_join_subtree() {
 	Output_id: 864
 		[id: 864] expression: Row [distribution = Some(Any)]
 			List:
-				[id: 1232] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1232] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 1164] relation: ScanSubQuery
@@ -220,7 +220,7 @@ fn simple_join_subtree() {
 	Output_id: 1064
 		[id: 1064] expression: Row [distribution = Some(Any)]
 			List:
-				[id: 1332] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 11, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1332] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 11, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 0136] relation: Motion [policy = Segment(MotionKey { targets: [Reference(0)] }), alias = t2]
@@ -229,7 +229,7 @@ fn simple_join_subtree() {
 	Output_id: 2064
 		[id: 2064] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 1932] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 0, arena_type: Arena136 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
+				[id: 1932] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 0, arena_type: Arena136 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
 ---------------------------------------------
 "#
     );
@@ -252,26 +252,26 @@ fn simple_aggregation_with_group_by() {
 	Output_id: 064
 		[id: 064] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0, 1] }}) })]
 			List:
-				[id: 032] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 0, col_type: Integer, asterisk_source: None })]
-				[id: 132] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 1, col_type: String, asterisk_source: None })]
-				[id: 232] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 2, col_type: Boolean, asterisk_source: None })]
-				[id: 332] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 3, col_type: Unsigned, asterisk_source: None })]
-				[id: 432] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 4, col_type: Unsigned, asterisk_source: None })]
+				[id: 032] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
+				[id: 132] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
+				[id: 232] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 2, col_type: DerivedType(Some(Boolean)), asterisk_source: None })]
+				[id: 332] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 3, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 432] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 1, arena_type: Arena64 }), targets: None, position: 4, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 364] relation: GroupBy [is_final = false]
 	Gr_cols:
-		Gr_col: Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: String, asterisk_source: None })
+		Gr_col: Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })
 	Children:
 		Child_id = 164
 	Output_id: 264
 		[id: 264] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0, 1] }}) })]
 			List:
-				[id: 532] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: Integer, asterisk_source: None })]
-				[id: 632] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: String, asterisk_source: None })]
-				[id: 732] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 2, col_type: Boolean, asterisk_source: None })]
-				[id: 832] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 3, col_type: Unsigned, asterisk_source: None })]
-				[id: 932] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 4, col_type: Unsigned, asterisk_source: None })]
+				[id: 532] expression: Alias [name = identification_number, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(Integer)), asterisk_source: None })]
+				[id: 632] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
+				[id: 732] expression: Alias [name = product_units, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 2, col_type: DerivedType(Some(Boolean)), asterisk_source: None })]
+				[id: 832] expression: Alias [name = sys_op, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 3, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
+				[id: 932] expression: Alias [name = bucket_id, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 4, col_type: DerivedType(Some(Unsigned)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 764] relation: Projection
@@ -280,7 +280,7 @@ fn simple_aggregation_with_group_by() {
 	Output_id: 664
 		[id: 664] expression: Row [distribution = Some(Any)]
 			List:
-				[id: 1132] expression: Alias [name = column_596, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: String, asterisk_source: None })]
+				[id: 1132] expression: Alias [name = column_596, child = Reference(Reference { parent: Some(NodeId { offset: 3, arena_type: Arena64 }), targets: Some([0]), position: 1, col_type: DerivedType(Some(String)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 0136] relation: Motion [policy = Segment(MotionKey { targets: [Reference(0)] }), alias = None]
@@ -289,18 +289,18 @@ fn simple_aggregation_with_group_by() {
 	Output_id: 1064
 		[id: 1064] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 1332] expression: Alias [name = column_596, child = Reference(Reference { parent: Some(NodeId { offset: 0, arena_type: Arena136 }), targets: Some([0]), position: 0, col_type: String, asterisk_source: None })]
+				[id: 1332] expression: Alias [name = column_596, child = Reference(Reference { parent: Some(NodeId { offset: 0, arena_type: Arena136 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(String)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 964] relation: GroupBy [is_final = true]
 	Gr_cols:
-		Gr_col: Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: String, asterisk_source: None })
+		Gr_col: Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(String)), asterisk_source: None })
 	Children:
 		Child_id = 0136
 	Output_id: 864
 		[id: 864] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 1232] expression: Alias [name = column_596, child = Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: String, asterisk_source: None })]
+				[id: 1232] expression: Alias [name = column_596, child = Reference(Reference { parent: Some(NodeId { offset: 9, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(String)), asterisk_source: None })]
 ---------------------------------------------
 ---------------------------------------------
 [id: 564] relation: Projection
@@ -309,7 +309,7 @@ fn simple_aggregation_with_group_by() {
 	Output_id: 464
 		[id: 464] expression: Row [distribution = Some(Segment { keys: KeySet({Key { positions: [0] }}) })]
 			List:
-				[id: 1032] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 5, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: String, asterisk_source: None })]
+				[id: 1032] expression: Alias [name = product_code, child = Reference(Reference { parent: Some(NodeId { offset: 5, arena_type: Arena64 }), targets: Some([0]), position: 0, col_type: DerivedType(Some(String)), asterisk_source: None })]
 ---------------------------------------------
 "#);
 
diff --git a/sbroad/sbroad-core/src/ir/node.rs b/sbroad/sbroad-core/src/ir/node.rs
index a228fae44cbab421b24406811b47ea6bdd91552f..f0b4b5003d25079b2235f0dd35cfee44c2622e1d 100644
--- a/sbroad/sbroad-core/src/ir/node.rs
+++ b/sbroad/sbroad-core/src/ir/node.rs
@@ -19,13 +19,13 @@ use super::{
     ddl::AlterSystemType,
     expression::{cast, FunctionFeature, TrimKind},
     operator::{self, ConflictStrategy, JoinKind, OrderByElement, UpdateStrategy},
+    relation::DerivedType,
 };
 use crate::ir::{
     acl::{AlterOption, GrantRevokeType},
     ddl::{ColumnDef, Language, ParamDef, SetParamScopeType, SetParamValue},
     distribution::Distribution,
     helpers::RepeatableState,
-    relation::Type,
     transformation::redistribution::{ColumnPosition, MotionPolicy, Program},
     value::Value,
 };
@@ -257,7 +257,7 @@ pub struct Reference {
     /// Expression position in the input tuple (i.e. `Alias` column).
     pub position: usize,
     /// Referred column type in the input tuple.
-    pub col_type: Type,
+    pub col_type: DerivedType,
     /// Field indicating whether this reference resulted
     /// from an asterisk "*" under projection.
     pub asterisk_source: Option<ReferenceAsteriskSource>,
@@ -308,7 +308,7 @@ pub struct StableFunction {
     /// Optional function feature.
     pub feature: Option<FunctionFeature>,
     /// Function return type.
-    pub func_type: Type,
+    pub func_type: DerivedType,
     /// Whether function is provided by tarantool,
     /// when referencing these funcs from local
     /// sql we must not use quotes.
@@ -380,7 +380,7 @@ impl From<Case> for NodeAligned {
 
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub struct Parameter {
-    pub param_type: Option<Type>,
+    pub param_type: DerivedType,
 }
 
 impl From<Parameter> for NodeAligned {
diff --git a/sbroad/sbroad-core/src/ir/operator.rs b/sbroad/sbroad-core/src/ir/operator.rs
index 263e6e7fd9561ff876502b697601e0e5a512e1e7..7c29601338bb85831e9c5dd48a8a75008806cc3b 100644
--- a/sbroad/sbroad-core/src/ir/operator.rs
+++ b/sbroad/sbroad-core/src/ir/operator.rs
@@ -3,6 +3,7 @@
 //! Contains operator nodes that transform the tuples in IR tree.
 
 use crate::executor::engine::helpers::to_user;
+use crate::executor::vtable::calculate_unified_types;
 use crate::frontend::sql::get_unnamed_column_alias;
 use crate::ir::api::children::Children;
 use crate::ir::expression::PlanExpr;
@@ -28,6 +29,7 @@ use super::expression::{ColumnPositionMap, ExpressionId};
 use super::node::expression::{Expression, MutExpression};
 use super::node::relational::{MutRelational, Relational};
 use super::node::{ArenaType, Limit, Node, NodeAligned, SelectWithoutScan};
+use super::relation::DerivedType;
 use super::transformation::redistribution::{MotionPolicy, Program};
 use super::tree::traversal::{LevelNode, PostOrderWithFilter, EXPR_CAPACITY};
 use crate::ir::distribution::{Distribution, Key, KeySet};
@@ -1452,22 +1454,32 @@ impl Plan {
             ));
         };
 
+        let mut types = Vec::new();
+        for row_id in &value_rows {
+            let value_row = self.get_relation_node(*row_id)?;
+            let output_id = value_row.output();
+            let output: Expression<'_> = self.get_expression_node(output_id)?;
+            let row_list = output.get_row_list()?;
+            let tuple_types: Result<Vec<DerivedType>, SbroadError> = row_list
+                .iter()
+                .map(|col_id| {
+                    let col_expr = self.get_expression_node(*col_id)?;
+                    col_expr.calculate_type(self)
+                })
+                .collect();
+            types.push(tuple_types?)
+        }
+        let unified_types = calculate_unified_types(&types)?;
+
         // Generate a row of aliases referencing all the children.
         let mut aliases: Vec<NodeId> = Vec::with_capacity(names.len());
-        let columns = last_output.clone_row_list()?;
         for (pos, name) in names.iter().enumerate() {
-            let col_id = *columns.get(pos).ok_or_else(|| {
-                SbroadError::UnexpectedNumberOfValues(format_smolstr!(
-                    "Values node has no column at position {pos}"
-                ))
-            })?;
-            let col_expr = self.get_expression_node(col_id)?;
-            let col_type = col_expr.calculate_type(self)?;
+            let unified_type = unified_types[pos].1;
             let ref_id = self.nodes.add_ref(
                 None,
                 Some((0..value_rows.len()).collect::<Vec<usize>>()),
                 pos,
-                col_type,
+                unified_type,
                 None,
             );
             let alias_id = self.nodes.add_alias(name, ref_id)?;
diff --git a/sbroad/sbroad-core/src/ir/operator/tests.rs b/sbroad/sbroad-core/src/ir/operator/tests.rs
index 48bade8fab422df96370510050aa06b607b06ef0..5fc2bbd6fb7e85931221bfea9ddc526ca6e0c562 100644
--- a/sbroad/sbroad-core/src/ir/operator/tests.rs
+++ b/sbroad/sbroad-core/src/ir/operator/tests.rs
@@ -230,7 +230,12 @@ fn insert() {
         vec![
             column_user_non_null(SmolStr::from("a"), Type::Unsigned),
             column_user_non_null(SmolStr::from("b"), Type::Unsigned),
-            Column::new("c", Type::Unsigned, ColumnRole::Sharding, true),
+            Column::new(
+                "c",
+                DerivedType::new(Type::Unsigned),
+                ColumnRole::Sharding,
+                true,
+            ),
         ],
         &["a"],
         &["a"],
diff --git a/sbroad/sbroad-core/src/ir/relation.rs b/sbroad/sbroad-core/src/ir/relation.rs
index 558a8e948fcda7763dd99a66020841777c83f8ab..576cab1581735e713c34cb79e74ef084f660bee2 100644
--- a/sbroad/sbroad-core/src/ir/relation.rs
+++ b/sbroad/sbroad-core/src/ir/relation.rs
@@ -29,7 +29,7 @@ const DEFAULT_VALUE: Value = Value::Null;
 
 /// Supported column types, which is used in a schema only.
 /// This `Type` is derived from the result's metadata.
-#[derive(Serialize, Default, Deserialize, PartialEq, Hash, Debug, Eq, Clone, Copy)]
+#[derive(Serialize, Deserialize, PartialEq, Hash, Debug, Eq, Clone, Copy)]
 pub enum Type {
     Any,
     Map,
@@ -38,13 +38,39 @@ pub enum Type {
     Datetime,
     Decimal,
     Double,
-    #[default]
     Integer,
     String,
     Uuid,
     Unsigned,
 }
 
+/// Derived type (`Some<Type>`) or its absence (`None`).
+/// Type absence is possible in case we met a Null.
+#[derive(Serialize, Deserialize, PartialEq, Hash, Debug, Eq, Clone, Copy)]
+pub struct DerivedType(Option<Type>);
+
+impl DerivedType {
+    pub fn unknown() -> Self {
+        Self(None)
+    }
+
+    pub fn new(ty: Type) -> Self {
+        Self(Some(ty))
+    }
+
+    pub fn get(&self) -> &Option<Type> {
+        &self.0
+    }
+
+    pub fn get_mut(&mut self) -> &mut Option<Type> {
+        &mut self.0
+    }
+
+    pub fn set(&mut self, ty: Type) {
+        self.0 = Some(ty)
+    }
+}
+
 impl fmt::Display for Type {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         match self {
@@ -63,6 +89,15 @@ impl fmt::Display for Type {
     }
 }
 
+impl fmt::Display for DerivedType {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self.0 {
+            None => write!(f, "unknown"),
+            Some(t) => t.fmt(f),
+        }
+    }
+}
+
 impl From<&Type> for FieldType {
     fn from(data_type: &Type) -> Self {
         match data_type {
@@ -204,7 +239,7 @@ pub struct Column {
     /// Column name.
     pub name: SmolStr,
     /// Column type.
-    pub r#type: Type,
+    pub r#type: DerivedType,
     /// Column role.
     pub role: ColumnRole,
     /// Column is_nullable status.
@@ -216,7 +251,7 @@ impl Default for Column {
     fn default() -> Self {
         Column {
             name: SmolStr::default(),
-            r#type: Type::default(),
+            r#type: DerivedType::unknown(),
             role: ColumnRole::default(),
             is_nullable: true,
         }
@@ -225,18 +260,22 @@ impl Default for Column {
 
 impl From<Column> for Field {
     fn from(column: Column) -> Self {
-        let field = match column.r#type {
-            Type::Boolean => Field::boolean(column.name),
-            Type::Datetime => Field::datetime(column.name),
-            Type::Decimal => Field::decimal(column.name),
-            Type::Double => Field::double(column.name),
-            Type::Integer => Field::integer(column.name),
-            Type::String => Field::string(column.name),
-            Type::Uuid => Field::uuid(column.name),
-            Type::Unsigned => Field::unsigned(column.name),
-            Type::Array => Field::array(column.name),
-            Type::Any => Field::any(column.name),
-            Type::Map => Field::map(column.name),
+        let field = if let Some(ty) = column.r#type.get() {
+            match ty {
+                Type::Boolean => Field::boolean(column.name),
+                Type::Datetime => Field::datetime(column.name),
+                Type::Decimal => Field::decimal(column.name),
+                Type::Double => Field::double(column.name),
+                Type::Integer => Field::integer(column.name),
+                Type::String => Field::string(column.name),
+                Type::Uuid => Field::uuid(column.name),
+                Type::Unsigned => Field::unsigned(column.name),
+                Type::Array => Field::array(column.name),
+                Type::Any => Field::any(column.name),
+                Type::Map => Field::map(column.name),
+            }
+        } else {
+            Field::scalar(column.name)
         };
         field.is_nullable(true)
     }
@@ -244,7 +283,11 @@ impl From<Column> for Field {
 
 impl From<&Column> for FieldType {
     fn from(column: &Column) -> Self {
-        FieldType::from(&column.r#type)
+        if let Some(ty) = &column.r#type.get() {
+            FieldType::from(ty)
+        } else {
+            FieldType::Scalar
+        }
     }
 }
 
@@ -263,19 +306,25 @@ impl SerSerialize for Column {
     {
         let mut map = serializer.serialize_map(Some(3))?;
         map.serialize_entry("name", &self.name)?;
-        match &self.r#type {
-            Type::Boolean => map.serialize_entry("type", "boolean")?,
-            Type::Datetime => map.serialize_entry("type", "datetime")?,
-            Type::Decimal => map.serialize_entry("type", "decimal")?,
-            Type::Double => map.serialize_entry("type", "double")?,
-            Type::Integer => map.serialize_entry("type", "integer")?,
-            Type::String => map.serialize_entry("type", "string")?,
-            Type::Uuid => map.serialize_entry("type", "uuid")?,
-            Type::Unsigned => map.serialize_entry("type", "unsigned")?,
-            Type::Array => map.serialize_entry("type", "array")?,
-            Type::Any => map.serialize_entry("type", "any")?,
-            Type::Map => map.serialize_entry("type", "map")?,
-        }
+
+        let type_str = match &self.r#type.get() {
+            Some(ty) => match ty {
+                Type::Boolean => "boolean",
+                Type::Datetime => "datetime",
+                Type::Decimal => "decimal",
+                Type::Double => "double",
+                Type::Integer => "integer",
+                Type::String => "string",
+                Type::Uuid => "uuid",
+                Type::Unsigned => "unsigned",
+                Type::Array => "array",
+                Type::Any => "any",
+                Type::Map => "map",
+            },
+            None => "unknown",
+        };
+        map.serialize_entry("type", type_str)?;
+
         map.serialize_entry(
             "role",
             match self.role {
@@ -322,28 +371,22 @@ impl<'de> Visitor<'de> for ColumnVisitor {
 
         let is_nullable = matches!(column_is_nullable.as_str(), "true");
 
-        match column_type.as_str() {
-            "any" => Ok(Column::new(&column_name, Type::Any, role, is_nullable)),
-            "boolean" => Ok(Column::new(&column_name, Type::Boolean, role, is_nullable)),
-            "datetime" => Ok(Column::new(&column_name, Type::Datetime, role, is_nullable)),
-            "decimal" => Ok(Column::new(&column_name, Type::Decimal, role, is_nullable)),
-            "double" => Ok(Column::new(&column_name, Type::Double, role, is_nullable)),
-            "integer" => Ok(Column::new(&column_name, Type::Integer, role, is_nullable)),
-            "numeric" => Ok(Column::new(
-                &column_name,
-                Type::default(),
-                role,
-                is_nullable,
-            )),
-            "string" | "text" | "varchar" => {
-                Ok(Column::new(&column_name, Type::String, role, is_nullable))
-            }
-            "unsigned" => Ok(Column::new(&column_name, Type::Unsigned, role, is_nullable)),
-            "array" => Ok(Column::new(&column_name, Type::Array, role, is_nullable)),
-            "uuid" => Ok(Column::new(&column_name, Type::Uuid, role, is_nullable)),
-            "map" => Ok(Column::new(&column_name, Type::Map, role, is_nullable)),
-            s => Err(Error::custom(format!("unsupported column type: {s}"))),
-        }
+        let ty = match column_type.as_str() {
+            "any" => DerivedType::new(Type::Any),
+            "boolean" => DerivedType::new(Type::Boolean),
+            "datetime" => DerivedType::new(Type::Datetime),
+            "decimal" => DerivedType::new(Type::Decimal),
+            "double" => DerivedType::new(Type::Double),
+            "integer" | "numeric" => DerivedType::new(Type::Integer),
+            "string" | "text" | "varchar" => DerivedType::new(Type::String),
+            "unsigned" => DerivedType::new(Type::Unsigned),
+            "array" => DerivedType::new(Type::Array),
+            "uuid" => DerivedType::new(Type::Uuid),
+            "map" => DerivedType::new(Type::Map),
+            "unknown" => DerivedType::unknown(),
+            s => return Err(Error::custom(format!("unsupported column type: {s}"))),
+        };
+        Ok(Column::new(&column_name, ty, role, is_nullable))
     }
 }
 
@@ -359,10 +402,10 @@ impl<'de> Deserialize<'de> for Column {
 impl Column {
     /// Column constructor.
     #[must_use]
-    pub fn new(n: &str, t: Type, role: ColumnRole, is_nullable: bool) -> Self {
+    pub fn new(n: &str, ty: DerivedType, role: ColumnRole, is_nullable: bool) -> Self {
         Column {
             name: n.into(),
-            r#type: t,
+            r#type: ty,
             role,
             is_nullable,
         }
diff --git a/sbroad/sbroad-core/src/ir/relation/tests.rs b/sbroad/sbroad-core/src/ir/relation/tests.rs
index d260af142138df67c8f56aac72cdaa9b60fdad48..f073e68835523bbaa73e874061f43a18c0468d9b 100644
--- a/sbroad/sbroad-core/src/ir/relation/tests.rs
+++ b/sbroad/sbroad-core/src/ir/relation/tests.rs
@@ -98,8 +98,18 @@ fn table_seg_dno_bucket_id_column() {
             column_user_non_null(SmolStr::from("a"), Type::Boolean),
             column_user_non_null(SmolStr::from("b"), Type::Unsigned),
             column_user_non_null(SmolStr::from("c"), Type::String),
-            Column::new("bucket_id", Type::String, ColumnRole::Sharding, false),
-            Column::new("bucket_id2", Type::String, ColumnRole::Sharding, false),
+            Column::new(
+                "bucket_id",
+                DerivedType::new(Type::String),
+                ColumnRole::Sharding,
+                false,
+            ),
+            Column::new(
+                "bucket_id2",
+                DerivedType::new(Type::String),
+                ColumnRole::Sharding,
+                false,
+            ),
         ],
         &["b", "a"],
         &["b", "a"],
@@ -139,7 +149,12 @@ fn table_seg_compound_type_in_key() {
         Table::new_sharded(
             "t",
             vec![
-                Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, false),
+                Column::new(
+                    "bucket_id",
+                    DerivedType::new(Type::Unsigned),
+                    ColumnRole::Sharding,
+                    false
+                ),
                 column_user_non_null(SmolStr::from("a"), Type::Array),
             ],
             &["a"],
diff --git a/sbroad/sbroad-core/src/ir/tests.rs b/sbroad/sbroad-core/src/ir/tests.rs
index 4582bb1820d5057b1cbfd0d1fd3b1b8dcf4c0bc8..55dd49ecc2fd1dfb57b29b41794ef2d776b3ef4a 100644
--- a/sbroad/sbroad-core/src/ir/tests.rs
+++ b/sbroad/sbroad-core/src/ir/tests.rs
@@ -16,7 +16,7 @@ use smol_str::SmolStr;
 pub fn column_integer_user_non_null(name: SmolStr) -> Column {
     Column {
         name,
-        r#type: Type::Integer,
+        r#type: DerivedType::new(Type::Integer),
         role: ColumnRole::User,
         is_nullable: false,
     }
@@ -27,7 +27,7 @@ pub fn column_integer_user_non_null(name: SmolStr) -> Column {
 #[cfg(test)]
 pub fn vcolumn_integer_user_non_null() -> VTableColumn {
     VTableColumn {
-        r#type: Type::Integer,
+        r#type: DerivedType::new(Type::Integer),
         role: ColumnRole::User,
         is_nullable: false,
     }
@@ -43,7 +43,7 @@ pub fn vcolumn_integer_user_non_null() -> VTableColumn {
 pub fn column_user_non_null(name: SmolStr, r#type: Type) -> Column {
     Column {
         name,
-        r#type,
+        r#type: DerivedType::new(r#type),
         role: ColumnRole::User,
         is_nullable: false,
     }
@@ -54,7 +54,7 @@ pub fn column_user_non_null(name: SmolStr, r#type: Type) -> Column {
 #[cfg(test)]
 pub fn vcolumn_user_non_null(r#type: Type) -> VTableColumn {
     VTableColumn {
-        r#type,
+        r#type: DerivedType::new(r#type),
         role: ColumnRole::User,
         is_nullable: false,
     }
@@ -71,7 +71,7 @@ pub fn vcolumn_user_non_null(r#type: Type) -> VTableColumn {
 pub fn sharding_column() -> Column {
     Column {
         name: SmolStr::from("bucket_id"),
-        r#type: Type::Unsigned,
+        r#type: DerivedType::new(Type::Unsigned),
         role: ColumnRole::Sharding,
         is_nullable: true,
     }
@@ -83,7 +83,12 @@ fn get_node() {
 
     let t = Table::new_sharded(
         "t",
-        vec![Column::new("a", Type::Boolean, ColumnRole::User, false)],
+        vec![Column::new(
+            "a",
+            DerivedType::new(Type::Boolean),
+            ColumnRole::User,
+            false,
+        )],
         &["a"],
         &["a"],
         SpaceEngine::Memtx,
diff --git a/sbroad/sbroad-core/src/ir/transformation/equality_propagation.rs b/sbroad/sbroad-core/src/ir/transformation/equality_propagation.rs
index 1446e0ae70f3e81cb3793a0b397c17cba5c3e6fd..68b970cb83c9c94796d14ffd535621e68a579145 100644
--- a/sbroad/sbroad-core/src/ir/transformation/equality_propagation.rs
+++ b/sbroad/sbroad-core/src/ir/transformation/equality_propagation.rs
@@ -94,7 +94,7 @@ use crate::ir::helpers::RepeatableState;
 use crate::ir::node::expression::Expression;
 use crate::ir::node::{Constant, NodeId, Reference, ReferenceAsteriskSource, Row};
 use crate::ir::operator::Bool;
-use crate::ir::relation::Type;
+use crate::ir::relation::DerivedType;
 use crate::ir::transformation::merge_tuples::Chain;
 use crate::ir::transformation::OldNewTopIdPair;
 use crate::ir::value::{Trivalent, Value};
@@ -109,7 +109,7 @@ struct EqClassRef {
     targets: Option<Vec<usize>>,
     position: usize,
     parent: Option<NodeId>,
-    col_type: Type,
+    col_type: DerivedType,
     asterisk_source: Option<ReferenceAsteriskSource>,
 }
 
diff --git a/sbroad/sbroad-core/src/ir/transformation/equality_propagation/tests.rs b/sbroad/sbroad-core/src/ir/transformation/equality_propagation/tests.rs
index 60f1ff74e2ffd2b900d86b12508aa6c2463f522a..9d001af77017996dbd40527087fd95c8530b4b8a 100644
--- a/sbroad/sbroad-core/src/ir/transformation/equality_propagation/tests.rs
+++ b/sbroad/sbroad-core/src/ir/transformation/equality_propagation/tests.rs
@@ -1,7 +1,7 @@
 use std::collections::HashMap;
 
 use crate::collection;
-use crate::ir::relation::Type;
+use crate::ir::relation::{DerivedType, Type};
 use crate::ir::transformation::helpers::check_transformation;
 use crate::ir::value::Value;
 use crate::ir::Plan;
@@ -86,7 +86,7 @@ fn equality_propagation4() {
             "{} {} {}",
             r#"SELECT "t"."a" FROM "t""#,
             r#"WHERE ("t"."b") = (?) and ("t"."a") = (?) and ("t"."a") = (?)"#,
-            r#"and ("t"."b") = (?) and ("t"."a") = ("t"."b")"#,
+            r#"and ("t"."b") = (?) and ("t"."b") = ("t"."a")"#,
         ),
         vec![
             Value::from(1_u64),
@@ -113,8 +113,8 @@ fn equality_propagation5() {
             r#"SELECT "t"."a" FROM "t""#,
             r#"WHERE ("t"."d") = (?) and ("t"."c") = (?)"#,
             r#"and ("t"."a") = (?) and ("t"."b") = (?)"#,
-            r#"and ("t"."d") = ("t"."b") and ("t"."b") = ("t"."c")"#,
-            r#"and ("t"."c") = ("t"."a")"#,
+            r#"and ("t"."b") = ("t"."c") and ("t"."c") = ("t"."d")"#,
+            r#"and ("t"."d") = ("t"."a")"#,
         ),
         vec![
             Value::from(1_u64),
@@ -153,7 +153,7 @@ impl ColumnBuilder {
                 offset: 0,
                 arena_type: crate::ir::node::ArenaType::Arena64,
             }),
-            col_type: Type::Integer,
+            col_type: DerivedType::new(Type::Integer),
             asterisk_source: None,
         })
     }
diff --git a/sbroad/sbroad-core/src/ir/transformation/redistribution/groupby.rs b/sbroad/sbroad-core/src/ir/transformation/redistribution/groupby.rs
index 5e015f79831d553e20ac48674a47b46eff355a6b..599e304d3e5cbb9a06d27564d6ea8534f8b94177 100644
--- a/sbroad/sbroad-core/src/ir/transformation/redistribution/groupby.rs
+++ b/sbroad/sbroad-core/src/ir/transformation/redistribution/groupby.rs
@@ -867,7 +867,7 @@ impl Plan {
         arguments: &[NodeId],
         local_alias: &str,
     ) -> Result<NodeId, SbroadError> {
-        let fun = Function {
+        let fun: Function = Function {
             name: kind.to_smolstr(),
             behavior: Behavior::Stable,
             func_type: kind.to_type(self, arguments)?,
@@ -1168,13 +1168,15 @@ impl Plan {
             };
             let position = child_map.get(local_alias)?;
             let col_type = self.get_expression_node(*expr_id)?.calculate_type(self)?;
-            if !col_type.is_scalar() {
-                return Err(SbroadError::Invalid(
-                    Entity::Type,
-                    Some(format_smolstr!(
-                        "add_final_groupby: GroupBy expr ({expr_id:?}) is not scalar ({col_type})!"
-                    )),
-                ));
+            if let Some(col_type) = col_type.get() {
+                if !col_type.is_scalar() {
+                    return Err(SbroadError::Invalid(
+                        Entity::Type,
+                        Some(format_smolstr!(
+                            "add_final_groupby: GroupBy expr ({expr_id:?}) is not scalar ({col_type})!"
+                        )),
+                    ));
+                }
             }
             let new_col = Reference {
                 position,
@@ -1268,14 +1270,16 @@ impl Plan {
                 };
                 let position = alias_to_pos_map.get(local_alias)?;
                 let col_type = self.get_expression_node(expr_id)?.calculate_type(self)?;
-                if !col_type.is_scalar() {
-                    return Err(SbroadError::Invalid(
-                        Entity::Type,
-                        Some(format_smolstr!(
-                            "patch_finals: expected scalar expression, found: {col_type}"
-                        )),
-                    ));
-                };
+                if let Some(col_type) = col_type.get() {
+                    if !col_type.is_scalar() {
+                        return Err(SbroadError::Invalid(
+                            Entity::Type,
+                            Some(format_smolstr!(
+                                "patch_finals: expected scalar expression, found: {col_type}"
+                            )),
+                        ));
+                    };
+                }
                 let new_ref = Reference {
                     parent: Some(rel_id),
                     targets: Some(vec![0]),
diff --git a/sbroad/sbroad-core/src/ir/value.rs b/sbroad/sbroad-core/src/ir/value.rs
index fbf2e7139b3d48321e5c47161c362471cbd1a67b..c1739249684c571efb0530dea2888caa000e6d36 100644
--- a/sbroad/sbroad-core/src/ir/value.rs
+++ b/sbroad/sbroad-core/src/ir/value.rs
@@ -17,9 +17,11 @@ use tarantool::uuid::Uuid;
 use crate::error;
 use crate::errors::{Entity, SbroadError};
 use crate::executor::hash::ToHashString;
-use crate::ir::relation::Type;
+use crate::ir::relation::DerivedType;
 use crate::ir::value::double::Double;
 
+use super::relation::Type;
+
 #[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone, PartialOrd, Ord)]
 pub struct Tuple(pub(crate) Vec<Value>);
 
@@ -880,7 +882,11 @@ impl Value {
     /// # Errors
     /// - the value cannot be cast to the given type.
     #[allow(clippy::too_many_lines)]
-    pub fn cast_and_encode(&self, column_type: &Type) -> Result<EncodedValue, SbroadError> {
+    pub fn cast_and_encode(&self, column_type: &DerivedType) -> Result<EncodedValue, SbroadError> {
+        let Some(column_type) = column_type.get() else {
+            return Ok(self.into());
+        };
+
         // First, try variants returning EncodedValue::Ref to avoid cloning.
         match (column_type, self) {
             (Type::Any, value) => return Ok(value.into()),
@@ -900,8 +906,8 @@ impl Value {
     }
 
     #[must_use]
-    pub fn get_type(&self) -> Type {
-        match self {
+    pub fn get_type(&self) -> DerivedType {
+        let ty = match self {
             Value::Unsigned(_) => Type::Unsigned,
             Value::Integer(_) => Type::Integer,
             Value::Datetime(_) => Type::Datetime,
@@ -911,8 +917,9 @@ impl Value {
             Value::String(_) => Type::String,
             Value::Tuple(_) => Type::Array,
             Value::Uuid(_) => Type::Uuid,
-            Value::Null => Type::default(),
-        }
+            Value::Null => return DerivedType::unknown(),
+        };
+        DerivedType::new(ty)
     }
 }
 
diff --git a/sbroad/sbroad-core/src/ir/value/tests.rs b/sbroad/sbroad-core/src/ir/value/tests.rs
index 4067eaf45c7110a24a3cda09cbc75324437657b9..ed39dbc5edf6f66218a4ba331c4824befa5fb2ec 100644
--- a/sbroad/sbroad-core/src/ir/value/tests.rs
+++ b/sbroad/sbroad-core/src/ir/value/tests.rs
@@ -18,7 +18,7 @@ fn uuid() {
 
     assert_eq!(Value::from(t_uid_1), v_uid);
     assert_eq!(format!("{}", v_uid), uid.to_string());
-    assert_eq!(v_uid.get_type(), Type::Uuid);
+    assert_eq!(v_uid.get_type(), DerivedType::new(Type::Uuid));
     assert_eq!(v_uid.eq(&Value::Uuid(t_uid_1)), Trivalent::True);
     assert_eq!(v_uid.eq(&Value::Uuid(t_uid_2)), Trivalent::False);
     assert_eq!(
@@ -35,7 +35,7 @@ fn uuid() {
     );
     assert_eq!(
         Value::String(uid.to_string())
-            .cast_and_encode(&Type::Uuid)
+            .cast_and_encode(&DerivedType::new(Type::Uuid))
             .is_ok(),
         true
     );
@@ -46,7 +46,7 @@ fn uuid() {
 fn uuid_negative() {
     assert_eq!(
         Value::String("hello".to_string())
-            .cast_and_encode(&Type::Uuid)
+            .cast_and_encode(&DerivedType::new(Type::Uuid))
             .unwrap_err(),
         SbroadError::Invalid(
             Entity::Value,
diff --git a/src/pgproto/backend/describe.rs b/src/pgproto/backend/describe.rs
index 05aaa48356dfe7256a088af948c52b12bd39b23d..7eebde830571506b8111123238998d3590e8020e 100644
--- a/src/pgproto/backend/describe.rs
+++ b/src/pgproto/backend/describe.rs
@@ -15,7 +15,7 @@ use sbroad::{
             acl::Acl, block::Block, ddl::Ddl, expression::Expression, plugin::Plugin,
             relational::Relational, tcl::Tcl, Alias, GrantPrivilege, Node, RevokePrivilege,
         },
-        relation::Type as SbroadType,
+        relation::{DerivedType, Type as SbroadType},
         Plan,
     },
 };
@@ -297,16 +297,20 @@ impl MetadataColumn {
     }
 }
 
-fn pg_type_from_sbroad(sbroad: &SbroadType) -> PgResult<Type> {
-    match sbroad {
-        SbroadType::Integer | SbroadType::Unsigned => Ok(Type::INT8),
-        SbroadType::Map | SbroadType::Array | SbroadType::Any => Ok(Type::JSON),
-        SbroadType::String => Ok(Type::TEXT),
-        SbroadType::Boolean => Ok(Type::BOOL),
-        SbroadType::Double => Ok(Type::FLOAT8),
-        SbroadType::Decimal => Ok(Type::NUMERIC),
-        SbroadType::Uuid => Ok(Type::UUID),
-        SbroadType::Datetime => Ok(Type::TIMESTAMPTZ),
+fn pg_type_from_sbroad(sbroad: &DerivedType) -> Type {
+    if let Some(sbroad) = sbroad.get() {
+        match sbroad {
+            SbroadType::Integer | SbroadType::Unsigned => Type::INT8,
+            SbroadType::Map | SbroadType::Array | SbroadType::Any => Type::JSON,
+            SbroadType::String => Type::TEXT,
+            SbroadType::Boolean => Type::BOOL,
+            SbroadType::Double => Type::FLOAT8,
+            SbroadType::Decimal => Type::NUMERIC,
+            SbroadType::Uuid => Type::UUID,
+            SbroadType::Datetime => Type::TIMESTAMPTZ,
+        }
+    } else {
+        Type::UNKNOWN
     }
 }
 
@@ -329,7 +333,7 @@ fn dql_output_format(ir: &Plan) -> PgResult<Vec<MetadataColumn>> {
             )
             .into());
         };
-        let ty = pg_type_from_sbroad(&column_type)?;
+        let ty = pg_type_from_sbroad(&column_type);
         metadata.push(MetadataColumn::new(column_name, ty));
     }
     Ok(metadata)
diff --git a/src/sql.rs b/src/sql.rs
index 9bb25c2336c9e629facdf0281a5d8afd73780715..af19f0cec85b3e20b62b4f748ce706e63f9cdfbb 100644
--- a/src/sql.rs
+++ b/src/sql.rs
@@ -53,7 +53,7 @@ use sbroad::ir::node::plugin::{
 };
 use sbroad::ir::node::Node;
 use sbroad::ir::operator::ConflictStrategy;
-use sbroad::ir::relation::Type;
+use sbroad::ir::relation::{DerivedType, Type};
 use sbroad::ir::tree::traversal::{LevelNode, PostOrderWithFilter, REL_CAPACITY};
 use sbroad::ir::value::{LuaValue, Value};
 use sbroad::ir::{Options, Plan as IrPlan};
@@ -438,15 +438,17 @@ pub fn dispatch(mut query: Query<RouterRuntime>) -> traft::Result<Tuple> {
                     let param_def = &routine.params[pos];
                     let param_type = Type::try_from(param_def.r#type)?;
                     // Check that the value has a correct type.
-                    if !value.get_type().is_castable_to(&param_type) {
-                        return Err(Error::Sbroad(SbroadError::Invalid(
-                            Entity::Routine,
-                            Some(format_smolstr!(
-                                "expected {} for parameter on position {pos}, got {}",
-                                param_def.r#type,
-                                value.get_type(),
-                            )),
-                        )));
+                    if let Some(ty) = value.get_type().get() {
+                        if !ty.is_castable_to(&param_type) {
+                            return Err(Error::Sbroad(SbroadError::Invalid(
+                                Entity::Routine,
+                                Some(format_smolstr!(
+                                    "expected {} for parameter on position {pos}, got {}",
+                                    param_def.r#type,
+                                    ty,
+                                )),
+                            )));
+                        }
                     }
                     params.push(value);
                 }
@@ -1158,7 +1160,8 @@ fn alter_system_ir_node_to_op_or_result(
             else {
                 return Err(Error::other(format!("unknown parameter: '{param_name}'")));
             };
-            let Ok(casted_value) = param_value.cast_and_encode(&expected_type) else {
+            let Ok(casted_value) = param_value.cast_and_encode(&DerivedType::new(expected_type))
+            else {
                 let actual_type = value_type_str(param_value);
                 return Err(Error::other(format!(
                     "invalid value for '{param_name}' expected {expected_type}, got {actual_type}",
diff --git a/src/sql/router.rs b/src/sql/router.rs
index edd51f6d720e3b3dadef87cd9c25576c5fd08a9b..6386d75f348cba59b5f0d4ff0d91df1ba89195ca 100644
--- a/src/sql/router.rs
+++ b/src/sql/router.rs
@@ -37,7 +37,7 @@ use crate::storage::{Clusterwide, ClusterwideTable};
 use sbroad::executor::engine::helpers::normalize_name_from_sql;
 use sbroad::executor::engine::Metadata;
 use sbroad::ir::function::Function;
-use sbroad::ir::relation::{space_pk_columns, Column, ColumnRole, Table, Type};
+use sbroad::ir::relation::{space_pk_columns, Column, ColumnRole, DerivedType, Table, Type};
 
 use crate::sql::storage::StorageRuntime;
 use crate::traft::node;
@@ -513,7 +513,7 @@ impl Metadata for RouterMetadata {
             };
             let column = Column {
                 name: col_name.to_smolstr(),
-                r#type: col_type,
+                r#type: DerivedType::new(col_type),
                 role,
                 is_nullable,
             };
diff --git a/test/int/test_sql.py b/test/int/test_sql.py
index 2700bce809f2c54e24fcc2b33fbacb2cf14b29bb..aec9eae67b1a534a4fb1ec3656e1c1a8ad1db0ec 100644
--- a/test/int/test_sql.py
+++ b/test/int/test_sql.py
@@ -1899,6 +1899,33 @@ def test_check_format(cluster: Cluster):
     assert dml["row_count"] == 2
 
 
+def test_values(cluster: Cluster):
+    # Initially test scenarios (presented below) were described in the issue:
+    # https://git.picodata.io/core/picodata/-/issues/1278.
+    cluster.deploy(instance_count=2)
+    i1, _ = cluster.instances
+
+    ddl = i1.sql(""" create table t (a int primary key, b text) """)
+    assert ddl["row_count"] == 1
+
+    dml = i1.sql(""" insert into t values (1, (values(?))), (2, 'hi') """, None)
+    assert dml["row_count"] == 2
+
+    data = i1.sql("""select * from t """)
+    assert sorted(data) == [[1, None], [2, "hi"]]
+
+    ddl = i1.sql(
+        """ create table gt (a unsigned primary key, b integer) distributed globally """
+    )
+    assert ddl["row_count"] == 1
+
+    dml = i1.sql(""" insert into gt values (0, -9), (1, 9) """)
+    assert dml["row_count"] == 2
+
+    data = i1.sql("""select * from gt """)
+    assert data == [[0, -9], [1, 9]]
+
+
 def test_insert(cluster: Cluster):
     cluster.deploy(instance_count=2)
     i1, _ = cluster.instances