From ceb73b0839bbbdb0ea36b3c2cc0b857abcbddf14 Mon Sep 17 00:00:00 2001 From: Emir Vildanov <e.vildanov@picodata.io> Date: Fri, 13 Oct 2023 09:53:11 +0000 Subject: [PATCH] feat: add checks on table columns creation and insertion, add check on parameters number, add support of is_nullable metadata --- sbroad-cartridge/src/cartridge/config.rs | 13 +- .../src/cartridge/config/tests.rs | 19 +- .../test_app/test/integration/ddl_test.lua | 4 +- sbroad-core/src/backend/sql/tree/tests.rs | 44 +- sbroad-core/src/core-router.lua | 12 +- sbroad-core/src/executor/engine/helpers.rs | 53 +- sbroad-core/src/executor/engine/mock.rs | 496 ++++++++++++++---- sbroad-core/src/executor/result.rs | 60 ++- sbroad-core/src/executor/result/tests.rs | 6 +- sbroad-core/src/executor/tests.rs | 70 +-- sbroad-core/src/executor/tests/between.rs | 14 +- .../src/executor/tests/empty_motion.rs | 14 +- sbroad-core/src/executor/tests/exec_plan.rs | 155 ++---- sbroad-core/src/executor/tests/frontend.rs | 4 +- sbroad-core/src/executor/tests/not_eq.rs | 8 +- sbroad-core/src/executor/tests/not_in.rs | 8 +- sbroad-core/src/executor/vtable/tests.rs | 86 +-- sbroad-core/src/frontend/sql.rs | 62 ++- sbroad-core/src/frontend/sql/ir/tests.rs | 33 +- sbroad-core/src/ir.rs | 2 +- sbroad-core/src/ir/api/parameter.rs | 23 +- sbroad-core/src/ir/ddl.rs | 12 +- sbroad-core/src/ir/distribution/tests.rs | 11 +- sbroad-core/src/ir/expression/tests.rs | 7 +- sbroad-core/src/ir/operator/tests.rs | 92 ++-- sbroad-core/src/ir/relation.rs | 43 +- sbroad-core/src/ir/relation/tests.rs | 115 ++-- sbroad-core/src/ir/tests.rs | 50 +- .../ir/transformation/redistribution/tests.rs | 25 +- .../redistribution/tests/segment.rs | 20 +- sbroad-core/src/ir/tree/tests.rs | 17 +- .../sql/tree/arbitrary_projection_plan.yaml | 5 + .../sql/tree/arithmetic_projection_plan.yaml | 7 + .../sql/tree/arithmetic_selection_plan.yaml | 7 + 34 files changed, 971 insertions(+), 626 deletions(-) diff --git a/sbroad-cartridge/src/cartridge/config.rs b/sbroad-cartridge/src/cartridge/config.rs index 843f941f8a..dacc434e04 100644 --- a/sbroad-cartridge/src/cartridge/config.rs +++ b/sbroad-cartridge/src/cartridge/config.rs @@ -121,6 +121,17 @@ impl RouterConfiguration { )) } }; + let is_nullable: bool = match val["is_nullable"].as_bool() { + Some(b) => b, + None => { + return Err(SbroadError::Invalid( + Entity::ClusterSchema, + Some(format!( + "column is_nullable of table {current_space_name} is invalid" + )), + )) + } + }; let qualified_name = normalize_name_from_schema(name); debug!( Option::from("configuration parsing"), @@ -133,7 +144,7 @@ impl RouterConfiguration { } else { ColumnRole::User }; - let col = Column::new(&qualified_name, t, role); + let col = Column::new(&qualified_name, t, role, is_nullable); result.push(col); } result diff --git a/sbroad-cartridge/src/cartridge/config/tests.rs b/sbroad-cartridge/src/cartridge/config/tests.rs index ee32ffced4..2bd4134ed9 100644 --- a/sbroad-cartridge/src/cartridge/config/tests.rs +++ b/sbroad-cartridge/src/cartridge/config/tests.rs @@ -143,12 +143,17 @@ fn test_getting_table_segment() { let expected = Table::new_seg( "\"hash_testing\"", vec![ - Column::new("\"identification_number\"", Type::Integer, ColumnRole::User), - Column::new("\"product_code\"", Type::String, ColumnRole::User), - Column::new("\"product_units\"", Type::Boolean, ColumnRole::User), - Column::new("\"sys_op\"", Type::Number, ColumnRole::User), - Column::new("\"detail\"", Type::Array, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + Column::new( + "\"identification_number\"", + 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::Number, ColumnRole::User, false), + Column::new("\"detail\"", Type::Array, ColumnRole::User, false), + Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding, true), ], &["\"identification_number\"", "\"product_code\""], &["\"identification_number\""], @@ -231,7 +236,7 @@ fn test_invalid_schema() { parts: - path: bucket_id type: unsigned - is_nullable: false + is_nullable: true sharding_key: - FID - COMMON_ID diff --git a/sbroad-cartridge/test_app/test/integration/ddl_test.lua b/sbroad-cartridge/test_app/test/integration/ddl_test.lua index d7033291c7..85d04ab4d1 100644 --- a/sbroad-cartridge/test_app/test/integration/ddl_test.lua +++ b/sbroad-cartridge/test_app/test/integration/ddl_test.lua @@ -219,8 +219,8 @@ g.test_create_table = function() "sbroad.execute", { [[ CREATE TABLE t ( - a INT, - b TEXT, + a INT NOT NULL, + b TEXT NOT NULL, PRIMARY KEY (a, b) ) USING memtx diff --git a/sbroad-core/src/backend/sql/tree/tests.rs b/sbroad-core/src/backend/sql/tree/tests.rs index 1fb8d336b8..09b1574f66 100644 --- a/sbroad-core/src/backend/sql/tree/tests.rs +++ b/sbroad-core/src/backend/sql/tree/tests.rs @@ -5,12 +5,14 @@ use pretty_assertions::assert_eq; use crate::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::ir::operator::{Arithmetic, Bool, Unary}; -use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::relation::{SpaceEngine, Table, Type}; +use crate::ir::tests::sharding_column; use crate::ir::tree::Snapshot; use crate::ir::value::Value; use crate::ir::Plan; use super::*; +use crate::ir::tests::{column_integer_user_non_null, column_user_non_null}; #[test] fn sql_order_selection() { @@ -19,7 +21,7 @@ fn sql_order_selection() { let mut plan = Plan::default(); let t = Table::new_seg( "t", - vec![Column::new("a", Type::Boolean, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, @@ -99,13 +101,13 @@ fn sql_arithmetic_selection_plan() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), - Column::new("c", Type::Integer, ColumnRole::User), - Column::new("d", Type::Integer, ColumnRole::User), - Column::new("e", Type::Integer, ColumnRole::User), - Column::new("f", Type::Integer, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), + column_integer_user_non_null(String::from("c")), + column_integer_user_non_null(String::from("d")), + column_integer_user_non_null(String::from("e")), + column_integer_user_non_null(String::from("f")), + sharding_column(), ], &["a"], &["a"], @@ -296,13 +298,13 @@ fn sql_arithmetic_projection_plan() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), - Column::new("c", Type::Integer, ColumnRole::User), - Column::new("d", Type::Integer, ColumnRole::User), - Column::new("e", Type::Integer, ColumnRole::User), - Column::new("f", Type::Integer, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), + column_integer_user_non_null(String::from("c")), + column_integer_user_non_null(String::from("d")), + column_integer_user_non_null(String::from("e")), + column_integer_user_non_null(String::from("f")), + sharding_column(), ], &["a"], &["a"], @@ -470,11 +472,11 @@ fn sql_arbitrary_projection_plan() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), - Column::new("c", Type::Integer, ColumnRole::User), - Column::new("d", Type::Integer, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), + column_integer_user_non_null(String::from("c")), + column_integer_user_non_null(String::from("d")), + sharding_column(), ], &["a"], &["a"], diff --git a/sbroad-core/src/core-router.lua b/sbroad-core/src/core-router.lua index 4af155919d..ad9aea936f 100644 --- a/sbroad-core/src/core-router.lua +++ b/sbroad-core/src/core-router.lua @@ -197,11 +197,15 @@ end _G.group_buckets_by_replicasets = function(buckets) local map = {} for _, bucket_id in pairs(buckets) do - local rs = vshard.router.route(bucket_id).uuid - if map[rs] then - table.insert(map[rs], bucket_id) + local rs, err = vshard.router.route(bucket_id) + if err ~= nil then + return nil, err + end + local uuid = rs.uuid + if map[uuid] then + table.insert(map[uuid], bucket_id) else - map[rs] = {bucket_id} + map[uuid] = {bucket_id} end end diff --git a/sbroad-core/src/executor/engine/helpers.rs b/sbroad-core/src/executor/engine/helpers.rs index d1056c3bff..70e4862bdc 100644 --- a/sbroad-core/src/executor/engine/helpers.rs +++ b/sbroad-core/src/executor/engine/helpers.rs @@ -1,5 +1,6 @@ use ahash::AHashMap; +use itertools::enumerate; use std::{ any::Any, cmp::Ordering, @@ -628,9 +629,9 @@ pub fn dispatch( for (motion_id, vtable) in vtables.map() { if !vtable.get_bucket_index().is_empty() { return Err(SbroadError::Invalid( - Entity::Motion, - Some(format!("motion ({motion_id}) in subtree with distribution Single, but policy is not Full!")) - )); + Entity::Motion, + Some(format!("motion ({motion_id}) in subtree with distribution Single, but policy is not Full!")), + )); } } } @@ -650,6 +651,7 @@ pub fn dispatch( } } +#[allow(clippy::too_many_lines)] pub(crate) fn materialize_values( plan: &mut ExecutionPlan, motion_node_id: usize, @@ -678,14 +680,36 @@ pub(crate) fn materialize_values( let child_node_ref = plan.get_mut_ir_plan().get_mut_node(child_id)?; let child_node = std::mem::replace(child_node_ref, Node::Parameter); if let Node::Relational(Relational::Values { - children, output, .. + ref children, + output, + .. }) = child_node { // Build a new virtual table (check that all the rows are made of constants only). let mut vtable = VirtualTable::new(); vtable.get_mut_tuples().reserve(children.len()); + + let columns_len = if let Some(first_row_id) = children.first() { + let row_node = plan.get_ir_plan().get_relation_node(*first_row_id)?; + let Relational::ValuesRow { data, .. } = row_node else { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format!("Expected ValuesRow, got {row_node:?}")), + )); + }; + plan.get_ir_plan() + .get_expression_node(*data)? + .get_row_list()? + .len() + } else { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format!("Values node {child_node:?} must contain children")), + )); + }; + let mut nullable_column_indices = HashSet::with_capacity(columns_len); for row_id in children { - let row_node = plan.get_ir_plan().get_relation_node(row_id)?; + let row_node = plan.get_ir_plan().get_relation_node(*row_id)?; if let Relational::ValuesRow { data, children, .. } = row_node { // Check that there are no subqueries in the values node. // (If any we'll need to materialize them first with dispatch @@ -719,6 +743,9 @@ pub(crate) fn materialize_values( let column_node_ref = plan.get_mut_ir_plan().get_mut_node(column_id)?; let column_node = std::mem::replace(column_node_ref, Node::Parameter); if let Node::Expression(Expression::Constant { value, .. }) = column_node { + if let Value::Null = value { + nullable_column_indices.insert(idx); + } row.push(value); } else { return Err(SbroadError::Invalid( @@ -746,13 +773,15 @@ pub(crate) fn materialize_values( .get_row_list()?; let columns = vtable.get_mut_columns(); columns.reserve(output_cols.len()); - for column_id in output_cols { + for (idx, column_id) in enumerate(output_cols) { + let is_nullable = nullable_column_indices.contains(&idx); let alias = plan.get_ir_plan().get_expression_node(*column_id)?; if let Expression::Alias { name, .. } = alias { let column = Column { name: name.clone(), r#type: Type::Scalar, role: ColumnRole::User, + is_nullable, }; columns.push(column); } else { @@ -879,7 +908,7 @@ pub fn sharding_key_from_tuple<'tuple>( Some(format!( r#"the tuple {tuple:?} contains a "bucket_id" position {position} in a sharding key {sharding_positions:?}"# )), - )) + )); } Ordering::Greater => *position - 1, }; @@ -1291,7 +1320,7 @@ fn execute_sharded_update( return Err(SbroadError::Invalid( Entity::TupleBuilderCommand, Some(format!("got command {command:?} for update insert")), - )) + )); } } } @@ -1384,7 +1413,7 @@ fn execute_local_update( return Err(SbroadError::Invalid( Entity::TupleBuilderCommand, Some(format!("got command {command:?} for update")), - )) + )); } } } @@ -1601,7 +1630,7 @@ where Action::Insert, Some(Entity::Space), format!("{tnt_err}"), - )) + )); } } // if either DoReplace or DoNothing was done, @@ -1758,8 +1787,8 @@ pub fn sharding_key_from_map<'rec, S: ::std::hash::BuildHasher>( return Err(SbroadError::NotFound( Entity::ShardingKey, format!( - "(quoted) column {quoted_column:?} in the quoted map {quoted_map:?} (original map: {map:?})" - ))); + "(quoted) column {quoted_column:?} in the quoted map {quoted_map:?} (original map: {map:?})" + ))); } } Ok(tuple) diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs index d130744624..9c7ad9958a 100644 --- a/sbroad-core/src/executor/engine/mock.rs +++ b/sbroad-core/src/executor/engine/mock.rs @@ -103,11 +103,16 @@ impl RouterConfigurationMock { let mut tables = HashMap::new(); let columns = vec![ - Column::new("\"identification_number\"", Type::Integer, ColumnRole::User), - Column::new("\"product_code\"", Type::String, ColumnRole::User), - Column::new("\"product_units\"", Type::Boolean, ColumnRole::User), - Column::new("\"sys_op\"", Type::Unsigned, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + Column::new( + "\"identification_number\"", + 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), ]; let sharding_key = &["\"identification_number\"", "\"product_code\""]; let primary_key = &["\"product_code\"", "\"identification_number\""]; @@ -161,11 +166,11 @@ impl RouterConfigurationMock { ); let columns = vec![ - Column::new("\"id\"", Type::Unsigned, ColumnRole::User), - Column::new("\"sysFrom\"", Type::Unsigned, ColumnRole::User), - Column::new("\"FIRST_NAME\"", Type::String, ColumnRole::User), - Column::new("\"sys_op\"", Type::Unsigned, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + 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), ]; let sharding_key = &["\"id\""]; let primary_key = &["\"id\""]; @@ -195,8 +200,8 @@ impl RouterConfigurationMock { ); let columns = vec![ - Column::new("\"id\"", Type::Unsigned, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + Column::new("\"id\"", Type::Unsigned, ColumnRole::User, false), + Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding, true), ]; let sharding_key: &[&str] = &["\"id\""]; let primary_key: &[&str] = &["\"id\""]; @@ -213,11 +218,11 @@ impl RouterConfigurationMock { ); let columns = vec![ - Column::new("\"a\"", Type::Unsigned, ColumnRole::User), - Column::new("\"b\"", Type::Unsigned, ColumnRole::User), - Column::new("\"c\"", Type::Unsigned, ColumnRole::User), - Column::new("\"d\"", Type::Unsigned, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + 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), ]; let sharding_key: &[&str] = &["\"a\"", "\"b\""]; let primary_key: &[&str] = &["\"b\""]; @@ -234,9 +239,9 @@ impl RouterConfigurationMock { ); let columns = vec![ - Column::new("\"a\"", Type::String, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), - Column::new("\"b\"", Type::Integer, ColumnRole::User), + 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), ]; let sharding_key: &[&str] = &["\"a\"", "\"b\""]; let primary_key: &[&str] = &["\"a\"", "\"b\""]; @@ -253,11 +258,11 @@ impl RouterConfigurationMock { ); let columns = vec![ - Column::new("\"e\"", Type::Unsigned, ColumnRole::User), - Column::new("\"f\"", Type::Unsigned, ColumnRole::User), - Column::new("\"g\"", Type::Unsigned, ColumnRole::User), - Column::new("\"h\"", Type::Unsigned, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + 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), ]; let sharding_key: &[&str] = &["\"e\"", "\"f\""]; let primary_key: &[&str] = &["\"g\"", "\"h\""]; @@ -274,9 +279,9 @@ impl RouterConfigurationMock { ); let columns = vec![ - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), - Column::new("\"a\"", Type::String, ColumnRole::User), - Column::new("\"b\"", Type::Integer, ColumnRole::User), + 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), ]; let sharding_key: &[&str] = &["\"a\""]; let primary_key: &[&str] = &["\"a\""]; @@ -294,222 +299,505 @@ impl RouterConfigurationMock { // Table for sbroad-benches let columns = vec![ - Column::new("\"vehicleguid\"", Type::Unsigned, ColumnRole::User), - Column::new("\"reestrid\"", Type::Unsigned, ColumnRole::User), - Column::new("\"reestrstatus\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleregno\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclevin\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclevin2\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclechassisnum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclereleaseyear\"", Type::Unsigned, ColumnRole::User), + 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( + "\"vehiclereleaseyear\"", + Type::Unsigned, + ColumnRole::User, + false, + ), Column::new( "\"operationregdoctypename\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"operationregdoc\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"operationregdoc\"", Type::Unsigned, ColumnRole::User), Column::new( "\"operationregdocissuedate\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"operationregdoccomments\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"vehicleptstypename\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new("\"vehicleptsnum\"", Type::Unsigned, ColumnRole::User, false), + Column::new( + "\"vehicleptsissuedate\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"vehicleptsissuer\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"vehicleptscomments\"", + 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, + ColumnRole::User, + false, + ), + Column::new( + "\"vehiclegrossmass\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"vehicleptstypename\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleptsnum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleptsissuedate\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleptsissuer\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleptscomments\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclebodycolor\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclebrand\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclemodel\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclebrandmodel\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclebodynum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclecost\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclegasequip\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleproducername\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclegrossmass\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclemass\"", Type::Unsigned, ColumnRole::User), + Column::new("\"vehiclemass\"", Type::Unsigned, ColumnRole::User, false), Column::new( "\"vehiclesteeringwheeltypeid\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"vehiclekpptype\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"vehiclekpptype\"", Type::Unsigned, ColumnRole::User), Column::new( "\"vehicletransmissiontype\"", 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, + ColumnRole::User, + false, ), - Column::new("\"vehicletypename\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclecategory\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicletypeunit\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleecoclass\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehiclespecfuncname\"", Type::Unsigned, ColumnRole::User), Column::new( "\"vehicleenclosedvolume\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"vehicleenginemodel\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"vehicleenginenum\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"vehicleenginepower\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"vehicleenginepowerkw\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"vehicleenginetype\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"holdrestrictiondate\"", + 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, + 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("\"vehicleenginemodel\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleenginenum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleenginepower\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleenginepowerkw\"", Type::Unsigned, ColumnRole::User), - Column::new("\"vehicleenginetype\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holdrestrictiondate\"", Type::Unsigned, ColumnRole::User), - Column::new("\"approvalnum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"approvaldate\"", Type::Unsigned, ColumnRole::User), - Column::new("\"approvaltype\"", Type::Unsigned, ColumnRole::User), - Column::new("\"utilizationfeename\"", Type::Unsigned, ColumnRole::User), - Column::new("\"customsdoc\"", Type::Unsigned, ColumnRole::User), - Column::new("\"customsdocdate\"", Type::Unsigned, ColumnRole::User), - Column::new("\"customsdocissue\"", Type::Unsigned, ColumnRole::User), Column::new( "\"customsdocrestriction\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"customscountryremovalid\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"customscountryremovalname\"", 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, + ColumnRole::User, + false, + ), + Column::new( + "\"ownerpersonfirstname\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"ownerorgname\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerinn\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerogrn\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerkpp\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersonlastname\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersonfirstname\"", Type::Unsigned, ColumnRole::User), Column::new( "\"ownerpersonmiddlename\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"ownerpersonbirthdate\"", + 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("\"ownerpersonbirthdate\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerbirthplace\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersonogrnip\"", Type::Unsigned, ColumnRole::User), - Column::new("\"owneraddressindex\"", Type::Unsigned, ColumnRole::User), Column::new( "\"owneraddressmundistrict\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"owneraddresssettlement\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"owneraddressstreet\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"owneraddressstreet\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersoninn\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersondoccode\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersondocnum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"ownerpersondocdate\"", Type::Unsigned, ColumnRole::User), - Column::new("\"operationname\"", Type::Unsigned, ColumnRole::User), - Column::new("\"operationdate\"", Type::Unsigned, ColumnRole::User), + Column::new( + "\"ownerpersoninn\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"ownerpersondoccode\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"ownerpersondocnum\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"ownerpersondocdate\"", + 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, 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, + ColumnRole::User, + false, + ), + Column::new( + "\"holderpersondocnum\"", + Type::Unsigned, + ColumnRole::User, + false, + ), + Column::new( + "\"holderpersondocdate\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"operationattorney\"", Type::Unsigned, ColumnRole::User), - Column::new("\"operationlising\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holdertypeid\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holderpersondoccode\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holderpersondocnum\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holderpersondocdate\"", Type::Unsigned, ColumnRole::User), Column::new( "\"holderpersondocissuer\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"holderpersonlastname\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"holderpersonlastname\"", Type::Unsigned, ColumnRole::User), Column::new( "\"holderpersonfirstname\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderpersonmiddlename\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderpersonbirthdate\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderpersonbirthregionid\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"holderpersonsex\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"holderpersonsex\"", Type::Unsigned, ColumnRole::User), Column::new( "\"holderpersonbirthplace\"", 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, + ColumnRole::User, + false, + ), + Column::new( + "\"holderaddressguid\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"holderpersoninn\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holderpersonsnils\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holderpersonogrnip\"", Type::Unsigned, ColumnRole::User), - Column::new("\"holderaddressguid\"", Type::Unsigned, ColumnRole::User), Column::new( "\"holderaddressregionid\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddressregionname\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddressdistrict\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddressmundistrict\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddresssettlement\"", Type::Unsigned, ColumnRole::User, + false, + ), + Column::new( + "\"holderaddressstreet\"", + Type::Unsigned, + ColumnRole::User, + false, ), - Column::new("\"holderaddressstreet\"", Type::Unsigned, ColumnRole::User), Column::new( "\"holderaddressbuilding\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddressstructureid\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddressstructurename\"", Type::Unsigned, ColumnRole::User, + false, ), Column::new( "\"holderaddressstructure\"", Type::Unsigned, ColumnRole::User, + false, ), - Column::new("\"sys_from\"", Type::Unsigned, ColumnRole::User), - Column::new("\"sys_to\"", Type::Unsigned, ColumnRole::User), - Column::new("\"bucket_id\"", Type::Unsigned, ColumnRole::Sharding), + 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), ]; let sharding_key: &[&str] = &["\"reestrid\""]; let primary_key: &[&str] = &["\"reestrid\""]; diff --git a/sbroad-core/src/executor/result.rs b/sbroad-core/src/executor/result.rs index bd306eec05..b78a2d7870 100644 --- a/sbroad-core/src/executor/result.rs +++ b/sbroad-core/src/executor/result.rs @@ -56,16 +56,54 @@ impl TryInto<Column> for &MetadataColumn { fn try_into(self) -> Result<Column, Self::Error> { match self.r#type.as_str() { - "boolean" => Ok(Column::new(&self.name, Type::Boolean, ColumnRole::User)), - "decimal" => Ok(Column::new(&self.name, Type::Decimal, ColumnRole::User)), - "double" => Ok(Column::new(&self.name, Type::Double, ColumnRole::User)), - "integer" => Ok(Column::new(&self.name, Type::Integer, ColumnRole::User)), - "number" | "numeric" => Ok(Column::new(&self.name, Type::Number, ColumnRole::User)), - "scalar" => Ok(Column::new(&self.name, Type::Scalar, ColumnRole::User)), - "string" | "text" | "varchar" => { - Ok(Column::new(&self.name, Type::String, ColumnRole::User)) - } - "unsigned" => Ok(Column::new(&self.name, Type::Unsigned, ColumnRole::User)), + "boolean" => Ok(Column::new( + &self.name, + Type::Boolean, + ColumnRole::User, + true, + )), + "decimal" => Ok(Column::new( + &self.name, + Type::Decimal, + ColumnRole::User, + true, + )), + "double" => Ok(Column::new( + &self.name, + Type::Double, + ColumnRole::User, + true, + )), + "integer" => Ok(Column::new( + &self.name, + Type::Integer, + ColumnRole::User, + true, + )), + "number" | "numeric" => Ok(Column::new( + &self.name, + Type::Number, + ColumnRole::User, + true, + )), + "scalar" => Ok(Column::new( + &self.name, + Type::Scalar, + ColumnRole::User, + true, + )), + "string" | "text" | "varchar" => Ok(Column::new( + &self.name, + Type::String, + ColumnRole::User, + true, + )), + "unsigned" => Ok(Column::new( + &self.name, + Type::Unsigned, + ColumnRole::User, + true, + )), _ => Err(SbroadError::Unsupported( Entity::Type, Some(format!("column type {}", self.r#type)), @@ -118,7 +156,7 @@ impl ProducerResult { for col in &self.metadata { let column: Column = if possibly_incorrect_types { let column_type = Type::new_from_possibly_incorrect(&col.r#type)?; - Column::new(&col.name, column_type, ColumnRole::User) + Column::new(&col.name, column_type, ColumnRole::User, true) } else { col.try_into()? }; diff --git a/sbroad-core/src/executor/result/tests.rs b/sbroad-core/src/executor/result/tests.rs index 326aaa6a84..aa45cbe24d 100644 --- a/sbroad-core/src/executor/result/tests.rs +++ b/sbroad-core/src/executor/result/tests.rs @@ -2,7 +2,7 @@ use pretty_assertions::{assert_eq, assert_ne}; use tarantool::decimal; use super::*; -use crate::ir::relation::{ColumnRole, Type}; +use crate::ir::relation::Type; #[test] fn box_execute_result_serialize() { @@ -78,22 +78,26 @@ fn convert_to_vtable() { name: col_names[0].into(), r#type: Type::Integer, role: ColumnRole::User, + is_nullable: true, }); excepted.add_column(Column { name: col_names[1].into(), r#type: Type::String, role: ColumnRole::User, + is_nullable: true, }); excepted.add_column(Column { name: col_names[2].into(), r#type: Type::Unsigned, role: ColumnRole::User, + is_nullable: true, }); excepted.add_column(Column { name: col_names[3].into(), r#type: Type::Decimal, role: ColumnRole::User, + is_nullable: true, }); excepted.add_tuple(vec![ diff --git a/sbroad-core/src/executor/tests.rs b/sbroad-core/src/executor/tests.rs index a563d3153c..43c351453c 100644 --- a/sbroad-core/src/executor/tests.rs +++ b/sbroad-core/src/executor/tests.rs @@ -6,7 +6,7 @@ use crate::executor::engine::mock::RouterRuntimeMock; use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; use crate::ir::operator::Relational; -use crate::ir::relation::{Column, ColumnRole, Type}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::redistribution::MotionPolicy; use crate::ir::value::{LuaValue, Value}; @@ -406,16 +406,8 @@ fn join_linker2_test() { .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "id1".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "id2".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("id1"))); + virtual_table.add_column(column_integer_user_non_null(String::from("id2"))); virtual_table.add_tuple(vec![Value::from(1_u64), Value::from(1_u64)]); virtual_table.add_tuple(vec![Value::from(2_u64), Value::from(2_u64)]); virtual_table.set_alias("\"t2\"").unwrap(); @@ -478,16 +470,8 @@ fn join_linker3_test() { .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "id1".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "FIRST_NAME".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("id1"))); + virtual_table.add_column(column_integer_user_non_null(String::from("FIRST_NAME"))); virtual_table.add_tuple(vec![Value::from(1_u64), Value::from(1_u64)]); virtual_table.add_tuple(vec![Value::from(2_u64), Value::from(2_u64)]); virtual_table.set_alias("\"t2\"").unwrap(); @@ -549,11 +533,7 @@ fn join_linker4_test() { .position(0) .unwrap(); let mut virtual_t2 = VirtualTable::new(); - virtual_t2.add_column(Column { - name: "r_id".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_t2.add_column(column_integer_user_non_null(String::from("r_id"))); virtual_t2.add_tuple(vec![Value::from(1_u64)]); virtual_t2.add_tuple(vec![Value::from(2_u64)]); virtual_t2.set_alias("\"T2\"").unwrap(); @@ -575,11 +555,7 @@ fn join_linker4_test() { .position(1) .unwrap(); let mut virtual_sq = VirtualTable::new(); - virtual_sq.add_column(Column { - name: "fn".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_sq.add_column(column_integer_user_non_null(String::from("fn"))); virtual_sq.add_tuple(vec![Value::from(2_u64)]); virtual_sq.add_tuple(vec![Value::from(3_u64)]); if let MotionPolicy::Segment(key) = @@ -662,11 +638,7 @@ on q."f" = "t1"."a""#; .position(0) .unwrap(); let mut virtual_t2 = VirtualTable::new(); - virtual_t2.add_column(Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_t2.add_column(column_integer_user_non_null(String::from("b"))); virtual_t2.set_alias("\"t3\"").unwrap(); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_t2_id) @@ -686,16 +658,8 @@ on q."f" = "t1"."a""#; .position(0) .unwrap(); let mut virtual_sq = VirtualTable::new(); - virtual_sq.add_column(Column { - name: "f".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_sq.add_column(Column { - name: "B".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_sq.add_column(column_integer_user_non_null(String::from("f"))); + virtual_sq.add_column(column_integer_user_non_null(String::from("B"))); virtual_sq.set_alias("Q").unwrap(); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_sq_id) @@ -895,11 +859,9 @@ fn sharding_column2_test() { fn virtual_table_23(alias: Option<&str>) -> VirtualTable { let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "identification_number".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from( + "identification_number", + ))); virtual_table.add_tuple(vec![Value::from(2_u64)]); virtual_table.add_tuple(vec![Value::from(3_u64)]); @@ -964,11 +926,7 @@ fn groupby_linker_test() { "Expected Buckets::All for local groupby" ); let mut virtual_t1 = VirtualTable::new(); - virtual_t1.add_column(Column { - name: "id".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_t1.add_column(column_integer_user_non_null(String::from("id"))); let mut buckets: Vec<u64> = vec![]; let tuples: Vec<Vec<Value>> = vec![vec![Value::from(1_u64)], vec![Value::from(2_u64)]]; diff --git a/sbroad-core/src/executor/tests/between.rs b/sbroad-core/src/executor/tests/between.rs index 13d2540487..d8e60a81c6 100644 --- a/sbroad-core/src/executor/tests/between.rs +++ b/sbroad-core/src/executor/tests/between.rs @@ -4,7 +4,7 @@ use crate::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::RouterRuntimeMock; use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; -use crate::ir::relation::{Column, ColumnRole, Type}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::redistribution::tests::get_motion_id; use crate::ir::transformation::redistribution::MotionPolicy; use crate::ir::value::{LuaValue, Value}; @@ -32,11 +32,7 @@ fn between1_test() { // Mock a virtual table. let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "id".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("id"))); virtual_table.add_tuple(vec![Value::from(2_u64)]); query .coordinator @@ -88,11 +84,7 @@ fn between2_test() { // Mock a virtual table. let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "id".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("id"))); virtual_table.add_tuple(vec![Value::from(2_u64)]); // Bind the virtual table to both motions. diff --git a/sbroad-core/src/executor/tests/empty_motion.rs b/sbroad-core/src/executor/tests/empty_motion.rs index bb34976beb..ca323f4264 100644 --- a/sbroad-core/src/executor/tests/empty_motion.rs +++ b/sbroad-core/src/executor/tests/empty_motion.rs @@ -4,7 +4,7 @@ use crate::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::RouterRuntimeMock; use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; -use crate::ir::relation::{Column, ColumnRole, Type}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::redistribution::MotionPolicy; use crate::ir::value::{LuaValue, Value}; @@ -89,17 +89,9 @@ fn empty_motion1_test() { fn t2_empty() -> VirtualTable { let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "g".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("g"))); - virtual_table.add_column(Column { - name: "h".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("h"))); virtual_table.set_alias("\"t2\"").unwrap(); diff --git a/sbroad-core/src/executor/tests/exec_plan.rs b/sbroad-core/src/executor/tests/exec_plan.rs index 905b3297c3..c55371e287 100644 --- a/sbroad-core/src/executor/tests/exec_plan.rs +++ b/sbroad-core/src/executor/tests/exec_plan.rs @@ -2,6 +2,8 @@ use pretty_assertions::assert_eq; use crate::backend::sql::tree::{OrderedSyntaxNodes, SyntaxPlan}; use crate::executor::engine::mock::RouterRuntimeMock; +use crate::ir::relation::Type; +use crate::ir::tests::{column_integer_user_non_null, column_user_non_null}; use crate::ir::transformation::redistribution::MotionPolicy; use crate::ir::tree::Snapshot; @@ -95,11 +97,10 @@ fn exec_plan_subtree_two_stage_groupby_test() { .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "FIRST_NAME".into(), - r#type: Type::String, - role: ColumnRole::User, - }); + virtual_table.add_column(column_user_non_null( + String::from("FIRST_NAME"), + Type::String, + )); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { @@ -161,21 +162,18 @@ fn exec_plan_subtree_two_stage_groupby_test_2() { .position(0) .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "column_12".into(), - r#type: Type::String, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "column_13".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "column_14".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_user_non_null( + String::from("column_12"), + Type::String, + )); + virtual_table.add_column(column_user_non_null( + String::from("column_13"), + Type::Integer, + )); + virtual_table.add_column(column_user_non_null( + String::from("column_14"), + Type::Integer, + )); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { virtual_table.reshard(key, &query.coordinator).unwrap(); @@ -253,56 +251,19 @@ fn exec_plan_subtree_aggregates() { .position(0) .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "sys_op".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "sum_42".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "count_37".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "sum_49".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "count_51".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "group_concat_58".into(), - r#type: Type::String, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "count_61".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "total_64".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "min_67".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "max_70".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("sys_op"))); + virtual_table.add_column(column_integer_user_non_null(String::from("sum_42"))); + virtual_table.add_column(column_integer_user_non_null(String::from("count_37"))); + virtual_table.add_column(column_integer_user_non_null(String::from("sum_49"))); + virtual_table.add_column(column_integer_user_non_null(String::from("count_51"))); + virtual_table.add_column(column_user_non_null( + String::from("group_concat_58"), + Type::String, + )); + virtual_table.add_column(column_integer_user_non_null(String::from("count_61"))); + virtual_table.add_column(column_integer_user_non_null(String::from("total_64"))); + virtual_table.add_column(column_integer_user_non_null(String::from("min_67"))); + virtual_table.add_column(column_integer_user_non_null(String::from("max_70"))); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { virtual_table.reshard(key, &query.coordinator).unwrap(); @@ -381,16 +342,8 @@ fn exec_plan_subtree_aggregates_no_groupby() { .position(0) .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "column_19".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "count_13".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("column_19"))); + virtual_table.add_column(column_integer_user_non_null(String::from("count_13"))); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { virtual_table.reshard(key, &query.coordinator).unwrap(); @@ -609,11 +562,7 @@ fn exec_plan_subtree_count_asterisk() { .position(0) .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "count_13".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("count_13"))); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { virtual_table.reshard(key, &query.coordinator).unwrap(); @@ -681,21 +630,9 @@ fn exec_plan_subtree_having() { .position(0) .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "column_63".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "column_12".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "count_58".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("column_63"))); + virtual_table.add_column(column_integer_user_non_null(String::from("column_12"))); + virtual_table.add_column(column_integer_user_non_null(String::from("count_58"))); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { virtual_table.reshard(key, &query.coordinator).unwrap(); @@ -775,21 +712,9 @@ fn exec_plan_subtree_having_without_groupby() { .position(0) .unwrap(); let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "column_63".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "column_12".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - virtual_table.add_column(Column { - name: "count_58".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("column_63"))); + virtual_table.add_column(column_integer_user_non_null(String::from("column_12"))); + virtual_table.add_column(column_integer_user_non_null(String::from("count_58"))); if let MotionPolicy::Segment(key) = get_motion_policy(query.exec_plan.get_ir_plan(), motion_id) { virtual_table.reshard(key, &query.coordinator).unwrap(); diff --git a/sbroad-core/src/executor/tests/frontend.rs b/sbroad-core/src/executor/tests/frontend.rs index 8670b1179e..0d1de81035 100644 --- a/sbroad-core/src/executor/tests/frontend.rs +++ b/sbroad-core/src/executor/tests/frontend.rs @@ -28,14 +28,14 @@ fn front_ivalid_sql1() { #[test] fn front_invalid_sql2() { - let query = r#"INSERT INTO "t" ("a") VALUES(1, 2)"#; + let query = r#"INSERT INTO "t" ("a", "b", "c") VALUES(1, 2, 3, 4)"#; let metadata = &RouterRuntimeMock::new(); let plan_err = Query::new(metadata, query, vec![]).unwrap_err(); assert_eq!( SbroadError::UnexpectedNumberOfValues( - r#"invalid number of values: 2. Table "t" expects 1 column(s)."#.into() + r#"invalid number of values: 4. Table "t" expects 3 column(s)."#.into() ), plan_err ); diff --git a/sbroad-core/src/executor/tests/not_eq.rs b/sbroad-core/src/executor/tests/not_eq.rs index ca1ed7ea58..17512e55c7 100644 --- a/sbroad-core/src/executor/tests/not_eq.rs +++ b/sbroad-core/src/executor/tests/not_eq.rs @@ -4,7 +4,7 @@ use crate::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::RouterRuntimeMock; use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; -use crate::ir::relation::{Column, ColumnRole, Type}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::redistribution::tests::get_motion_id; use crate::ir::transformation::redistribution::MotionPolicy; use crate::ir::value::{LuaValue, Value}; @@ -71,11 +71,7 @@ fn not_eq2_test() { // Mock a virtual table. let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "id".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("id"))); virtual_table.add_tuple(vec![Value::from(3_u64)]); query .coordinator diff --git a/sbroad-core/src/executor/tests/not_in.rs b/sbroad-core/src/executor/tests/not_in.rs index e0bdf05321..eaee7cd6a4 100644 --- a/sbroad-core/src/executor/tests/not_in.rs +++ b/sbroad-core/src/executor/tests/not_in.rs @@ -4,7 +4,7 @@ use crate::backend::sql::ir::PatternWithParams; use crate::executor::engine::mock::RouterRuntimeMock; use crate::executor::result::ProducerResult; use crate::executor::vtable::VirtualTable; -use crate::ir::relation::{Column, ColumnRole, Type}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::redistribution::tests::get_motion_id; use crate::ir::transformation::redistribution::MotionPolicy; use crate::ir::value::{LuaValue, Value}; @@ -33,11 +33,7 @@ fn not_in1_test() { // Mock a virtual table. let mut virtual_table = VirtualTable::new(); - virtual_table.add_column(Column { - name: "id".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + virtual_table.add_column(column_integer_user_non_null(String::from("id"))); virtual_table.add_tuple(vec![Value::from(3_u64)]); query .coordinator diff --git a/sbroad-core/src/executor/vtable/tests.rs b/sbroad-core/src/executor/vtable/tests.rs index e63dd2848d..2371f3e400 100644 --- a/sbroad-core/src/executor/vtable/tests.rs +++ b/sbroad-core/src/executor/vtable/tests.rs @@ -1,6 +1,6 @@ use super::*; use crate::executor::engine::mock::RouterRuntimeMock; -use crate::ir::relation::{ColumnRole, Type}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::redistribution::{MotionKey, Target}; use pretty_assertions::assert_eq; use std::collections::HashMap; @@ -10,22 +10,14 @@ use std::collections::HashMap; fn virtual_table_1() { let mut vtable = VirtualTable::new(); - vtable.add_column(Column { - name: "name".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + vtable.add_column(column_integer_user_non_null(String::from("name"))); vtable.add_tuple(vec![Value::from(1_u64)]); vtable.set_alias("test").unwrap(); let expected = VirtualTable { - columns: vec![Column { - name: "name".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }], + columns: vec![column_integer_user_non_null(String::from("name"))], tuples: vec![vec![Value::from(1_u64)]], name: Some(String::from("test")), primary_key: None, @@ -39,16 +31,8 @@ fn virtual_table_1() { #[test] fn virtual_table_2() { let mut vtable = VirtualTable::new(); - vtable.add_column(Column { - name: "a".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - vtable.add_column(Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + vtable.add_column(column_integer_user_non_null(String::from("a"))); + vtable.add_column(column_integer_user_non_null(String::from("b"))); let tuple1 = vec![Value::from(1_u64), Value::from(2_u64)]; let tuple2 = vec![Value::from(3_u64), Value::from(4_u64)]; vtable.add_tuple(tuple1.clone()); @@ -63,16 +47,8 @@ fn virtual_table_2() { let expected = VirtualTable { columns: vec![ - Column { - name: "a".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }, - Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }, + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], tuples: vec![tuple1, tuple2], name: Some(String::from("t")), @@ -89,16 +65,8 @@ fn virtual_table_2() { #[test] fn virtual_table_3() { let mut vtable = VirtualTable::new(); - vtable.add_column(Column { - name: "a".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - vtable.add_column(Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + vtable.add_column(column_integer_user_non_null(String::from("a"))); + vtable.add_column(column_integer_user_non_null(String::from("b"))); let tuple1 = vec![Value::from(1_u64), Value::from(2_u64)]; let tuple2 = vec![Value::from(3_u64), Value::from(4_u64)]; vtable.add_tuple(tuple1.clone()); @@ -114,16 +82,8 @@ fn virtual_table_3() { let expected = VirtualTable { columns: vec![ - Column { - name: "a".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }, - Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }, + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], tuples: vec![tuple1, tuple2], name: Some(String::from("t")), @@ -149,16 +109,8 @@ fn vtable_rearrange_for_update() { new_sh_key_value.clone(), old_sh_key_value.clone(), ]; - vtable.add_column(Column { - name: "a".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); - vtable.add_column(Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }); + vtable.add_column(column_integer_user_non_null(String::from("a"))); + vtable.add_column(column_integer_user_non_null(String::from("b"))); vtable.set_alias("t").unwrap(); vtable.add_tuple(tuple); @@ -180,16 +132,8 @@ fn vtable_rearrange_for_update() { let expected = VirtualTable { columns: vec![ - Column { - name: "a".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }, - Column { - name: "b".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }, + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], tuples: vec![vec![pk_value.clone(), new_sh_key_value], vec![pk_value]], name: Some(String::from("t")), diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index 0fc3f4c97a..9ae2f5238a 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -264,6 +264,26 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, ) })?, ); + let mut column_found = false; + for column in &columns { + if column.name == pk_col_name { + column_found = true; + if column.is_nullable { + return Err(SbroadError::Invalid( + Entity::Column, + Some(String::from( + "Primary key mustn't contain nullable columns", + )), + )); + } + } + } + if !column_found { + return Err(SbroadError::Invalid( + Entity::Column, + Some(format!("Primary key column {pk_col_name} not found.")), + )); + } pk_keys.push(pk_col_name); } } @@ -331,6 +351,17 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, ) })?, ); + + let column_found = columns.iter().any(|c| c.name == shard_col_name); + if !column_found { + return Err(SbroadError::Invalid( + Entity::Column, + Some(format!( + "Sharding key column {shard_col_name} not found." + )), + )); + } + shard_key.push(shard_col_name); } } @@ -1814,11 +1845,12 @@ impl Ast for AbstractSyntaxTree { let ast_child = self.nodes.get_node(*ast_child_id)?; let plan_insert_id = if let Type::TargetColumns = ast_child.rule { // insert into t (a, b, c) ... - let mut col_names: Vec<&str> = Vec::with_capacity(ast_child.children.len()); + let mut selected_col_names: Vec<&str> = + Vec::with_capacity(ast_child.children.len()); for col_id in &ast_child.children { let col = self.nodes.get_node(*col_id)?; if let Type::ColumnName = col.rule { - col_names.push(col.value.as_ref().ok_or_else(|| { + selected_col_names.push(col.value.as_ref().ok_or_else(|| { SbroadError::NotFound( Entity::Name, "of Column among the AST target columns (insert)".into(), @@ -1831,6 +1863,30 @@ impl Ast for AbstractSyntaxTree { )); } } + + let rel = plan.relations.get(&relation).ok_or_else(|| { + SbroadError::NotFound( + Entity::Table, + format!("{relation} among plan relations"), + ) + })?; + for column in &rel.columns { + if let ColumnRole::Sharding = column.get_role() { + continue; + } + if !column.is_nullable + && !selected_col_names.contains(&column.name.as_str()) + { + return Err(SbroadError::Invalid( + Entity::Column, + Some(format!( + "NonNull column {} must be specified", + column.name + )), + )); + } + } + let ast_rel_child_id = node.children.get(2).ok_or_else(|| { SbroadError::NotFound( Entity::Node, @@ -1842,7 +1898,7 @@ impl Ast for AbstractSyntaxTree { plan.add_insert( &relation, plan_rel_child_id, - &col_names, + &selected_col_names, conflict_strategy, )? } else { diff --git a/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad-core/src/frontend/sql/ir/tests.rs index f664395c17..e343cdd3cd 100644 --- a/sbroad-core/src/frontend/sql/ir/tests.rs +++ b/sbroad-core/src/frontend/sql/ir/tests.rs @@ -285,13 +285,13 @@ vtable_max_rows = 5000 #[test] fn front_sql11() { - let input = r#"INSERT INTO "t" ("a", "c") VALUES(1, 2)"#; + let input = r#"INSERT INTO "t" ("b", "d") VALUES(1, 2)"#; let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( r#"insert "t" on conflict: fail - motion [policy: local segment([ref("COLUMN_1"), value(NULL)])] + motion [policy: local segment([value(NULL), ref("COLUMN_1")])] values value row (data=ROW(1::unsigned, 2::unsigned)) execution options: @@ -305,13 +305,13 @@ vtable_max_rows = 5000 #[test] fn front_sql14() { - let input = r#"INSERT INTO "t" ("a", "c") SELECT "b", "d" FROM "t""#; + let input = r#"INSERT INTO "t" ("b", "c") SELECT "b", "d" FROM "t""#; let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( r#"insert "t" on conflict: fail - motion [policy: segment([ref("b"), value(NULL)])] + motion [policy: segment([value(NULL), ref("b")])] projection ("t"."b"::unsigned -> "b", "t"."d"::unsigned -> "d") scan "t" execution options: @@ -727,13 +727,13 @@ vtable_max_rows = 5000 #[test] fn front_sql_groupby_insert() { - let input = r#"INSERT INTO "t" ("a", "c") + let input = r#"INSERT INTO "t" ("c", "b") SELECT "b", "d" FROM "t" group by "b", "d" ON CONFLICT DO FAIL"#; let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( r#"insert "t" on conflict: fail - motion [policy: segment([ref("b"), value(NULL)])] + motion [policy: segment([value(NULL), ref("d")])] projection ("column_24"::unsigned -> "b", "column_25"::unsigned -> "d") group by ("column_24"::unsigned, "column_25"::unsigned) output: ("column_25"::unsigned -> "column_25", "column_24"::unsigned -> "column_24") motion [policy: segment([ref("column_24"), ref("column_25")])] @@ -1327,13 +1327,13 @@ vtable_max_rows = 5000 #[test] fn front_sql_insert_single() { - let input = r#"INSERT INTO "t" ("a", "c") SELECT sum("b"), count("d") FROM "t""#; + let input = r#"INSERT INTO "t" ("c", "b") SELECT sum("b"), count("d") FROM "t""#; let plan = sql_to_optimized_ir(input, vec![]); let expected_explain = String::from( r#"insert "t" on conflict: fail - motion [policy: segment([ref("COL_1"), value(NULL)])] + motion [policy: segment([value(NULL), ref("COL_2")])] projection (sum(("sum_25"::decimal))::decimal -> "COL_1", sum(("count_28"::integer))::decimal -> "COL_2") motion [policy: full] scan @@ -2347,7 +2347,7 @@ vtable_max_rows = 5000 #[test] fn front_sql_insert_7() { // Check system column can't be inserted - let input = r#"insert into "hash_testing" ("sys_op", "bucket_id" ) values (1, 2)"#; + let input = r#"insert into "hash_testing" ("identification_number", "product_code", "bucket_id") values (1, 2, 3)"#; let metadata = &RouterConfigurationMock::new(); let ast = AbstractSyntaxTree::new(input).unwrap(); @@ -2785,6 +2785,21 @@ vtable_max_rows = 5000 ) } +#[test] +fn front_sql_check_non_null_columns_specified() { + let input = r#"insert into "test_space" ("sys_op") values (1)"#; + + let metadata = &RouterConfigurationMock::new(); + let ast = AbstractSyntaxTree::new(input).unwrap(); + let plan = ast.resolve_metadata(metadata); + let err = plan.unwrap_err(); + assert_eq!( + true, + err.to_string() + .contains("NonNull column \"id\" must be specified") + ); +} + fn assert_explain_eq(query: &str, params: Vec<Value>, expected: &str) { let plan = sql_to_optimized_ir(query, params); let actual = plan.as_explain().unwrap(); diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs index 45e0f8e6c5..f8ce70bbbe 100644 --- a/sbroad-core/src/ir.rs +++ b/sbroad-core/src/ir.rs @@ -1148,4 +1148,4 @@ impl Plan { pub mod api; mod explain; #[cfg(test)] -mod tests; +pub mod tests; diff --git a/sbroad-core/src/ir/api/parameter.rs b/sbroad-core/src/ir/api/parameter.rs index 9180092d77..b3eabab9d8 100644 --- a/sbroad-core/src/ir/api/parameter.rs +++ b/sbroad-core/src/ir/api/parameter.rs @@ -55,8 +55,9 @@ impl Plan { tree.populate_nodes(top_id); let nodes = tree.take_nodes(); + let mut binded_params_counter = 0; if !self.raw_options.is_empty() { - self.bind_option_params(&mut params)?; + binded_params_counter = self.bind_option_params(&mut params)?; } // Transform parameters to values (plan constants). The result values are stored in the @@ -78,6 +79,17 @@ impl Plan { let mut param_node_ids = self.get_param_set(); let mut param_node_ids_cloned = param_node_ids.clone(); + if param_node_ids.len() - binded_params_counter > value_ids.len() { + return Err(SbroadError::Invalid( + Entity::Value, + Some(format!( + "Expected at least {} values for parameters. Got {}.", + param_node_ids.len() - binded_params_counter, + value_ids.len() + )), + )); + } + // Closure to retrieve a corresponding value for a parameter node. let get_value = |pos: usize| -> Result<usize, SbroadError> { let val_id = value_ids.get(pos).ok_or_else(|| { @@ -295,17 +307,20 @@ impl Plan { Ok(()) } - /// Bind params related to `Option` clause + /// 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 - pub fn bind_option_params(&mut self, params: &mut Vec<Value>) -> Result<(), SbroadError> { + pub fn bind_option_params(&mut self, params: &mut Vec<Value>) -> Result<usize, SbroadError> { // Bind parameters in options to values. // Because the Option clause is the last clause in the // query the parameters are located in the end of params list. + let mut binded_params_counter = 0usize; for opt in self.raw_options.iter_mut().rev() { if opt.val.is_none() { if let Some(v) = params.pop() { + binded_params_counter += 1; opt.val = Some(v); } else { return Err(SbroadError::Invalid( @@ -318,6 +333,6 @@ impl Plan { } } } - Ok(()) + Ok(binded_params_counter) } } diff --git a/sbroad-core/src/ir/ddl.rs b/sbroad-core/src/ir/ddl.rs index 1e4b6a7dca..6004061066 100644 --- a/sbroad-core/src/ir/ddl.rs +++ b/sbroad-core/src/ir/ddl.rs @@ -6,13 +6,23 @@ use serde::{Deserialize, Serialize}; use tarantool::decimal::Decimal; use tarantool::space::SpaceEngineType; -#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct ColumnDef { pub name: String, pub data_type: Type, pub is_nullable: bool, } +impl Default for ColumnDef { + fn default() -> Self { + Self { + name: String::default(), + data_type: Type::default(), + is_nullable: true, + } + } +} + #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum Ddl { CreateTable { diff --git a/sbroad-core/src/ir/distribution/tests.rs b/sbroad-core/src/ir/distribution/tests.rs index d67faff613..e3d6241350 100644 --- a/sbroad-core/src/ir/distribution/tests.rs +++ b/sbroad-core/src/ir/distribution/tests.rs @@ -1,5 +1,6 @@ use super::*; -use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::relation::{SpaceEngine, Table, Type}; +use crate::ir::tests::column_user_non_null; use crate::ir::transformation::helpers::sql_to_optimized_ir; use crate::ir::tree::traversal::{PostOrder, REL_CAPACITY}; use crate::ir::{Node, Plan}; @@ -14,10 +15,10 @@ fn proj_preserve_dist_key() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], diff --git a/sbroad-core/src/ir/expression/tests.rs b/sbroad-core/src/ir/expression/tests.rs index 946d8b333c..5489fcdaa7 100644 --- a/sbroad-core/src/ir/expression/tests.rs +++ b/sbroad-core/src/ir/expression/tests.rs @@ -1,6 +1,7 @@ +use crate::ir::tests::column_integer_user_non_null; use pretty_assertions::assert_eq; -use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::relation::{SpaceEngine, Table}; use crate::ir::value::Value; use crate::ir::{Plan, SbroadError}; @@ -28,7 +29,7 @@ fn rel_nodes_from_reference_in_scan() { let t = Table::new_seg( "t", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, @@ -50,7 +51,7 @@ fn rel_nodes_from_reference_in_proj() { let t = Table::new_seg( "t", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, diff --git a/sbroad-core/src/ir/operator/tests.rs b/sbroad-core/src/ir/operator/tests.rs index 38d9de04f2..1b1df5bbc8 100644 --- a/sbroad-core/src/ir/operator/tests.rs +++ b/sbroad-core/src/ir/operator/tests.rs @@ -7,6 +7,8 @@ use crate::collection; use crate::errors::{Entity, SbroadError}; use crate::ir::distribution::{Distribution, Key}; use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::tests::column_integer_user_non_null; +use crate::ir::tests::{column_user_non_null, sharding_column}; use crate::ir::value::Value; use crate::ir::{Node, Plan}; @@ -19,10 +21,10 @@ fn scan_rel() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], @@ -57,10 +59,10 @@ fn scan_rel_serialized() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Number, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Number), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], @@ -93,10 +95,10 @@ fn projection() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], @@ -150,10 +152,10 @@ fn selection() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], @@ -213,7 +215,7 @@ fn except() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, @@ -225,7 +227,7 @@ fn except() { let t2 = Table::new_seg( "t2", - vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, @@ -245,8 +247,8 @@ fn except() { let t3 = Table::new_seg( "t3", vec![ - Column::new("a", Type::Unsigned, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Unsigned), + column_user_non_null(String::from("b"), Type::Unsigned), ], &["a"], &["a"], @@ -271,7 +273,7 @@ fn insert() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, @@ -284,9 +286,9 @@ fn insert() { let t2 = Table::new_seg( "t2", vec![ - Column::new("a", Type::Unsigned, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::Unsigned, ColumnRole::Sharding), + column_user_non_null(String::from("a"), Type::Unsigned), + column_user_non_null(String::from("b"), Type::Unsigned), + Column::new("c", Type::Unsigned, ColumnRole::Sharding, true), ], &["a"], &["a"], @@ -334,7 +336,7 @@ fn union_all() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, @@ -345,7 +347,7 @@ fn union_all() { let t2 = Table::new_seg( "t2", - vec![Column::new("a", Type::Unsigned, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, @@ -364,8 +366,8 @@ fn union_all_col_amount_mismatch() { let t1 = Table::new_seg( "t1", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), ], &["a"], &["a"], @@ -379,7 +381,7 @@ fn union_all_col_amount_mismatch() { // Check errors for children with different amount of column let t2 = Table::new_seg( "t2", - vec![Column::new("b", Type::Unsigned, ColumnRole::User)], + vec![column_user_non_null(String::from("b"), Type::Unsigned)], &["b"], &["b"], SpaceEngine::Memtx, @@ -404,8 +406,8 @@ fn sub_query() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), ], &["a"], &["b"], @@ -452,7 +454,7 @@ fn selection_with_sub_query() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, @@ -464,7 +466,7 @@ fn selection_with_sub_query() { let t2 = Table::new_seg( "t2", - vec![Column::new("b", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("b"))], &["b"], &["b"], SpaceEngine::Memtx, @@ -509,9 +511,9 @@ fn join() { let t1 = Table::new_seg( "t1", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + sharding_column(), ], &["a"], &["a"], @@ -524,9 +526,9 @@ fn join() { let t2 = Table::new_seg( "t2", vec![ - Column::new("c", Type::Boolean, ColumnRole::User), - Column::new("d", Type::Unsigned, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_user_non_null(String::from("c"), Type::Boolean), + column_user_non_null(String::from("d"), Type::Unsigned), + sharding_column(), ], &["d"], &["d"], @@ -570,9 +572,9 @@ fn join_duplicate_columns() { let t1 = Table::new_seg( "t1", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + sharding_column(), ], &["a"], &["a"], @@ -585,9 +587,9 @@ fn join_duplicate_columns() { let t2 = Table::new_seg( "t2", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("d", Type::Unsigned, ColumnRole::User), - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("d"), Type::Unsigned), + sharding_column(), ], &["d"], &["d"], diff --git a/sbroad-core/src/ir/relation.rs b/sbroad-core/src/ir/relation.rs index bf8f9c2339..4ed1805364 100644 --- a/sbroad-core/src/ir/relation.rs +++ b/sbroad-core/src/ir/relation.rs @@ -153,7 +153,7 @@ pub enum ColumnRole { } /// Table column. -#[derive(Default, PartialEq, Debug, Eq, Clone)] +#[derive(PartialEq, Debug, Eq, Clone)] pub struct Column { /// Column name. pub name: String, @@ -161,6 +161,20 @@ pub struct Column { pub r#type: Type, /// Column role. pub role: ColumnRole, + /// Column is_nullable status. + /// Possibly `None` (e.g. in case it's taken from Tarantool local query execution metatada). + pub is_nullable: bool, +} + +impl Default for Column { + fn default() -> Self { + Column { + name: String::default(), + r#type: Type::default(), + role: ColumnRole::default(), + is_nullable: true, + } + } } impl From<Column> for Field { @@ -240,11 +254,13 @@ impl<'de> Visitor<'de> for ColumnVisitor { let mut column_name = String::new(); let mut column_type = String::new(); let mut column_role = String::new(); + let mut column_is_nullable = String::new(); while let Some((key, value)) = map.next_entry::<String, String>()? { match key.as_str() { "name" => column_name.push_str(&value), "type" => column_type.push_str(&value.to_lowercase()), "role" => column_role.push_str(&value.to_lowercase()), + "is_nullable" => column_is_nullable.push_str(&value.to_lowercase()), _ => return Err(Error::custom(&format!("invalid column param: {key}"))), } } @@ -254,16 +270,20 @@ impl<'de> Visitor<'de> for ColumnVisitor { _ => ColumnRole::User, }; + let is_nullable = matches!(column_is_nullable.as_str(), "true"); + match column_type.as_str() { - "boolean" => Ok(Column::new(&column_name, Type::Boolean, role)), - "decimal" => Ok(Column::new(&column_name, Type::Decimal, role)), - "double" => Ok(Column::new(&column_name, Type::Double, role)), - "integer" => Ok(Column::new(&column_name, Type::Integer, role)), - "number" | "numeric" => Ok(Column::new(&column_name, Type::Number, role)), - "scalar" => Ok(Column::new(&column_name, Type::Scalar, role)), - "string" | "text" | "varchar" => Ok(Column::new(&column_name, Type::String, role)), - "unsigned" => Ok(Column::new(&column_name, Type::Unsigned, role)), - "array" => Ok(Column::new(&column_name, Type::Array, role)), + "boolean" => Ok(Column::new(&column_name, Type::Boolean, 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)), + "number" | "numeric" => Ok(Column::new(&column_name, Type::Number, role, is_nullable)), + "scalar" => Ok(Column::new(&column_name, Type::Scalar, 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)), _ => Err(Error::custom("unsupported column type")), } } @@ -281,11 +301,12 @@ impl<'de> Deserialize<'de> for Column { impl Column { /// Column constructor. #[must_use] - pub fn new(n: &str, t: Type, role: ColumnRole) -> Self { + pub fn new(n: &str, t: Type, role: ColumnRole, is_nullable: bool) -> Self { Column { name: n.into(), r#type: t, role, + is_nullable, } } diff --git a/sbroad-core/src/ir/relation/tests.rs b/sbroad-core/src/ir/relation/tests.rs index 6ae8c23e8e..28dd70be48 100644 --- a/sbroad-core/src/ir/relation/tests.rs +++ b/sbroad-core/src/ir/relation/tests.rs @@ -4,17 +4,14 @@ use std::path::Path; use pretty_assertions::{assert_eq, assert_ne}; use super::*; +use crate::ir::tests::column_user_non_null; #[test] fn column() { - let a = Column { - name: String::from("a"), - r#type: Type::Boolean, - role: ColumnRole::User, - }; - assert_eq!(a, Column::new("a", Type::Boolean, ColumnRole::User)); - assert_ne!(a, Column::new("a", Type::String, ColumnRole::User)); - assert_ne!(a, Column::new("b", Type::Boolean, ColumnRole::User)); + let a = column_user_non_null(String::from("a"), Type::Boolean); + assert_eq!(a, column_user_non_null(String::from("a"), Type::Boolean)); + assert_ne!(a, column_user_non_null(String::from("a"), Type::String)); + assert_ne!(a, column_user_non_null(String::from("b"), Type::Boolean)); } #[test] @@ -22,10 +19,10 @@ fn table_seg() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], @@ -42,7 +39,7 @@ fn table_seg() { fn table_seg_name() { let t = Table::new_seg( "t", - vec![Column::new("a", Type::Boolean, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, @@ -57,10 +54,10 @@ fn table_seg_duplicate_columns() { Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("a", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("a"), Type::String), ], &["b", "a"], &["b", "a"], @@ -76,9 +73,9 @@ fn table_seg_dno_bucket_id_column() { let t1 = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), ], &["b", "a"], &["b", "a"], @@ -94,11 +91,11 @@ fn table_seg_dno_bucket_id_column() { let t2 = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("bucket_id", Type::String, ColumnRole::Sharding), - Column::new("bucket_id2", Type::String, ColumnRole::Sharding), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + Column::new("bucket_id", Type::String, ColumnRole::Sharding, false), + Column::new("bucket_id2", Type::String, ColumnRole::Sharding, false), ], &["b", "a"], &["b", "a"], @@ -118,10 +115,10 @@ fn table_seg_wrong_key() { Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["a", "e"], &["a", "e"], @@ -138,8 +135,8 @@ fn table_seg_compound_type_in_key() { Table::new_seg( "t", vec![ - Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding), - Column::new("a", Type::Array, ColumnRole::User), + Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, false), + column_user_non_null(String::from("a"), Type::Array), ], &["a"], &["a"], @@ -158,12 +155,12 @@ fn table_seg_serialized() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Number, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), - Column::new("e", Type::Integer, ColumnRole::User), - Column::new("f", Type::Unsigned, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Number), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), + column_user_non_null(String::from("e"), Type::Integer), + column_user_non_null(String::from("f"), Type::Unsigned), ], &["a", "d"], &["a", "d"], @@ -254,11 +251,7 @@ fn table_seg_serialized_no_columns() { #[test] fn column_msgpack_serialize() { - let c = Column { - name: "name".into(), - r#type: Type::Boolean, - role: ColumnRole::User, - }; + let c = column_user_non_null(String::from("name"), Type::Boolean); assert_eq!( vec![ @@ -269,11 +262,7 @@ fn column_msgpack_serialize() { rmp_serde::to_vec(&c).unwrap() ); - let c = Column { - name: "name".into(), - r#type: Type::String, - role: ColumnRole::User, - }; + let c = column_user_non_null(String::from("name"), Type::String); assert_eq!( vec![ @@ -284,11 +273,7 @@ fn column_msgpack_serialize() { rmp_serde::to_vec(&c).unwrap() ); - let c = Column { - name: "name".into(), - r#type: Type::Integer, - role: ColumnRole::User, - }; + let c = column_user_non_null(String::from("name"), Type::Integer); assert_eq!( vec![ @@ -299,11 +284,7 @@ fn column_msgpack_serialize() { rmp_serde::to_vec(&c).unwrap() ); - let c = Column { - name: "name".into(), - r#type: Type::Unsigned, - role: ColumnRole::User, - }; + let c = column_user_non_null(String::from("name"), Type::Unsigned); assert_eq!( vec![ @@ -314,11 +295,7 @@ fn column_msgpack_serialize() { rmp_serde::to_vec(&c).unwrap() ); - let c = Column { - name: "name".into(), - r#type: Type::Number, - role: ColumnRole::User, - }; + let c = column_user_non_null(String::from("name"), Type::Number); assert_eq!( vec![ @@ -332,11 +309,7 @@ fn column_msgpack_serialize() { #[test] fn column_msgpack_deserialize() { - let c = Column { - name: "name".into(), - r#type: Type::Boolean, - role: ColumnRole::User, - }; + let c = column_user_non_null(String::from("name"), Type::Boolean); let expected_msgpack = vec![ 0x83, 0xA4, 0x6E, 0x61, 0x6D, 0x65, 0xA4, 0x6E, 0x61, 0x6D, 0x65, 0xA4, 0x74, 0x79, 0x70, @@ -357,10 +330,10 @@ fn table_converting() { let t = Table::new_seg( "t", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("b", Type::Unsigned, ColumnRole::User), - Column::new("c", Type::String, ColumnRole::User), - Column::new("d", Type::String, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("b"), Type::Unsigned), + column_user_non_null(String::from("c"), Type::String), + column_user_non_null(String::from("d"), Type::String), ], &["b", "a"], &["b", "a"], diff --git a/sbroad-core/src/ir/tests.rs b/sbroad-core/src/ir/tests.rs index 9a734c00cd..9572a738df 100644 --- a/sbroad-core/src/ir/tests.rs +++ b/sbroad-core/src/ir/tests.rs @@ -4,6 +4,54 @@ use pretty_assertions::assert_eq; use std::fs; use std::path::Path; +/// Helper function to create `Column` object with given name and default: +/// +/// * `type` = Integer +/// * `is_nullable` = false +/// * `role` = User +/// Used only for tests purposes. +#[must_use] +#[cfg(test)] +pub fn column_integer_user_non_null(name: String) -> Column { + Column { + name, + r#type: Type::Integer, + role: ColumnRole::User, + is_nullable: false, + } +} + +/// Helper function to create `Column` object with given name, type and default +/// * `is_nullable` = false +/// * `role` = User +/// Used only for tests purposes. +#[must_use] +#[cfg(test)] +pub fn column_user_non_null(name: String, r#type: Type) -> Column { + Column { + name, + r#type, + role: ColumnRole::User, + is_nullable: false, + } +} + +/// Helper function to create sharding `Column` object with default +/// * `is_nullable` = true +/// * `role` = Sharding +/// * `type` = Unsigned +/// Used only for tests purposes. +#[must_use] +#[cfg(test)] +pub fn sharding_column() -> Column { + Column { + name: String::from("bucket_id"), + r#type: Type::Unsigned, + role: ColumnRole::Sharding, + is_nullable: true, + } +} + #[test] fn plan_no_top() { let path = Path::new("") @@ -38,7 +86,7 @@ fn get_node() { let t = Table::new_seg( "t", - vec![Column::new("a", Type::Boolean, ColumnRole::User)], + vec![Column::new("a", Type::Boolean, ColumnRole::User, false)], &["a"], &["a"], SpaceEngine::Memtx, diff --git a/sbroad-core/src/ir/transformation/redistribution/tests.rs b/sbroad-core/src/ir/transformation/redistribution/tests.rs index 109c62109a..294da9d383 100644 --- a/sbroad-core/src/ir/transformation/redistribution/tests.rs +++ b/sbroad-core/src/ir/transformation/redistribution/tests.rs @@ -1,6 +1,7 @@ use super::*; use crate::ir::operator::Relational; -use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::relation::{SpaceEngine, Table}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::helpers::sql_to_ir; use crate::ir::Plan; use crate::ir::Slices; @@ -18,7 +19,7 @@ fn full_motion_less_for_sub_query() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Vinyl, @@ -31,8 +32,8 @@ fn full_motion_less_for_sub_query() { let t2 = Table::new_seg( "t2", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], &["a"], &["a"], @@ -82,8 +83,8 @@ fn full_motion_non_segment_outer_for_sub_query() { let t1 = Table::new_seg( "t1", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], &["a"], &["a"], @@ -96,7 +97,7 @@ fn full_motion_non_segment_outer_for_sub_query() { let t2 = Table::new_seg( "t2", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Vinyl, @@ -144,7 +145,7 @@ fn local_sub_query() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, @@ -156,7 +157,7 @@ fn local_sub_query() { let t2 = Table::new_seg( "t2", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, @@ -203,7 +204,7 @@ fn multiple_sub_queries() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, @@ -216,8 +217,8 @@ fn multiple_sub_queries() { let t2 = Table::new_seg( "t2", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], &["a"], &["a"], diff --git a/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs b/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs index 856879838b..61fd6603c3 100644 --- a/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs +++ b/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs @@ -3,7 +3,8 @@ use crate::errors::{Entity, SbroadError}; use crate::ir::distribution::{Distribution, Key}; use crate::ir::helpers::RepeatableState; use crate::ir::operator::{Bool, Relational}; -use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::relation::{Column, SpaceEngine, Table}; +use crate::ir::tests::column_integer_user_non_null; use crate::ir::transformation::helpers::sql_to_ir; use crate::ir::transformation::redistribution::{MotionKey, MotionPolicy, Target}; use crate::ir::{Node, Plan}; @@ -24,7 +25,7 @@ fn sub_query1() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Integer, ColumnRole::User)], + vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, @@ -37,8 +38,8 @@ fn sub_query1() { let t2 = Table::new_seg( "t2", vec![ - Column::new("a", Type::Integer, ColumnRole::User), - Column::new("b", Type::Integer, ColumnRole::User), + column_integer_user_non_null(String::from("a")), + column_integer_user_non_null(String::from("b")), ], &["a"], &["a"], @@ -334,7 +335,7 @@ fn insert4() { #[test] fn insert5() { - let query = r#"INSERT INTO "t" ("c", "d") SELECT "a", "b" FROM "t""#; + let query = r#"INSERT INTO "t" ("c", "d", "b") SELECT "a", "c", "b" FROM "t""#; let mut plan = sql_to_ir(query, vec![]); plan.add_motions().unwrap(); @@ -344,10 +345,7 @@ fn insert5() { assert_eq!( *policy, MotionPolicy::Segment(MotionKey { - targets: vec![ - Target::Value(Column::default_value()), - Target::Value(Column::default_value()), - ] + targets: vec![Target::Value(Column::default_value()), Target::Reference(2),] }) ); } else { @@ -357,7 +355,7 @@ fn insert5() { #[test] fn insert6() { - let query = r#"INSERT INTO "t" ("a", "c") SELECT "a", "b" FROM "t""#; + let query = r#"INSERT INTO "t" ("c", "b") SELECT "b", "c" FROM "t""#; let mut plan = sql_to_ir(query, vec![]); plan.add_motions().unwrap(); @@ -367,7 +365,7 @@ fn insert6() { assert_eq!( *policy, MotionPolicy::Segment(MotionKey { - targets: vec![Target::Reference(0), Target::Value(Column::default_value()),] + targets: vec![Target::Value(Column::default_value()), Target::Reference(1)] }) ); } else { diff --git a/sbroad-core/src/ir/tree/tests.rs b/sbroad-core/src/ir/tree/tests.rs index 59247ba6f0..9a4ae2478e 100644 --- a/sbroad-core/src/ir/tree/tests.rs +++ b/sbroad-core/src/ir/tree/tests.rs @@ -1,5 +1,6 @@ use crate::ir::operator::Bool; -use crate::ir::relation::{Column, ColumnRole, SpaceEngine, Table, Type}; +use crate::ir::relation::{SpaceEngine, Table, Type}; +use crate::ir::tests::column_user_non_null; use crate::ir::tree::traversal::{BreadthFirst, PostOrder, EXPR_CAPACITY, REL_CAPACITY}; use crate::ir::value::Value; use crate::ir::{Expression, Plan}; @@ -55,7 +56,7 @@ fn relational_post() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Boolean, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, @@ -66,7 +67,7 @@ fn relational_post() { let t2 = Table::new_seg( "t2", - vec![Column::new("a", Type::Boolean, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, @@ -119,7 +120,7 @@ fn selection_subquery_dfs_post() { let t1 = Table::new_seg( "t1", - vec![Column::new("a", Type::Boolean, ColumnRole::User)], + vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, @@ -132,8 +133,8 @@ fn selection_subquery_dfs_post() { let t2 = Table::new_seg( "t2", vec![ - Column::new("b", Type::Boolean, ColumnRole::User), - Column::new("c", Type::Boolean, ColumnRole::User), + column_user_non_null(String::from("b"), Type::Boolean), + column_user_non_null(String::from("c"), Type::Boolean), ], &["b"], &["b"], @@ -207,8 +208,8 @@ fn subtree_dfs_post() { let t1 = Table::new_seg( "t1", vec![ - Column::new("a", Type::Boolean, ColumnRole::User), - Column::new("c", Type::Boolean, ColumnRole::User), + column_user_non_null(String::from("a"), Type::Boolean), + column_user_non_null(String::from("c"), Type::Boolean), ], &["a", "c"], &["a", "c"], diff --git a/sbroad-core/tests/artifactory/backend/sql/tree/arbitrary_projection_plan.yaml b/sbroad-core/tests/artifactory/backend/sql/tree/arbitrary_projection_plan.yaml index 4eacd2c991..e80d1875f9 100644 --- a/sbroad-core/tests/artifactory/backend/sql/tree/arbitrary_projection_plan.yaml +++ b/sbroad-core/tests/artifactory/backend/sql/tree/arbitrary_projection_plan.yaml @@ -176,18 +176,23 @@ relations: - name: a type: Integer role: User + is_nullable: false - name: b type: Integer role: User + is_nullable: false - name: c type: Integer role: User + is_nullable: false - name: d type: Integer role: User + is_nullable: false - name: bucket_id type: Unsigned role: Sharding + is_nullable: true shard_key: positions: - 0 diff --git a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml index b1b4bc3de5..5ccfc4e467 100644 --- a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml +++ b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_projection_plan.yaml @@ -241,24 +241,31 @@ relations: - name: a type: Integer role: User + is_nullable: false - name: b type: Integer role: User + is_nullable: false - name: c type: Integer role: User + is_nullable: false - name: d type: Integer role: User + is_nullable: false - name: e type: Integer role: User + is_nullable: false - name: f type: Integer role: User + is_nullable: false - name: bucket_id type: Unsigned role: Sharding + is_nullable: true shard_key: positions: - 0 diff --git a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_selection_plan.yaml b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_selection_plan.yaml index 6fe16c181f..4072a4975c 100644 --- a/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_selection_plan.yaml +++ b/sbroad-core/tests/artifactory/backend/sql/tree/arithmetic_selection_plan.yaml @@ -373,24 +373,31 @@ relations: - name: a type: Integer role: User + is_nullable: false - name: b type: Integer role: User + is_nullable: false - name: c type: Integer role: User + is_nullable: false - name: d type: Integer role: User + is_nullable: false - name: e type: Integer role: User + is_nullable: false - name: f type: Integer role: User + is_nullable: false - name: bucket_id type: Unsigned role: Sharding + is_nullable: true shard_key: positions: - 0 -- GitLab