insertion

main
Ziyang Hu 1 year ago
parent 55c27e4396
commit 4208989f31

@ -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_not | relation_ensure } 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"}

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

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

@ -1177,6 +1177,16 @@ fn ensure_not() {
", Default::default()).unwrap(); ", 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();

Loading…
Cancel
Save