Add tests for insert and delete, and fix security bug

This commit makes an important security fix that caused non-matching
of field data which could have had terrible impacts.

Second, it adds tests for insert and delete.
next
Sayan Nandan 1 year ago
parent b714647905
commit abf1e29344
No known key found for this signature in database
GPG Key ID: 42EEDF4AE9D96B54

@ -31,7 +31,7 @@ use crate::engine::{
GlobalNS,
},
error::{DatabaseError, DatabaseResult},
idx::{IndexBaseSpec, STIndex},
idx::{IndexBaseSpec, STIndex, STIndexSeq},
ql::dml::ins::{InsertData, InsertStatement},
sync::atm::cpin,
};
@ -62,7 +62,7 @@ fn prepare_insert(
let mut prepared_data = DcFieldIndex::idx_init_cap(fields.len());
match insert {
InsertData::Ordered(tuple) => {
let mut fields = fields.st_iter_kv();
let mut fields = fields.stseq_ord_kv();
let mut tuple = tuple.into_iter();
while (tuple.len() != 0) & okay {
let data;

@ -0,0 +1,56 @@
/*
* Created on Wed May 10 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, error::DatabaseError};
#[test]
fn simple_delete() {
let gns = GlobalNS::empty();
super::exec_delete(
&gns,
"create model myspace.mymodel(username: string, password: string)",
Some("insert into myspace.mymodel('sayan', 'pass123')"),
"delete from myspace.mymodel where username = 'sayan'",
"sayan",
)
.unwrap();
}
#[test]
fn delete_nonexisting() {
let gns = GlobalNS::empty();
assert_eq!(
super::exec_delete(
&gns,
"create model myspace.mymodel(username: string, password: string)",
None,
"delete from myspace.mymodel where username = 'sayan'",
"sayan",
)
.unwrap_err(),
DatabaseError::DmlEntryNotFound
);
}

@ -24,9 +24,9 @@
*
*/
use crate::engine::{core::GlobalNS, data::cell::Datacell};
use crate::engine::{core::GlobalNS, data::cell::Datacell, error::DatabaseError};
#[derive(sky_macros::Wrapper)]
#[derive(sky_macros::Wrapper, Debug)]
struct Tuple(Vec<(Box<str>, Datacell)>);
#[test]
@ -66,3 +66,23 @@ fn insert_with_null() {
}
).unwrap();
}
#[test]
fn insert_duplicate() {
let gns = GlobalNS::empty();
super::exec_insert(
&gns,
"create model myspace.mymodel(username: string, password: string)",
"insert into myspace.mymodel('sayan', 'pass123')",
"sayan",
|row| {
assert_veceq_transposed!(row.cloned_data(), Tuple(pairvec!(("password", "pass123"))));
},
)
.unwrap();
assert_eq!(
super::exec_insert_only(&gns, "insert into myspace.mymodel('sayan', 'pass123')")
.unwrap_err(),
DatabaseError::DmlConstraintViolationDuplicate
);
}

@ -24,33 +24,49 @@
*
*/
mod delete;
mod insert;
use crate::engine::{
core::{dml, index::Row, model::ModelData, GlobalNS},
data::lit::LitIR,
error::DatabaseResult,
ql::{ast::parse_ast_node_full, dml::ins::InsertStatement, tests::lex_insecure},
ql::{
ast::{parse_ast_node_full, Entity},
dml::{del::DeleteStatement, ins::InsertStatement},
tests::lex_insecure,
},
sync,
};
pub(self) fn exec_insert<T>(
gns: &GlobalNS,
model: &str,
insert: &str,
key_name: &str,
f: impl Fn(Row) -> T,
) -> DatabaseResult<T> {
fn _exec_only_create_space_model(gns: &GlobalNS, model: &str) -> DatabaseResult<()> {
if !gns.spaces().read().contains_key("myspace") {
gns.test_new_empty_space("myspace");
}
let lex_create_model = lex_insecure(model.as_bytes()).unwrap();
let stmt_create_model = parse_ast_node_full(&lex_create_model[2..]).unwrap();
ModelData::exec_create(gns, stmt_create_model)?;
ModelData::exec_create(gns, stmt_create_model)
}
fn _exec_only_insert_only<T>(
gns: &GlobalNS,
insert: &str,
and_then: impl Fn(Entity) -> T,
) -> DatabaseResult<T> {
let lex_insert = lex_insecure(insert.as_bytes()).unwrap();
let stmt_insert = parse_ast_node_full::<InsertStatement>(&lex_insert[1..]).unwrap();
let entity = stmt_insert.entity();
dml::insert(gns, stmt_insert)?;
let r = and_then(entity);
Ok(r)
}
fn _exec_only_read_key_and_then<T>(
gns: &GlobalNS,
entity: Entity,
key_name: &str,
and_then: impl Fn(Row) -> T,
) -> DatabaseResult<T> {
let guard = sync::atm::cpin();
gns.with_model(entity, |mdl| {
let _irm = mdl.intent_read_model();
@ -60,6 +76,53 @@ pub(self) fn exec_insert<T>(
.unwrap()
.clone();
drop(guard);
Ok(f(row))
Ok(and_then(row))
})
}
fn _exec_delete_only(gns: &GlobalNS, delete: &str, key: &str) -> DatabaseResult<()> {
let lex_del = lex_insecure(delete.as_bytes()).unwrap();
let delete = parse_ast_node_full::<DeleteStatement>(&lex_del[1..]).unwrap();
let entity = delete.entity();
dml::delete(gns, delete)?;
assert_eq!(
gns.with_model(entity, |model| {
let _ = model.intent_read_model();
let g = sync::atm::cpin();
Ok(model.primary_index().select(key.into(), &g).is_none())
}),
Ok(true)
);
Ok(())
}
pub(self) fn exec_insert<T: Default>(
gns: &GlobalNS,
model: &str,
insert: &str,
key_name: &str,
f: impl Fn(Row) -> T,
) -> DatabaseResult<T> {
_exec_only_create_space_model(gns, model)?;
_exec_only_insert_only(gns, insert, |entity| {
_exec_only_read_key_and_then(gns, entity, key_name, |row| f(row))
})?
}
pub(self) fn exec_insert_only(gns: &GlobalNS, insert: &str) -> DatabaseResult<()> {
_exec_only_insert_only(gns, insert, |_| {})
}
pub(self) fn exec_delete(
gns: &GlobalNS,
model: &str,
insert: Option<&str>,
delete: &str,
key: &str,
) -> DatabaseResult<()> {
_exec_only_create_space_model(gns, model)?;
if let Some(insert) = insert {
_exec_only_insert_only(gns, insert, |_| {})?;
}
_exec_delete_only(gns, delete, key)
}

@ -86,9 +86,14 @@ macro_rules! veceq_transposed {
#[macro_export]
macro_rules! assert_veceq_transposed {
($v1:expr, $v2:expr) => {
assert!(veceq_transposed!($v1, $v2))
};
($v1:expr, $v2:expr) => {{
if !veceq_transposed!($v1, $v2) {
panic!(
"failed to assert transposed veceq. v1: `{:#?}`, v2: `{:#?}`",
$v1, $v2
)
}
}};
}
#[cfg(test)]

Loading…
Cancel
Save