diff --git a/sbroad-cartridge/src/cartridge/config.rs b/sbroad-cartridge/src/cartridge/config.rs index cfd339ed55c70bcc9dce475b0dc19cd763e1f714..960f9026cf146193df36fdef84c8436ff725c64d 100644 --- a/sbroad-cartridge/src/cartridge/config.rs +++ b/sbroad-cartridge/src/cartridge/config.rs @@ -244,13 +244,12 @@ impl RouterConfiguration { .iter() .map(String::as_str) .collect::<Vec<&str>>(); - let t = Table::new( + let t = Table::new_sharded( &table_name, fields, shard_key_str.as_slice(), primary_key_str.as_slice(), engine, - false, )?; self.tables.insert(table_name, t); } else { diff --git a/sbroad-cartridge/src/cartridge/config/tests.rs b/sbroad-cartridge/src/cartridge/config/tests.rs index 98fbec2fd3ed2e6df42b436e7ab556e75c78389f..8bb83c9d963a7995184e07043e166fa540df6ade 100644 --- a/sbroad-cartridge/src/cartridge/config/tests.rs +++ b/sbroad-cartridge/src/cartridge/config/tests.rs @@ -140,7 +140,7 @@ fn test_getting_table_segment() { s.set_sharding_column("\"bucket_id\"".into()); s.load_schema(test_schema).unwrap(); - let expected = Table::new( + let expected = Table::new_sharded( "\"hash_testing\"", vec![ Column::new( @@ -158,7 +158,6 @@ fn test_getting_table_segment() { &["\"identification_number\"", "\"product_code\""], &["\"identification_number\""], SpaceEngine::Memtx, - false, ) .unwrap(); diff --git a/sbroad-core/src/backend/sql/tree/tests.rs b/sbroad-core/src/backend/sql/tree/tests.rs index c9476f0513fcb69fbd279ecbd1b9fb801bcbb3e7..77e128e8a1d617e7a6e5e30370f887857695a377 100644 --- a/sbroad-core/src/backend/sql/tree/tests.rs +++ b/sbroad-core/src/backend/sql/tree/tests.rs @@ -19,13 +19,12 @@ fn sql_order_selection() { // select a from t where a = 1 let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -99,7 +98,7 @@ fn sql_order_selection() { fn sql_arithmetic_selection_plan() { // select a from t where a + (b/c + d*e) * f - b = 1 let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_integer_user_non_null(String::from("a")), @@ -113,7 +112,6 @@ fn sql_arithmetic_selection_plan() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -297,7 +295,7 @@ fn sql_arithmetic_selection_plan() { fn sql_arithmetic_projection_plan() { // select a + (b/c + d*e) * f - b from t let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_integer_user_non_null(String::from("a")), @@ -311,7 +309,6 @@ fn sql_arithmetic_projection_plan() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -472,7 +469,7 @@ fn sql_arithmetic_projection_plan() { fn sql_arbitrary_projection_plan() { // select a + b > c and d is not null from t let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_integer_user_non_null(String::from("a")), @@ -484,7 +481,6 @@ fn sql_arbitrary_projection_plan() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs index 518ae9b9f877ab99a8d08e0e93c4be506e649996..ba42a672e7be99ea4eb72d5623ccacfee1dfa7e7 100644 --- a/sbroad-core/src/executor/engine/mock.rs +++ b/sbroad-core/src/executor/engine/mock.rs @@ -121,26 +121,24 @@ impl RouterConfigurationMock { let primary_key = &["\"product_code\"", "\"identification_number\""]; tables.insert( "\"hash_testing\"".to_string(), - Table::new( + Table::new_sharded( "\"hash_testing\"", columns.clone(), sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); tables.insert( "\"hash_testing_hist\"".to_string(), - Table::new( + Table::new_sharded( "\"hash_testing_hist\"", columns.clone(), sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -148,26 +146,24 @@ impl RouterConfigurationMock { let sharding_key = &["\"identification_number\""]; tables.insert( "\"hash_single_testing\"".to_string(), - Table::new( + Table::new_sharded( "\"hash_single_testing\"", columns.clone(), sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); tables.insert( "\"hash_single_testing_hist\"".to_string(), - Table::new( + Table::new_sharded( "\"hash_single_testing_hist\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -184,26 +180,24 @@ impl RouterConfigurationMock { tables.insert( "\"test_space\"".to_string(), - Table::new( + Table::new_sharded( "\"test_space\"", columns.clone(), sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); tables.insert( "\"test_space_hist\"".to_string(), - Table::new( + Table::new_sharded( "\"test_space_hist\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -216,13 +210,12 @@ impl RouterConfigurationMock { let primary_key: &[&str] = &["\"id\""]; tables.insert( "\"history\"".to_string(), - Table::new( + Table::new_sharded( "\"history\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -238,13 +231,12 @@ impl RouterConfigurationMock { let primary_key: &[&str] = &["\"b\""]; tables.insert( "\"t\"".to_string(), - Table::new( + Table::new_sharded( "\"t\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -258,13 +250,12 @@ impl RouterConfigurationMock { let primary_key: &[&str] = &["\"a\"", "\"b\""]; tables.insert( "\"t1\"".to_string(), - Table::new( + Table::new_sharded( "\"t1\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -280,13 +271,12 @@ impl RouterConfigurationMock { let primary_key: &[&str] = &["\"g\"", "\"h\""]; tables.insert( "\"t2\"".to_string(), - Table::new( + Table::new_sharded( "\"t2\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -300,13 +290,12 @@ impl RouterConfigurationMock { let primary_key: &[&str] = &["\"a\""]; tables.insert( "\"t3\"".to_string(), - Table::new( + Table::new_sharded( "\"t3\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); @@ -315,19 +304,10 @@ impl RouterConfigurationMock { Column::new("\"a\"", Type::Integer, ColumnRole::User, false), Column::new("\"b\"", Type::Integer, ColumnRole::User, false), ]; - let sharding_key: &[&str] = &[]; let primary_key: &[&str] = &["\"a\""]; tables.insert( "\"global_t\"".to_string(), - Table::new( - "\"global_t\"", - columns, - sharding_key, - primary_key, - SpaceEngine::Memtx, - false, - ) - .unwrap(), + Table::new_global("\"global_t\"", columns, primary_key).unwrap(), ); // Table for sbroad-benches @@ -836,25 +816,23 @@ impl RouterConfigurationMock { let primary_key: &[&str] = &["\"reestrid\""]; tables.insert( "\"test__gibdd_db__vehicle_reg_and_res100_actual\"".to_string(), - Table::new( + Table::new_sharded( "\"test__gibdd_db__vehicle_reg_and_res100_actual\"", columns.clone(), sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); tables.insert( "\"test__gibdd_db__vehicle_reg_and_res100_history\"".to_string(), - Table::new( + Table::new_sharded( "\"test__gibdd_db__vehicle_reg_and_res100_history\"", columns, sharding_key, primary_key, SpaceEngine::Memtx, - false, ) .unwrap(), ); diff --git a/sbroad-core/src/ir/distribution.rs b/sbroad-core/src/ir/distribution.rs index 1e8b321d85b937cc27e6fe9db8f86632927d35f1..ef0c82e2542dc6ab01ee61181439ab5c48b7a4da 100644 --- a/sbroad-core/src/ir/distribution.rs +++ b/sbroad-core/src/ir/distribution.rs @@ -12,6 +12,7 @@ use crate::ir::transformation::redistribution::{MotionKey, Target}; use super::expression::Expression; use super::operator::Relational; +use super::relation::{Column, ColumnPositions}; use super::{Node, Plan}; /// Tuple columns that determinate its segment distribution. @@ -32,6 +33,38 @@ impl Key { Key { positions } } + pub(crate) fn with_columns( + columns: &[Column], + pos_map: &ColumnPositions, + sharding_key: &[&str], + ) -> Result<Self, SbroadError> { + let shard_positions = sharding_key + .iter() + .map(|name| match pos_map.get(name) { + Some(pos) => { + // Check that the column type is scalar. + // Compound types are not supported as sharding keys. + let column = &columns.get(pos).ok_or_else(|| { + SbroadError::FailedTo( + Action::Create, + Some(Entity::Column), + format!("column {name} not found at position {pos}"), + ) + })?; + if !column.r#type.is_scalar() { + return Err(SbroadError::Invalid( + Entity::Column, + Some(format!("column {name} at position {pos} is not scalar",)), + )); + } + Ok(pos) + } + None => Err(SbroadError::Invalid(Entity::ShardingKey, None)), + }) + .collect::<Result<Vec<usize>, _>>()?; + Ok(Key::new(shard_positions)) + } + #[must_use] pub fn is_empty(&self) -> bool { self.positions.is_empty() diff --git a/sbroad-core/src/ir/distribution/tests.rs b/sbroad-core/src/ir/distribution/tests.rs index 33f640631a556782c769200f90ec6c2ffe39f5f5..986f485af0b1843e147e4e7e93e4794ec0423858 100644 --- a/sbroad-core/src/ir/distribution/tests.rs +++ b/sbroad-core/src/ir/distribution/tests.rs @@ -12,7 +12,7 @@ use std::path::Path; fn proj_preserve_dist_key() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -23,7 +23,6 @@ fn proj_preserve_dist_key() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); diff --git a/sbroad-core/src/ir/expression/tests.rs b/sbroad-core/src/ir/expression/tests.rs index 10c0d6448f45feff832a5e173066158994684602..2ac4a2801163e4e66c4951d10c92245817a6d8ec 100644 --- a/sbroad-core/src/ir/expression/tests.rs +++ b/sbroad-core/src/ir/expression/tests.rs @@ -27,13 +27,12 @@ fn rel_nodes_from_reference_in_scan() { // select * from t let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -50,13 +49,12 @@ fn rel_nodes_from_reference_in_proj() { // select a from t let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); diff --git a/sbroad-core/src/ir/operator/tests.rs b/sbroad-core/src/ir/operator/tests.rs index 9061b3f117ce708408f77ef98a036ea1620d4efe..460ecf826f09ebb0b5c185f26f11fde1ab63dea9 100644 --- a/sbroad-core/src/ir/operator/tests.rs +++ b/sbroad-core/src/ir/operator/tests.rs @@ -18,7 +18,7 @@ use super::*; fn scan_rel() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -29,7 +29,6 @@ fn scan_rel() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -57,7 +56,7 @@ fn scan_rel() { fn scan_rel_serialized() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -68,7 +67,6 @@ fn scan_rel_serialized() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -94,7 +92,7 @@ fn scan_rel_serialized() { fn projection() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -105,7 +103,6 @@ fn projection() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -152,7 +149,7 @@ fn selection() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -163,7 +160,6 @@ fn selection() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -217,26 +213,24 @@ fn selection_serialize() { fn except() { let mut valid_plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); let t1_copy = t1.clone(); valid_plan.add_rel(t1); let scan_t1_id = valid_plan.add_scan("t1", None).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); valid_plan.add_rel(t2); @@ -250,7 +244,7 @@ fn except() { invalid_plan.add_rel(t1_copy); let scan_t1_id = invalid_plan.add_scan("t1", None).unwrap(); - let t3 = Table::new( + let t3 = Table::new_sharded( "t3", vec![ column_user_non_null(String::from("a"), Type::Unsigned), @@ -259,7 +253,6 @@ fn except() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); invalid_plan.add_rel(t3); @@ -278,20 +271,19 @@ fn except() { fn insert() { let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_user_non_null(String::from("a"), Type::Unsigned), @@ -301,7 +293,6 @@ fn insert() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -343,25 +334,23 @@ fn insert() { fn union_all() { let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_user_non_null(String::from("a"), Type::Unsigned)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -374,7 +363,7 @@ fn union_all() { fn union_all_col_amount_mismatch() { let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -383,7 +372,6 @@ fn union_all_col_amount_mismatch() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); @@ -391,13 +379,12 @@ fn union_all_col_amount_mismatch() { let scan_t1_id = plan.add_scan("t1", None).unwrap(); // Check errors for children with different amount of column - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_user_non_null(String::from("b"), Type::Unsigned)], &["b"], &["b"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -416,7 +403,7 @@ fn union_all_col_amount_mismatch() { fn sub_query() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -425,7 +412,6 @@ fn sub_query() { &["a"], &["b"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); @@ -466,26 +452,24 @@ fn selection_with_sub_query() { let mut plan = Plan::default(); let mut children: Vec<usize> = Vec::new(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); children.push(scan_t1_id); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_integer_user_non_null(String::from("b"))], &["b"], &["b"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -524,7 +508,7 @@ fn join() { // i.e. (a), (d) - tuples containing a single column. let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -534,13 +518,12 @@ fn join() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1 = plan.add_scan("t1", None).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_user_non_null(String::from("c"), Type::Boolean), @@ -550,7 +533,6 @@ fn join() { &["d"], &["d"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -587,7 +569,7 @@ fn join_duplicate_columns() { // select * from t1 join t2 on t1.a = t2.d let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -597,13 +579,12 @@ fn join_duplicate_columns() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1 = plan.add_scan("t1", None).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -613,7 +594,6 @@ fn join_duplicate_columns() { &["d"], &["d"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); diff --git a/sbroad-core/src/ir/relation.rs b/sbroad-core/src/ir/relation.rs index a81e556f0b56bfc27a7303469a7b9170138de77b..1bc1723e607ca3ff6ab77900171dcb3f036158b8 100644 --- a/sbroad-core/src/ir/relation.rs +++ b/sbroad-core/src/ir/relation.rs @@ -7,6 +7,7 @@ //! * Table, representing unnamed tuples storage (`Table`) //! * Relation, representing named tables (`Relations` as a map of { name -> table }) +use ahash::AHashMap; use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; use std::fmt::{self, Formatter}; @@ -381,16 +382,33 @@ impl TryFrom<&str> for SpaceEngine { } } -/// Table is a tuple storage in the cluster. -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -pub struct Table { - /// List of the columns. - pub columns: Vec<Column>, - /// Primary key of the table (column positions). - pub primary_key: Key, - /// Unique table name. - pub name: String, - pub kind: TableKind, +// A helper struct to collect column positions. +#[derive(Debug)] +pub(crate) struct ColumnPositions<'column> { + // Column positions with names as keys. + map: AHashMap<&'column str, usize>, +} + +impl<'column> ColumnPositions<'column> { + #[allow(clippy::uninlined_format_args)] + pub(crate) fn new(columns: &'column [Column], table: &str) -> Result<Self, SbroadError> { + let mut map = AHashMap::with_capacity(columns.len()); + for (pos, col) in columns.iter().enumerate() { + let name = col.name.as_str(); + if let Some(old_pos) = map.insert(name, pos) { + return Err(SbroadError::DuplicatedValue(format!( + r#"Table "{}" has a duplicating column "{}" at positions {} and {}"#, + table, name, old_pos, pos, + ))); + } + } + map.shrink_to_fit(); + Ok(Self { map }) + } + + pub(crate) fn get(&self, name: &str) -> Option<usize> { + self.map.get(name).copied() + } } #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] @@ -403,101 +421,130 @@ pub enum TableKind { SystemSpace, } +impl TableKind { + #[must_use] + pub fn new_sharded(sharding_key: Key, engine: SpaceEngine) -> Self { + Self::ShardedSpace { + sharding_key, + engine, + } + } + + #[must_use] + pub fn new_global() -> Self { + Self::GlobalSpace + } + + #[must_use] + pub fn new_system() -> Self { + Self::SystemSpace + } +} + +fn table_new_impl<'column>( + name: &str, + columns: &'column [Column], + primary_key: &'column [&str], +) -> Result<(ColumnPositions<'column>, Key), SbroadError> { + let pos_map = ColumnPositions::new(columns, name)?; + let primary_positions = primary_key + .iter() + .map(|name| match pos_map.get(name) { + Some(pos) => { + let _ = &columns.get(pos).ok_or_else(|| { + SbroadError::FailedTo( + Action::Create, + Some(Entity::Column), + format!("column {name} not found at position {pos}"), + ) + })?; + Ok(pos) + } + None => Err(SbroadError::Invalid(Entity::PrimaryKey, None)), + }) + .collect::<Result<Vec<usize>, _>>()?; + Ok((pos_map, Key::new(primary_positions))) +} + +/// Table is a tuple storage in the cluster. +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub struct Table { + /// List of the columns. + pub columns: Vec<Column>, + /// Primary key of the table (column positions). + pub primary_key: Key, + /// Unique table name. + pub name: String, + pub kind: TableKind, +} + impl Table { #[must_use] pub fn name(&self) -> &str { &self.name } - /// Table constructor. + /// Sharded table constructor. /// /// # Errors - /// Returns `SbroadError` when the input arguments are invalid. - pub fn new( + /// - column names are duplicated; + /// - primary key is not found among the columns; + /// - sharding key is not found among the columns; + pub fn new_sharded( name: &str, columns: Vec<Column>, sharding_key: &[&str], primary_key: &[&str], engine: SpaceEngine, - is_system: bool, ) -> Result<Self, SbroadError> { - let mut pos_map: HashMap<&str, usize> = HashMap::new(); - let no_duplicates = &columns - .iter() - .enumerate() - .all(|(pos, col)| matches!(pos_map.insert(&col.name, pos), None)); - - if !no_duplicates { - return Err(SbroadError::DuplicatedValue( - "Table has duplicated columns and couldn't be loaded".into(), - )); - } - - let table_kind = if sharding_key.is_empty() { - if engine != SpaceEngine::Memtx { - return Err(SbroadError::Unsupported( - Entity::Table, - Some("global table can only have memtx engine".into()), - )); - } - if is_system { - TableKind::SystemSpace - } else { - TableKind::GlobalSpace - } - } else { - let shard_positions = sharding_key - .iter() - .map(|name| match pos_map.get(*name) { - Some(pos) => { - // Check that the column type is scalar. - // Compound types are not supported as sharding keys. - let column = &columns.get(*pos).ok_or_else(|| { - SbroadError::FailedTo( - Action::Create, - Some(Entity::Column), - format!("column {name} not found at position {pos}"), - ) - })?; - if !column.r#type.is_scalar() { - return Err(SbroadError::Invalid( - Entity::Column, - Some(format!("column {name} at position {pos} is not scalar",)), - )); - } - Ok(*pos) - } - None => Err(SbroadError::Invalid(Entity::ShardingKey, None)), - }) - .collect::<Result<Vec<usize>, _>>()?; - TableKind::ShardedSpace { - sharding_key: Key::new(shard_positions), - engine, - } - }; + let (pos_map, primary_key) = table_new_impl(name, &columns, primary_key)?; + let sharding_key = Key::with_columns(&columns, &pos_map, sharding_key)?; + let kind = TableKind::new_sharded(sharding_key, engine); + Ok(Table { + name: name.into(), + columns, + primary_key, + kind, + }) + } - let primary_positions = primary_key - .iter() - .map(|name| match pos_map.get(*name) { - Some(pos) => { - let _ = &columns.get(*pos).ok_or_else(|| { - SbroadError::FailedTo( - Action::Create, - Some(Entity::Column), - format!("column {name} not found at position {pos}"), - ) - })?; - Ok(*pos) - } - None => Err(SbroadError::Invalid(Entity::PrimaryKey, None)), - }) - .collect::<Result<Vec<usize>, _>>()?; + /// Global table constructor. + /// + /// # Errors + /// - column names are duplicated; + /// - primary key is not found among the columns; + pub fn new_global( + name: &str, + columns: Vec<Column>, + primary_key: &[&str], + ) -> Result<Self, SbroadError> { + let (_, primary_key) = table_new_impl(name, &columns, primary_key)?; + let kind = TableKind::new_global(); + Ok(Table { + name: name.into(), + columns, + primary_key, + kind, + }) + } + /// System table constructor. + /// + /// # Errors + /// - column names are duplicated; + /// - primary key is not found among the columns; + pub fn new_system( + name: &str, + columns: Vec<Column>, + primary_key: &[&str], + ) -> Result<Self, SbroadError> { + let (_, primary_key) = table_new_impl(name, &columns, primary_key)?; + let kind = TableKind::new_system(); Ok(Table { name: name.into(), columns, - primary_key: Key::new(primary_positions), - kind: table_kind, + primary_key, + kind, }) } @@ -566,9 +613,10 @@ impl Table { Ordering::Greater => Err(SbroadError::UnexpectedNumberOfValues( "Table has more than one bucket_id column".into(), )), - Ordering::Less => Err(SbroadError::UnexpectedNumberOfValues( - "Table has no bucket_id columns".into(), - )), + Ordering::Less => Err(SbroadError::UnexpectedNumberOfValues(format!( + "Table {} has no bucket_id columns", + self.name + ))), } } diff --git a/sbroad-core/src/ir/relation/tests.rs b/sbroad-core/src/ir/relation/tests.rs index b4c7c90f0a8069a0038eae9d827eb71a9144637f..ee9ffaa2ed28b737ed5154ee01baf94d2c49ed1d 100644 --- a/sbroad-core/src/ir/relation/tests.rs +++ b/sbroad-core/src/ir/relation/tests.rs @@ -16,7 +16,7 @@ fn column() { #[test] fn table_seg() { - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -27,7 +27,6 @@ fn table_seg() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); @@ -39,13 +38,12 @@ fn table_seg() { #[test] fn table_seg_name() { - let t = Table::new( + let t = Table::new_sharded( "t", vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); assert_eq!("t", t.name()); @@ -54,7 +52,7 @@ fn table_seg_name() { #[test] fn table_seg_duplicate_columns() { assert_eq!( - Table::new( + Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -65,16 +63,17 @@ fn table_seg_duplicate_columns() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false ) .unwrap_err(), - SbroadError::DuplicatedValue("Table has duplicated columns and couldn't be loaded".into()) + SbroadError::DuplicatedValue(format!( + r#"Table "t" has a duplicating column "a" at positions 0 and 3"#, + )) ); } #[test] fn table_seg_dno_bucket_id_column() { - let t1 = Table::new( + let t1 = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -84,16 +83,15 @@ fn table_seg_dno_bucket_id_column() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); assert_eq!( - SbroadError::UnexpectedNumberOfValues("Table has no bucket_id columns".into()), + SbroadError::UnexpectedNumberOfValues(format!("Table {} has no bucket_id columns", "t")), t1.get_bucket_id_position().unwrap_err() ); - let t2 = Table::new( + let t2 = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -105,7 +103,6 @@ fn table_seg_dno_bucket_id_column() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); @@ -118,7 +115,7 @@ fn table_seg_dno_bucket_id_column() { #[test] fn table_seg_wrong_key() { assert_eq!( - Table::new( + Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -127,9 +124,8 @@ fn table_seg_wrong_key() { column_user_non_null(String::from("d"), Type::String), ], &["a", "e"], - &["a", "e"], + &["a"], SpaceEngine::Memtx, - false ) .unwrap_err(), SbroadError::Invalid(Entity::ShardingKey, None) @@ -139,7 +135,7 @@ fn table_seg_wrong_key() { #[test] fn table_seg_compound_type_in_key() { assert_eq!( - Table::new( + Table::new_sharded( "t", vec![ Column::new("bucket_id", Type::Unsigned, ColumnRole::Sharding, false), @@ -148,7 +144,6 @@ fn table_seg_compound_type_in_key() { &["a"], &["a"], SpaceEngine::Memtx, - false ) .unwrap_err(), SbroadError::Invalid( @@ -160,7 +155,7 @@ fn table_seg_compound_type_in_key() { #[test] fn table_seg_serialized() { - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -173,7 +168,6 @@ fn table_seg_serialized() { &["a", "d"], &["a", "d"], SpaceEngine::Memtx, - false, ) .unwrap(); let path = Path::new("") @@ -336,7 +330,7 @@ fn column_msgpack_deserialize() { #[test] fn table_converting() { - let t = Table::new( + let t = Table::new_sharded( "t", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -347,7 +341,6 @@ fn table_converting() { &["b", "a"], &["b", "a"], SpaceEngine::Memtx, - false, ) .unwrap(); diff --git a/sbroad-core/src/ir/tests.rs b/sbroad-core/src/ir/tests.rs index f6c128800207534415fbe35bcf59eb421d9c4adf..ee2e10f4a97cd36e1db55689b9d0665d51699fd7 100644 --- a/sbroad-core/src/ir/tests.rs +++ b/sbroad-core/src/ir/tests.rs @@ -84,13 +84,12 @@ fn plan_oor_top() { fn get_node() { let mut plan = Plan::default(); - let t = Table::new( + let t = Table::new_sharded( "t", vec![Column::new("a", Type::Boolean, ColumnRole::User, false)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t); diff --git a/sbroad-core/src/ir/transformation/redistribution/tests.rs b/sbroad-core/src/ir/transformation/redistribution/tests.rs index 4b1012f99450116c26802647e986e56c98272d82..75902704c302a5eed80f8280266a5925733091eb 100644 --- a/sbroad-core/src/ir/transformation/redistribution/tests.rs +++ b/sbroad-core/src/ir/transformation/redistribution/tests.rs @@ -17,20 +17,19 @@ fn full_motion_less_for_sub_query() { let mut plan = Plan::default(); let mut children: Vec<usize> = Vec::new(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Vinyl, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); children.push(scan_t1_id); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_integer_user_non_null(String::from("a")), @@ -39,7 +38,6 @@ fn full_motion_less_for_sub_query() { &["a"], &["a"], SpaceEngine::Vinyl, - false, ) .unwrap(); plan.add_rel(t2); @@ -82,7 +80,7 @@ fn full_motion_non_segment_outer_for_sub_query() { let mut plan = Plan::default(); let mut children: Vec<usize> = Vec::new(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![ column_integer_user_non_null(String::from("a")), @@ -91,20 +89,18 @@ fn full_motion_non_segment_outer_for_sub_query() { &["a"], &["a"], SpaceEngine::Vinyl, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); children.push(scan_t1_id); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Vinyl, - false, ) .unwrap(); plan.add_rel(t2); @@ -147,26 +143,24 @@ fn local_sub_query() { let mut plan = Plan::default(); let mut children: Vec<usize> = Vec::new(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); children.push(scan_t1_id); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -208,20 +202,19 @@ fn multiple_sub_queries() { let mut plan = Plan::default(); let mut children: Vec<usize> = Vec::new(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); children.push(scan_t1_id); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_integer_user_non_null(String::from("a")), @@ -230,7 +223,6 @@ fn multiple_sub_queries() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); diff --git a/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs b/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs index b904f07cc2df9fe448436eec96667980ba99625b..894265a7ff96fb74ae0265d3b63b1a4f819db59d 100644 --- a/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs +++ b/sbroad-core/src/ir/transformation/redistribution/tests/segment.rs @@ -23,20 +23,19 @@ fn sub_query1() { let mut plan = Plan::default(); let mut children: Vec<usize> = Vec::new(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_integer_user_non_null(String::from("a"))], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); children.push(scan_t1_id); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_integer_user_non_null(String::from("a")), @@ -45,7 +44,6 @@ fn sub_query1() { &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); diff --git a/sbroad-core/src/ir/tree/tests.rs b/sbroad-core/src/ir/tree/tests.rs index ed69dc4c57092a9492a5cb4e3d19bdb97ef01b4a..85ae443a13bea329a57b02c32acbdba1cdf7fde5 100644 --- a/sbroad-core/src/ir/tree/tests.rs +++ b/sbroad-core/src/ir/tree/tests.rs @@ -54,25 +54,23 @@ fn relational_post() { // Initialize plan let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -120,20 +118,19 @@ fn selection_subquery_dfs_post() { // Initialize plan let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![column_user_non_null(String::from("a"), Type::Boolean)], &["a"], &["a"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1); let scan_t1_id = plan.add_scan("t1", None).unwrap(); let a = plan.add_row_from_child(scan_t1_id, &["a"]).unwrap(); - let t2 = Table::new( + let t2 = Table::new_sharded( "t2", vec![ column_user_non_null(String::from("b"), Type::Boolean), @@ -142,7 +139,6 @@ fn selection_subquery_dfs_post() { &["b"], &["b"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t2); @@ -209,7 +205,7 @@ fn subtree_dfs_post() { // Initialize plan let mut plan = Plan::default(); - let t1 = Table::new( + let t1 = Table::new_sharded( "t1", vec![ column_user_non_null(String::from("a"), Type::Boolean), @@ -218,7 +214,6 @@ fn subtree_dfs_post() { &["a", "c"], &["a", "c"], SpaceEngine::Memtx, - false, ) .unwrap(); plan.add_rel(t1);