Implement v1 executor for update

next
Sayan Nandan 1 year ago
parent f2a0fda29d
commit 4580b76d55
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -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),
}
}
}

@ -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 <ohsayan@outlook.com>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
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
})
}

@ -43,9 +43,9 @@ pub type DcFieldIndex = IndexST<Box<str>, Datacell, HasherNativeFx>;
#[derive(Debug)]
pub struct Row {
txn_genesis: DeltaVersion,
pk: ManuallyDrop<PrimaryIndexKey>,
rc: RawRC<RwLock<RowData>>,
__txn_genesis: DeltaVersion,
__pk: ManuallyDrop<PrimaryIndexKey>,
__rc: RawRC<RwLock<RowData>>,
}
#[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<T>(&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<T>(&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<RowData> {
self.rc.data()
self.__rc.data()
}
#[cfg(test)]
pub fn cloned_data(&self) -> Vec<(Box<str>, 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);
});
}
}

@ -27,6 +27,7 @@
mod dml;
mod index;
mod model;
pub(in crate::engine) mod query_meta;
mod space;
mod util;
// test

@ -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)

@ -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 <ohsayan@outlook.com>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
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
}
}

@ -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<Vec<Datacel
Ok(r)
}
fn _exec_only_update(gns: &GlobalNS, update: &str) -> 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<T: Default>(
gns: &GlobalNS,
model: &str,
@ -150,3 +157,16 @@ pub(self) fn exec_select(
pub(self) fn exec_select_only(gns: &GlobalNS, select: &str) -> DatabaseResult<Vec<Datacell>> {
_exec_only_select(gns, select)
}
pub(self) fn exec_update(
gns: &GlobalNS,
model: &str,
insert: &str,
update: &str,
select: &str,
) -> DatabaseResult<Vec<Datacell>> {
_exec_only_create_space_model(gns, model)?;
_exec_only_insert(gns, insert, |_| {})?;
_exec_only_update(gns, update)?;
_exec_only_select(gns, select)
}

@ -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 <ohsayan@outlook.com>
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
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]
);
}

@ -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 {

@ -128,4 +128,9 @@ pub enum DatabaseError {
DmlWhereClauseUnindexedExpr,
/// The entry was not found
DmlEntryNotFound,
/// illegal data
DmlIllegalData,
/// field definition violation
DmlConstraintViolationFieldTypedef,
ServerError,
}

@ -257,6 +257,10 @@ pub trait STIndex<K: ?Sized, V>: IndexBaseSpec {
K: AsKey + Borrow<Q>,
Q: ?Sized + AsKey,
V: AsValueClone;
fn st_get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: AsKey + Borrow<Q>,
Q: ?Sized + AsKey;
// update
/// Returns true if the entry is updated
fn st_update<Q>(&mut self, key: &Q, val: V) -> bool

@ -138,6 +138,14 @@ where
self.get(key).cloned()
}
fn st_get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: AsKey + Borrow<Q>,
Q: ?Sized + AsKey,
{
self.get_mut(key)
}
fn st_update<Q>(&mut self, key: &Q, val: V) -> bool
where
K: Borrow<Q>,

@ -625,6 +625,14 @@ where
self._get(key).cloned()
}
fn st_get_mut<Q>(&mut self, _: &Q) -> Option<&mut V>
where
K: AsKey + Borrow<Q>,
Q: ?Sized + AsKey,
{
todo!()
}
fn st_update<Q>(&mut self, key: &Q, val: V) -> bool
where
K: Borrow<Q>,

@ -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<AssignmentExpression<'a>> {
self.expressions
}
}
impl<'a> UpdateStatement<'a> {
#[inline(always)]
#[cfg(test)]

@ -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! {

Loading…
Cancel
Save