check existing keys on insertion

main
Ziyang Hu 2 years ago
parent 00da0e33fc
commit e0fb7c024a

@ -56,6 +56,8 @@ pub trait Environment<'t, T: AsRef<[u8]>> where Self: Sized {
}
fn delete_defined(&mut self, name: &str, in_root: bool) -> Result<()>;
fn define_data(&mut self, name: &str, data: OwnTuple, in_root: bool) -> Result<()>;
fn key_exists(&self, key: &OwnTuple, in_root: bool) -> Result<bool>;
fn del_key(&self, key: &OwnTuple, in_root: bool) -> Result<()>;
fn define_raw_key(&self, key: &OwnTuple, value: Option<&OwnTuple>, in_root: bool) -> Result<()>;
fn encode_definable_key(&self, name: &str, in_root: bool) -> OwnTuple {
let depth_code = if in_root { 0 } else { self.get_stack_depth() as i64 };
@ -954,6 +956,16 @@ impl<'a, 't> Environment<'t, SlicePtr> for Session<'a, 't> {
Ok(())
}
fn key_exists(&self, key: &OwnTuple, in_root: bool) -> Result<bool> {
let res = self.txn.get(in_root, if in_root { &self.perm_cf } else { &self.temp_cf }, key)?;
Ok(res.is_some())
}
fn del_key(&self, key: &OwnTuple, in_root: bool) -> Result<()> {
self.txn.del(in_root, if in_root { &self.perm_cf } else { &self.temp_cf }, key)?;
Ok(())
}
fn define_raw_key(&self, key: &OwnTuple, value: Option<&OwnTuple>, in_root: bool) -> Result<()> {
if in_root {
match value {

@ -22,11 +22,28 @@ use crate::relation::value::Value;
/// * `[table_id, fst_table_id, fst_keys, is_forward, snd_keys, other_keys]` twice, the backward has no data
/// * Associate
/// * Same as the main one
///
/// # Logic of the operations
///
/// * Insert/Upsert
/// * Just add the stuff in
/// * No update or delete: only possible through relational query
/// * Delete
/// * Only need the keys
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
enum MutationKind {
Upsert,
Insert
}
impl<'a, 't> Session<'a, 't> {
pub fn run_mutation(&mut self, pair: Pair<Rule>) -> Result<()> {
let mut pairs = pair.into_inner();
let kind = pairs.next().unwrap();
let kind = match pairs.next().unwrap().as_rule() {
Rule::upsert => MutationKind::Upsert,
Rule::insert => MutationKind::Insert,
_ => unreachable!()
};
let (evaluated, expr) = self.partial_eval(Value::from_pair(pairs.next().unwrap())?)?;
if !evaluated {
return Err(LogicError("Mutation encountered unevaluated expression".to_string()));
@ -52,12 +69,17 @@ impl<'a, 't> Session<'a, 't> {
let mut mutation_manager = MutationManager::new(self, default_kind);
// Coercion
for item in expr {
let val_map = match item {
Value::Dict(d) => d,
_ => return Err(LogicError("Must be structs".to_string()))
};
mutation_manager.add(val_map)?;
match kind {
MutationKind::Insert | MutationKind::Upsert => {
for item in expr {
let val_map = match item {
Value::Dict(d) => d,
_ => return Err(LogicError("Must be structs".to_string()))
};
mutation_manager.process_insert(kind == MutationKind::Insert, val_map)?;
}
}
_ => todo!()
}
Ok(())
@ -203,7 +225,7 @@ impl<'a, 'b, 't> MutationManager<'a, 'b, 't> {
Ok(info.clone())
}
fn add(&mut self, mut val_map: BTreeMap<Cow<str>, Value>) -> Result<()> {
fn process_insert(&mut self, error_on_existing: bool, mut val_map: BTreeMap<Cow<str>, Value>) -> Result<()> {
let tbl_name = match val_map.get("_type") {
Some(Value::Text(t)) => t.clone(),
Some(_) => return Err(LogicError("Table kind must be text".to_string())),
@ -231,6 +253,9 @@ impl<'a, 'b, 't> MutationManager<'a, 'b, 't> {
let processed = v.coerce(raw)?;
val_tuple.push_value(&processed);
}
if error_on_existing && self.sess.key_exists(&key_tuple, table_info.in_root)? {
return Err(CozoError::KeyConflict(key_tuple));
}
self.sess.define_raw_key(&key_tuple, Some(&val_tuple), table_info.in_root)?;
}
DataKind::Edge => {
@ -294,6 +319,9 @@ impl<'a, 'b, 't> MutationManager<'a, 'b, 't> {
let processed = v.coerce(raw)?;
val_tuple.push_value(&processed);
}
if error_on_existing && self.sess.key_exists(&key_tuple, table_info.in_root)? {
return Err(CozoError::KeyConflict(key_tuple));
}
self.sess.define_raw_key(&key_tuple, Some(&val_tuple), table_info.in_root)?;
self.sess.define_raw_key(&ikey_tuple, Some(&key_tuple), table_info.in_root)?;
}
@ -312,7 +340,6 @@ impl<'a, 'b, 't> MutationManager<'a, 'b, 't> {
}
key_tuple.overwrite_prefix(assoc.table_id as u32);
self.sess.define_raw_key(&key_tuple, Some(&val_tuple), assoc.in_root)?;
}
}
Ok(())
@ -375,7 +402,10 @@ mod tests {
] as Person;
"#;
let p = Parser::parse(Rule::file, s).unwrap().next().unwrap();
sess.run_mutation(p).unwrap();
assert!(sess.run_mutation(p.clone()).is_ok());
sess.commit().unwrap();
assert!(sess.run_mutation(p.clone()).is_err());
sess.rollback().unwrap();
let it = sess.txn.iterator(true, &sess.perm_cf);
it.to_first();
for (k, v) in it.iter() {

@ -4,6 +4,7 @@ use thiserror::Error;
use cozorocks::BridgeError;
use crate::parser::Rule;
use crate::relation::data::DataKind;
use crate::relation::tuple::OwnTuple;
use crate::relation::value::{StaticValue};
#[derive(Error, Debug)]
@ -35,6 +36,12 @@ pub enum CozoError {
#[error("Logic error: {0}")]
LogicError(String),
#[error("Key conflict: {0:?}")]
KeyConflict(OwnTuple),
#[error("Key not found: {0:?}")]
KeyNotFound(OwnTuple),
#[error("Bad data format {0:?}")]
BadDataFormat(Vec<u8>),

@ -160,10 +160,9 @@ global_def = { "create" ~ definition }
local_def = { "local" ~ definition }
statement = _{ global_def | local_def }
mutation = { (insert|update|delete) ~ expr ~ ("as" ~ name_in_def)? ~ mutation_filter? ~ ";" }
mutation = { (upsert|insert) ~ expr ~ ("as" ~ name_in_def)? ~ mutation_filter? ~ ";" }
mutation_filter = { (exclude|include) ~ name_in_def ~ ("," ~ name_in_def)* }
exclude = {"exclude"}
include = {"include"}
insert = {"insert"}
update = {"update"}
delete = {"delete"}
upsert = {"upsert"}
insert = {"insert"}
Loading…
Cancel
Save