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(¶m_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(¶m_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