From 4580b76d55ee823d49410d78b0f4a941201ee2fa Mon Sep 17 00:00:00 2001 From: Sayan Nandan Date: Mon, 15 May 2023 08:14:48 -0700 Subject: [PATCH] Implement v1 executor for update --- server/src/engine/core/dml/mod.rs | 18 +- server/src/engine/core/dml/upd.rs | 308 +++++++++++++++++++++ server/src/engine/core/index/row.rs | 35 +-- server/src/engine/core/mod.rs | 1 + server/src/engine/core/model/mod.rs | 3 + server/src/engine/core/query_meta.rs | 55 ++++ server/src/engine/core/tests/dml/mod.rs | 20 ++ server/src/engine/core/tests/dml/update.rs | 105 +++++++ server/src/engine/data/tag.rs | 7 + server/src/engine/error.rs | 5 + server/src/engine/idx/mod.rs | 4 + server/src/engine/idx/stdhm.rs | 8 + server/src/engine/idx/stord/mod.rs | 8 + server/src/engine/ql/dml/upd.rs | 51 ++-- server/src/engine/ql/tests/dml_tests.rs | 26 +- 15 files changed, 596 insertions(+), 58 deletions(-) create mode 100644 server/src/engine/core/dml/upd.rs create mode 100644 server/src/engine/core/query_meta.rs create mode 100644 server/src/engine/core/tests/dml/update.rs diff --git a/server/src/engine/core/dml/mod.rs b/server/src/engine/core/dml/mod.rs index 1cc57ff6..325f881b 100644 --- a/server/src/engine/core/dml/mod.rs +++ b/server/src/engine/core/dml/mod.rs @@ -27,15 +27,19 @@ mod del; mod ins; mod sel; +mod upd; -use crate::engine::{ - core::model::ModelData, - data::{lit::LitIR, spec::DataspecMeta1D, tag::DataTag}, - error::{DatabaseError, DatabaseResult}, - ql::dml::WhereClause, +use crate::{ + engine::{ + core::model::ModelData, + data::{lit::LitIR, spec::DataspecMeta1D, tag::DataTag}, + error::{DatabaseError, DatabaseResult}, + ql::dml::WhereClause, + }, + util::compiler, }; -pub use {del::delete, ins::insert, sel::select_custom}; +pub use {del::delete, ins::insert, sel::select_custom, upd::update}; impl ModelData { pub(self) fn resolve_where<'a>( @@ -49,7 +53,7 @@ impl ModelData { { Ok(clause.rhs()) } - _ => Err(DatabaseError::DmlWhereClauseUnindexedExpr), + _ => compiler::cold_rerr(DatabaseError::DmlWhereClauseUnindexedExpr), } } } diff --git a/server/src/engine/core/dml/upd.rs b/server/src/engine/core/dml/upd.rs new file mode 100644 index 00000000..9ae552c9 --- /dev/null +++ b/server/src/engine/core/dml/upd.rs @@ -0,0 +1,308 @@ +/* + * Created on Thu May 11 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 . + * +*/ + +use { + crate::{ + engine::{ + core::{query_meta::AssignmentOperator, GlobalNS}, + data::{ + cell::Datacell, + lit::LitIR, + spec::{Dataspec1D, DataspecMeta1D}, + tag::{DataTag, TagClass}, + }, + error::{DatabaseError, DatabaseResult}, + idx::STIndex, + ql::dml::upd::{AssignmentExpression, UpdateStatement}, + sync, + }, + util::compiler, + }, + std::mem, +}; + +#[inline(always)] +unsafe fn dc_op_fail(_: &Datacell, _: LitIR) -> (bool, Datacell) { + (false, Datacell::null()) +} +// bool +unsafe fn dc_op_bool_ass(_: &Datacell, rhs: LitIR) -> (bool, Datacell) { + (true, Datacell::new_bool(rhs.read_bool_uck())) +} +// uint +unsafe fn dc_op_uint_ass(_: &Datacell, rhs: LitIR) -> (bool, Datacell) { + (true, Datacell::new_uint(rhs.read_uint_uck())) +} +unsafe fn dc_op_uint_add(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (sum, of) = dc.read_uint().overflowing_add(rhs.read_uint_uck()); + (of, Datacell::new_uint(sum)) +} +unsafe fn dc_op_uint_sub(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (diff, of) = dc.read_uint().overflowing_sub(rhs.read_uint_uck()); + (of, Datacell::new_uint(diff)) +} +unsafe fn dc_op_uint_mul(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (prod, of) = dc.read_uint().overflowing_mul(rhs.read_uint_uck()); + (of, Datacell::new_uint(prod)) +} +unsafe fn dc_op_uint_div(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (quo, of) = dc.read_uint().overflowing_div(rhs.read_uint_uck()); + (of, Datacell::new_uint(quo)) +} +// sint +unsafe fn dc_op_sint_ass(_: &Datacell, rhs: LitIR) -> (bool, Datacell) { + (true, Datacell::new_sint(rhs.read_sint_uck())) +} +unsafe fn dc_op_sint_add(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (sum, of) = dc.read_sint().overflowing_add(rhs.read_sint_uck()); + (of, Datacell::new_sint(sum)) +} +unsafe fn dc_op_sint_sub(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (diff, of) = dc.read_sint().overflowing_sub(rhs.read_sint_uck()); + (of, Datacell::new_sint(diff)) +} +unsafe fn dc_op_sint_mul(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (prod, of) = dc.read_sint().overflowing_mul(rhs.read_sint_uck()); + (of, Datacell::new_sint(prod)) +} +unsafe fn dc_op_sint_div(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let (quo, of) = dc.read_sint().overflowing_div(rhs.read_sint_uck()); + (of, Datacell::new_sint(quo)) +} +/* + float + --- + FIXME(@ohsayan): floating point always upsets me now and then, this time its + the silent overflow boom and I think I should implement a strict mode (no MySQL, + not `STRICT_ALL_TABLES` unless we do actually end up going down that route. In + that case, oops) + -- + TODO(@ohsayan): account for float32 overflow +*/ +unsafe fn dc_op_float_ass(_: &Datacell, rhs: LitIR) -> (bool, Datacell) { + (true, Datacell::new_float(rhs.read_float_uck())) +} +unsafe fn dc_op_float_add(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let sum = dc.read_float() + rhs.read_float_uck(); + (true, Datacell::new_float(sum)) +} +unsafe fn dc_op_float_sub(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let diff = dc.read_float() - rhs.read_float_uck(); + (true, Datacell::new_float(diff)) +} +unsafe fn dc_op_float_mul(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let prod = dc.read_float() - rhs.read_float_uck(); + (true, Datacell::new_float(prod)) +} +unsafe fn dc_op_float_div(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let quo = dc.read_float() * rhs.read_float_uck(); + (true, Datacell::new_float(quo)) +} +// binary +unsafe fn dc_op_bin_ass(_dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let new_bin = rhs.read_bin_uck(); + let mut v = Vec::new(); + if v.try_reserve_exact(new_bin.len()).is_err() { + return dc_op_fail(_dc, rhs); + } + v.extend_from_slice(new_bin); + (true, Datacell::new_bin(v.into_boxed_slice())) +} +unsafe fn dc_op_bin_add(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let push_into_bin = rhs.read_bin_uck(); + let mut bin = Vec::new(); + if compiler::unlikely(bin.try_reserve_exact(push_into_bin.len()).is_err()) { + return dc_op_fail(dc, rhs); + } + bin.extend_from_slice(dc.read_bin()); + bin.extend_from_slice(push_into_bin); + (true, Datacell::new_bin(bin.into_boxed_slice())) +} +// string +unsafe fn dc_op_str_ass(_dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let new_str = rhs.read_str_uck(); + let mut v = String::new(); + if v.try_reserve_exact(new_str.len()).is_err() { + return dc_op_fail(_dc, rhs); + } + v.push_str(new_str); + (true, Datacell::new_str(v.into_boxed_str())) +} +unsafe fn dc_op_str_add(dc: &Datacell, rhs: LitIR) -> (bool, Datacell) { + let push_into_str = rhs.read_str_uck(); + let mut str = String::new(); + if compiler::unlikely(str.try_reserve_exact(push_into_str.len()).is_err()) { + return dc_op_fail(dc, rhs); + } + str.push_str(dc.read_str()); + str.push_str(push_into_str); + (true, Datacell::new_str(str.into_boxed_str())) +} + +static OPERATOR: [unsafe fn(&Datacell, LitIR) -> (bool, Datacell); { + TagClass::max() * (AssignmentOperator::max() + 1) +}] = [ + // bool + dc_op_bool_ass, + // -- pad: 4 + dc_op_fail, + dc_op_fail, + dc_op_fail, + dc_op_fail, + // uint + dc_op_uint_ass, + dc_op_uint_add, + dc_op_uint_sub, + dc_op_uint_mul, + dc_op_uint_div, + // sint + dc_op_sint_ass, + dc_op_sint_add, + dc_op_sint_sub, + dc_op_sint_mul, + dc_op_sint_div, + // float + dc_op_float_ass, + dc_op_float_add, + dc_op_float_sub, + dc_op_float_mul, + dc_op_float_div, + // bin + dc_op_bin_ass, + dc_op_bin_add, + // -- pad: 3 + dc_op_fail, + dc_op_fail, + dc_op_fail, + // str + dc_op_str_ass, + dc_op_str_add, + // -- pad: 3 + dc_op_fail, + dc_op_fail, + dc_op_fail, +]; + +#[inline(always)] +const fn opc(opr: TagClass, ope: AssignmentOperator) -> usize { + (AssignmentOperator::count() * opr.word()) + ope.word() +} + +pub fn update(gns: &GlobalNS, mut update: UpdateStatement) -> DatabaseResult<()> { + gns.with_model(update.entity(), |mdl| { + let mut ret = Ok(()); + let key = mdl.resolve_where(update.clauses_mut())?; + let irm = mdl.intent_read_model(); + let g = sync::atm::cpin(); + let Some(row) = mdl.primary_index().select(key, &g) else { + return Err(DatabaseError::DmlEntryNotFound) + }; + let mut row_data_wl = row.d_data().write(); + let mut rollback_now = false; + let mut rollback_data = Vec::with_capacity(update.expressions().len()); + let mut assn_expressions = update.into_expressions().into_iter(); + /* + FIXME(@ohsayan): where's my usual magic? I'll do it once we have the SE stabilized + */ + while (assn_expressions.len() != 0) & (!rollback_now) { + let AssignmentExpression { + lhs, + rhs, + operator_fn, + } = unsafe { + // UNSAFE(@ohsayan): pre-loop cond + assn_expressions.next().unwrap_unchecked() + }; + let field_definition; + let field_data; + match ( + irm.fields().st_get(lhs.as_str()), + row_data_wl.fields_mut().st_get_mut(lhs.as_str()), + ) { + (Some(fdef), Some(fdata)) => { + field_definition = fdef; + field_data = fdata; + } + _ => { + rollback_now = true; + ret = Err(DatabaseError::FieldNotFound); + break; + } + } + match ( + field_definition.layers()[0].tag().tag_class(), + rhs.kind().tag_class(), + ) { + (tag_a, tag_b) + if (tag_a == tag_b) & (tag_a < TagClass::List) & field_data.is_init() => + { + let (okay, new) = unsafe { OPERATOR[opc(tag_a, operator_fn)](field_data, rhs) }; + rollback_now &= !okay; + rollback_data.push((lhs.as_str(), mem::replace(field_data, new))); + } + (tag_a, tag_b) + if (tag_a == tag_b) + & field_data.is_null() + & (operator_fn == AssignmentOperator::Assign) => + { + rollback_data.push((lhs.as_str(), mem::replace(field_data, rhs.into()))); + } + (TagClass::List, tag_b) => { + if field_definition.layers()[1].tag().tag_class() == tag_b { + unsafe { + // UNSAFE(@ohsayan): matched tags + let mut list = field_data.read_list().write(); + if list.try_reserve(1).is_ok() { + list.push(rhs.into()); + } else { + rollback_now = true; + ret = Err(DatabaseError::ServerError); + break; + } + } + } else { + rollback_now = true; + ret = Err(DatabaseError::DmlConstraintViolationFieldTypedef); + break; + } + } + _ => { + ret = Err(DatabaseError::DmlConstraintViolationFieldTypedef); + rollback_now = true; + break; + } + } + } + if compiler::unlikely(rollback_now) { + rollback_data + .into_iter() + .for_each(|(field_id, restored_data)| { + row_data_wl.fields_mut().st_update(field_id, restored_data); + }); + } + ret + }) +} diff --git a/server/src/engine/core/index/row.rs b/server/src/engine/core/index/row.rs index 46e5962e..6cb7a907 100644 --- a/server/src/engine/core/index/row.rs +++ b/server/src/engine/core/index/row.rs @@ -43,9 +43,9 @@ pub type DcFieldIndex = IndexST, Datacell, HasherNativeFx>; #[derive(Debug)] pub struct Row { - txn_genesis: DeltaVersion, - pk: ManuallyDrop, - rc: RawRC>, + __txn_genesis: DeltaVersion, + __pk: ManuallyDrop, + __rc: RawRC>, } #[derive(Debug, PartialEq)] @@ -58,6 +58,9 @@ impl RowData { pub fn fields(&self) -> &DcFieldIndex { &self.fields } + pub fn fields_mut(&mut self) -> &mut DcFieldIndex { + &mut self.fields + } } impl TreeElement for Row { @@ -91,9 +94,9 @@ impl Row { txn_revised: DeltaVersion, ) -> Self { Self { - txn_genesis, - pk: ManuallyDrop::new(pk), - rc: unsafe { + __txn_genesis: txn_genesis, + __pk: ManuallyDrop::new(pk), + __rc: unsafe { // UNSAFE(@ohsayan): we free this up later RawRC::new(RwLock::new(RowData { fields: data, @@ -103,18 +106,18 @@ impl Row { } } pub fn with_data_read(&self, f: impl Fn(&DcFieldIndex) -> T) -> T { - let data = self.rc.data().read(); + let data = self.__rc.data().read(); f(&data.fields) } pub fn with_data_write(&self, f: impl Fn(&mut DcFieldIndex) -> T) -> T { - let mut data = self.rc.data().write(); + let mut data = self.__rc.data().write(); f(&mut data.fields) } pub fn d_key(&self) -> &PrimaryIndexKey { - &self.pk + &self.__pk } pub fn d_data(&self) -> &RwLock { - self.rc.data() + self.__rc.data() } #[cfg(test)] pub fn cloned_data(&self) -> Vec<(Box, Datacell)> { @@ -161,14 +164,14 @@ impl Clone for Row { fn clone(&self) -> Self { let rc = unsafe { // UNSAFE(@ohsayan): we're calling this in the clone implementation - self.rc.rc_clone() + self.__rc.rc_clone() }; Self { - pk: unsafe { + __pk: unsafe { // UNSAFE(@ohsayan): this is safe because of the refcount - ManuallyDrop::new(self.pk.raw_clone()) + ManuallyDrop::new(self.__pk.raw_clone()) }, - rc, + __rc: rc, ..*self } } @@ -178,9 +181,9 @@ impl Drop for Row { fn drop(&mut self) { unsafe { // UNSAFE(@ohsayan): we call in this the dtor itself - self.rc.rc_drop(|| { + self.__rc.rc_drop(|| { // UNSAFE(@ohsayan): we rely on the correctness of the rc - ManuallyDrop::drop(&mut self.pk); + ManuallyDrop::drop(&mut self.__pk); }); } } diff --git a/server/src/engine/core/mod.rs b/server/src/engine/core/mod.rs index 4806b3ab..d12b7f2f 100644 --- a/server/src/engine/core/mod.rs +++ b/server/src/engine/core/mod.rs @@ -27,6 +27,7 @@ mod dml; mod index; mod model; +pub(in crate::engine) mod query_meta; mod space; mod util; // test diff --git a/server/src/engine/core/model/mod.rs b/server/src/engine/core/model/mod.rs index 3213fc7e..e5e14640 100644 --- a/server/src/engine/core/model/mod.rs +++ b/server/src/engine/core/model/mod.rs @@ -373,6 +373,9 @@ impl Layer { } impl Layer { + pub fn tag(&self) -> FullTag { + self.tag + } #[cfg(test)] pub fn new_test(tag: FullTag, config: [usize; 2]) -> Self { Self::new(tag, config) diff --git a/server/src/engine/core/query_meta.rs b/server/src/engine/core/query_meta.rs new file mode 100644 index 00000000..7c429cba --- /dev/null +++ b/server/src/engine/core/query_meta.rs @@ -0,0 +1,55 @@ +/* + * Created on Fri May 12 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 . + * +*/ + +use std::mem; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +#[repr(u8)] +pub enum AssignmentOperator { + Assign = 0, + AddAssign = 1, + SubAssign = 2, + MulAssign = 3, + DivAssign = 4, +} + +impl AssignmentOperator { + pub const fn disc(&self) -> u8 { + unsafe { + // UNSAFE(@ohsayan): just go back to school already; dscr + mem::transmute(*self) + } + } + pub const fn max() -> usize { + Self::DivAssign.disc() as _ + } + pub const fn word(&self) -> usize { + self.disc() as _ + } + pub const fn count() -> usize { + 5 + } +} diff --git a/server/src/engine/core/tests/dml/mod.rs b/server/src/engine/core/tests/dml/mod.rs index 76b5d113..4857c339 100644 --- a/server/src/engine/core/tests/dml/mod.rs +++ b/server/src/engine/core/tests/dml/mod.rs @@ -27,6 +27,7 @@ mod delete; mod insert; mod select; +mod update; use crate::engine::{ core::{dml, index::Row, model::ModelData, GlobalNS}, @@ -105,6 +106,12 @@ fn _exec_only_select(gns: &GlobalNS, select: &str) -> DatabaseResult DatabaseResult<()> { + let lex_upd = lex_insecure(update.as_bytes()).unwrap(); + let update = parse_ast_node_full(&lex_upd[1..]).unwrap(); + dml::update(gns, update) +} + pub(self) fn exec_insert( gns: &GlobalNS, model: &str, @@ -150,3 +157,16 @@ pub(self) fn exec_select( pub(self) fn exec_select_only(gns: &GlobalNS, select: &str) -> DatabaseResult> { _exec_only_select(gns, select) } + +pub(self) fn exec_update( + gns: &GlobalNS, + model: &str, + insert: &str, + update: &str, + select: &str, +) -> DatabaseResult> { + _exec_only_create_space_model(gns, model)?; + _exec_only_insert(gns, insert, |_| {})?; + _exec_only_update(gns, update)?; + _exec_only_select(gns, select) +} diff --git a/server/src/engine/core/tests/dml/update.rs b/server/src/engine/core/tests/dml/update.rs new file mode 100644 index 00000000..5dea2d81 --- /dev/null +++ b/server/src/engine/core/tests/dml/update.rs @@ -0,0 +1,105 @@ +/* + * Created on Sun May 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 . + * +*/ + +use crate::engine::{core::GlobalNS, data::cell::Datacell, error::DatabaseError}; + +#[test] +fn simple() { + let gns = GlobalNS::empty(); + assert_eq!( + super::exec_update( + &gns, + "create model myspace.mymodel(username: string, email: string, followers: uint64, following: uint64)", + "insert into myspace.mymodel('sayan', 'sayan@example.com', 0, 100)", + "update myspace.mymodel set followers += 200000, following -= 15, email = 'sn@example.com' where username = 'sayan'", + "select * from myspace.mymodel where username = 'sayan'" + ).unwrap(), + intovec!["sayan", "sn@example.com", 200_000_u64, 85_u64], + ); +} + +#[test] +fn with_null() { + let gns = GlobalNS::empty(); + assert_eq!( + super::exec_update( + &gns, + "create model myspace.mymodel(username: string, password: string, null email: string)", + "insert into myspace.mymodel('sayan', 'pass123', null)", + "update myspace.mymodel set email = 'sayan@example.com' where username = 'sayan'", + "select * from myspace.mymodel where username='sayan'" + ) + .unwrap(), + intovec!["sayan", "pass123", "sayan@example.com"] + ); +} + +#[test] +fn fail_unknown_fields() { + let gns = GlobalNS::empty(); + assert_eq!( + super::exec_update( + &gns, + "create model myspace.mymodel(username: string, password: string, null email: string)", + "insert into myspace.mymodel('sayan', 'pass123', null)", + "update myspace.mymodel set email2 = 'sayan@example.com', password += '4' where username = 'sayan'", + "select * from myspace.mymodel where username='sayan'" + ) + .unwrap_err(), + DatabaseError::FieldNotFound + ); + // verify integrity + assert_eq!( + super::exec_select_only(&gns, "select * from myspace.mymodel where username='sayan'") + .unwrap(), + intovec!["sayan", "pass123", Datacell::null()] + ); +} + +#[test] +fn fail_typedef_violation() { + let gns = GlobalNS::empty(); + assert_eq!( + super::exec_update( + &gns, + "create model myspace.mymodel(username: string, password: string, rank: uint8)", + "insert into myspace.mymodel('sayan', 'pass123', 1)", + "update myspace.mymodel set password = 'pass1234', rank = 'one' where username = 'sayan'", + "select * from myspace.mymodel where username = 'sayan'" + ) + .unwrap_err(), + DatabaseError::DmlConstraintViolationFieldTypedef + ); + // verify integrity + assert_eq!( + super::exec_select_only( + &gns, + "select * from myspace.mymodel where username = 'sayan'" + ) + .unwrap(), + intovec!["sayan", "pass123", 1u64] + ); +} diff --git a/server/src/engine/data/tag.rs b/server/src/engine/data/tag.rs index 16194a6c..03d1c9cc 100644 --- a/server/src/engine/data/tag.rs +++ b/server/src/engine/data/tag.rs @@ -36,6 +36,13 @@ pub enum TagClass { List = 6, } +impl TagClass { + /// ☢WARNING☢: Don't forget offset + pub const fn max() -> usize { + Self::List.d() as _ + } +} + #[repr(u8)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] pub enum TagSelector { diff --git a/server/src/engine/error.rs b/server/src/engine/error.rs index 941d3136..54894643 100644 --- a/server/src/engine/error.rs +++ b/server/src/engine/error.rs @@ -128,4 +128,9 @@ pub enum DatabaseError { DmlWhereClauseUnindexedExpr, /// The entry was not found DmlEntryNotFound, + /// illegal data + DmlIllegalData, + /// field definition violation + DmlConstraintViolationFieldTypedef, + ServerError, } diff --git a/server/src/engine/idx/mod.rs b/server/src/engine/idx/mod.rs index 3731f4b3..211bcecf 100644 --- a/server/src/engine/idx/mod.rs +++ b/server/src/engine/idx/mod.rs @@ -257,6 +257,10 @@ pub trait STIndex: IndexBaseSpec { K: AsKey + Borrow, Q: ?Sized + AsKey, V: AsValueClone; + fn st_get_mut(&mut self, key: &Q) -> Option<&mut V> + where + K: AsKey + Borrow, + Q: ?Sized + AsKey; // update /// Returns true if the entry is updated fn st_update(&mut self, key: &Q, val: V) -> bool diff --git a/server/src/engine/idx/stdhm.rs b/server/src/engine/idx/stdhm.rs index 43306513..4161d03b 100644 --- a/server/src/engine/idx/stdhm.rs +++ b/server/src/engine/idx/stdhm.rs @@ -138,6 +138,14 @@ where self.get(key).cloned() } + fn st_get_mut(&mut self, key: &Q) -> Option<&mut V> + where + K: AsKey + Borrow, + Q: ?Sized + AsKey, + { + self.get_mut(key) + } + fn st_update(&mut self, key: &Q, val: V) -> bool where K: Borrow, diff --git a/server/src/engine/idx/stord/mod.rs b/server/src/engine/idx/stord/mod.rs index 969f12b4..67ae4a93 100644 --- a/server/src/engine/idx/stord/mod.rs +++ b/server/src/engine/idx/stord/mod.rs @@ -625,6 +625,14 @@ where self._get(key).cloned() } + fn st_get_mut(&mut self, _: &Q) -> Option<&mut V> + where + K: AsKey + Borrow, + Q: ?Sized + AsKey, + { + todo!() + } + fn st_update(&mut self, key: &Q, val: V) -> bool where K: Borrow, diff --git a/server/src/engine/ql/dml/upd.rs b/server/src/engine/ql/dml/upd.rs index 22428725..6a395bc9 100644 --- a/server/src/engine/ql/dml/upd.rs +++ b/server/src/engine/ql/dml/upd.rs @@ -30,6 +30,7 @@ use { super::{u, WhereClause}, crate::{ engine::{ + core::query_meta::AssignmentOperator, data::lit::LitIR, error::{LangError, LangResult}, ql::{ @@ -45,37 +46,27 @@ use { Impls for update */ -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -/// TODO(@ohsayan): This only helps with the parser test for now. Replace this with actual operator expressions -pub enum Operator { - Assign, - AddAssign, - SubAssign, - MulAssign, - DivAssign, -} - -static OPERATOR: [Operator; 6] = [ - Operator::Assign, - Operator::Assign, - Operator::AddAssign, - Operator::SubAssign, - Operator::MulAssign, - Operator::DivAssign, +static OPERATOR: [AssignmentOperator; 6] = [ + AssignmentOperator::Assign, + AssignmentOperator::Assign, + AssignmentOperator::AddAssign, + AssignmentOperator::SubAssign, + AssignmentOperator::MulAssign, + AssignmentOperator::DivAssign, ]; #[derive(Debug, PartialEq)] pub struct AssignmentExpression<'a> { /// the LHS ident - pub(super) lhs: Ident<'a>, + pub lhs: Ident<'a>, /// the RHS lit - pub(super) rhs: LitIR<'a>, + pub rhs: LitIR<'a>, /// operator - pub(super) operator_fn: Operator, + pub operator_fn: AssignmentOperator, } impl<'a> AssignmentExpression<'a> { - pub fn new(lhs: Ident<'a>, rhs: LitIR<'a>, operator_fn: Operator) -> Self { + pub fn new(lhs: Ident<'a>, rhs: LitIR<'a>, operator_fn: AssignmentOperator) -> Self { Self { lhs, rhs, @@ -140,6 +131,24 @@ pub struct UpdateStatement<'a> { pub(super) wc: WhereClause<'a>, } +impl<'a> UpdateStatement<'a> { + pub fn entity(&self) -> Entity<'a> { + self.entity + } + pub fn expressions(&self) -> &[AssignmentExpression<'a>] { + &self.expressions + } + pub fn clauses(&self) -> &WhereClause<'a> { + &self.wc + } + pub fn clauses_mut(&mut self) -> &mut WhereClause<'a> { + &mut self.wc + } + pub fn into_expressions(self) -> Vec> { + self.expressions + } +} + impl<'a> UpdateStatement<'a> { #[inline(always)] #[cfg(test)] diff --git a/server/src/engine/ql/tests/dml_tests.rs b/server/src/engine/ql/tests/dml_tests.rs index 4c830c40..2b0cc879 100644 --- a/server/src/engine/ql/tests/dml_tests.rs +++ b/server/src/engine/ql/tests/dml_tests.rs @@ -696,12 +696,9 @@ mod expression_tests { use { super::*, crate::engine::{ + core::query_meta::AssignmentOperator, data::{lit::LitIR, spec::Dataspec1D}, - ql::{ - ast::parse_ast_node_full, - dml::upd::{AssignmentExpression, Operator}, - lex::Ident, - }, + ql::{ast::parse_ast_node_full, dml::upd::AssignmentExpression, lex::Ident}, }, }; #[test] @@ -713,7 +710,7 @@ mod expression_tests { AssignmentExpression::new( Ident::from("username"), LitIR::Str("sayan"), - Operator::Assign + AssignmentOperator::Assign ) ); } @@ -726,7 +723,7 @@ mod expression_tests { AssignmentExpression::new( Ident::from("followers"), LitIR::UnsignedInt(100), - Operator::AddAssign + AssignmentOperator::AddAssign ) ); } @@ -739,7 +736,7 @@ mod expression_tests { AssignmentExpression::new( Ident::from("following"), LitIR::UnsignedInt(150), - Operator::SubAssign + AssignmentOperator::SubAssign ) ); } @@ -752,7 +749,7 @@ mod expression_tests { AssignmentExpression::new( Ident::from("product_qty"), LitIR::UnsignedInt(2), - Operator::MulAssign + AssignmentOperator::MulAssign ) ); } @@ -765,7 +762,7 @@ mod expression_tests { AssignmentExpression::new( Ident::from("image_crop_factor"), LitIR::UnsignedInt(2), - Operator::DivAssign + AssignmentOperator::DivAssign ) ); } @@ -774,11 +771,12 @@ mod update_statement { use { super::*, crate::engine::{ + core::query_meta::AssignmentOperator, data::{lit::LitIR, spec::Dataspec1D}, ql::{ ast::{parse_ast_node_full, Entity}, dml::{ - upd::{AssignmentExpression, Operator, UpdateStatement}, + upd::{AssignmentExpression, UpdateStatement}, RelationalExpr, WhereClause, }, lex::Ident, @@ -799,7 +797,7 @@ mod update_statement { vec![AssignmentExpression::new( Ident::from("notes"), LitIR::Str("this is my new note"), - Operator::AddAssign, + AssignmentOperator::AddAssign, )], WhereClause::new(dict! { Ident::from("username") => RelationalExpr::new( @@ -832,12 +830,12 @@ mod update_statement { AssignmentExpression::new( Ident::from("notes"), LitIR::Str("this is my new note"), - Operator::AddAssign, + AssignmentOperator::AddAssign, ), AssignmentExpression::new( Ident::from("email"), LitIR::Str("sayan@example.com"), - Operator::Assign, + AssignmentOperator::Assign, ), ], WhereClause::new(dict! {