Merge branch 'dev' of https://github.com/cozodb/cozo into dev

main
Ziyang Hu 1 year ago
commit 662c59c967

@ -135,9 +135,10 @@ limit_option = {":limit" ~ expr}
offset_option = {":offset" ~ expr}
sort_option = {(":sort" | ":order") ~ (sort_arg ~ ",")* ~ sort_arg }
relation_option = {relation_op ~ (compound_ident | underscore_ident) ~ table_schema?}
relation_op = _{relation_create | relation_replace | relation_put | relation_update | relation_rm | relation_ensure | relation_ensure_not}
relation_op = _{relation_create | relation_replace | relation_insert | relation_put | relation_update | relation_rm | relation_ensure_not | relation_ensure }
relation_create = {":create"}
relation_replace = {":replace"}
relation_insert = {":insert"}
relation_put = {":put"}
relation_update = {":update"}
relation_rm = {":rm"}

@ -1727,13 +1727,13 @@ pub(crate) fn op_windows(args: &[DataValue]) -> Result<DataValue> {
Ok(DataValue::List(res))
}
fn get_index(mut i: i64, total: usize) -> Result<usize> {
fn get_index(mut i: i64, total: usize, is_upper: bool) -> Result<usize> {
if i < 0 {
i += total as i64;
}
Ok(if i >= 0 {
let i = i as usize;
if i >= total {
if i > total || (!is_upper && i == total) {
bail!("index {} out of bound", i)
} else {
i
@ -1763,7 +1763,7 @@ fn get_impl(args: &[DataValue]) -> Result<DataValue> {
let n = args[1]
.get_int()
.ok_or_else(|| miette!("second argument to 'get' mut be an integer"))?;
let idx = get_index(n, l.len())?;
let idx = get_index(n, l.len(), false)?;
Ok(l[idx].clone())
}
DataValue::Json(json) => {
@ -1830,8 +1830,8 @@ pub(crate) fn op_slice(args: &[DataValue]) -> Result<DataValue> {
let n = args[2]
.get_int()
.ok_or_else(|| miette!("third argument to 'slice' mut be an integer"))?;
let m = get_index(m, l.len())?;
let n = get_index(n, l.len())?;
let m = get_index(m, l.len(), false)?;
let n = get_index(n, l.len(), true)?;
Ok(DataValue::List(l[m..n].to_vec()))
}

@ -93,6 +93,9 @@ impl Display for QueryOutOptions {
RelationOp::Replace => {
write!(f, ":replace ")?;
}
RelationOp::Insert => {
write!(f, ":insert ")?;
}
RelationOp::Put => {
write!(f, ":put ")?;
}
@ -178,6 +181,7 @@ pub(crate) enum RelationOp {
Create,
Replace,
Put,
Insert,
Update,
Rm,
Ensure,
@ -989,7 +993,7 @@ pub(crate) struct FtsSearch {
}
impl HnswSearch {
pub(crate) fn all_bindings(&self) -> impl Iterator<Item = &Symbol> {
pub(crate) fn all_bindings(&self) -> impl Iterator<Item=&Symbol> {
self.bindings
.iter()
.chain(self.bind_field.iter())
@ -1000,7 +1004,7 @@ impl HnswSearch {
}
impl FtsSearch {
pub(crate) fn all_bindings(&self) -> impl Iterator<Item = &Symbol> {
pub(crate) fn all_bindings(&self) -> impl Iterator<Item=&Symbol> {
self.bindings.iter().chain(self.bind_score.iter())
}
}

@ -251,7 +251,7 @@ fn test_comparators() {
#[test]
fn test_max_min() {
assert_eq!(op_max(&[DataValue::from(1),]).unwrap(), DataValue::from(1));
assert_eq!(op_max(&[DataValue::from(1), ]).unwrap(), DataValue::from(1));
assert_eq!(
op_max(&[
DataValue::from(1),
@ -284,7 +284,7 @@ fn test_max_min() {
);
assert!(op_max(&[DataValue::from(true)]).is_err());
assert_eq!(op_min(&[DataValue::from(1),]).unwrap(), DataValue::from(1));
assert_eq!(op_min(&[DataValue::from(1), ]).unwrap(), DataValue::from(1));
assert_eq!(
op_min(&[
DataValue::from(1),
@ -1183,6 +1183,17 @@ fn test_slice() {
])
.is_err());
assert!(op_slice(&[
DataValue::List(vec![
DataValue::from(1),
DataValue::from(2),
DataValue::from(3),
]),
DataValue::from(1),
DataValue::from(3)
])
.is_ok());
assert_eq!(
op_slice(&[
DataValue::List(vec![

@ -317,6 +317,7 @@ pub(crate) fn parse_query(
Rule::relation_create => RelationOp::Create,
Rule::relation_replace => RelationOp::Replace,
Rule::relation_put => RelationOp::Put,
Rule::relation_insert => RelationOp::Insert,
Rule::relation_update => RelationOp::Update,
Rule::relation_rm => RelationOp::Rm,
Rule::relation_ensure => RelationOp::Ensure,

@ -44,7 +44,7 @@ impl<'a> SessionTx<'a> {
pub(crate) fn execute_relation<'s, S: Storage<'s>>(
&mut self,
db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>,
res_iter: impl Iterator<Item=Tuple>,
op: RelationOp,
meta: &InputRelationHandle,
headers: &[Symbol],
@ -178,7 +178,7 @@ impl<'a> SessionTx<'a> {
key_bindings,
*span,
)?,
RelationOp::Create | RelationOp::Replace | RelationOp::Put => self.put_into_relation(
RelationOp::Create | RelationOp::Replace | RelationOp::Put | RelationOp::Insert => self.put_into_relation(
db,
res_iter,
headers,
@ -191,6 +191,7 @@ impl<'a> SessionTx<'a> {
metadata,
key_bindings,
dep_bindings,
op == RelationOp::Insert,
*span,
)?,
};
@ -201,7 +202,7 @@ impl<'a> SessionTx<'a> {
fn put_into_relation<'s, S: Storage<'s>>(
&mut self,
db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>,
res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol],
cur_vld: ValidityTs,
callback_targets: &BTreeSet<SmartString<LazyCompact>>,
@ -212,6 +213,7 @@ impl<'a> SessionTx<'a> {
metadata: &StoredRelationMetadata,
key_bindings: &[Symbol],
dep_bindings: &[Symbol],
is_insert: bool,
span: SourceSpan,
) -> Result<()> {
let is_callback_target = callback_targets.contains(&relation_store.name);
@ -268,6 +270,16 @@ impl<'a> SessionTx<'a> {
.map(|ex| ex.extract_data(&tuple, cur_vld))
.try_collect()?;
if is_insert {
if relation_store.exists(self, &extracted[..relation_store.metadata.keys.len()])? {
bail!(TransactAssertionFailure {
relation: relation_store.name.to_string(),
key: extracted,
notice: "key exists in database".to_string()
});
}
}
let key = relation_store.encode_key_for_store(&extracted, span)?;
let val = relation_store.encode_val_for_store(&extracted, span)?;
@ -496,7 +508,7 @@ impl<'a> SessionTx<'a> {
fn update_in_relation<'s, S: Storage<'s>>(
&mut self,
db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>,
res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol],
cur_vld: ValidityTs,
callback_targets: &BTreeSet<SmartString<LazyCompact>>,
@ -770,7 +782,7 @@ impl<'a> SessionTx<'a> {
fn ensure_not_in_relation(
&mut self,
res_iter: impl Iterator<Item = Tuple>,
res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol],
cur_vld: ValidityTs,
relation_store: &mut RelationHandle,
@ -817,7 +829,7 @@ impl<'a> SessionTx<'a> {
fn ensure_in_relation(
&mut self,
res_iter: impl Iterator<Item = Tuple>,
res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol],
cur_vld: ValidityTs,
relation_store: &mut RelationHandle,
@ -887,7 +899,7 @@ impl<'a> SessionTx<'a> {
fn remove_from_relation<'s, S: Storage<'s>>(
&mut self,
db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>,
res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol],
cur_vld: ValidityTs,
callback_targets: &BTreeSet<SmartString<LazyCompact>>,

@ -58,6 +58,7 @@ fn test_limit_offset() {
.into_json();
assert_eq!(res["rows"], json!([]));
}
#[test]
fn test_normal_aggr_empty() {
let db = new_cozo_mem().unwrap();
@ -67,6 +68,7 @@ fn test_normal_aggr_empty() {
.rows;
assert_eq!(res, vec![vec![DataValue::from(0)]]);
}
#[test]
fn test_meet_aggr_empty() {
let db = new_cozo_mem().unwrap();
@ -82,6 +84,7 @@ fn test_meet_aggr_empty() {
.rows;
assert_eq!(res, vec![vec![DataValue::Null, DataValue::from(0)]]);
}
#[test]
fn test_layers() {
let _ = env_logger::builder().is_test(true).try_init();
@ -101,6 +104,7 @@ fn test_layers() {
.rows;
assert_eq!(res[0][0], DataValue::from(21.))
}
#[test]
fn test_conditions() {
let _ = env_logger::builder().is_test(true).try_init();
@ -132,6 +136,7 @@ fn test_conditions() {
.rows;
assert_eq!(res[0][0], DataValue::from(1.1))
}
#[test]
fn test_classical() {
let _ = env_logger::builder().is_test(true).try_init();
@ -183,19 +188,19 @@ fn rm_does_not_need_all_keys() {
assert!(db
.run_script(
"?[uid, mood] <- [[1, 2]] :put status {uid => mood}",
Default::default()
Default::default(),
)
.is_ok());
assert!(db
.run_script(
"?[uid, mood] <- [[2]] :put status {uid}",
Default::default()
Default::default(),
)
.is_err());
assert!(db
.run_script(
"?[uid, mood] <- [[3, 2]] :rm status {uid => mood}",
Default::default()
Default::default(),
)
.is_ok());
assert!(db
@ -1160,6 +1165,28 @@ fn multi_index_vec() {
}
}
#[test]
fn ensure_not() {
let db = DbInstance::new("mem", "", "").unwrap();
db.run_script(r"
%ignore_error { :create id_alloc{id: Int => next_id: Int, last_id: Int}}
%ignore_error {
?[id, next_id, last_id] <- [[0, 1, 1000]];
:ensure_not id_alloc{id => next_id, last_id}
}
", Default::default()).unwrap();
}
#[test]
fn insertion() {
let db = DbInstance::new("mem", "", "").unwrap();
db.run_script(r":create a {x => y}", Default::default()).unwrap();
assert!(db.run_script(r"?[x, y] <- [[1, 2]] :insert a {x => y}", Default::default())
.is_ok());
assert!(db.run_script(r"?[x, y] <- [[1, 3]] :insert a {x => y}", Default::default())
.is_err());
}
#[test]
fn parser_corner_case() {
let db = DbInstance::new("mem", "", "").unwrap();

Loading…
Cancel
Save