|
|
@ -27,7 +27,7 @@
|
|
|
|
use {
|
|
|
|
use {
|
|
|
|
crate::{
|
|
|
|
crate::{
|
|
|
|
engine::{
|
|
|
|
engine::{
|
|
|
|
core::{dml, model::Model, space::Space, EntityIDRef},
|
|
|
|
core::{dml, index::RowData, model::Model, space::Space, EntityID, EntityIDRef},
|
|
|
|
data::lit::Lit,
|
|
|
|
data::lit::Lit,
|
|
|
|
error::QueryResult,
|
|
|
|
error::QueryResult,
|
|
|
|
fractal::{test_utils::TestGlobal, GlobalInstanceLike},
|
|
|
|
fractal::{test_utils::TestGlobal, GlobalInstanceLike},
|
|
|
@ -44,15 +44,42 @@ use {
|
|
|
|
crossbeam_epoch::pin,
|
|
|
|
crossbeam_epoch::pin,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
fn create_model_and_space(global: &TestGlobal<VirtualFS>, create_model: &str) -> QueryResult<()> {
|
|
|
|
const TEST_DATASET_SIZE: usize = 1000;
|
|
|
|
|
|
|
|
const TEST_UPDATE_DATASET_SIZE: usize = 8200; // this peculiar size to force the buffer to flush
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn create_test_kv_strings(change_count: usize) -> Vec<(String, String)> {
|
|
|
|
|
|
|
|
(1..=change_count)
|
|
|
|
|
|
|
|
.map(|i| {
|
|
|
|
|
|
|
|
(
|
|
|
|
|
|
|
|
format!("user-{i:0>change_count$}"),
|
|
|
|
|
|
|
|
format!("password-{i:0>change_count$}"),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.collect()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn create_test_kv_int(change_count: usize) -> Vec<(u64, String)> {
|
|
|
|
|
|
|
|
(0..change_count)
|
|
|
|
|
|
|
|
.map(|i| (i as u64, format!("password-{i:0>change_count$}")))
|
|
|
|
|
|
|
|
.collect()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn create_model_and_space(
|
|
|
|
|
|
|
|
global: &TestGlobal<VirtualFS>,
|
|
|
|
|
|
|
|
create_model: &str,
|
|
|
|
|
|
|
|
) -> QueryResult<EntityID> {
|
|
|
|
let tokens = lex_insecure(create_model.as_bytes()).unwrap();
|
|
|
|
let tokens = lex_insecure(create_model.as_bytes()).unwrap();
|
|
|
|
let create_model: CreateModel = ast::parse_ast_node_full(&tokens[2..]).unwrap();
|
|
|
|
let create_model: CreateModel = ast::parse_ast_node_full(&tokens[2..]).unwrap();
|
|
|
|
|
|
|
|
let mdl_name = EntityID::new(
|
|
|
|
|
|
|
|
create_model.model_name.space(),
|
|
|
|
|
|
|
|
create_model.model_name.entity(),
|
|
|
|
|
|
|
|
);
|
|
|
|
// first create space
|
|
|
|
// first create space
|
|
|
|
let create_space_str = format!("create space {}", create_model.model_name.space());
|
|
|
|
let create_space_str = format!("create space {}", create_model.model_name.space());
|
|
|
|
let create_space_tokens = lex_insecure(create_space_str.as_bytes()).unwrap();
|
|
|
|
let create_space_tokens = lex_insecure(create_space_str.as_bytes()).unwrap();
|
|
|
|
let create_space: CreateSpace = ast::parse_ast_node_full(&create_space_tokens[2..]).unwrap();
|
|
|
|
let create_space: CreateSpace = ast::parse_ast_node_full(&create_space_tokens[2..]).unwrap();
|
|
|
|
Space::transactional_exec_create(global, create_space)?;
|
|
|
|
Space::transactional_exec_create(global, create_space)?;
|
|
|
|
Model::transactional_exec_create(global, create_model).map(|_| ())
|
|
|
|
Model::transactional_exec_create(global, create_model).map(|_| mdl_name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn run_insert(global: &TestGlobal<VirtualFS>, insert: &str) -> QueryResult<()> {
|
|
|
|
fn run_insert(global: &TestGlobal<VirtualFS>, insert: &str) -> QueryResult<()> {
|
|
|
@ -67,17 +94,24 @@ fn run_update(global: &TestGlobal<VirtualFS>, update: &str) -> QueryResult<()> {
|
|
|
|
dml::update(global, insert)
|
|
|
|
dml::update(global, insert)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn auto_hook<T>(msg: &str, f: impl Fn() -> T) -> T {
|
|
|
|
fn empty_model_data() {
|
|
|
|
let hook = std::panic::take_hook();
|
|
|
|
test_utils::with_variable("empty_model_data", |log_name| {
|
|
|
|
let decl_owned = msg.to_owned();
|
|
|
|
|
|
|
|
std::panic::set_hook(Box::new(move |pinfo| {
|
|
|
|
|
|
|
|
eprintln!("panic due to `{decl_owned}`: {pinfo}")
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
let r = f();
|
|
|
|
|
|
|
|
std::panic::set_hook(hook);
|
|
|
|
|
|
|
|
r
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn create_and_close(log_name: &str, decl: &str) {
|
|
|
|
|
|
|
|
auto_hook(decl, || {
|
|
|
|
|
|
|
|
test_utils::with_variable(log_name, |log_name| {
|
|
|
|
// create and close
|
|
|
|
// create and close
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
let global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
let _ = create_model_and_space(
|
|
|
|
let _ = create_model_and_space(&global, decl).unwrap();
|
|
|
|
&global,
|
|
|
|
|
|
|
|
"create model milky_way.solar_system(planet_name: string, population: uint64)",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// open
|
|
|
|
// open
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -85,38 +119,27 @@ fn empty_model_data() {
|
|
|
|
drop(global);
|
|
|
|
drop(global);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn create_test_kv(change_count: usize) -> Vec<(String, String)> {
|
|
|
|
|
|
|
|
(1..=change_count)
|
|
|
|
|
|
|
|
.map(|i| {
|
|
|
|
|
|
|
|
(
|
|
|
|
|
|
|
|
format!("user-{i:0>change_count$}"),
|
|
|
|
|
|
|
|
format!("password-{i:0>change_count$}"),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn run_sample_inserts<K, V>(
|
|
|
|
fn model_data_inserts() {
|
|
|
|
log_name: &str,
|
|
|
|
test_utils::with_variable(("model_data_inserts", 1000), |(log_name, change_count)| {
|
|
|
|
decl: &str,
|
|
|
|
let key_values = create_test_kv(change_count);
|
|
|
|
key_values: Vec<(K, V)>,
|
|
|
|
|
|
|
|
make_insert_query: impl Fn(&K, &V) -> String,
|
|
|
|
|
|
|
|
as_pk: for<'a> fn(&'a K) -> Lit<'a>,
|
|
|
|
|
|
|
|
check_row: impl Fn(&K, &V, &RowData),
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
auto_hook(decl, || {
|
|
|
|
|
|
|
|
test_utils::with_variable(log_name, |log_name| {
|
|
|
|
// create, insert and close
|
|
|
|
// create, insert and close
|
|
|
|
|
|
|
|
let mdl_name;
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let mut global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
let mut global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
global.set_max_data_pressure(change_count);
|
|
|
|
global.set_max_data_pressure(key_values.len());
|
|
|
|
let _ = create_model_and_space(
|
|
|
|
mdl_name = create_model_and_space(&global, decl).unwrap();
|
|
|
|
&global,
|
|
|
|
|
|
|
|
"create model apps.social(user_name: string, password: string)",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
for (username, password) in key_values.iter() {
|
|
|
|
for (username, password) in key_values.iter() {
|
|
|
|
run_insert(
|
|
|
|
run_insert(&global, &make_insert_query(username, password)).unwrap();
|
|
|
|
&global,
|
|
|
|
|
|
|
|
&format!("insert into apps.social('{username}', '{password}')"),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// reopen and verify 100 times
|
|
|
|
// reopen and verify 100 times
|
|
|
@ -125,54 +148,52 @@ fn model_data_inserts() {
|
|
|
|
global.load_model_drivers().unwrap();
|
|
|
|
global.load_model_drivers().unwrap();
|
|
|
|
global
|
|
|
|
global
|
|
|
|
.state()
|
|
|
|
.state()
|
|
|
|
.with_model(EntityIDRef::new("apps", "social"), |model| {
|
|
|
|
.with_model(
|
|
|
|
|
|
|
|
EntityIDRef::new(mdl_name.space(), mdl_name.entity()),
|
|
|
|
|
|
|
|
|model| {
|
|
|
|
let g = pin();
|
|
|
|
let g = pin();
|
|
|
|
for (username, password) in key_values.iter() {
|
|
|
|
for (username, password) in key_values.iter() {
|
|
|
|
assert_eq!(
|
|
|
|
let row = model
|
|
|
|
model
|
|
|
|
|
|
|
|
.primary_index()
|
|
|
|
.primary_index()
|
|
|
|
.select(Lit::new_str(username.as_str()), &g)
|
|
|
|
.select(as_pk(username), &g)
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.d_data()
|
|
|
|
.d_data()
|
|
|
|
.read()
|
|
|
|
.read();
|
|
|
|
.fields()
|
|
|
|
check_row(username, password, &row)
|
|
|
|
.get("password")
|
|
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
|
|
.str(),
|
|
|
|
|
|
|
|
password.as_str()
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn run_sample_updates<K, V>(
|
|
|
|
fn model_data_updates() {
|
|
|
|
log_name: &str,
|
|
|
|
test_utils::with_variable(("model_data_updates", 8200), |(log_name, n)| {
|
|
|
|
decl: &str,
|
|
|
|
let key_values = create_test_kv(n);
|
|
|
|
key_values: Vec<(K, V)>,
|
|
|
|
|
|
|
|
make_insert_query: impl Fn(&K, &V) -> String,
|
|
|
|
|
|
|
|
make_update_query: impl Fn(&K, &V) -> String,
|
|
|
|
|
|
|
|
as_pk: for<'a> fn(&'a K) -> Lit<'a>,
|
|
|
|
|
|
|
|
check_row: impl Fn(&K, &V, &RowData),
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
auto_hook(decl, || {
|
|
|
|
|
|
|
|
test_utils::with_variable((log_name, TEST_UPDATE_DATASET_SIZE), |(log_name, n)| {
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
- we first open the log and then insert n values
|
|
|
|
- we first open the log and then insert n values
|
|
|
|
- we then reopen the log 100 times, changing n / 100 values every time (we set the string to an empty one)
|
|
|
|
- we then reopen the log 100 times, changing n / 100 values every time (we set the string to an empty one)
|
|
|
|
- we finally reopen the log and check if all the keys have empty string as the password
|
|
|
|
- we finally reopen the log and check if all the keys have empty string as the password
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
let mdl_name;
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// insert n values
|
|
|
|
// insert n values
|
|
|
|
let mut global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
let mut global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
global.set_max_data_pressure(n);
|
|
|
|
global.set_max_data_pressure(n);
|
|
|
|
let _ = create_model_and_space(
|
|
|
|
mdl_name = create_model_and_space(&global, decl).unwrap();
|
|
|
|
&global,
|
|
|
|
|
|
|
|
"create model apps.social(user_name: string, password: string)",
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
for (username, password) in key_values.iter() {
|
|
|
|
for (username, password) in key_values.iter() {
|
|
|
|
run_insert(
|
|
|
|
run_insert(&global, &make_insert_query(username, password)).unwrap();
|
|
|
|
&global,
|
|
|
|
|
|
|
|
&format!("insert into apps.social('{username}', '{password}')"),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -188,14 +209,8 @@ fn model_data_updates() {
|
|
|
|
global.load_model_drivers().unwrap();
|
|
|
|
global.load_model_drivers().unwrap();
|
|
|
|
let mut j = 0;
|
|
|
|
let mut j = 0;
|
|
|
|
for _ in 0..changes_per_cycle {
|
|
|
|
for _ in 0..changes_per_cycle {
|
|
|
|
let (username, _) = &key_values[actual_position];
|
|
|
|
let (username, pass) = &key_values[actual_position];
|
|
|
|
run_update(
|
|
|
|
run_update(&global, &make_update_query(username, pass)).unwrap();
|
|
|
|
&global,
|
|
|
|
|
|
|
|
&format!(
|
|
|
|
|
|
|
|
"update apps.social set password = '' where user_name = '{username}'"
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
actual_position += 1;
|
|
|
|
actual_position += 1;
|
|
|
|
j += 1;
|
|
|
|
j += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -207,31 +222,101 @@ fn model_data_updates() {
|
|
|
|
{
|
|
|
|
{
|
|
|
|
let global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
let global = TestGlobal::new_with_vfs_driver(log_name);
|
|
|
|
global.load_model_drivers().unwrap();
|
|
|
|
global.load_model_drivers().unwrap();
|
|
|
|
for (txn_id, (username, _)) in key_values
|
|
|
|
for (txn_id, (username, password)) in key_values
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, x)| ((i + n) as u64, x))
|
|
|
|
.map(|(i, x)| ((i + n) as u64, x))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
global
|
|
|
|
global
|
|
|
|
.state()
|
|
|
|
.state()
|
|
|
|
.with_model(EntityIDRef::new("apps", "social"), |model| {
|
|
|
|
.with_model(
|
|
|
|
|
|
|
|
EntityIDRef::new(mdl_name.space(), mdl_name.entity()),
|
|
|
|
|
|
|
|
|model| {
|
|
|
|
let g = pin();
|
|
|
|
let g = pin();
|
|
|
|
let row = model
|
|
|
|
let row = model
|
|
|
|
.primary_index()
|
|
|
|
.primary_index()
|
|
|
|
.select(Lit::new_str(username.as_str()), &g)
|
|
|
|
.select(as_pk(username), &g)
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.d_data()
|
|
|
|
.d_data()
|
|
|
|
.read();
|
|
|
|
.read();
|
|
|
|
let pass = row.fields().get("password").unwrap().str();
|
|
|
|
check_row(username, password, &row);
|
|
|
|
assert!(
|
|
|
|
|
|
|
|
pass.is_empty(),
|
|
|
|
|
|
|
|
"failed for {username} because pass is {pass}",
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(row.get_txn_revised().value_u64(), txn_id);
|
|
|
|
assert_eq!(row.get_txn_revised().value_u64(), txn_id);
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
test runs
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn empty_model_data() {
|
|
|
|
|
|
|
|
create_and_close(
|
|
|
|
|
|
|
|
"empty_model_data_variable_index_key",
|
|
|
|
|
|
|
|
"create model milky_way.solar_system(planet_name: string, population: uint64)",
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
create_and_close(
|
|
|
|
|
|
|
|
"empty_model_data_fixed_index_key",
|
|
|
|
|
|
|
|
"create model milky_way.solar_system(planet_id: uint64, population: uint64)",
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn model_data_inserts() {
|
|
|
|
|
|
|
|
run_sample_inserts(
|
|
|
|
|
|
|
|
"model_data_inserts_variable_pk",
|
|
|
|
|
|
|
|
"create model apps.social(user_name: string, password: string)",
|
|
|
|
|
|
|
|
create_test_kv_strings(TEST_DATASET_SIZE),
|
|
|
|
|
|
|
|
|k, v| format!("insert into apps.social('{k}', '{v}')"),
|
|
|
|
|
|
|
|
|k| Lit::new_str(k),
|
|
|
|
|
|
|
|
|_, v, row| assert_eq!(row.fields().get("password").unwrap().str(), v),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
run_sample_inserts(
|
|
|
|
|
|
|
|
"model_data_inserts_fixed_pk",
|
|
|
|
|
|
|
|
"create model apps.social(user_id: uint64, password: string)",
|
|
|
|
|
|
|
|
create_test_kv_int(TEST_DATASET_SIZE),
|
|
|
|
|
|
|
|
|k, v| format!("insert into apps.social({k}, '{v}')"),
|
|
|
|
|
|
|
|
|k| Lit::new_uint(*k),
|
|
|
|
|
|
|
|
|_, v, row| assert_eq!(row.fields().get("password").unwrap().str(), v),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
|
|
fn model_data_updates() {
|
|
|
|
|
|
|
|
run_sample_updates(
|
|
|
|
|
|
|
|
"model_data_updates_variable_key",
|
|
|
|
|
|
|
|
"create model apps.social(user_name: string, password: string)",
|
|
|
|
|
|
|
|
create_test_kv_strings(TEST_UPDATE_DATASET_SIZE),
|
|
|
|
|
|
|
|
|k, v| format!("insert into apps.social('{k}', '{v}')"),
|
|
|
|
|
|
|
|
|k, _| format!("update apps.social set password = '' where user_name = '{k}'"),
|
|
|
|
|
|
|
|
|k| Lit::new_str(k),
|
|
|
|
|
|
|
|
|username, _, row| {
|
|
|
|
|
|
|
|
let pass = row.fields().get("password").unwrap().str();
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
|
|
|
pass.is_empty(),
|
|
|
|
|
|
|
|
"failed for {username} because pass is {pass}",
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
run_sample_updates(
|
|
|
|
|
|
|
|
"model_data_updates_fixed_key",
|
|
|
|
|
|
|
|
"create model apps.social(user_name: uint64, password: string)",
|
|
|
|
|
|
|
|
create_test_kv_int(TEST_UPDATE_DATASET_SIZE),
|
|
|
|
|
|
|
|
|k, v| format!("insert into apps.social({k}, '{v}')"),
|
|
|
|
|
|
|
|
|k, _| format!("update apps.social set password = '' where user_name = {k}"),
|
|
|
|
|
|
|
|
|k| Lit::new_uint(*k),
|
|
|
|
|
|
|
|
|username, _, row| {
|
|
|
|
|
|
|
|
let pass = row.fields().get("password").unwrap().str();
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
|
|
|
pass.is_empty(),
|
|
|
|
|
|
|
|
"failed for {username} because pass is {pass}",
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|