diff --git a/server/src/engine/core/model/mod.rs b/server/src/engine/core/model/mod.rs index 413a8b45..0c938b1c 100644 --- a/server/src/engine/core/model/mod.rs +++ b/server/src/engine/core/model/mod.rs @@ -37,6 +37,7 @@ use { data::{ cell::Datacell, tag::{DataTag, FullTag, TagClass, TagSelector}, + uuid::Uuid, }, error::{DatabaseError, DatabaseResult}, idx::{IndexBaseSpec, IndexSTSeqCns, STIndex, STIndexSeq}, @@ -55,6 +56,7 @@ pub(in crate::engine::core) type Fields = IndexSTSeqCns, Field>; #[derive(Debug)] pub struct ModelData { + uuid: Uuid, p_key: Box, p_tag: FullTag, fields: UnsafeCell, @@ -68,11 +70,16 @@ impl PartialEq for ModelData { fn eq(&self, m: &Self) -> bool { let mdl1 = self.intent_read_model(); let mdl2 = m.intent_read_model(); - self.p_key == m.p_key && self.p_tag == m.p_tag && mdl1.fields() == mdl2.fields() + ((self.p_key == m.p_key) & (self.p_tag == m.p_tag)) + && self.uuid == m.uuid + && mdl1.fields() == mdl2.fields() } } impl ModelData { + pub fn get_uuid(&self) -> Uuid { + self.uuid + } pub fn p_key(&self) -> &str { &self.p_key } @@ -157,6 +164,7 @@ impl ModelData { let tag = fields.st_get(last_pk).unwrap().layers()[0].tag; if tag.tag_unique().is_unique() { return Ok(Self { + uuid: Uuid::new(), p_key: last_pk.into(), p_tag: tag, fields: UnsafeCell::new(fields), diff --git a/server/src/engine/core/space.rs b/server/src/engine/core/space.rs index c311780b..88736ead 100644 --- a/server/src/engine/core/space.rs +++ b/server/src/engine/core/space.rs @@ -27,7 +27,7 @@ use { crate::engine::{ core::{model::ModelData, RWLIdx}, - data::{dict, DictEntryGeneric, MetaDict}, + data::{dict, uuid::Uuid, DictEntryGeneric, MetaDict}, error::{DatabaseError, DatabaseResult}, idx::{IndexST, STIndex}, ql::ddl::{alt::AlterSpace, crt::CreateSpace, drop::DropSpace}, @@ -38,6 +38,7 @@ use { #[derive(Debug)] /// A space with the model namespace pub struct Space { + uuid: Uuid, mns: RWLIdx, ModelData>, pub(super) meta: SpaceMeta, } @@ -85,6 +86,9 @@ impl Space { Err(DatabaseError::DdlModelAlreadyExists) } } + pub fn get_uuid(&self) -> Uuid { + self.uuid + } pub(super) fn models(&self) -> &RWLIdx, ModelData> { &self.mns } @@ -103,15 +107,23 @@ impl Space { impl Space { pub fn empty() -> Self { - Space::new(Default::default(), SpaceMeta::with_env(into_dict! {})) + Space::new_auto(Default::default(), SpaceMeta::with_env(into_dict! {})) } #[inline(always)] - pub fn new(mns: IndexST, ModelData>, meta: SpaceMeta) -> Self { + pub fn new_auto(mns: IndexST, ModelData>, meta: SpaceMeta) -> Self { Self { + uuid: Uuid::new(), mns: RWLIdx::new(mns), meta, } } + pub fn new_with_uuid(mns: IndexST, ModelData>, meta: SpaceMeta, uuid: Uuid) -> Self { + Self { + uuid, + meta, + mns: RwLock::new(mns), + } + } #[inline] /// Validate a `create` stmt fn process_create( @@ -132,7 +144,7 @@ impl Space { }; Ok(ProcedureCreate { space_name, - space: Self::new( + space: Self::new_auto( IndexST::default(), SpaceMeta::with_env( // FIXME(@ohsayan): see this is bad. attempt to do it at AST build time @@ -211,6 +223,6 @@ impl PartialEq for Space { fn eq(&self, other: &Self) -> bool { let self_mns = self.mns.read(); let other_mns = other.mns.read(); - self.meta == other.meta && *self_mns == *other_mns + self.meta == other.meta && *self_mns == *other_mns && self.uuid == other.uuid } } diff --git a/server/src/engine/core/tests/ddl_model/alt.rs b/server/src/engine/core/tests/ddl_model/alt.rs index adc1f6f7..de832f74 100644 --- a/server/src/engine/core/tests/ddl_model/alt.rs +++ b/server/src/engine/core/tests/ddl_model/alt.rs @@ -53,7 +53,13 @@ fn exec_plan( plan: &str, f: impl Fn(&ModelData), ) -> DatabaseResult<()> { - exec_create(gns, model, new_space)?; + let mdl_name = exec_create(gns, model, new_space)?; + let prev_uuid = { + let gns = gns.spaces().read(); + let space = gns.get("myspace").unwrap(); + let space_read = space.models().read(); + space_read.get(mdl_name.as_str()).unwrap().get_uuid() + }; let tok = lex_insecure(plan.as_bytes()).unwrap(); let alter = parse_ast_node_full::(&tok[2..]).unwrap(); let (_space, model_name) = alter.model.into_full().unwrap(); @@ -61,7 +67,9 @@ fn exec_plan( let gns_read = gns.spaces().read(); let space = gns_read.st_get("myspace").unwrap(); let model = space.models().read(); - f(model.st_get(model_name.as_str()).unwrap()); + let model = model.st_get(model_name.as_str()).unwrap(); + assert_eq!(prev_uuid, model.get_uuid()); + f(model); Ok(()) } diff --git a/server/src/engine/core/tests/ddl_model/mod.rs b/server/src/engine/core/tests/ddl_model/mod.rs index dfebbba0..73ae4e95 100644 --- a/server/src/engine/core/tests/ddl_model/mod.rs +++ b/server/src/engine/core/tests/ddl_model/mod.rs @@ -32,7 +32,11 @@ use crate::engine::{ core::{model::ModelData, space::Space, GlobalNS}, error::DatabaseResult, idx::STIndex, - ql::{ast::parse_ast_node_full, ddl::crt::CreateModel, tests::lex_insecure}, + ql::{ + ast::{parse_ast_node_full, Entity}, + ddl::crt::CreateModel, + tests::lex_insecure, + }, }; fn create(s: &str) -> DatabaseResult { @@ -45,21 +49,24 @@ pub fn exec_create( gns: &GlobalNS, create_stmt: &str, create_new_space: bool, -) -> DatabaseResult<()> { +) -> DatabaseResult { let tok = lex_insecure(create_stmt.as_bytes()).unwrap(); let create_model = parse_ast_node_full::(&tok[2..]).unwrap(); + let name = match create_model.model_name { + Entity::Single(tbl) | Entity::Full(_, tbl) => tbl.to_string(), + }; if create_new_space { gns.test_new_empty_space(&create_model.model_name.into_full().unwrap().0); } - ModelData::exec_create(gns, create_model) + ModelData::exec_create(gns, create_model).map(|_| name) } pub fn exec_create_new_space(gns: &GlobalNS, create_stmt: &str) -> DatabaseResult<()> { - exec_create(gns, create_stmt, true) + exec_create(gns, create_stmt, true).map(|_| ()) } pub fn exec_create_no_create(gns: &GlobalNS, create_stmt: &str) -> DatabaseResult<()> { - exec_create(gns, create_stmt, false) + exec_create(gns, create_stmt, false).map(|_| ()) } fn with_space(gns: &GlobalNS, space_name: &str, f: impl Fn(&Space)) { diff --git a/server/src/engine/core/tests/ddl_space/alter.rs b/server/src/engine/core/tests/ddl_space/alter.rs index 190d4fa8..4e58e648 100644 --- a/server/src/engine/core/tests/ddl_space/alter.rs +++ b/server/src/engine/core/tests/ddl_space/alter.rs @@ -36,18 +36,20 @@ use crate::engine::{ #[test] fn alter_add_prop_env_var() { let gns = GlobalNS::empty(); - super::exec_create_empty_verify(&gns, "create space myspace"); + let uuid = super::exec_create_empty_verify(&gns, "create space myspace").unwrap(); super::exec_alter_and_verify( &gns, "alter space myspace with { env: { MY_NEW_PROP: 100 } }", |space| { + let space = space.unwrap(); assert_eq!( - space.unwrap(), - &Space::new( + space, + &Space::new_with_uuid( into_dict!(), - SpaceMeta::with_env(into_dict! ("MY_NEW_PROP" => Datacell::new_uint(100))) + SpaceMeta::with_env(into_dict! ("MY_NEW_PROP" => Datacell::new_uint(100))), + uuid ) - ) + ); }, ) } @@ -71,7 +73,7 @@ fn alter_update_prop_env_var() { |space| { assert_eq!( space.unwrap(), - &Space::new( + &Space::new_auto( into_dict!(), SpaceMeta::with_env(into_dict! ("MY_NEW_PROP" => Datacell::new_uint(200))) ) @@ -99,7 +101,7 @@ fn alter_remove_prop_env_var() { |space| { assert_eq!( space.unwrap(), - &Space::new(into_dict!(), SpaceMeta::with_env(into_dict!())) + &Space::new_auto(into_dict!(), SpaceMeta::with_env(into_dict!())) ) }, ) diff --git a/server/src/engine/core/tests/ddl_space/create.rs b/server/src/engine/core/tests/ddl_space/create.rs index 707a6305..a38ea866 100644 --- a/server/src/engine/core/tests/ddl_space/create.rs +++ b/server/src/engine/core/tests/ddl_space/create.rs @@ -36,7 +36,7 @@ use crate::engine::{ #[test] fn exec_create_space_simple() { let gns = GlobalNS::empty(); - super::exec_create_empty_verify(&gns, "create space myspace"); + super::exec_create_empty_verify(&gns, "create space myspace").unwrap(); } #[test] @@ -54,7 +54,7 @@ fn exec_create_space_with_env() { |space| { assert_eq!( space.unwrap(), - &Space::new( + &Space::new_auto( into_dict! {}, SpaceMeta::with_env(into_dict! { "MAX_MODELS" => Datacell::new_uint(100) diff --git a/server/src/engine/core/tests/ddl_space/mod.rs b/server/src/engine/core/tests/ddl_space/mod.rs index 0ca2f61a..3a1139dc 100644 --- a/server/src/engine/core/tests/ddl_space/mod.rs +++ b/server/src/engine/core/tests/ddl_space/mod.rs @@ -40,8 +40,8 @@ use crate::engine::{ fn exec_verify( gns: &GlobalNS, query: &str, - exec: impl Fn(&GlobalNS, Statement<'_>) -> (DatabaseResult<()>, Box), - verify: impl Fn(DatabaseResult<&Space>), + mut exec: impl FnMut(&GlobalNS, Statement<'_>) -> (DatabaseResult<()>, Box), + mut verify: impl FnMut(DatabaseResult<&Space>), ) { let tok = lex(query.as_bytes()).unwrap(); let ast_node = compile_test(&tok).unwrap(); @@ -68,7 +68,7 @@ fn exec_alter_and_verify(gns: &GlobalNS, tok: &str, verify: impl Fn(DatabaseResu } /// Creates a space using the given tokens and allows the caller to verify it -fn exec_create_and_verify(gns: &GlobalNS, tok: &str, verify: impl Fn(DatabaseResult<&Space>)) { +fn exec_create_and_verify(gns: &GlobalNS, tok: &str, verify: impl FnMut(DatabaseResult<&Space>)) { exec_verify( gns, tok, @@ -83,8 +83,14 @@ fn exec_create_and_verify(gns: &GlobalNS, tok: &str, verify: impl Fn(DatabaseRes } /// Creates an empty space with the given tokens -fn exec_create_empty_verify(gns: &GlobalNS, tok: &str) { +fn exec_create_empty_verify( + gns: &GlobalNS, + tok: &str, +) -> DatabaseResult { + let mut name = None; self::exec_create_and_verify(gns, tok, |space| { assert_eq!(space.unwrap(), &Space::empty()); + name = Some(space.unwrap().get_uuid()); }); + Ok(name.unwrap()) } diff --git a/server/src/engine/data/mod.rs b/server/src/engine/data/mod.rs index 082c08b2..aaa35691 100644 --- a/server/src/engine/data/mod.rs +++ b/server/src/engine/data/mod.rs @@ -31,6 +31,8 @@ pub mod dict; pub mod lit; pub mod spec; pub mod tag; +pub mod uuid; +// test #[cfg(test)] mod tests; diff --git a/server/src/engine/data/uuid.rs b/server/src/engine/data/uuid.rs new file mode 100644 index 00000000..354f09a2 --- /dev/null +++ b/server/src/engine/data/uuid.rs @@ -0,0 +1,47 @@ +/* + * Created on Mon Aug 14 2023 + * + * This file is a part of Skytable + * Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source + * NoSQL database written by Sayan Nandan ("the Author") with the + * vision to provide flexibility in data modelling without compromising + * on performance, queryability or scalability. + * + * Copyright (c) 2023, Sayan Nandan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * +*/ + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Uuid { + data: uuid::Uuid, +} + +impl Uuid { + pub fn new() -> Self { + Self { + data: uuid::Uuid::new_v4(), + } + } + pub fn as_slice(&self) -> &[u8] { + self.data.as_bytes() + } +} + +impl ToString for Uuid { + fn to_string(&self) -> String { + self.data.to_string() + } +}