From 20c4e0344604ce1389c0c414c72ae274c9d82731 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Fri, 17 Jun 2022 23:55:09 +0100 Subject: [PATCH] Ensure graph edges are purged correctly when deleted --- lib/src/dbs/iterator.rs | 3 +++ lib/src/doc/edges.rs | 19 ++++++++++----- lib/src/doc/purge.rs | 47 ++++++++++++++++++++++++++++++++---- lib/src/sql/mod.rs | 1 + lib/src/sql/paths.rs | 8 ++++++ lib/src/sql/value/def.rs | 5 +--- lib/src/sql/value/get.rs | 4 +-- lib/src/sql/value/retable.rs | 5 +--- 8 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 lib/src/sql/paths.rs diff --git a/lib/src/dbs/iterator.rs b/lib/src/dbs/iterator.rs index 1d98db5c..89bec676 100644 --- a/lib/src/dbs/iterator.rs +++ b/lib/src/dbs/iterator.rs @@ -12,6 +12,7 @@ use crate::sql::part::Part; use crate::sql::table::Table; use crate::sql::thing::Thing; use crate::sql::value::Value; +use async_recursion::async_recursion; use std::cmp::Ordering; use std::collections::BTreeMap; use std::mem; @@ -329,6 +330,7 @@ impl Iterator { } #[cfg(not(feature = "parallel"))] + #[cfg_attr(not(feature = "parallel"), async_recursion(?Send))] async fn iterate( &mut self, ctx: &Context<'_>, @@ -345,6 +347,7 @@ impl Iterator { } #[cfg(feature = "parallel")] + #[cfg_attr(feature = "parallel", async_recursion)] async fn iterate( &mut self, ctx: &Context<'_>, diff --git a/lib/src/doc/edges.rs b/lib/src/doc/edges.rs index c492336a..f07dfe5d 100644 --- a/lib/src/doc/edges.rs +++ b/lib/src/doc/edges.rs @@ -5,12 +5,14 @@ use crate::dbs::Transaction; use crate::dbs::Workable; use crate::doc::Document; use crate::err::Error; +use crate::sql::paths::IN; +use crate::sql::paths::OUT; use crate::sql::Dir; impl<'a> Document<'a> { pub async fn edges( - &self, - _ctx: &Context<'_>, + &mut self, + ctx: &Context<'_>, opt: &Options, txn: &Transaction, _stm: &Statement<'_>, @@ -27,18 +29,23 @@ impl<'a> Document<'a> { let rid = self.id.as_ref().unwrap(); // Store the record edges if let Workable::Relate(l, r) = &self.extras { + // Get temporary edge references + let (ref o, ref i) = (Dir::Out, Dir::In); // Store the left pointer edge - let key = crate::key::graph::new(opt.ns(), opt.db(), &l.tb, &l.id, &Dir::Out, rid); + let key = crate::key::graph::new(opt.ns(), opt.db(), &l.tb, &l.id, o, rid); run.set(key, vec![]).await?; // Store the left inner edge - let key = crate::key::graph::new(opt.ns(), opt.db(), &rid.tb, &rid.id, &Dir::In, l); + let key = crate::key::graph::new(opt.ns(), opt.db(), &rid.tb, &rid.id, i, l); run.set(key, vec![]).await?; // Store the right inner edge - let key = crate::key::graph::new(opt.ns(), opt.db(), &rid.tb, &rid.id, &Dir::Out, r); + let key = crate::key::graph::new(opt.ns(), opt.db(), &rid.tb, &rid.id, o, r); run.set(key, vec![]).await?; // Store the right pointer edge - let key = crate::key::graph::new(opt.ns(), opt.db(), &r.tb, &r.id, &Dir::In, rid); + let key = crate::key::graph::new(opt.ns(), opt.db(), &r.tb, &r.id, i, rid); run.set(key, vec![]).await?; + // Store the edges on the record + self.current.to_mut().set(ctx, opt, txn, &*IN, l.clone().into()).await?; + self.current.to_mut().set(ctx, opt, txn, &*OUT, r.clone().into()).await?; } // Carry on Ok(()) diff --git a/lib/src/doc/purge.rs b/lib/src/doc/purge.rs index 9a716615..6a6dab0c 100644 --- a/lib/src/doc/purge.rs +++ b/lib/src/doc/purge.rs @@ -4,11 +4,18 @@ use crate::dbs::Statement; use crate::dbs::Transaction; use crate::doc::Document; use crate::err::Error; +use crate::sql::dir::Dir; +use crate::sql::edges::Edges; +use crate::sql::paths::IN; +use crate::sql::paths::OUT; +use crate::sql::statements::DeleteStatement; +use crate::sql::table::Tables; +use crate::sql::value::{Value, Values}; impl<'a> Document<'a> { pub async fn purge( &self, - _ctx: &Context<'_>, + ctx: &Context<'_>, opt: &Options, txn: &Transaction, _stm: &Statement<'_>, @@ -30,10 +37,40 @@ impl<'a> Document<'a> { // Purge the record data let key = crate::key::thing::new(opt.ns(), opt.db(), &rid.tb, &rid.id); run.del(key).await?; - // Remove the graph data - let beg = crate::key::graph::prefix(opt.ns(), opt.db(), &rid.tb, &rid.id); - let end = crate::key::graph::suffix(opt.ns(), opt.db(), &rid.tb, &rid.id); - run.delr(beg..end, u32::MAX).await?; + // Purge the record edges + match (self.initial.pick(&*IN), self.initial.pick(&*OUT)) { + (Value::Thing(ref l), Value::Thing(ref r)) => { + // Get temporary edge references + let (ref o, ref i) = (Dir::Out, Dir::In); + // Purge the left pointer edge + let key = crate::key::graph::new(opt.ns(), opt.db(), &l.tb, &l.id, o, rid); + run.del(key).await?; + // Purge the left inner edge + let key = crate::key::graph::new(opt.ns(), opt.db(), &rid.tb, &rid.id, i, l); + run.del(key).await?; + // Purge the right inner edge + let key = crate::key::graph::new(opt.ns(), opt.db(), &rid.tb, &rid.id, o, r); + run.del(key).await?; + // Purge the right pointer edge + let key = crate::key::graph::new(opt.ns(), opt.db(), &r.tb, &r.id, i, rid); + run.del(key).await?; + } + _ => { + // Release the transaction + drop(run); + // Setup the delete statement + let stm = DeleteStatement { + what: Values(vec![Value::from(Edges { + dir: Dir::Both, + from: rid.clone(), + what: Tables::default(), + })]), + ..DeleteStatement::default() + }; + // Execute the delete statement + stm.compute(ctx, opt, txn, None).await?; + } + } // Carry on Ok(()) } diff --git a/lib/src/sql/mod.rs b/lib/src/sql/mod.rs index a5c2657a..430475b9 100644 --- a/lib/src/sql/mod.rs +++ b/lib/src/sql/mod.rs @@ -34,6 +34,7 @@ pub(crate) mod output; pub(crate) mod param; pub(crate) mod parser; pub(crate) mod part; +pub(crate) mod paths; pub(crate) mod permission; pub(crate) mod query; pub(crate) mod regex; diff --git a/lib/src/sql/paths.rs b/lib/src/sql/paths.rs new file mode 100644 index 00000000..819ebe4a --- /dev/null +++ b/lib/src/sql/paths.rs @@ -0,0 +1,8 @@ +use crate::sql::part::Part; +use once_cell::sync::Lazy; + +pub static ID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]); + +pub static IN: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("in")]); + +pub static OUT: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("out")]); diff --git a/lib/src/sql/value/def.rs b/lib/src/sql/value/def.rs index 5f195c8f..bff87d44 100644 --- a/lib/src/sql/value/def.rs +++ b/lib/src/sql/value/def.rs @@ -2,12 +2,9 @@ use crate::ctx::Context; use crate::dbs::Options; use crate::dbs::Transaction; use crate::err::Error; -use crate::sql::part::Part; +use crate::sql::paths::ID; use crate::sql::thing::Thing; use crate::sql::value::Value; -use once_cell::sync::Lazy; - -static ID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]); impl Value { pub async fn def( diff --git a/lib/src/sql/value/get.rs b/lib/src/sql/value/get.rs index 11ad7056..d9fd0544 100644 --- a/lib/src/sql/value/get.rs +++ b/lib/src/sql/value/get.rs @@ -6,13 +6,11 @@ use crate::sql::edges::Edges; use crate::sql::field::{Field, Fields}; use crate::sql::part::Next; use crate::sql::part::Part; +use crate::sql::paths::ID; use crate::sql::statements::select::SelectStatement; use crate::sql::value::{Value, Values}; use async_recursion::async_recursion; use futures::future::try_join_all; -use once_cell::sync::Lazy; - -static ID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]); impl Value { #[cfg_attr(feature = "parallel", async_recursion)] diff --git a/lib/src/sql/value/retable.rs b/lib/src/sql/value/retable.rs index ccf900c3..8c0c9b3a 100644 --- a/lib/src/sql/value/retable.rs +++ b/lib/src/sql/value/retable.rs @@ -1,12 +1,9 @@ use crate::err::Error; use crate::sql::id::Id; -use crate::sql::part::Part; +use crate::sql::paths::ID; use crate::sql::table::Table; use crate::sql::thing::Thing; use crate::sql::value::Value; -use once_cell::sync::Lazy; - -static ID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]); impl Value { pub fn retable(&self, val: &Table) -> Result {