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} offset_option = {":offset" ~ expr}
sort_option = {(":sort" | ":order") ~ (sort_arg ~ ",")* ~ sort_arg } sort_option = {(":sort" | ":order") ~ (sort_arg ~ ",")* ~ sort_arg }
relation_option = {relation_op ~ (compound_ident | underscore_ident) ~ table_schema?} 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_create = {":create"}
relation_replace = {":replace"} relation_replace = {":replace"}
relation_insert = {":insert"}
relation_put = {":put"} relation_put = {":put"}
relation_update = {":update"} relation_update = {":update"}
relation_rm = {":rm"} relation_rm = {":rm"}

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

@ -76,15 +76,15 @@ impl Display for QueryOutOptions {
writeln!(f, "{symb};")?; writeln!(f, "{symb};")?;
} }
if let Some(( if let Some((
InputRelationHandle { InputRelationHandle {
name, name,
metadata: StoredRelationMetadata { keys, non_keys }, metadata: StoredRelationMetadata { keys, non_keys },
key_bindings, key_bindings,
dep_bindings, dep_bindings,
.. ..
}, },
op, op,
)) = &self.store_relation )) = &self.store_relation
{ {
match op { match op {
RelationOp::Create => { RelationOp::Create => {
@ -93,6 +93,9 @@ impl Display for QueryOutOptions {
RelationOp::Replace => { RelationOp::Replace => {
write!(f, ":replace ")?; write!(f, ":replace ")?;
} }
RelationOp::Insert => {
write!(f, ":insert ")?;
}
RelationOp::Put => { RelationOp::Put => {
write!(f, ":put ")?; write!(f, ":put ")?;
} }
@ -178,6 +181,7 @@ pub(crate) enum RelationOp {
Create, Create,
Replace, Replace,
Put, Put,
Insert,
Update, Update,
Rm, Rm,
Ensure, Ensure,
@ -486,13 +490,13 @@ impl Display for InputProgram {
} }
InputInlineRulesOrFixed::Fixed { InputInlineRulesOrFixed::Fixed {
fixed: fixed:
FixedRuleApply { FixedRuleApply {
fixed_handle: handle, fixed_handle: handle,
rule_args, rule_args,
options, options,
head, head,
.. ..
}, },
} => { } => {
write!(f, "{name}")?; write!(f, "{name}")?;
f.debug_list().entries(head).finish()?; f.debug_list().entries(head).finish()?;
@ -627,7 +631,7 @@ impl InputProgram {
inner: rule.body, inner: rule.body,
span: rule.span, span: rule.span,
} }
.disjunctive_normal_form(tx)?; .disjunctive_normal_form(tx)?;
let mut new_head = Vec::with_capacity(rule.head.len()); let mut new_head = Vec::with_capacity(rule.head.len());
let mut seen: BTreeMap<&Symbol, Vec<Symbol>> = BTreeMap::default(); let mut seen: BTreeMap<&Symbol, Vec<Symbol>> = BTreeMap::default();
for symb in rule.head.iter() { for symb in rule.head.iter() {
@ -989,7 +993,7 @@ pub(crate) struct FtsSearch {
} }
impl HnswSearch { impl HnswSearch {
pub(crate) fn all_bindings(&self) -> impl Iterator<Item = &Symbol> { pub(crate) fn all_bindings(&self) -> impl Iterator<Item=&Symbol> {
self.bindings self.bindings
.iter() .iter()
.chain(self.bind_field.iter()) .chain(self.bind_field.iter())
@ -1000,7 +1004,7 @@ impl HnswSearch {
} }
impl FtsSearch { 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()) self.bindings.iter().chain(self.bind_score.iter())
} }
} }
@ -1652,12 +1656,12 @@ impl Display for InputAtom {
} }
InputAtom::Unification { InputAtom::Unification {
inner: inner:
Unification { Unification {
binding, binding,
expr, expr,
one_many_unif, one_many_unif,
.. ..
}, },
} => { } => {
write!(f, "{binding}")?; write!(f, "{binding}")?;
if *one_many_unif { if *one_many_unif {

@ -127,7 +127,7 @@ fn test_is_in() {
DataValue::from(1), DataValue::from(1),
DataValue::List(vec![DataValue::from(1), DataValue::from(2)]) DataValue::List(vec![DataValue::from(1), DataValue::from(2)])
]) ])
.unwrap(), .unwrap(),
DataValue::from(true) DataValue::from(true)
); );
assert_eq!( assert_eq!(
@ -135,7 +135,7 @@ fn test_is_in() {
DataValue::from(3), DataValue::from(3),
DataValue::List(vec![DataValue::from(1), DataValue::from(2)]) DataValue::List(vec![DataValue::from(1), DataValue::from(2)])
]) ])
.unwrap(), .unwrap(),
DataValue::from(false) DataValue::from(false)
); );
assert_eq!( assert_eq!(
@ -251,7 +251,7 @@ fn test_comparators() {
#[test] #[test]
fn test_max_min() { 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!( assert_eq!(
op_max(&[ op_max(&[
DataValue::from(1), DataValue::from(1),
@ -259,7 +259,7 @@ fn test_max_min() {
DataValue::from(3), DataValue::from(3),
DataValue::from(4) DataValue::from(4)
]) ])
.unwrap(), .unwrap(),
DataValue::from(4) DataValue::from(4)
); );
assert_eq!( assert_eq!(
@ -269,7 +269,7 @@ fn test_max_min() {
DataValue::from(3), DataValue::from(3),
DataValue::from(4) DataValue::from(4)
]) ])
.unwrap(), .unwrap(),
DataValue::from(4) DataValue::from(4)
); );
assert_eq!( assert_eq!(
@ -279,12 +279,12 @@ fn test_max_min() {
DataValue::from(3), DataValue::from(3),
DataValue::from(4.0) DataValue::from(4.0)
]) ])
.unwrap(), .unwrap(),
DataValue::from(4.0) DataValue::from(4.0)
); );
assert!(op_max(&[DataValue::from(true)]).is_err()); 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!( assert_eq!(
op_min(&[ op_min(&[
DataValue::from(1), DataValue::from(1),
@ -292,7 +292,7 @@ fn test_max_min() {
DataValue::from(3), DataValue::from(3),
DataValue::from(4) DataValue::from(4)
]) ])
.unwrap(), .unwrap(),
DataValue::from(1) DataValue::from(1)
); );
assert_eq!( assert_eq!(
@ -302,7 +302,7 @@ fn test_max_min() {
DataValue::from(3), DataValue::from(3),
DataValue::from(4) DataValue::from(4)
]) ])
.unwrap(), .unwrap(),
DataValue::from(1.0) DataValue::from(1.0)
); );
assert_eq!( assert_eq!(
@ -312,7 +312,7 @@ fn test_max_min() {
DataValue::from(3), DataValue::from(3),
DataValue::from(4.0) DataValue::from(4.0)
]) ])
.unwrap(), .unwrap(),
DataValue::from(1) DataValue::from(1)
); );
assert!(op_max(&[DataValue::from(true)]).is_err()); assert!(op_max(&[DataValue::from(true)]).is_err());
@ -570,7 +570,7 @@ fn test_bits() {
DataValue::Bytes([0b111000].into()), DataValue::Bytes([0b111000].into()),
DataValue::Bytes([0b010101].into()) DataValue::Bytes([0b010101].into())
]) ])
.unwrap(), .unwrap(),
DataValue::Bytes([0b010000].into()) DataValue::Bytes([0b010000].into())
); );
assert_eq!( assert_eq!(
@ -578,7 +578,7 @@ fn test_bits() {
DataValue::Bytes([0b111000].into()), DataValue::Bytes([0b111000].into()),
DataValue::Bytes([0b010101].into()) DataValue::Bytes([0b010101].into())
]) ])
.unwrap(), .unwrap(),
DataValue::Bytes([0b111101].into()) DataValue::Bytes([0b111101].into())
); );
assert_eq!( assert_eq!(
@ -590,7 +590,7 @@ fn test_bits() {
DataValue::Bytes([0b111000].into()), DataValue::Bytes([0b111000].into()),
DataValue::Bytes([0b010101].into()) DataValue::Bytes([0b010101].into())
]) ])
.unwrap(), .unwrap(),
DataValue::Bytes([0b101101].into()) DataValue::Bytes([0b101101].into())
); );
} }
@ -628,7 +628,7 @@ fn test_concat() {
DataValue::List(vec![DataValue::from(true), DataValue::from(false)]), DataValue::List(vec![DataValue::from(true), DataValue::from(false)]),
DataValue::List(vec![DataValue::from(true)]) DataValue::List(vec![DataValue::from(true)])
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::from(true), DataValue::from(true),
DataValue::from(false), DataValue::from(false),
@ -644,7 +644,7 @@ fn test_str_includes() {
DataValue::Str("abcdef".into()), DataValue::Str("abcdef".into()),
DataValue::Str("bcd".into()) DataValue::Str("bcd".into())
]) ])
.unwrap(), .unwrap(),
DataValue::from(true) DataValue::from(true)
); );
assert_eq!( assert_eq!(
@ -688,7 +688,7 @@ fn test_starts_ends_with() {
DataValue::Str("abcdef".into()), DataValue::Str("abcdef".into()),
DataValue::Str("abc".into()) DataValue::Str("abc".into())
]) ])
.unwrap(), .unwrap(),
DataValue::from(true) DataValue::from(true)
); );
assert_eq!( assert_eq!(
@ -700,7 +700,7 @@ fn test_starts_ends_with() {
DataValue::Str("abcdef".into()), DataValue::Str("abcdef".into()),
DataValue::Str("def".into()) DataValue::Str("def".into())
]) ])
.unwrap(), .unwrap(),
DataValue::from(true) DataValue::from(true)
); );
assert_eq!( assert_eq!(
@ -716,7 +716,7 @@ fn test_regex() {
DataValue::Str("abcdef".into()), DataValue::Str("abcdef".into()),
DataValue::Regex(RegexWrapper(Regex::new("c.e").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("c.e").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::from(true) DataValue::from(true)
); );
@ -725,7 +725,7 @@ fn test_regex() {
DataValue::Str("abcdef".into()), DataValue::Str("abcdef".into()),
DataValue::Regex(RegexWrapper(Regex::new("c.ef$").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("c.ef$").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::from(true) DataValue::from(true)
); );
@ -734,7 +734,7 @@ fn test_regex() {
DataValue::Str("abcdef".into()), DataValue::Str("abcdef".into()),
DataValue::Regex(RegexWrapper(Regex::new("c.e$").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("c.e$").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::from(false) DataValue::from(false)
); );
@ -744,7 +744,7 @@ fn test_regex() {
DataValue::Regex(RegexWrapper(Regex::new("[be]").unwrap())), DataValue::Regex(RegexWrapper(Regex::new("[be]").unwrap())),
DataValue::Str("x".into()) DataValue::Str("x".into())
]) ])
.unwrap(), .unwrap(),
DataValue::Str("axcdef".into()) DataValue::Str("axcdef".into())
); );
@ -754,7 +754,7 @@ fn test_regex() {
DataValue::Regex(RegexWrapper(Regex::new("[be]").unwrap())), DataValue::Regex(RegexWrapper(Regex::new("[be]").unwrap())),
DataValue::Str("x".into()) DataValue::Str("x".into())
]) ])
.unwrap(), .unwrap(),
DataValue::Str("axcdxf".into()) DataValue::Str("axcdxf".into())
); );
assert_eq!( assert_eq!(
@ -762,7 +762,7 @@ fn test_regex() {
DataValue::Str("abCDefGH".into()), DataValue::Str("abCDefGH".into()),
DataValue::Regex(RegexWrapper(Regex::new("[xayef]|(GH)").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("[xayef]|(GH)").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::Str("a".into()), DataValue::Str("a".into()),
DataValue::Str("e".into()), DataValue::Str("e".into()),
@ -775,7 +775,7 @@ fn test_regex() {
DataValue::Str("abCDefGH".into()), DataValue::Str("abCDefGH".into()),
DataValue::Regex(RegexWrapper(Regex::new("[xayef]|(GH)").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("[xayef]|(GH)").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::Str("a".into()), DataValue::Str("a".into()),
); );
assert_eq!( assert_eq!(
@ -783,7 +783,7 @@ fn test_regex() {
DataValue::Str("abCDefGH".into()), DataValue::Str("abCDefGH".into()),
DataValue::Regex(RegexWrapper(Regex::new("xyz").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("xyz").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![]) DataValue::List(vec![])
); );
@ -792,7 +792,7 @@ fn test_regex() {
DataValue::Str("abCDefGH".into()), DataValue::Str("abCDefGH".into()),
DataValue::Regex(RegexWrapper(Regex::new("xyz").unwrap())) DataValue::Regex(RegexWrapper(Regex::new("xyz").unwrap()))
]) ])
.unwrap(), .unwrap(),
DataValue::Null DataValue::Null
); );
} }
@ -912,7 +912,7 @@ fn test_prepend_append() {
DataValue::List(vec![DataValue::from(1), DataValue::from(2)]), DataValue::List(vec![DataValue::from(1), DataValue::from(2)]),
DataValue::Null, DataValue::Null,
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::Null, DataValue::Null,
DataValue::from(1), DataValue::from(1),
@ -924,7 +924,7 @@ fn test_prepend_append() {
DataValue::List(vec![DataValue::from(1), DataValue::from(2)]), DataValue::List(vec![DataValue::from(1), DataValue::from(2)]),
DataValue::Null, DataValue::Null,
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::from(1), DataValue::from(1),
DataValue::from(2), DataValue::from(2),
@ -967,7 +967,7 @@ fn test_sort_reverse() {
DataValue::from(2), DataValue::from(2),
DataValue::Null, DataValue::Null,
])]) ])])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::Null, DataValue::Null,
DataValue::from(1), DataValue::from(1),
@ -982,7 +982,7 @@ fn test_sort_reverse() {
DataValue::from(2), DataValue::from(2),
DataValue::Null, DataValue::Null,
])]) ])])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::Null, DataValue::Null,
DataValue::from(2), DataValue::from(2),
@ -1000,9 +1000,9 @@ fn test_haversine() {
DataValue::from(0), DataValue::from(0),
DataValue::from(180), DataValue::from(180),
]) ])
.unwrap() .unwrap()
.get_float() .get_float()
.unwrap(); .unwrap();
assert!(d.abs_diff_eq(&f64::PI(), 1e-5)); assert!(d.abs_diff_eq(&f64::PI(), 1e-5));
let d = op_haversine_deg_input(&[ let d = op_haversine_deg_input(&[
@ -1011,9 +1011,9 @@ fn test_haversine() {
DataValue::from(0), DataValue::from(0),
DataValue::from(123), DataValue::from(123),
]) ])
.unwrap() .unwrap()
.get_float() .get_float()
.unwrap(); .unwrap();
assert!(d.abs_diff_eq(&(f64::PI() / 2.), 1e-5)); assert!(d.abs_diff_eq(&(f64::PI() / 2.), 1e-5));
let d = op_haversine(&[ let d = op_haversine(&[
@ -1022,9 +1022,9 @@ fn test_haversine() {
DataValue::from(0), DataValue::from(0),
DataValue::from(f64::PI()), DataValue::from(f64::PI()),
]) ])
.unwrap() .unwrap()
.get_float() .get_float()
.unwrap(); .unwrap();
assert!(d.abs_diff_eq(&f64::PI(), 1e-5)); assert!(d.abs_diff_eq(&f64::PI(), 1e-5));
} }
@ -1055,7 +1055,7 @@ fn test_first_last() {
DataValue::from(1), DataValue::from(1),
DataValue::from(2), DataValue::from(2),
])]) ])])
.unwrap(), .unwrap(),
DataValue::from(1), DataValue::from(1),
); );
assert_eq!( assert_eq!(
@ -1063,7 +1063,7 @@ fn test_first_last() {
DataValue::from(1), DataValue::from(1),
DataValue::from(2), DataValue::from(2),
])]) ])])
.unwrap(), .unwrap(),
DataValue::from(2), DataValue::from(2),
); );
} }
@ -1081,7 +1081,7 @@ fn test_chunks() {
]), ]),
DataValue::from(2), DataValue::from(2),
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::List(vec![DataValue::from(1), DataValue::from(2)]), DataValue::List(vec![DataValue::from(1), DataValue::from(2)]),
DataValue::List(vec![DataValue::from(3), DataValue::from(4)]), DataValue::List(vec![DataValue::from(3), DataValue::from(4)]),
@ -1099,7 +1099,7 @@ fn test_chunks() {
]), ]),
DataValue::from(2), DataValue::from(2),
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::List(vec![DataValue::from(1), DataValue::from(2)]), DataValue::List(vec![DataValue::from(1), DataValue::from(2)]),
DataValue::List(vec![DataValue::from(3), DataValue::from(4)]), DataValue::List(vec![DataValue::from(3), DataValue::from(4)]),
@ -1116,7 +1116,7 @@ fn test_chunks() {
]), ]),
DataValue::from(3), DataValue::from(3),
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![ DataValue::List(vec![
DataValue::List(vec![ DataValue::List(vec![
DataValue::from(1), DataValue::from(1),
@ -1149,7 +1149,7 @@ fn test_get() {
]), ]),
DataValue::from(1) DataValue::from(1)
]) ])
.unwrap(), .unwrap(),
DataValue::from(2) DataValue::from(2)
); );
assert_eq!( assert_eq!(
@ -1165,7 +1165,7 @@ fn test_get() {
]), ]),
DataValue::from(1) DataValue::from(1)
]) ])
.unwrap(), .unwrap(),
DataValue::from(2) DataValue::from(2)
); );
} }
@ -1181,7 +1181,18 @@ fn test_slice() {
DataValue::from(1), DataValue::from(1),
DataValue::from(4) DataValue::from(4)
]) ])
.is_err()); .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!( assert_eq!(
op_slice(&[ op_slice(&[
@ -1193,7 +1204,7 @@ fn test_slice() {
DataValue::from(1), DataValue::from(1),
DataValue::from(-1) DataValue::from(-1)
]) ])
.unwrap(), .unwrap(),
DataValue::List(vec![DataValue::from(2)]) DataValue::List(vec![DataValue::from(2)])
); );
} }
@ -1348,7 +1359,7 @@ fn test_set_ops() {
DataValue::List([2, 3, 4].into_iter().map(DataValue::from).collect()), DataValue::List([2, 3, 4].into_iter().map(DataValue::from).collect()),
DataValue::List([3, 4, 5].into_iter().map(DataValue::from).collect()) DataValue::List([3, 4, 5].into_iter().map(DataValue::from).collect())
]) ])
.unwrap(), .unwrap(),
DataValue::List([1, 2, 3, 4, 5].into_iter().map(DataValue::from).collect()) DataValue::List([1, 2, 3, 4, 5].into_iter().map(DataValue::from).collect())
); );
assert_eq!( assert_eq!(
@ -1362,7 +1373,7 @@ fn test_set_ops() {
DataValue::List([2, 3, 4].into_iter().map(DataValue::from).collect()), DataValue::List([2, 3, 4].into_iter().map(DataValue::from).collect()),
DataValue::List([3, 4, 5].into_iter().map(DataValue::from).collect()) DataValue::List([3, 4, 5].into_iter().map(DataValue::from).collect())
]) ])
.unwrap(), .unwrap(),
DataValue::List([3, 4].into_iter().map(DataValue::from).collect()) DataValue::List([3, 4].into_iter().map(DataValue::from).collect())
); );
assert_eq!( assert_eq!(
@ -1376,7 +1387,7 @@ fn test_set_ops() {
DataValue::List([2, 3, 4].into_iter().map(DataValue::from).collect()), DataValue::List([2, 3, 4].into_iter().map(DataValue::from).collect()),
DataValue::List([3, 4, 5].into_iter().map(DataValue::from).collect()) DataValue::List([3, 4, 5].into_iter().map(DataValue::from).collect())
]) ])
.unwrap(), .unwrap(),
DataValue::List([1, 6].into_iter().map(DataValue::from).collect()) DataValue::List([1, 6].into_iter().map(DataValue::from).collect())
); );
} }

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

@ -44,7 +44,7 @@ impl<'a> SessionTx<'a> {
pub(crate) fn execute_relation<'s, S: Storage<'s>>( pub(crate) fn execute_relation<'s, S: Storage<'s>>(
&mut self, &mut self,
db: &Db<S>, db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>, res_iter: impl Iterator<Item=Tuple>,
op: RelationOp, op: RelationOp,
meta: &InputRelationHandle, meta: &InputRelationHandle,
headers: &[Symbol], headers: &[Symbol],
@ -88,7 +88,7 @@ impl<'a> SessionTx<'a> {
&db.fixed_rules.read().unwrap(), &db.fixed_rules.read().unwrap(),
cur_vld, cur_vld,
)? )?
.get_single_program()?; .get_single_program()?;
let (_, cleanups) = db let (_, cleanups) = db
.run_query( .run_query(
@ -178,7 +178,7 @@ impl<'a> SessionTx<'a> {
key_bindings, key_bindings,
*span, *span,
)?, )?,
RelationOp::Create | RelationOp::Replace | RelationOp::Put => self.put_into_relation( RelationOp::Create | RelationOp::Replace | RelationOp::Put | RelationOp::Insert => self.put_into_relation(
db, db,
res_iter, res_iter,
headers, headers,
@ -191,6 +191,7 @@ impl<'a> SessionTx<'a> {
metadata, metadata,
key_bindings, key_bindings,
dep_bindings, dep_bindings,
op == RelationOp::Insert,
*span, *span,
)?, )?,
}; };
@ -201,7 +202,7 @@ impl<'a> SessionTx<'a> {
fn put_into_relation<'s, S: Storage<'s>>( fn put_into_relation<'s, S: Storage<'s>>(
&mut self, &mut self,
db: &Db<S>, db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>, res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol], headers: &[Symbol],
cur_vld: ValidityTs, cur_vld: ValidityTs,
callback_targets: &BTreeSet<SmartString<LazyCompact>>, callback_targets: &BTreeSet<SmartString<LazyCompact>>,
@ -212,6 +213,7 @@ impl<'a> SessionTx<'a> {
metadata: &StoredRelationMetadata, metadata: &StoredRelationMetadata,
key_bindings: &[Symbol], key_bindings: &[Symbol],
dep_bindings: &[Symbol], dep_bindings: &[Symbol],
is_insert: bool,
span: SourceSpan, span: SourceSpan,
) -> Result<()> { ) -> Result<()> {
let is_callback_target = callback_targets.contains(&relation_store.name); let is_callback_target = callback_targets.contains(&relation_store.name);
@ -233,7 +235,7 @@ impl<'a> SessionTx<'a> {
let need_to_collect = !relation_store.is_temp let need_to_collect = !relation_store.is_temp
&& (is_callback_target && (is_callback_target
|| (propagate_triggers && !relation_store.put_triggers.is_empty())); || (propagate_triggers && !relation_store.put_triggers.is_empty()));
let has_indices = !relation_store.indices.is_empty(); let has_indices = !relation_store.indices.is_empty();
let has_hnsw_indices = !relation_store.hnsw_indices.is_empty(); let has_hnsw_indices = !relation_store.hnsw_indices.is_empty();
let has_fts_indices = !relation_store.fts_indices.is_empty(); let has_fts_indices = !relation_store.fts_indices.is_empty();
@ -268,6 +270,16 @@ impl<'a> SessionTx<'a> {
.map(|ex| ex.extract_data(&tuple, cur_vld)) .map(|ex| ex.extract_data(&tuple, cur_vld))
.try_collect()?; .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 key = relation_store.encode_key_for_store(&extracted, span)?;
let val = relation_store.encode_val_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>>( fn update_in_relation<'s, S: Storage<'s>>(
&mut self, &mut self,
db: &Db<S>, db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>, res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol], headers: &[Symbol],
cur_vld: ValidityTs, cur_vld: ValidityTs,
callback_targets: &BTreeSet<SmartString<LazyCompact>>, callback_targets: &BTreeSet<SmartString<LazyCompact>>,
@ -527,7 +539,7 @@ impl<'a> SessionTx<'a> {
let need_to_collect = !relation_store.is_temp let need_to_collect = !relation_store.is_temp
&& (is_callback_target && (is_callback_target
|| (propagate_triggers && !relation_store.put_triggers.is_empty())); || (propagate_triggers && !relation_store.put_triggers.is_empty()));
let has_indices = !relation_store.indices.is_empty(); let has_indices = !relation_store.indices.is_empty();
let has_hnsw_indices = !relation_store.hnsw_indices.is_empty(); let has_hnsw_indices = !relation_store.hnsw_indices.is_empty();
let has_fts_indices = !relation_store.fts_indices.is_empty(); let has_fts_indices = !relation_store.fts_indices.is_empty();
@ -674,7 +686,7 @@ impl<'a> SessionTx<'a> {
&db.fixed_rules.read().unwrap(), &db.fixed_rules.read().unwrap(),
cur_vld, cur_vld,
)? )?
.get_single_program()?; .get_single_program()?;
make_const_rule( make_const_rule(
&mut program, &mut program,
@ -770,7 +782,7 @@ impl<'a> SessionTx<'a> {
fn ensure_not_in_relation( fn ensure_not_in_relation(
&mut self, &mut self,
res_iter: impl Iterator<Item = Tuple>, res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol], headers: &[Symbol],
cur_vld: ValidityTs, cur_vld: ValidityTs,
relation_store: &mut RelationHandle, relation_store: &mut RelationHandle,
@ -817,7 +829,7 @@ impl<'a> SessionTx<'a> {
fn ensure_in_relation( fn ensure_in_relation(
&mut self, &mut self,
res_iter: impl Iterator<Item = Tuple>, res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol], headers: &[Symbol],
cur_vld: ValidityTs, cur_vld: ValidityTs,
relation_store: &mut RelationHandle, relation_store: &mut RelationHandle,
@ -887,7 +899,7 @@ impl<'a> SessionTx<'a> {
fn remove_from_relation<'s, S: Storage<'s>>( fn remove_from_relation<'s, S: Storage<'s>>(
&mut self, &mut self,
db: &Db<S>, db: &Db<S>,
res_iter: impl Iterator<Item = Tuple>, res_iter: impl Iterator<Item=Tuple>,
headers: &[Symbol], headers: &[Symbol],
cur_vld: ValidityTs, cur_vld: ValidityTs,
callback_targets: &BTreeSet<SmartString<LazyCompact>>, callback_targets: &BTreeSet<SmartString<LazyCompact>>,
@ -917,7 +929,7 @@ impl<'a> SessionTx<'a> {
let need_to_collect = !relation_store.is_temp let need_to_collect = !relation_store.is_temp
&& (is_callback_target && (is_callback_target
|| (propagate_triggers && !relation_store.rm_triggers.is_empty())); || (propagate_triggers && !relation_store.rm_triggers.is_empty()));
let has_indices = !relation_store.indices.is_empty(); let has_indices = !relation_store.indices.is_empty();
let has_hnsw_indices = !relation_store.hnsw_indices.is_empty(); let has_hnsw_indices = !relation_store.hnsw_indices.is_empty();
let has_fts_indices = !relation_store.fts_indices.is_empty(); let has_fts_indices = !relation_store.fts_indices.is_empty();
@ -992,7 +1004,7 @@ impl<'a> SessionTx<'a> {
&db.fixed_rules.read().unwrap(), &db.fixed_rules.read().unwrap(),
cur_vld, cur_vld,
)? )?
.get_single_program()?; .get_single_program()?;
make_const_rule(&mut program, "_new", k_bindings.clone(), new_tuples.clone()); make_const_rule(&mut program, "_new", k_bindings.clone(), new_tuples.clone());

@ -58,6 +58,7 @@ fn test_limit_offset() {
.into_json(); .into_json();
assert_eq!(res["rows"], json!([])); assert_eq!(res["rows"], json!([]));
} }
#[test] #[test]
fn test_normal_aggr_empty() { fn test_normal_aggr_empty() {
let db = new_cozo_mem().unwrap(); let db = new_cozo_mem().unwrap();
@ -67,6 +68,7 @@ fn test_normal_aggr_empty() {
.rows; .rows;
assert_eq!(res, vec![vec![DataValue::from(0)]]); assert_eq!(res, vec![vec![DataValue::from(0)]]);
} }
#[test] #[test]
fn test_meet_aggr_empty() { fn test_meet_aggr_empty() {
let db = new_cozo_mem().unwrap(); let db = new_cozo_mem().unwrap();
@ -82,6 +84,7 @@ fn test_meet_aggr_empty() {
.rows; .rows;
assert_eq!(res, vec![vec![DataValue::Null, DataValue::from(0)]]); assert_eq!(res, vec![vec![DataValue::Null, DataValue::from(0)]]);
} }
#[test] #[test]
fn test_layers() { fn test_layers() {
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
@ -101,6 +104,7 @@ fn test_layers() {
.rows; .rows;
assert_eq!(res[0][0], DataValue::from(21.)) assert_eq!(res[0][0], DataValue::from(21.))
} }
#[test] #[test]
fn test_conditions() { fn test_conditions() {
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
@ -118,7 +122,7 @@ fn test_conditions() {
"#, "#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
debug!("real test begins"); debug!("real test begins");
let res = db let res = db
.run_script( .run_script(
@ -132,6 +136,7 @@ fn test_conditions() {
.rows; .rows;
assert_eq!(res[0][0], DataValue::from(1.1)) assert_eq!(res[0][0], DataValue::from(1.1))
} }
#[test] #[test]
fn test_classical() { fn test_classical() {
let _ = env_logger::builder().is_test(true).try_init(); let _ = env_logger::builder().is_test(true).try_init();
@ -163,7 +168,7 @@ fn default_columns() {
"#, "#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r#" r#"
@ -172,7 +177,7 @@ fn default_columns() {
"#, "#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
} }
#[test] #[test]
@ -183,19 +188,19 @@ fn rm_does_not_need_all_keys() {
assert!(db assert!(db
.run_script( .run_script(
"?[uid, mood] <- [[1, 2]] :put status {uid => mood}", "?[uid, mood] <- [[1, 2]] :put status {uid => mood}",
Default::default() Default::default(),
) )
.is_ok()); .is_ok());
assert!(db assert!(db
.run_script( .run_script(
"?[uid, mood] <- [[2]] :put status {uid}", "?[uid, mood] <- [[2]] :put status {uid}",
Default::default() Default::default(),
) )
.is_err()); .is_err());
assert!(db assert!(db
.run_script( .run_script(
"?[uid, mood] <- [[3, 2]] :rm status {uid => mood}", "?[uid, mood] <- [[3, 2]] :rm status {uid => mood}",
Default::default() Default::default(),
) )
.is_ok()); .is_ok());
assert!(db assert!(db
@ -386,12 +391,12 @@ fn test_trigger() {
":create friends {fr: Int, to: Int => data: Any}", ":create friends {fr: Int, to: Int => data: Any}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
":create friends.rev {to: Int, fr: Int => data: Any}", ":create friends.rev {to: Int, fr: Int => data: Any}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r#" r#"
::set_triggers friends ::set_triggers friends
@ -409,12 +414,12 @@ fn test_trigger() {
"#, "#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to, data] <- [[1,2,3]] :put friends {fr, to => data}", r"?[fr, to, data] <- [[1,2,3]] :put friends {fr, to => data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let ret = db let ret = db
.export_relations(["friends", "friends.rev"].into_iter()) .export_relations(["friends", "friends.rev"].into_iter())
.unwrap(); .unwrap();
@ -433,7 +438,7 @@ fn test_trigger() {
r"?[fr, to] <- [[1,2], [2,3]] :rm friends {fr, to}", r"?[fr, to] <- [[1,2], [2,3]] :rm friends {fr, to}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let ret = db let ret = db
.export_relations(["friends", "friends.rev"].into_iter()) .export_relations(["friends", "friends.rev"].into_iter())
.unwrap(); .unwrap();
@ -450,22 +455,22 @@ fn test_callback() {
":create friends {fr: Int, to: Int => data: Any}", ":create friends {fr: Int, to: Int => data: Any}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to, data] <- [[1,2,3],[4,5,6]] :put friends {fr, to => data}", r"?[fr, to, data] <- [[1,2,3],[4,5,6]] :put friends {fr, to => data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to, data] <- [[1,2,4],[4,7,6]] :put friends {fr, to => data}", r"?[fr, to, data] <- [[1,2,4],[4,7,6]] :put friends {fr, to => data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to] <- [[1,9],[4,5]] :rm friends {fr, to}", r"?[fr, to] <- [[1,9],[4,5]] :rm friends {fr, to}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
std::thread::sleep(Duration::from_secs_f64(0.01)); std::thread::sleep(Duration::from_secs_f64(0.01));
while let Ok(d) = receiver.try_recv() { while let Ok(d) = receiver.try_recv() {
collected.push(d); collected.push(d);
@ -497,12 +502,12 @@ fn test_update() {
":create friends {fr: Int, to: Int => a: Any, b: Any, c: Any}", ":create friends {fr: Int, to: Int => a: Any, b: Any, c: Any}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
"?[fr, to, a, b, c] <- [[1,2,3,4,5]] :put friends {fr, to => a, b, c}", "?[fr, to, a, b, c] <- [[1,2,3,4,5]] :put friends {fr, to => a, b, c}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let res = db let res = db
.run_script( .run_script(
"?[fr, to, a, b, c] := *friends{fr, to, a, b, c}", "?[fr, to, a, b, c] := *friends{fr, to, a, b, c}",
@ -515,7 +520,7 @@ fn test_update() {
"?[fr, to, b] <- [[1, 2, 100]] :update friends {fr, to => b}", "?[fr, to, b] <- [[1, 2, 100]] :update friends {fr, to => b}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let res = db let res = db
.run_script( .run_script(
"?[fr, to, a, b, c] := *friends{fr, to, a, b, c}", "?[fr, to, a, b, c] := *friends{fr, to, a, b, c}",
@ -533,13 +538,13 @@ fn test_index() {
":create friends {fr: Int, to: Int => data: Any}", ":create friends {fr: Int, to: Int => data: Any}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to, data] <- [[1,2,3],[4,5,6]] :put friends {fr, to, data}", r"?[fr, to, data] <- [[1,2,3],[4,5,6]] :put friends {fr, to, data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
assert!(db assert!(db
.run_script("::index create friends:rev {to, no}", Default::default()) .run_script("::index create friends:rev {to, no}", Default::default())
@ -551,12 +556,12 @@ fn test_index() {
r"?[fr, to, data] <- [[1,2,5],[6,5,7]] :put friends {fr, to => data}", r"?[fr, to, data] <- [[1,2,5],[6,5,7]] :put friends {fr, to => data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to] <- [[4,5]] :rm friends {fr, to}", r"?[fr, to] <- [[4,5]] :rm friends {fr, to}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let rels_data = db let rels_data = db
.export_relations(["friends", "friends:rev"].into_iter()) .export_relations(["friends", "friends:rev"].into_iter())
@ -624,7 +629,7 @@ fn test_json_objects() {
}", }",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
} }
#[test] #[test]
@ -685,13 +690,13 @@ fn test_index_short() {
":create friends {fr: Int, to: Int => data: Any}", ":create friends {fr: Int, to: Int => data: Any}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to, data] <- [[1,2,3],[4,5,6]] :put friends {fr, to => data}", r"?[fr, to, data] <- [[1,2,3],[4,5,6]] :put friends {fr, to => data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script("::index create friends:rev {to}", Default::default()) db.run_script("::index create friends:rev {to}", Default::default())
.unwrap(); .unwrap();
@ -700,12 +705,12 @@ fn test_index_short() {
r"?[fr, to, data] <- [[1,2,5],[6,5,7]] :put friends {fr, to => data}", r"?[fr, to, data] <- [[1,2,5],[6,5,7]] :put friends {fr, to => data}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[fr, to] <- [[4,5]] :rm friends {fr, to}", r"?[fr, to] <- [[4,5]] :rm friends {fr, to}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let rels_data = db let rels_data = db
.export_relations(["friends", "friends:rev"].into_iter()) .export_relations(["friends", "friends:rev"].into_iter())
@ -801,7 +806,7 @@ fn test_vec_types() {
"?[k, v] <- [['k', [1,2,3,4,5,6,7,8]]] :put a {k => v}", "?[k, v] <- [['k', [1,2,3,4,5,6,7,8]]] :put a {k => v}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let res = db let res = db
.run_script("?[k, v] := *a{k, v}", Default::default()) .run_script("?[k, v] := *a{k, v}", Default::default())
.unwrap(); .unwrap();
@ -846,7 +851,7 @@ fn test_vec_index() {
", ",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r" r"
::hnsw create a:vec { ::hnsw create a:vec {
@ -862,7 +867,7 @@ fn test_vec_index() {
}", }",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r" r"
?[k, v] <- [ ?[k, v] <- [
@ -877,7 +882,7 @@ fn test_vec_index() {
", ",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
println!("all links"); println!("all links");
for (_, nrows) in db.export_relations(["a:vec"].iter()).unwrap() { for (_, nrows) in db.export_relations(["a:vec"].iter()).unwrap() {
@ -912,7 +917,7 @@ fn test_fts_indexing() {
r"?[k, v] <- [['a', 'hello world!'], ['b', 'the world is round']] :put a {k => v}", r"?[k, v] <- [['a', 'hello world!'], ['b', 'the world is round']] :put a {k => v}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"::fts create a:fts { r"::fts create a:fts {
extractor: v, extractor: v,
@ -921,7 +926,7 @@ fn test_fts_indexing() {
}", }",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[k, v] <- [ r"?[k, v] <- [
['b', 'the world is square!'], ['b', 'the world is square!'],
@ -930,7 +935,7 @@ fn test_fts_indexing() {
] :put a {k => v}", ] :put a {k => v}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let res = db let res = db
.run_script( .run_script(
r" r"
@ -964,12 +969,12 @@ fn test_lsh_indexing() {
r"?[k, v] <- [['a', 'hello world!'], ['b', 'the world is round']] :put a {k => v}", r"?[k, v] <- [['a', 'hello world!'], ['b', 'the world is round']] :put a {k => v}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"::lsh create a:lsh {extractor: v, tokenizer: Simple, n_gram: 3, target_threshold: 0.3 }", r"::lsh create a:lsh {extractor: v, tokenizer: Simple, n_gram: 3, target_threshold: 0.3 }",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r"?[k, v] <- [ r"?[k, v] <- [
['b', 'the world is square!'], ['b', 'the world is square!'],
@ -979,7 +984,7 @@ fn test_lsh_indexing() {
] :put a {k => v}", ] :put a {k => v}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let res = db let res = db
.run_script("::columns a:lsh", Default::default()) .run_script("::columns a:lsh", Default::default())
.unwrap(); .unwrap();
@ -1038,7 +1043,7 @@ fn test_insertions() {
r":create a {k => v: <F32; 1536> default rand_vec(1536)}", r":create a {k => v: <F32; 1536> default rand_vec(1536)}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script(r"?[k] <- [[1]] :put a {k}", Default::default()) db.run_script(r"?[k] <- [[1]] :put a {k}", Default::default())
.unwrap(); .unwrap();
db.run_script(r"?[k, v] := *a{k, v}", Default::default()) db.run_script(r"?[k, v] := *a{k, v}", Default::default())
@ -1050,7 +1055,7 @@ fn test_insertions() {
}", }",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script(r"?[count(fr_k)] := *a:i{fr_k}", Default::default()) db.run_script(r"?[count(fr_k)] := *a:i{fr_k}", Default::default())
.unwrap(); .unwrap();
db.run_script(r"?[k] <- [[1]] :put a {k}", Default::default()) db.run_script(r"?[k] <- [[1]] :put a {k}", Default::default())
@ -1059,7 +1064,7 @@ fn test_insertions() {
r"?[k] := k in int_range(300) :put a {k}", r"?[k] := k in int_range(300) :put a {k}",
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
let res = db let res = db
.run_script( .run_script(
r"?[dist, k] := ~a:i{k | query: v, bind_distance: dist, k:10, ef: 50, filter: k % 2 == 0, radius: 245}, *a{k: 96, v}", r"?[dist, k] := ~a:i{k | query: v, bind_distance: dist, k:10, ef: 50, filter: k % 2 == 0, radius: 245}, *a{k: 96, v}",
@ -1131,7 +1136,7 @@ fn multi_index_vec() {
"#, "#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r#" r#"
::hnsw create product:semantic{ ::hnsw create product:semantic{
@ -1143,7 +1148,7 @@ fn multi_index_vec() {
"#, "#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script( db.run_script(
r#" r#"
?[id, name, description, price, name_vec, description_vec] <- [[1, "name", "description", 100, [1], [1]]] ?[id, name, description, price, name_vec, description_vec] <- [[1, "name", "description", 100, [1], [1]]]
@ -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] #[test]
fn parser_corner_case() { fn parser_corner_case() {
let db = DbInstance::new("mem", "", "").unwrap(); let db = DbInstance::new("mem", "", "").unwrap();
@ -1171,12 +1198,12 @@ fn parser_corner_case() {
r#"?[C] := C = true, C inx[C] := C = 1"#, r#"?[C] := C = true, C inx[C] := C = 1"#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
db.run_script(r#"?[k] := k in int_range(300)"#, Default::default()) db.run_script(r#"?[k] := k in int_range(300)"#, Default::default())
.unwrap(); .unwrap();
db.run_script( db.run_script(
r#"ywcc[a] <- [[1]] noto[A] := ywcc[A] ?[A] := noto[A]"#, r#"ywcc[a] <- [[1]] noto[A] := ywcc[A] ?[A] := noto[A]"#,
Default::default(), Default::default(),
) )
.unwrap(); .unwrap();
} }

Loading…
Cancel
Save