Implement reblessive into the executor. (#3803)

This commit is contained in:
Mees Delzenne 2024-04-18 17:51:47 +02:00 committed by GitHub
parent 0462d6a395
commit 49ad32f45e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
94 changed files with 2048 additions and 1114 deletions

16
Cargo.lock generated
View file

@ -597,12 +597,6 @@ dependencies = [
"critical-section",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.2.0"
@ -4535,14 +4529,9 @@ dependencies = [
[[package]]
name = "reblessive"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb88832db7cfbac349e0276a84d4fbbcdb9cfe8069affbc765d8fd012265ba45"
dependencies = [
"atomic-waker",
"pin-project-lite",
"pin-utils",
]
checksum = "ffb68fbdf0b0509dcff7c86244a6e2408968c6153c7e50c4eb14a7be598b83a7"
[[package]]
name = "redox_syscall"
@ -5863,6 +5852,7 @@ dependencies = [
"pprof",
"radix_trie",
"rand 0.8.5",
"reblessive",
"regex",
"reqwest",
"revision",

View file

@ -114,7 +114,7 @@ pin-project-lite = "0.2.13"
quick_cache = "0.4.0"
radix_trie = { version = "0.2.1", features = ["serde"] }
rand = "0.8.5"
reblessive = { version = "0.3.0" }
reblessive = { version = "0.3.3", features = ["tree"] }
regex = "1.10.2"
regex-syntax = { version = "0.8.2", optional = true, features = ["arbitrary"] }
reqwest = { version = "0.11.22", default-features = false, features = [

View file

@ -17,8 +17,8 @@ pub const MAX_CONCURRENT_TASKS: usize = 64;
/// During query execution, all potentially-recursive code paths count against this limit. Whereas
/// parsing assigns equal weight to each recursion, certain expensive code paths are allowed to
/// count for more than one unit of depth during execution.
pub static MAX_COMPUTATION_DEPTH: Lazy<u8> =
lazy_env_parse!("SURREAL_MAX_COMPUTATION_DEPTH", u8, 120);
pub static MAX_COMPUTATION_DEPTH: Lazy<u32> =
lazy_env_parse!("SURREAL_MAX_COMPUTATION_DEPTH", u32, 120);
/// Specifies the names of parameters which can not be specified in a query.
pub const PROTECTED_PARAM_NAMES: &[&str] = &["auth", "scope", "token", "session"];

View file

@ -3,6 +3,7 @@ use std::sync::Arc;
use channel::Receiver;
use futures::lock::Mutex;
use futures::StreamExt;
use reblessive::TreeStack;
#[cfg(not(target_arch = "wasm32"))]
use tokio::spawn;
use tracing::instrument;
@ -213,6 +214,9 @@ impl<'a> Executor<'a> {
opt: Options,
qry: Query,
) -> Result<(Vec<Response>, Vec<TrackedResult>), Error> {
// The stack to run the executor in.
let mut stack = TreeStack::new();
// Create a notification channel
let (send, recv) = channel::unbounded();
// Set the notification channel
@ -308,7 +312,12 @@ impl<'a> Executor<'a> {
// The transaction began successfully
false => {
// Check the statement
match stm.compute(&ctx, &opt, &self.txn(), None).await {
let txn = self.txn();
match stack
.enter(|stk| stm.compute(stk, &ctx, &opt, &txn, None))
.finish()
.await
{
Ok(val) => {
// Check if writeable
let writeable = stm.writeable();
@ -375,9 +384,14 @@ impl<'a> Executor<'a> {
if let Err(err) = ctx.add_timeout(timeout) {
Err(err)
} else {
let txn = self.txn();
// Process the statement
let res =
stm.compute(&ctx, &opt, &self.txn(), None).await;
let res = stack
.enter(|stk| {
stm.compute(stk, &ctx, &opt, &txn, None)
})
.finish()
.await;
// Catch statement timeout
match ctx.is_timedout() {
true => Err(Error::QueryTimedout),
@ -386,7 +400,13 @@ impl<'a> Executor<'a> {
}
}
// There is no timeout clause
None => stm.compute(&ctx, &opt, &self.txn(), None).await,
None => {
let txn = self.txn();
stack
.enter(|stk| stm.compute(stk, &ctx, &opt, &txn, None))
.finish()
.await
}
};
// Catch global timeout
let res = match ctx.is_timedout() {

View file

@ -6,6 +6,7 @@ use crate::err::Error;
use crate::sql::function::OptimisedAggregate;
use crate::sql::value::{TryAdd, TryDiv, Value};
use crate::sql::{Array, Field, Function, Idiom};
use reblessive::tree::Stk;
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
@ -59,6 +60,7 @@ impl GroupsCollector {
pub(super) async fn push(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -80,12 +82,13 @@ impl GroupsCollector {
.grp
.entry(arr)
.or_insert_with(|| self.base.iter().map(|a| a.new_instance()).collect());
Self::pushes(ctx, opt, txn, agr, &self.idioms, obj).await?
Self::pushes(stk, ctx, opt, txn, agr, &self.idioms, obj).await?
}
Ok(())
}
async fn pushes(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -94,8 +97,8 @@ impl GroupsCollector {
obj: Value,
) -> Result<(), Error> {
for (agr, idiom) in agrs.iter_mut().zip(idioms) {
let val = obj.get(ctx, opt, txn, None, idiom).await?;
agr.push(ctx, opt, txn, val).await?;
let val = stk.run(|stk| obj.get(stk, ctx, opt, txn, None, idiom)).await?;
agr.push(stk, ctx, opt, txn, val).await?;
}
Ok(())
}
@ -106,6 +109,7 @@ impl GroupsCollector {
pub(super) async fn output(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -139,16 +143,18 @@ impl GroupsCollector {
let x = if matches!(a, OptimisedAggregate::None) {
// The aggregation is not optimised, let's compute it with the values
let vals = agr.take();
f.aggregate(vals).compute(ctx, opt, txn, None).await?
f.aggregate(vals)
.compute(stk, ctx, opt, txn, None)
.await?
} else {
// The aggregation is optimised, just get the value
agr.compute(a)?
};
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
obj.set(stk, ctx, opt, txn, idiom.as_ref(), x).await?;
}
_ => {
let x = agr.take().first();
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
obj.set(stk, ctx, opt, txn, idiom.as_ref(), x).await?;
}
}
}
@ -254,6 +260,7 @@ impl Aggregator {
async fn push(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -263,7 +270,7 @@ impl Aggregator {
*c += 1;
}
if let Some((ref f, ref mut c)) = self.count_function {
if f.aggregate(val.clone()).compute(ctx, opt, txn, None).await?.is_truthy() {
if f.aggregate(val.clone()).compute(stk, ctx, opt, txn, None).await?.is_truthy() {
*c += 1;
}
}

View file

@ -17,7 +17,7 @@ use crate::sql::range::Range;
use crate::sql::table::Table;
use crate::sql::thing::Thing;
use crate::sql::value::Value;
use async_recursion::async_recursion;
use reblessive::{tree::Stk, TreeStack};
use std::mem;
#[derive(Clone)]
@ -95,6 +95,7 @@ impl Iterator {
/// Prepares a value for processing
pub async fn prepare(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -107,7 +108,7 @@ impl Iterator {
// There is a data clause so fetch a record id
Some(data) => match stm {
Statement::Create(_) => {
let id = match data.rid(ctx, opt, txn).await? {
let id = match data.rid(stk, ctx, opt, txn).await? {
// Generate a new id from the id field
Some(id) => id.generate(&v, false)?,
// Generate a new random table id
@ -136,7 +137,7 @@ impl Iterator {
// Check if there is a data clause
if let Some(data) = stm.data() {
// Check if there is an id field specified
if let Some(id) = data.rid(ctx, opt, txn).await? {
if let Some(id) = data.rid(stk, ctx, opt, txn).await? {
// Check to see the type of the id
match id {
// The id is a match, so don't error
@ -164,7 +165,7 @@ impl Iterator {
// Check if there is a data clause
if let Some(data) = stm.data() {
// Check if there is an id field specified
if let Some(id) = data.rid(ctx, opt, txn).await? {
if let Some(id) = data.rid(stk, ctx, opt, txn).await? {
return Err(Error::IdMismatch {
value: id.to_string(),
});
@ -185,7 +186,7 @@ impl Iterator {
// Check if there is a data clause
if let Some(data) = stm.data() {
// Check if there is an id field specified
if let Some(id) = data.rid(ctx, opt, txn).await? {
if let Some(id) = data.rid(stk, ctx, opt, txn).await? {
return Err(Error::IdMismatch {
value: id.to_string(),
});
@ -204,7 +205,7 @@ impl Iterator {
// Check if there is a data clause
if let Some(data) = stm.data() {
// Check if there is an id field specified
if let Some(id) = data.rid(ctx, opt, txn).await? {
if let Some(id) = data.rid(stk, ctx, opt, txn).await? {
return Err(Error::IdMismatch {
value: id.to_string(),
});
@ -217,7 +218,7 @@ impl Iterator {
// Check if there is a data clause
if let Some(data) = stm.data() {
// Check if there is an id field specified
if let Some(id) = data.rid(ctx, opt, txn).await? {
if let Some(id) = data.rid(stk, ctx, opt, txn).await? {
return Err(Error::IdMismatch {
value: id.to_string(),
});
@ -240,7 +241,7 @@ impl Iterator {
// Check if there is a data clause
if let Some(data) = stm.data() {
// Check if there is an id field specified
if let Some(id) = data.rid(ctx, opt, txn).await? {
if let Some(id) = data.rid(stk, ctx, opt, txn).await? {
return Err(Error::IdMismatch {
value: id.to_string(),
});
@ -280,6 +281,7 @@ impl Iterator {
/// Process the records and output
pub async fn output(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -291,9 +293,9 @@ impl Iterator {
let mut cancel_ctx = Context::new(ctx);
self.run = cancel_ctx.add_cancel();
// Process the query LIMIT clause
self.setup_limit(&cancel_ctx, opt, txn, stm).await?;
self.setup_limit(stk, &cancel_ctx, opt, txn, stm).await?;
// Process the query START clause
self.setup_start(&cancel_ctx, opt, txn, stm).await?;
self.setup_start(stk, &cancel_ctx, opt, txn, stm).await?;
// Prepare the results with possible optimisations on groups
self.results = self.results.prepare(
#[cfg(any(
@ -316,21 +318,20 @@ impl Iterator {
let is_last = matches!(s, IterationStage::Iterate(_));
cancel_ctx.set_iteration_stage(s);
if !is_last {
self.clone().iterate(&cancel_ctx, opt, txn, stm).await?;
self.clone().iterate(stk, &cancel_ctx, opt, txn, stm).await?;
};
}
}
self.iterate(&cancel_ctx, opt, txn, stm).await?;
self.iterate(stk, &cancel_ctx, opt, txn, stm).await?;
// Return any document errors
if let Some(e) = self.error.take() {
return Err(e);
}
// Process any SPLIT clause
self.output_split(ctx, opt, txn, stm).await?;
self.output_split(stk, ctx, opt, txn, stm).await?;
// Process any GROUP clause
if let Results::Groups(g) = &mut self.results {
self.results = Results::Memory(g.output(ctx, opt, txn, stm).await?);
self.results = Results::Memory(g.output(stk, ctx, opt, txn, stm).await?);
}
// Process any ORDER clause
@ -345,7 +346,7 @@ impl Iterator {
e.add_fetch(self.results.len());
} else {
// Process any FETCH clause
self.output_fetch(ctx, opt, txn, stm).await?;
self.output_fetch(stk, ctx, opt, txn, stm).await?;
}
}
@ -367,13 +368,14 @@ impl Iterator {
#[inline]
async fn setup_limit(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
stm: &Statement<'_>,
) -> Result<(), Error> {
if let Some(v) = stm.limit() {
self.limit = Some(v.process(ctx, opt, txn, None).await?);
self.limit = Some(v.process(stk, ctx, opt, txn, None).await?);
}
Ok(())
}
@ -381,13 +383,14 @@ impl Iterator {
#[inline]
async fn setup_start(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
stm: &Statement<'_>,
) -> Result<(), Error> {
if let Some(v) = stm.start() {
self.start = Some(v.process(ctx, opt, txn, None).await?);
self.start = Some(v.process(stk, ctx, opt, txn, None).await?);
}
Ok(())
}
@ -395,6 +398,7 @@ impl Iterator {
#[inline]
async fn output_split(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -416,18 +420,18 @@ impl Iterator {
// Make a copy of object
let mut obj = obj.clone();
// Set the value at the path
obj.set(ctx, opt, txn, split, val).await?;
obj.set(stk, ctx, opt, txn, split, val).await?;
// Add the object to the results
self.results.push(ctx, opt, txn, stm, obj).await?;
self.results.push(stk, ctx, opt, txn, stm, obj).await?;
}
}
_ => {
// Make a copy of object
let mut obj = obj.clone();
// Set the value at the path
obj.set(ctx, opt, txn, split, val).await?;
obj.set(stk, ctx, opt, txn, split, val).await?;
// Add the object to the results
self.results.push(ctx, opt, txn, stm, obj).await?;
self.results.push(stk, ctx, opt, txn, stm, obj).await?;
}
}
}
@ -439,6 +443,7 @@ impl Iterator {
#[inline]
async fn output_fetch(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -450,7 +455,7 @@ impl Iterator {
// Loop over each result value
for obj in &mut values {
// Fetch the value at the path
obj.fetch(ctx, opt, txn, fetch).await?;
stk.run(|stk| obj.fetch(stk, ctx, opt, txn, fetch)).await?;
}
self.results = values.into();
}
@ -459,9 +464,9 @@ impl Iterator {
}
#[cfg(target_arch = "wasm32")]
#[async_recursion(?Send)]
async fn iterate(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -473,16 +478,16 @@ impl Iterator {
let mut distinct = SyncDistinct::new(ctx);
// Process all prepared values
for v in mem::take(&mut self.entries) {
v.iterate(ctx, opt, txn, stm, self, distinct.as_mut()).await?;
v.iterate(stk, ctx, opt, txn, stm, self, distinct.as_mut()).await?;
}
// Everything processed ok
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
#[async_recursion]
async fn iterate(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -498,7 +503,7 @@ impl Iterator {
let mut distinct = SyncDistinct::new(ctx);
// Process all prepared values
for v in mem::take(&mut self.entries) {
v.iterate(ctx, opt, txn, stm, self, distinct.as_mut()).await?;
v.iterate(stk, ctx, opt, txn, stm, self, distinct.as_mut()).await?;
}
// Everything processed ok
Ok(())
@ -520,9 +525,19 @@ impl Iterator {
// Process all prepared values
for v in vals {
// Distinct is passed only for iterators that really requires it
e.spawn(v.channel(ctx, opt, txn, stm, chn.clone(), distinct.clone()))
// Ensure we detach the spawned task
.detach();
let chn_clone = chn.clone();
let distinct_clone = distinct.clone();
e.spawn(async move {
let mut stack = TreeStack::new();
stack
.enter(|stk| {
v.channel(stk, ctx, opt, txn, stm, chn_clone, distinct_clone)
})
.finish()
.await
})
// Ensure we detach the spawned task
.detach();
}
// Drop the uncloned channel instance
drop(chn);
@ -533,9 +548,18 @@ impl Iterator {
let avals = async {
// Process all received values
while let Ok(pro) = docs.recv().await {
e.spawn(Document::compute(ctx, opt, txn, stm, chn.clone(), pro))
// Ensure we detach the spawned task
.detach();
let chn_clone = chn.clone();
e.spawn(async move {
let mut stack = TreeStack::new();
stack
.enter(|stk| {
Document::compute(stk, ctx, opt, txn, stm, chn_clone, pro)
})
.finish()
.await
})
// Ensure we detach the spawned task
.detach();
}
// Drop the uncloned channel instance
drop(chn);
@ -544,7 +568,7 @@ impl Iterator {
let aproc = async {
// Process all processed values
while let Ok(r) = vals.recv().await {
self.result(ctx, opt, txn, stm, r).await;
self.result(stk, ctx, opt, txn, stm, r).await;
}
// Shutdown the executor
let _ = end.send(()).await;
@ -564,6 +588,7 @@ impl Iterator {
/// Process a new record Thing and Value
pub async fn process(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -571,14 +596,15 @@ impl Iterator {
pro: Processed,
) {
// Process the document
let res = Document::process(ctx, opt, txn, stm, pro).await;
let res = stk.run(|stk| Document::process(stk, ctx, opt, txn, stm, pro)).await;
// Process the result
self.result(ctx, opt, txn, stm, res).await;
self.result(stk, ctx, opt, txn, stm, res).await;
}
/// Accept a processed record result
async fn result(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -596,7 +622,7 @@ impl Iterator {
return;
}
Ok(v) => {
if let Err(e) = self.results.push(ctx, opt, txn, stm, v).await {
if let Err(e) = self.results.push(stk, ctx, opt, txn, stm, v).await {
self.error = Some(e);
self.run.cancel();
return;

View file

@ -1,5 +1,5 @@
use super::capabilities::Capabilities;
use crate::cnf;
use crate::cnf::MAX_COMPUTATION_DEPTH;
use crate::dbs::Notification;
use crate::err::Error;
use crate::iam::{Action, Auth, ResourceKind, Role};
@ -26,7 +26,7 @@ pub struct Options {
/// Currently selected DB
db: Option<Arc<str>>,
/// Approximately how large is the current call stack?
dive: u8,
dive: u32,
/// Connection authentication data
pub auth: Arc<Auth>,
/// Is authentication enabled?
@ -83,7 +83,7 @@ impl Options {
id: None,
ns: None,
db: None,
dive: 0,
dive: *MAX_COMPUTATION_DEPTH,
live: false,
perms: true,
force: Force::None,
@ -131,6 +131,12 @@ impl Options {
self
}
/// Set the maximum depth a computation can reach.
pub fn with_max_computation_depth(mut self, depth: u32) -> Self {
self.dive = depth;
self
}
/// Set the Node ID for subsequent code which uses
/// this `Options`, with support for chaining.
pub fn with_id(mut self, id: Uuid) -> Self {
@ -328,21 +334,19 @@ impl Options {
/// The parameter is the approximate cost of the operation (more concretely, the size of the
/// stack frame it uses relative to a simple function call). When in doubt, use a value of 1.
pub fn dive(&self, cost: u8) -> Result<Self, Error> {
let dive = self.dive.saturating_add(cost);
if dive <= *cnf::MAX_COMPUTATION_DEPTH {
Ok(Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
force: self.force.clone(),
dive,
..*self
})
} else {
Err(Error::ComputationDepthExceeded)
if self.dive < cost as u32 {
return Err(Error::ComputationDepthExceeded);
}
Ok(Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
force: self.force.clone(),
dive: self.dive - cost as u32,
..*self
})
}
// --------------------------------------------------

View file

@ -13,11 +13,14 @@ use crate::sql::dir::Dir;
use crate::sql::{Edges, Range, Table, Thing, Value};
#[cfg(not(target_arch = "wasm32"))]
use channel::Sender;
use reblessive::tree::Stk;
use std::ops::Bound;
impl Iterable {
#[allow(clippy::too_many_arguments)]
pub(crate) async fn iterate(
self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -26,15 +29,17 @@ impl Iterable {
dis: Option<&mut SyncDistinct>,
) -> Result<(), Error> {
if self.iteration_stage_check(ctx) {
Processor::Iterator(dis, ite).process_iterable(ctx, opt, txn, stm, self).await
Processor::Iterator(dis, ite).process_iterable(stk, ctx, opt, txn, stm, self).await
} else {
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
#[allow(clippy::too_many_arguments)]
pub(crate) async fn channel(
self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -43,7 +48,7 @@ impl Iterable {
dis: Option<AsyncDistinct>,
) -> Result<(), Error> {
if self.iteration_stage_check(ctx) {
Processor::Channel(dis, chn).process_iterable(ctx, opt, txn, stm, self).await
Processor::Channel(dis, chn).process_iterable(stk, ctx, opt, txn, stm, self).await
} else {
Ok(())
}
@ -75,6 +80,7 @@ enum Processor<'a> {
impl<'a> Processor<'a> {
async fn process(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -89,7 +95,7 @@ impl<'a> Processor<'a> {
false
};
if !is_processed {
ite.process(ctx, opt, txn, stm, pro).await;
ite.process(stk, ctx, opt, txn, stm, pro).await;
}
}
#[cfg(not(target_arch = "wasm32"))]
@ -109,6 +115,7 @@ impl<'a> Processor<'a> {
async fn process_iterable(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -117,9 +124,9 @@ impl<'a> Processor<'a> {
) -> Result<(), Error> {
if ctx.is_ok() {
match iterable {
Iterable::Value(v) => self.process_value(ctx, opt, txn, stm, v).await?,
Iterable::Thing(v) => self.process_thing(ctx, opt, txn, stm, v).await?,
Iterable::Defer(v) => self.process_defer(ctx, opt, txn, stm, v).await?,
Iterable::Value(v) => self.process_value(stk, ctx, opt, txn, stm, v).await?,
Iterable::Thing(v) => self.process_thing(stk, ctx, opt, txn, stm, v).await?,
Iterable::Defer(v) => self.process_defer(stk, ctx, opt, txn, stm, v).await?,
Iterable::Table(v) => {
if let Some(qp) = ctx.get_query_planner() {
if let Some(exe) = qp.get_query_executor(&v.0) {
@ -127,13 +134,13 @@ impl<'a> Processor<'a> {
// Avoiding search in the hashmap of the query planner for each doc
let mut ctx = Context::new(ctx);
ctx.set_query_executor(exe.clone());
return self.process_table(&ctx, opt, txn, stm, &v).await;
return self.process_table(stk, &ctx, opt, txn, stm, &v).await;
}
}
self.process_table(ctx, opt, txn, stm, &v).await?
self.process_table(stk, ctx, opt, txn, stm, &v).await?
}
Iterable::Range(v) => self.process_range(ctx, opt, txn, stm, v).await?,
Iterable::Edges(e) => self.process_edge(ctx, opt, txn, stm, e).await?,
Iterable::Range(v) => self.process_range(stk, ctx, opt, txn, stm, v).await?,
Iterable::Edges(e) => self.process_edge(stk, ctx, opt, txn, stm, e).await?,
Iterable::Index(t, ir) => {
if let Some(qp) = ctx.get_query_planner() {
if let Some(exe) = qp.get_query_executor(&t.0) {
@ -141,16 +148,16 @@ impl<'a> Processor<'a> {
// Avoiding search in the hashmap of the query planner for each doc
let mut ctx = Context::new(ctx);
ctx.set_query_executor(exe.clone());
return self.process_index(&ctx, opt, txn, stm, &t, ir).await;
return self.process_index(stk, &ctx, opt, txn, stm, &t, ir).await;
}
}
self.process_index(ctx, opt, txn, stm, &t, ir).await?
self.process_index(stk, ctx, opt, txn, stm, &t, ir).await?
}
Iterable::Mergeable(v, o) => {
self.process_mergeable(ctx, opt, txn, stm, v, o).await?
self.process_mergeable(stk, ctx, opt, txn, stm, v, o).await?
}
Iterable::Relatable(f, v, w) => {
self.process_relatable(ctx, opt, txn, stm, f, v, w).await?
self.process_relatable(stk, ctx, opt, txn, stm, f, v, w).await?
}
}
}
@ -159,6 +166,7 @@ impl<'a> Processor<'a> {
async fn process_value(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -173,11 +181,12 @@ impl<'a> Processor<'a> {
val: Operable::Value(v),
};
// Process the document record
self.process(ctx, opt, txn, stm, pro).await
self.process(stk, ctx, opt, txn, stm, pro).await
}
async fn process_thing(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -201,13 +210,14 @@ impl<'a> Processor<'a> {
doc_id: None,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
// Everything ok
Ok(())
}
async fn process_defer(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -223,13 +233,15 @@ impl<'a> Processor<'a> {
doc_id: None,
val: Operable::Value(Value::None),
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
// Everything ok
Ok(())
}
#[allow(clippy::too_many_arguments)]
async fn process_mergeable(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -256,7 +268,7 @@ impl<'a> Processor<'a> {
doc_id: None,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
// Everything ok
Ok(())
}
@ -264,6 +276,7 @@ impl<'a> Processor<'a> {
#[allow(clippy::too_many_arguments)]
async fn process_relatable(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -291,13 +304,14 @@ impl<'a> Processor<'a> {
doc_id: None,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
// Everything ok
Ok(())
}
async fn process_table(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -343,7 +357,7 @@ impl<'a> Processor<'a> {
doc_id: None,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
}
continue;
}
@ -353,6 +367,7 @@ impl<'a> Processor<'a> {
async fn process_range(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -415,7 +430,7 @@ impl<'a> Processor<'a> {
doc_id: None,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
}
continue;
}
@ -425,6 +440,7 @@ impl<'a> Processor<'a> {
async fn process_edge(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -540,7 +556,7 @@ impl<'a> Processor<'a> {
doc_id: None,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
}
continue;
}
@ -552,6 +568,7 @@ impl<'a> Processor<'a> {
#[allow(clippy::too_many_arguments)]
async fn process_index(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -598,7 +615,7 @@ impl<'a> Processor<'a> {
doc_id,
val,
};
self.process(ctx, opt, txn, stm, pro).await?;
self.process(stk, ctx, opt, txn, stm, pro).await?;
}
// Collect the next batch of ids

View file

@ -14,6 +14,7 @@ use crate::dbs::store::MemoryCollector;
use crate::dbs::{Options, Statement, Transaction};
use crate::err::Error;
use crate::sql::{Orders, Value};
use reblessive::tree::Stk;
pub(super) enum Results {
None,
@ -63,6 +64,7 @@ impl Results {
pub(super) async fn push(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -86,7 +88,7 @@ impl Results {
e.push(val)?;
}
Self::Groups(g) => {
g.push(ctx, opt, txn, stm, val).await?;
g.push(stk, ctx, opt, txn, stm, val).await?;
}
}
Ok(())

View file

@ -4,10 +4,12 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::permission::Permission;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn allow(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -39,6 +41,7 @@ impl<'a> Document<'a> {
// Process the PERMISSION clause
if !e
.compute(
stk,
ctx,
opt,
txn,

View file

@ -7,10 +7,12 @@ use crate::err::Error;
use crate::sql::data::Data;
use crate::sql::operator::Operator;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn alter(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -24,39 +26,57 @@ impl<'a> Document<'a> {
if let Some(v) = stm.data() {
match v {
Data::PatchExpression(data) => {
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
let data = data.compute(stk, ctx, opt, txn, Some(&self.current)).await?;
self.current.doc.to_mut().patch(data)?
}
Data::MergeExpression(data) => {
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
let data = data.compute(stk, ctx, opt, txn, Some(&self.current)).await?;
self.current.doc.to_mut().merge(data)?
}
Data::ReplaceExpression(data) => {
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
let data = data.compute(stk, ctx, opt, txn, Some(&self.current)).await?;
self.current.doc.to_mut().replace(data)?
}
Data::ContentExpression(data) => {
let data = data.compute(ctx, opt, txn, Some(&self.current)).await?;
let data = data.compute(stk, ctx, opt, txn, Some(&self.current)).await?;
self.current.doc.to_mut().replace(data)?
}
Data::SetExpression(x) => {
for x in x.iter() {
let v = x.2.compute(ctx, opt, txn, Some(&self.current)).await?;
let v = x.2.compute(stk, ctx, opt, txn, Some(&self.current)).await?;
match x.1 {
Operator::Equal => match v {
Value::None => {
self.current.doc.to_mut().del(ctx, opt, txn, &x.0).await?
self.current.doc.to_mut().del(stk, ctx, opt, txn, &x.0).await?
}
_ => {
self.current
.doc
.to_mut()
.set(stk, ctx, opt, txn, &x.0, v)
.await?
}
_ => self.current.doc.to_mut().set(ctx, opt, txn, &x.0, v).await?,
},
Operator::Inc => {
self.current.doc.to_mut().increment(ctx, opt, txn, &x.0, v).await?
self.current
.doc
.to_mut()
.increment(stk, ctx, opt, txn, &x.0, v)
.await?
}
Operator::Dec => {
self.current.doc.to_mut().decrement(ctx, opt, txn, &x.0, v).await?
self.current
.doc
.to_mut()
.decrement(stk, ctx, opt, txn, &x.0, v)
.await?
}
Operator::Ext => {
self.current.doc.to_mut().extend(ctx, opt, txn, &x.0, v).await?
self.current
.doc
.to_mut()
.extend(stk, ctx, opt, txn, &x.0, v)
.await?
}
_ => unreachable!(),
}
@ -64,7 +84,7 @@ impl<'a> Document<'a> {
}
Data::UnsetExpression(i) => {
for i in i.iter() {
self.current.doc.to_mut().del(ctx, opt, txn, i).await?
self.current.doc.to_mut().del(stk, ctx, opt, txn, i).await?
}
}
Data::UpdateExpression(x) => {
@ -76,22 +96,40 @@ impl<'a> Document<'a> {
}
// Process ON DUPLICATE KEY clause
for x in x.iter() {
let v = x.2.compute(&ctx, opt, txn, Some(&self.current)).await?;
let v = x.2.compute(stk, &ctx, opt, txn, Some(&self.current)).await?;
match x.1 {
Operator::Equal => match v {
Value::None => {
self.current.doc.to_mut().del(&ctx, opt, txn, &x.0).await?
self.current.doc.to_mut().del(stk, &ctx, opt, txn, &x.0).await?
}
_ => {
self.current
.doc
.to_mut()
.set(stk, &ctx, opt, txn, &x.0, v)
.await?
}
_ => self.current.doc.to_mut().set(&ctx, opt, txn, &x.0, v).await?,
},
Operator::Inc => {
self.current.doc.to_mut().increment(&ctx, opt, txn, &x.0, v).await?
self.current
.doc
.to_mut()
.increment(stk, &ctx, opt, txn, &x.0, v)
.await?
}
Operator::Dec => {
self.current.doc.to_mut().decrement(&ctx, opt, txn, &x.0, v).await?
self.current
.doc
.to_mut()
.decrement(stk, &ctx, opt, txn, &x.0, v)
.await?
}
Operator::Ext => {
self.current.doc.to_mut().extend(&ctx, opt, txn, &x.0, v).await?
self.current
.doc
.to_mut()
.extend(stk, &ctx, opt, txn, &x.0, v)
.await?
}
_ => unreachable!(),
}

View file

@ -3,10 +3,12 @@ use crate::dbs::Statement;
use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn check(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -15,7 +17,7 @@ impl<'a> Document<'a> {
// Check where condition
if let Some(cond) = stm.conds() {
// Check if the expression is truthy
if !cond.compute(ctx, opt, txn, Some(&self.current)).await?.is_truthy() {
if !cond.compute(stk, ctx, opt, txn, Some(&self.current)).await?.is_truthy() {
// Ignore this document
return Err(Error::Ignore);
}

View file

@ -4,10 +4,12 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::idiom::Idiom;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn clean(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -45,7 +47,7 @@ impl<'a> Document<'a> {
fd if fd.is_in() => continue,
fd if fd.is_out() => continue,
fd if fd.is_meta() => continue,
fd => self.current.doc.to_mut().del(ctx, opt, txn, fd).await?,
fd => self.current.doc.to_mut().del(stk, ctx, opt, txn, fd).await?,
}
}
}

View file

@ -7,10 +7,12 @@ use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use channel::Sender;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
#[allow(dead_code)]
pub(crate) async fn compute(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -34,12 +36,12 @@ impl<'a> Document<'a> {
let mut doc = Document::new(pro.ir, pro.rid.as_ref(), pro.doc_id, &ins.0, ins.1);
// Process the statement
let res = match stm {
Statement::Select(_) => doc.select(ctx, opt, txn, stm).await,
Statement::Create(_) => doc.create(ctx, opt, txn, stm).await,
Statement::Update(_) => doc.update(ctx, opt, txn, stm).await,
Statement::Relate(_) => doc.relate(ctx, opt, txn, stm).await,
Statement::Delete(_) => doc.delete(ctx, opt, txn, stm).await,
Statement::Insert(_) => doc.insert(ctx, opt, txn, stm).await,
Statement::Select(_) => doc.select(stk, ctx, opt, txn, stm).await,
Statement::Create(_) => doc.create(stk, ctx, opt, txn, stm).await,
Statement::Update(_) => doc.update(stk, ctx, opt, txn, stm).await,
Statement::Relate(_) => doc.relate(stk, ctx, opt, txn, stm).await,
Statement::Delete(_) => doc.delete(stk, ctx, opt, txn, stm).await,
Statement::Insert(_) => doc.insert(stk, ctx, opt, txn, stm).await,
_ => unreachable!(),
};
// Check the result

View file

@ -4,10 +4,12 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn create(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -16,28 +18,28 @@ impl<'a> Document<'a> {
// Check if table has corrent relation status
self.relation(ctx, opt, txn, stm).await?;
// Alter record data
self.alter(ctx, opt, txn, stm).await?;
self.alter(stk, ctx, opt, txn, stm).await?;
// Merge fields data
self.field(ctx, opt, txn, stm).await?;
self.field(stk, ctx, opt, txn, stm).await?;
// Reset fields data
self.reset(ctx, opt, txn, stm).await?;
// Clean fields data
self.clean(ctx, opt, txn, stm).await?;
self.clean(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store record data
self.store(ctx, opt, txn, stm).await?;
// Store index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
}

View file

@ -4,34 +4,36 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn delete(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
stm: &Statement<'_>,
) -> Result<Value, Error> {
// Check where clause
self.check(ctx, opt, txn, stm).await?;
self.check(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Erase document
self.erase(ctx, opt, stm).await?;
// Purge index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Purge record data
self.purge(ctx, opt, txn, stm).await?;
self.purge(stk, ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
}

View file

@ -4,11 +4,13 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
use std::ops::Deref;
impl<'a> Document<'a> {
pub async fn event(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -46,11 +48,11 @@ impl<'a> Document<'a> {
ctx.add_value("after", self.current.doc.deref());
ctx.add_value("before", self.initial.doc.deref());
// Process conditional clause
let val = ev.when.compute(&ctx, opt, txn, Some(doc)).await?;
let val = ev.when.compute(stk, &ctx, opt, txn, Some(doc)).await?;
// Execute event if value is truthy
if val.is_truthy() {
for v in ev.then.iter() {
v.compute(&ctx, opt, txn, Some(doc)).await?;
v.compute(stk, &ctx, opt, txn, Some(doc)).await?;
}
}
}

View file

@ -6,10 +6,12 @@ use crate::err::Error;
use crate::iam::Action;
use crate::sql::permission::Permission;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn field(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -56,7 +58,7 @@ impl<'a> Document<'a> {
ctx.add_value("after", &val);
ctx.add_value("before", &old);
// Process the VALUE clause
val = expr.compute(&ctx, opt, txn, Some(&self.current)).await?;
val = expr.compute(stk, &ctx, opt, txn, Some(&self.current)).await?;
}
}
// Check for a TYPE clause
@ -87,7 +89,7 @@ impl<'a> Document<'a> {
ctx.add_value("after", &val);
ctx.add_value("before", &old);
// Process the VALUE clause
val = expr.compute(&ctx, opt, txn, Some(&self.current)).await?;
val = expr.compute(stk, &ctx, opt, txn, Some(&self.current)).await?;
}
}
// Check for a TYPE clause
@ -116,7 +118,7 @@ impl<'a> Document<'a> {
ctx.add_value("after", &val);
ctx.add_value("before", &old);
// Process the ASSERT clause
if !expr.compute(&ctx, opt, txn, Some(&self.current)).await?.is_truthy() {
if !expr.compute(stk, &ctx, opt, txn, Some(&self.current)).await?.is_truthy() {
return Err(Error::FieldValue {
thing: rid.to_string(),
field: fd.name.clone(),
@ -157,7 +159,11 @@ impl<'a> Document<'a> {
ctx.add_value("after", &val);
ctx.add_value("before", &old);
// Process the PERMISSION clause
if !e.compute(&ctx, opt, txn, Some(&self.current)).await?.is_truthy() {
if !e
.compute(stk, &ctx, opt, txn, Some(&self.current))
.await?
.is_truthy()
{
val = old
}
}
@ -165,8 +171,8 @@ impl<'a> Document<'a> {
}
// Set the value of the field
match val {
Value::None => self.current.doc.to_mut().del(ctx, opt, txn, &k).await?,
_ => self.current.doc.to_mut().set(ctx, opt, txn, &k, val).await?,
Value::None => self.current.doc.to_mut().del(stk, ctx, opt, txn, &k).await?,
_ => self.current.doc.to_mut().set(stk, ctx, opt, txn, &k, val).await?,
};
}
}

View file

@ -12,10 +12,12 @@ use crate::sql::array::Array;
use crate::sql::index::{Index, MTreeParams, SearchParams};
use crate::sql::statements::DefineIndexStatement;
use crate::sql::{Part, Thing, Value};
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn index(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -47,10 +49,10 @@ impl<'a> Document<'a> {
// Loop through all index statements
for ix in ixs.iter() {
// Calculate old values
let o = build_opt_values(ctx, opt, txn, ix, &self.initial).await?;
let o = build_opt_values(stk, ctx, opt, txn, ix, &self.initial).await?;
// Calculate new values
let n = build_opt_values(ctx, opt, txn, ix, &self.current).await?;
let n = build_opt_values(stk, ctx, opt, txn, ix, &self.current).await?;
// Update the index entries
if targeted_force || o != n {
@ -61,8 +63,8 @@ impl<'a> Document<'a> {
match &ix.index {
Index::Uniq => ic.index_unique(txn).await?,
Index::Idx => ic.index_non_unique(txn).await?,
Index::Search(p) => ic.index_full_text(ctx, txn, p).await?,
Index::MTree(p) => ic.index_mtree(ctx, txn, p).await?,
Index::Search(p) => ic.index_full_text(stk, ctx, txn, p).await?,
Index::MTree(p) => ic.index_mtree(stk, ctx, txn, p).await?,
};
}
}
@ -76,6 +78,7 @@ impl<'a> Document<'a> {
/// Given this doc: { "id": 1, "instrument":"piano", "name":"Tobie" }
/// It will return: ["Tobie", "piano"]
async fn build_opt_values(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -87,7 +90,7 @@ async fn build_opt_values(
}
let mut o = Vec::with_capacity(ix.cols.len());
for i in ix.cols.iter() {
let v = i.compute(ctx, opt, txn, Some(doc)).await?;
let v = i.compute(stk, ctx, opt, txn, Some(doc)).await?;
o.push(v);
}
Ok(Some(o))
@ -356,6 +359,7 @@ impl<'a> IndexOperation<'a> {
async fn index_full_text(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
txn: &Transaction,
p: &SearchParams,
@ -374,7 +378,7 @@ impl<'a> IndexOperation<'a> {
.await?;
if let Some(n) = self.n.take() {
ft.index_document(ctx, self.opt, txn, self.rid, n).await?;
ft.index_document(stk, ctx, self.opt, txn, self.rid, n).await?;
} else {
ft.remove_document(txn, self.rid).await?;
}
@ -383,6 +387,7 @@ impl<'a> IndexOperation<'a> {
async fn index_mtree(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
txn: &Transaction,
p: &MTreeParams,
@ -394,11 +399,11 @@ impl<'a> IndexOperation<'a> {
.await?;
// Delete the old index data
if let Some(o) = self.o.take() {
mt.remove_document(&mut tx, self.rid, o).await?;
mt.remove_document(stk, &mut tx, self.rid, o).await?;
}
// Create the new index data
if let Some(n) = self.n.take() {
mt.index_document(&mut tx, self.rid, n).await?;
mt.index_document(stk, &mut tx, self.rid, n).await?;
}
mt.finish(&mut tx).await
}

View file

@ -4,10 +4,12 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn insert(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -18,13 +20,13 @@ impl<'a> Document<'a> {
// We attempted to INSERT a document with an ID,
// and this ID already exists in the database,
// so we need to update the record instead.
true => self.insert_update(ctx, opt, txn, stm).await,
true => self.insert_update(stk, ctx, opt, txn, stm).await,
// We attempted to INSERT a document with an ID,
// which does not exist in the database, or we
// are creating a new record with a new ID.
false => {
// First of all let's try to create the record
match self.insert_create(ctx, opt, txn, stm).await {
match self.insert_create(stk, ctx, opt, txn, stm).await {
// We received an index exists error, so we
// ignore the error, and attempt to update the
// record using the ON DUPLICATE KEY clause
@ -45,6 +47,7 @@ impl<'a> Document<'a> {
// Attempt to run an INSERT clause
async fn insert_create(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -53,63 +56,64 @@ impl<'a> Document<'a> {
// Check if table has correct relation status
self.relation(ctx, opt, txn, stm).await?;
// Merge record data
self.merge(ctx, opt, txn, stm).await?;
self.merge(stk, ctx, opt, txn, stm).await?;
// Merge fields data
self.field(ctx, opt, txn, stm).await?;
self.field(stk, ctx, opt, txn, stm).await?;
// Reset fields data
self.reset(ctx, opt, txn, stm).await?;
// Clean fields data
self.clean(ctx, opt, txn, stm).await?;
self.clean(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Store record data
self.store(ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
// Attempt to run an UPDATE clause
async fn insert_update(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
stm: &Statement<'_>,
) -> Result<Value, Error> {
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Alter record data
self.alter(ctx, opt, txn, stm).await?;
self.alter(stk, ctx, opt, txn, stm).await?;
// Merge fields data
self.field(ctx, opt, txn, stm).await?;
self.field(stk, ctx, opt, txn, stm).await?;
// Reset fields data
self.reset(ctx, opt, txn, stm).await?;
// Clean fields data
self.clean(ctx, opt, txn, stm).await?;
self.clean(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Store record data
self.store(ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
}

View file

@ -15,6 +15,7 @@ use crate::sql::permission::Permission;
use crate::sql::statements::LiveStatement;
use crate::sql::Value;
use channel::Sender;
use reblessive::tree::Stk;
use std::ops::Deref;
use std::sync::Arc;
use uuid::Uuid;
@ -22,6 +23,7 @@ use uuid::Uuid;
impl<'a> Document<'a> {
pub async fn lives(
&self,
stk: &mut Stk,
_ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -41,7 +43,8 @@ impl<'a> Document<'a> {
// Loop through all index statements
let lq_stms = self.lv(opt, txn).await?;
let borrows = lq_stms.iter().collect::<Vec<_>>();
self.check_lqs_and_send_notifications(opt, stm, txn, borrows.as_slice(), chn).await?;
self.check_lqs_and_send_notifications(stk, opt, stm, txn, borrows.as_slice(), chn)
.await?;
}
// Carry on
Ok(())
@ -50,6 +53,7 @@ impl<'a> Document<'a> {
/// Check the WHERE clause for a LIVE query
async fn lq_check(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -59,7 +63,7 @@ impl<'a> Document<'a> {
// Check where condition
if let Some(cond) = stm.conds() {
// Check if the expression is truthy
if !cond.compute(ctx, opt, txn, Some(doc)).await?.is_truthy() {
if !cond.compute(stk, ctx, opt, txn, Some(doc)).await?.is_truthy() {
// Ignore this document
return Err(Error::Ignore);
}
@ -70,6 +74,7 @@ impl<'a> Document<'a> {
/// Check any PERRMISSIONS for a LIVE query
async fn lq_allow(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -88,7 +93,7 @@ impl<'a> Document<'a> {
// Disable permissions
let opt = &opt.new_with_perms(false);
// Process the PERMISSION clause
if !e.compute(ctx, opt, txn, Some(doc)).await?.is_truthy() {
if !e.compute(stk, ctx, opt, txn, Some(doc)).await?.is_truthy() {
return Err(Error::Ignore);
}
}
@ -101,6 +106,7 @@ impl<'a> Document<'a> {
/// Process live query for notifications
pub(crate) async fn check_lqs_and_send_notifications(
&self,
stk: &mut Stk,
opt: &Options,
stm: &Statement<'_>,
txn: &Transaction,
@ -167,7 +173,7 @@ impl<'a> Document<'a> {
// First of all, let's check to see if the WHERE
// clause of the LIVE query is matched by this
// document. If it is then we can continue.
match self.lq_check(&lqctx, &lqopt, txn, &lq, doc).await {
match self.lq_check(stk, &lqctx, &lqopt, txn, &lq, doc).await {
Err(Error::Ignore) => {
trace!("live query did not match the where clause, skipping");
continue;
@ -179,7 +185,7 @@ impl<'a> Document<'a> {
// clause for this table allows this document to
// be viewed by the user who created this LIVE
// query. If it does, then we can continue.
match self.lq_allow(&lqctx, &lqopt, txn, &lq, doc).await {
match self.lq_allow(stk, &lqctx, &lqopt, txn, &lq, doc).await {
Err(Error::Ignore) => {
trace!("live query did not have permission to view this document, skipping");
continue;
@ -216,9 +222,9 @@ impl<'a> Document<'a> {
let lqopt: &Options = &lqopt.new_with_futures(true);
// Output the full document before any changes were applied
let mut value =
doc.doc.compute(&lqctx, lqopt, txn, Some(doc)).await?;
doc.doc.compute(stk, &lqctx, lqopt, txn, Some(doc)).await?;
// Remove metadata fields on output
value.del(&lqctx, lqopt, txn, &*META).await?;
value.del(stk, &lqctx, lqopt, txn, &*META).await?;
// Output result
value
},
@ -233,7 +239,7 @@ impl<'a> Document<'a> {
.send(Notification {
id: lv.id,
action: Action::Create,
result: self.pluck(&lqctx, &lqopt, txn, &lq).await?,
result: self.pluck(stk, &lqctx, &lqopt, txn, &lq).await?,
})
.await?;
}
@ -245,7 +251,7 @@ impl<'a> Document<'a> {
.send(Notification {
id: lv.id,
action: Action::Update,
result: self.pluck(&lqctx, &lqopt, txn, &lq).await?,
result: self.pluck(stk, &lqctx, &lqopt, txn, &lq).await?,
})
.await?;
}

View file

@ -4,10 +4,12 @@ use crate::dbs::Workable;
use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn merge(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -19,7 +21,7 @@ impl<'a> Document<'a> {
self.current.doc.to_mut().def(rid);
// This is an INSERT statement
if let Workable::Insert(v) = &self.extras {
let v = v.compute(ctx, opt, txn, Some(&self.current)).await?;
let v = v.compute(stk, ctx, opt, txn, Some(&self.current)).await?;
self.current.doc.to_mut().merge(v)?;
}
// Set default field values

View file

@ -9,10 +9,12 @@ use crate::sql::output::Output;
use crate::sql::paths::META;
use crate::sql::permission::Permission;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn pluck(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -31,11 +33,11 @@ impl<'a> Document<'a> {
}
Output::After => {
// Output the full document after all changes were applied
self.current.doc.compute(ctx, opt, txn, Some(&self.current)).await
self.current.doc.compute(stk, ctx, opt, txn, Some(&self.current)).await
}
Output::Before => {
// Output the full document before any changes were applied
self.initial.doc.compute(ctx, opt, txn, Some(&self.initial)).await
self.initial.doc.compute(stk, ctx, opt, txn, Some(&self.initial)).await
}
Output::Fields(v) => {
// Configure the context
@ -43,28 +45,28 @@ impl<'a> Document<'a> {
ctx.add_value("after", self.current.doc.as_ref());
ctx.add_value("before", self.initial.doc.as_ref());
// Output the specified fields
v.compute(&ctx, opt, txn, Some(&self.current), false).await
v.compute(stk, &ctx, opt, txn, Some(&self.current), false).await
}
},
None => match stm {
Statement::Live(s) => match s.expr.len() {
0 => Ok(self.initial.doc.diff(&self.current.doc, Idiom::default()).into()),
_ => s.expr.compute(ctx, opt, txn, Some(&self.current), false).await,
_ => s.expr.compute(stk, ctx, opt, txn, Some(&self.current), false).await,
},
Statement::Select(s) => {
s.expr.compute(ctx, opt, txn, Some(&self.current), s.group.is_some()).await
s.expr.compute(stk, ctx, opt, txn, Some(&self.current), s.group.is_some()).await
}
Statement::Create(_) => {
self.current.doc.compute(ctx, opt, txn, Some(&self.current)).await
self.current.doc.compute(stk, ctx, opt, txn, Some(&self.current)).await
}
Statement::Update(_) => {
self.current.doc.compute(ctx, opt, txn, Some(&self.current)).await
self.current.doc.compute(stk, ctx, opt, txn, Some(&self.current)).await
}
Statement::Relate(_) => {
self.current.doc.compute(ctx, opt, txn, Some(&self.current)).await
self.current.doc.compute(stk, ctx, opt, txn, Some(&self.current)).await
}
Statement::Insert(_) => {
self.current.doc.compute(ctx, opt, txn, Some(&self.current)).await
self.current.doc.compute(stk, ctx, opt, txn, Some(&self.current)).await
}
_ => Err(Error::Ignore),
},
@ -80,7 +82,7 @@ impl<'a> Document<'a> {
// Process the field permissions
match &fd.permissions.select {
Permission::Full => (),
Permission::None => out.del(ctx, opt, txn, k).await?,
Permission::None => out.del(stk, ctx, opt, txn, k).await?,
Permission::Specific(e) => {
// Disable permissions
let opt = &opt.new_with_perms(false);
@ -91,11 +93,11 @@ impl<'a> Document<'a> {
ctx.add_value("value", &val);
// Process the PERMISSION clause
if !e
.compute(&ctx, opt, txn, Some(&self.current))
.compute(stk, &ctx, opt, txn, Some(&self.current))
.await?
.is_truthy()
{
out.del(&ctx, opt, txn, k).await?
out.del(stk, &ctx, opt, txn, k).await?
}
}
}
@ -106,7 +108,7 @@ impl<'a> Document<'a> {
// Remove any omitted fields from output
if let Some(v) = stm.omit() {
for v in v.iter() {
out.del(ctx, opt, txn, v).await?;
out.del(stk, ctx, opt, txn, v).await?;
}
}
// Remove metadata fields on output

View file

@ -6,10 +6,12 @@ use crate::dbs::{Options, Processed};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
#[allow(dead_code)]
pub(crate) async fn process(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -28,12 +30,12 @@ impl<'a> Document<'a> {
let mut doc = Document::new(pro.ir, pro.rid.as_ref(), pro.doc_id, &ins.0, ins.1);
// Process the statement
let res = match stm {
Statement::Select(_) => doc.select(ctx, opt, txn, stm).await,
Statement::Create(_) => doc.create(ctx, opt, txn, stm).await,
Statement::Update(_) => doc.update(ctx, opt, txn, stm).await,
Statement::Relate(_) => doc.relate(ctx, opt, txn, stm).await,
Statement::Delete(_) => doc.delete(ctx, opt, txn, stm).await,
Statement::Insert(_) => doc.insert(ctx, opt, txn, stm).await,
Statement::Select(_) => doc.select(stk, ctx, opt, txn, stm).await,
Statement::Update(_) => doc.update(stk, ctx, opt, txn, stm).await,
Statement::Create(_) => doc.create(stk, ctx, opt, txn, stm).await,
Statement::Relate(_) => doc.relate(stk, ctx, opt, txn, stm).await,
Statement::Delete(_) => doc.delete(stk, ctx, opt, txn, stm).await,
Statement::Insert(_) => doc.insert(stk, ctx, opt, txn, stm).await,
_ => unreachable!(),
};
// Check the result

View file

@ -11,10 +11,12 @@ use crate::sql::paths::OUT;
use crate::sql::statements::DeleteStatement;
use crate::sql::table::Tables;
use crate::sql::value::{Value, Values};
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn purge(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -68,7 +70,7 @@ impl<'a> Document<'a> {
..DeleteStatement::default()
};
// Execute the delete statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
}
}

View file

@ -4,10 +4,12 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn relate(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -22,60 +24,60 @@ impl<'a> Document<'a> {
// Store record edges
self.edges(ctx, opt, txn, stm).await?;
// Alter record data
self.alter(ctx, opt, txn, stm).await?;
self.alter(stk, ctx, opt, txn, stm).await?;
// Merge fields data
self.field(ctx, opt, txn, stm).await?;
self.field(stk, ctx, opt, txn, stm).await?;
// Reset fields data
self.reset(ctx, opt, txn, stm).await?;
// Clean fields data
self.clean(ctx, opt, txn, stm).await?;
self.clean(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store record data
self.store(ctx, opt, txn, stm).await?;
// Store index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
// Update old edge
true => {
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store record edges
self.edges(ctx, opt, txn, stm).await?;
// Alter record data
self.alter(ctx, opt, txn, stm).await?;
self.alter(stk, ctx, opt, txn, stm).await?;
// Merge fields data
self.field(ctx, opt, txn, stm).await?;
self.field(stk, ctx, opt, txn, stm).await?;
// Reset fields data
self.reset(ctx, opt, txn, stm).await?;
// Clean fields data
self.clean(ctx, opt, txn, stm).await?;
self.clean(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store record data
self.store(ctx, opt, txn, stm).await?;
// Store index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
}
}

View file

@ -4,10 +4,12 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn select(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -16,10 +18,10 @@ impl<'a> Document<'a> {
// Check if record exists
self.empty(ctx, opt, txn, stm).await?;
// Check where clause
self.check(ctx, opt, txn, stm).await?;
self.check(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
}

View file

@ -19,6 +19,7 @@ use crate::sql::subquery::Subquery;
use crate::sql::thing::Thing;
use crate::sql::value::{Value, Values};
use futures::future::try_join_all;
use reblessive::tree::Stk;
type Ops = Vec<(Idiom, Operator, Value)>;
@ -32,6 +33,7 @@ enum Action {
impl<'a> Document<'a> {
pub async fn table(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -78,33 +80,42 @@ impl<'a> Document<'a> {
match &tb.group {
// There is a GROUP BY clause specified
Some(group) => {
let id = stk
.scope(|scope| {
try_join_all(group.iter().map(|v| {
scope.run(|stk| v.compute(stk, ctx, opt, txn, Some(&self.initial)))
}))
})
.await?
.into_iter()
.collect::<Vec<_>>()
.into();
// Set the previous record id
let old = Thing {
tb: ft.name.to_raw(),
id: try_join_all(
group.iter().map(|v| v.compute(ctx, opt, txn, Some(&self.initial))),
)
id,
};
let id = stk
.scope(|scope| {
try_join_all(group.iter().map(|v| {
scope.run(|stk| v.compute(stk, ctx, opt, txn, Some(&self.current)))
}))
})
.await?
.into_iter()
.collect::<Vec<_>>()
.into(),
};
.into();
// Set the current record id
let rid = Thing {
tb: ft.name.to_raw(),
id: try_join_all(
group.iter().map(|v| v.compute(ctx, opt, txn, Some(&self.current))),
)
.await?
.into_iter()
.collect::<Vec<_>>()
.into(),
id,
};
// Check if a WHERE clause is specified
match &tb.cond {
// There is a WHERE clause specified
Some(cond) => {
match cond.compute(ctx, opt, txn, Some(&self.current)).await? {
match cond.compute(stk, ctx, opt, txn, Some(&self.current)).await? {
v if v.is_truthy() => {
if !targeted_force && act != Action::Create {
// Delete the old value
@ -113,12 +124,13 @@ impl<'a> Document<'a> {
let stm = UpdateStatement {
what: Values(vec![Value::from(old)]),
data: Some(
self.data(ctx, opt, txn, act, &tb.expr).await?,
self.data(stk, ctx, opt, txn, act, &tb.expr)
.await?,
),
..UpdateStatement::default()
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
if act != Action::Delete {
// Update the new value
@ -127,12 +139,13 @@ impl<'a> Document<'a> {
let stm = UpdateStatement {
what: Values(vec![Value::from(rid)]),
data: Some(
self.data(ctx, opt, txn, act, &tb.expr).await?,
self.data(stk, ctx, opt, txn, act, &tb.expr)
.await?,
),
..UpdateStatement::default()
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
}
_ => {
@ -143,12 +156,13 @@ impl<'a> Document<'a> {
let stm = UpdateStatement {
what: Values(vec![Value::from(old)]),
data: Some(
self.data(ctx, opt, txn, act, &tb.expr).await?,
self.data(stk, ctx, opt, txn, act, &tb.expr)
.await?,
),
..UpdateStatement::default()
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
}
}
@ -161,11 +175,11 @@ impl<'a> Document<'a> {
// Modify the value in the table
let stm = UpdateStatement {
what: Values(vec![Value::from(old)]),
data: Some(self.data(ctx, opt, txn, act, &tb.expr).await?),
data: Some(self.data(stk, ctx, opt, txn, act, &tb.expr).await?),
..UpdateStatement::default()
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
if act != Action::Delete {
// Update the new value
@ -173,11 +187,11 @@ impl<'a> Document<'a> {
// Modify the value in the table
let stm = UpdateStatement {
what: Values(vec![Value::from(rid)]),
data: Some(self.data(ctx, opt, txn, act, &tb.expr).await?),
data: Some(self.data(stk, ctx, opt, txn, act, &tb.expr).await?),
..UpdateStatement::default()
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
}
}
@ -193,7 +207,7 @@ impl<'a> Document<'a> {
match &tb.cond {
// There is a WHERE clause specified
Some(cond) => {
match cond.compute(ctx, opt, txn, Some(&self.current)).await? {
match cond.compute(stk, ctx, opt, txn, Some(&self.current)).await? {
v if v.is_truthy() => {
// Define the statement
let stm = match act {
@ -205,12 +219,14 @@ impl<'a> Document<'a> {
// Update the value in the table
_ => Query::Update(UpdateStatement {
what: Values(vec![Value::from(rid)]),
data: Some(self.full(ctx, opt, txn, &tb.expr).await?),
data: Some(
self.full(stk, ctx, opt, txn, &tb.expr).await?,
),
..UpdateStatement::default()
}),
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
_ => {
// Delete the value in the table
@ -219,7 +235,7 @@ impl<'a> Document<'a> {
..DeleteStatement::default()
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
}
}
@ -235,12 +251,12 @@ impl<'a> Document<'a> {
// Update the value in the table
_ => Query::Update(UpdateStatement {
what: Values(vec![Value::from(rid)]),
data: Some(self.full(ctx, opt, txn, &tb.expr).await?),
data: Some(self.full(stk, ctx, opt, txn, &tb.expr).await?),
..UpdateStatement::default()
}),
};
// Execute the statement
stm.compute(ctx, opt, txn, None).await?;
stm.compute(stk, ctx, opt, txn, None).await?;
}
}
}
@ -252,18 +268,20 @@ impl<'a> Document<'a> {
//
async fn full(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
exp: &Fields,
) -> Result<Data, Error> {
let mut data = exp.compute(ctx, opt, txn, Some(&self.current), false).await?;
let mut data = exp.compute(stk, ctx, opt, txn, Some(&self.current), false).await?;
data.cut(ID.as_ref());
Ok(Data::ReplaceExpression(data))
}
//
async fn data(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -296,29 +314,29 @@ impl<'a> Document<'a> {
match expr {
Value::Function(f) if f.is_rolling() => match f.name() {
Some("count") => {
let val = f.compute(ctx, opt, txn, doc).await?;
let val = f.compute(stk, ctx, opt, txn, doc).await?;
self.chg(&mut ops, &act, idiom, val);
}
Some("math::sum") => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
let val = f.args()[0].compute(stk, ctx, opt, txn, doc).await?;
self.chg(&mut ops, &act, idiom, val);
}
Some("math::min") | Some("time::min") => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
let val = f.args()[0].compute(stk, ctx, opt, txn, doc).await?;
self.min(&mut ops, &act, idiom, val);
}
Some("math::max") | Some("time::max") => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
let val = f.args()[0].compute(stk, ctx, opt, txn, doc).await?;
self.max(&mut ops, &act, idiom, val);
}
Some("math::mean") => {
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
let val = f.args()[0].compute(stk, ctx, opt, txn, doc).await?;
self.mean(&mut ops, &act, idiom, val);
}
_ => unreachable!(),
},
_ => {
let val = expr.compute(ctx, opt, txn, doc).await?;
let val = expr.compute(stk, ctx, opt, txn, doc).await?;
self.set(&mut ops, idiom, val);
}
}

View file

@ -4,42 +4,44 @@ use crate::dbs::{Options, Transaction};
use crate::doc::Document;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl<'a> Document<'a> {
pub async fn update(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
stm: &Statement<'_>,
) -> Result<Value, Error> {
// Check where clause
self.check(ctx, opt, txn, stm).await?;
self.check(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Alter record data
self.alter(ctx, opt, txn, stm).await?;
self.alter(stk, ctx, opt, txn, stm).await?;
// Merge fields data
self.field(ctx, opt, txn, stm).await?;
self.field(stk, ctx, opt, txn, stm).await?;
// Reset fields data
self.reset(ctx, opt, txn, stm).await?;
// Clean fields data
self.clean(ctx, opt, txn, stm).await?;
self.clean(stk, ctx, opt, txn, stm).await?;
// Check if allowed
self.allow(ctx, opt, txn, stm).await?;
self.allow(stk, ctx, opt, txn, stm).await?;
// Store record data
self.store(ctx, opt, txn, stm).await?;
// Store index data
self.index(ctx, opt, txn, stm).await?;
self.index(stk, ctx, opt, txn, stm).await?;
// Run table queries
self.table(ctx, opt, txn, stm).await?;
self.table(stk, ctx, opt, txn, stm).await?;
// Run lives queries
self.lives(ctx, opt, txn, stm).await?;
self.lives(stk, ctx, opt, txn, stm).await?;
// Run change feeds queries
self.changefeeds(ctx, opt, txn, stm).await?;
// Run event queries
self.event(ctx, opt, txn, stm).await?;
self.event(stk, ctx, opt, txn, stm).await?;
// Yield document
self.pluck(ctx, opt, txn, stm).await
self.pluck(stk, ctx, opt, txn, stm).await
}
}

View file

@ -5,6 +5,7 @@ use crate::dbs::Transaction;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::value::Value;
use reblessive::tree::Stk;
pub mod args;
pub mod array;
@ -34,6 +35,7 @@ pub mod vector;
/// Attempts to run any function
pub async fn run(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -51,7 +53,7 @@ pub async fn run(
|| name.starts_with("crypto::pbkdf2")
|| name.starts_with("crypto::scrypt")
{
asynchronous(ctx, Some(opt), Some(txn), doc, name, args).await
stk.run(|stk| asynchronous(stk, ctx, Some(opt), Some(txn), doc, name, args)).await
} else {
synchronous(ctx, name, args)
}
@ -371,6 +373,7 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
/// Attempts to run any asynchronous function.
pub async fn asynchronous(
stk: &mut Stk,
ctx: &Context<'_>,
opt: Option<&Options>,
txn: Option<&Transaction>,
@ -413,15 +416,15 @@ pub async fn asynchronous(
"http::patch" => http::patch(ctx).await,
"http::delete" => http::delete(ctx).await,
//
"search::analyze" => search::analyze((ctx, txn, opt)).await,
"search::analyze" => search::analyze((stk,ctx, txn, opt)).await,
"search::score" => search::score((ctx, txn, doc)).await,
"search::highlight" => search::highlight((ctx,txn, doc)).await,
"search::offsets" => search::offsets((ctx, txn, doc)).await,
//
"sleep" => sleep::sleep(ctx).await,
//
"type::field" => r#type::field((ctx, opt, txn, doc)).await,
"type::fields" => r#type::fields((ctx, opt, txn, doc)).await,
"type::field" => r#type::field((stk,ctx, opt, txn, doc)).await,
"type::fields" => r#type::fields((stk,ctx, opt, txn, doc)).await,
)
}

View file

@ -6,6 +6,7 @@ use crate::idx::planner::executor::QueryExecutor;
use crate::sql::value::TryRem;
use crate::sql::value::{TryAdd, TryDiv, TryMul, TryNeg, TryPow, TrySub, Value};
use crate::sql::{Expression, Thing};
use reblessive::tree::Stk;
pub fn neg(a: Value) -> Result<Value, Error> {
a.try_neg()
@ -210,7 +211,9 @@ fn get_executor_option<'a>(
ExecutorOption::None
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn matches(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -222,12 +225,15 @@ pub(crate) async fn matches(
let res = match get_executor_option(ctx, doc, exp) {
ExecutorOption::PreMatch => true,
ExecutorOption::None => false,
ExecutorOption::Execute(exe, thg) => exe.matches(ctx, opt, txn, thg, exp, l, r).await?,
ExecutorOption::Execute(exe, thg) => {
exe.matches(stk, ctx, opt, txn, thg, exp, l, r).await?
}
};
Ok(res.into())
}
pub(crate) async fn knn(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -237,7 +243,7 @@ pub(crate) async fn knn(
match get_executor_option(ctx, doc, exp) {
ExecutorOption::PreMatch => Ok(Value::Bool(true)),
ExecutorOption::None => Ok(Value::Bool(false)),
ExecutorOption::Execute(exe, thg) => exe.knn(ctx, opt, txn, thg, doc, exp).await,
ExecutorOption::Execute(exe, thg) => exe.knn(stk, ctx, opt, txn, thg, doc, exp).await,
}
}

View file

@ -4,6 +4,7 @@ use crate::sql::Value;
use js::class::OwnedBorrow;
use js::prelude::Async;
use js::Result;
use reblessive::tree::Stk;
use super::query::{QueryContext, QUERY_DATA_PROP_NAME};
@ -71,8 +72,10 @@ fn run(js_ctx: js::Ctx<'_>, name: &str, args: Vec<Value>) -> Result<Value> {
async fn fut(js_ctx: js::Ctx<'_>, name: &str, args: Vec<Value>) -> Result<Value> {
let this = js_ctx.globals().get::<_, OwnedBorrow<QueryContext>>(QUERY_DATA_PROP_NAME)?;
// Process the called function
let res =
fnc::asynchronous(this.context, Some(this.opt), Some(this.txn), this.doc, name, args).await;
let res = Stk::enter_run(|stk| {
fnc::asynchronous(stk, this.context, Some(this.opt), Some(this.txn), this.doc, name, args)
})
.await;
// Convert any response error
res.map_err(|err| {
js::Exception::from_message(js_ctx, &err.to_string())

View file

@ -3,6 +3,7 @@ use js::class::OwnedBorrow;
use js::prelude::Coerced;
use js::Exception;
use js::{module::ModuleDef, Class, Ctx, Function, Module, Result, String as JsString, Value};
use reblessive::tree::Stk;
use self::query::{QueryContext, QUERY_DATA_PROP_NAME};
@ -16,10 +17,13 @@ pub struct Package;
async fn value(ctx: Ctx<'_>, value: Coerced<String>) -> Result<SurValue> {
let value = parse_value(&value.0).map_err(|e| Exception::throw_type(&ctx, &e.to_string()))?;
let this = ctx.globals().get::<_, OwnedBorrow<QueryContext>>(QUERY_DATA_PROP_NAME)?;
let value = value
.compute(this.context, this.opt, this.txn, this.doc)
.await
.map_err(|e| Exception::throw_message(&ctx, &e.to_string()))?;
let value = Stk::enter_run(|stk| async {
value
.compute(stk, this.context, this.opt, this.txn, this.doc)
.await
.map_err(|e| Exception::throw_message(&ctx, &e.to_string()))
})
.await?;
Ok(value)
}

View file

@ -3,6 +3,7 @@ use js::{
prelude::{Coerced, Opt},
Ctx, Exception, FromJs, Result, Value,
};
use reblessive::tree::Stk;
use crate::{
ctx::Context,
@ -77,10 +78,9 @@ pub async fn query<'js>(
.attach(context)
.map_err(|e| Exception::throw_message(&ctx, &e.to_string()))?;
let value = query
.query
.compute(&context, this.opt, this.txn, this.doc)
.await
.map_err(|e| Exception::throw_message(&ctx, &e.to_string()))?;
let value =
Stk::enter_run(|stk| query.query.compute(stk, &context, this.opt, this.txn, this.doc))
.await
.map_err(|e| Exception::throw_message(&ctx, &e.to_string()))?;
Result::Ok(value)
}

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::idx::ft::analyzer::Analyzer;
use crate::idx::planner::executor::QueryExecutor;
use crate::sql::{Thing, Value};
use reblessive::tree::Stk;
fn get_execution_context<'a>(
ctx: &'a Context<'_>,
@ -26,13 +27,13 @@ fn get_execution_context<'a>(
}
pub async fn analyze(
(ctx, txn, opt): (&Context<'_>, Option<&Transaction>, Option<&Options>),
(stk, ctx, txn, opt): (&mut Stk, &Context<'_>, Option<&Transaction>, Option<&Options>),
(az, val): (Value, Value),
) -> Result<Value, Error> {
if let (Some(txn), Some(opt), Value::Strand(az), Value::Strand(val)) = (txn, opt, az, val) {
let az: Analyzer =
txn.lock().await.get_db_analyzer(opt.ns(), opt.db(), az.as_str()).await?.into();
az.analyze(ctx, opt, txn, val.0).await
az.analyze(stk, ctx, opt, txn, val.0).await
} else {
Ok(Value::None)
}

View file

@ -9,6 +9,7 @@ use crate::sql::thing::Thing;
use crate::sql::value::Value;
use crate::sql::{Id, Range, Strand};
use crate::syn;
use reblessive::tree::Stk;
pub fn bool((val,): (Value,)) -> Result<Value, Error> {
val.convert_to_bool().map(Value::from)
@ -27,7 +28,8 @@ pub fn duration((val,): (Value,)) -> Result<Value, Error> {
}
pub async fn field(
(ctx, opt, txn, doc): (
(stk, ctx, opt, txn, doc): (
&mut Stk,
&Context<'_>,
Option<&Options>,
Option<&Transaction>,
@ -41,7 +43,7 @@ pub async fn field(
let idi = syn::idiom(&val)?;
// Return the Idiom or fetch the field
match opt.projections {
true => Ok(idi.compute(ctx, opt, txn, doc).await?),
true => Ok(idi.compute(stk, ctx, opt, txn, doc).await?),
false => Ok(idi.into()),
}
}
@ -50,7 +52,8 @@ pub async fn field(
}
pub async fn fields(
(ctx, opt, txn, doc): (
(stk, ctx, opt, txn, doc): (
&mut Stk,
&Context<'_>,
Option<&Options>,
Option<&Transaction>,
@ -66,7 +69,7 @@ pub async fn fields(
let idi = syn::idiom(&v)?;
// Return the Idiom or fetch the field
match opt.projections {
true => args.push(idi.compute(ctx, opt, txn, doc).await?),
true => args.push(idi.compute(stk, ctx, opt, txn, doc).await?),
false => args.push(idi.into()),
}
}

View file

@ -11,8 +11,8 @@ use crate::sql::statements::DefineAnalyzerStatement;
use crate::sql::tokenizer::Tokenizer as SqlTokenizer;
use crate::sql::Value;
use crate::sql::{Function, Strand};
use async_recursion::async_recursion;
use filter::Filter;
use reblessive::tree::Stk;
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
@ -61,13 +61,15 @@ impl TermsSet {
impl Analyzer {
pub(super) async fn extract_querying_terms(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
t: &Terms,
content: String,
) -> Result<(TermsList, TermsSet), Error> {
let tokens = self.generate_tokens(ctx, opt, txn, FilteringStage::Querying, content).await?;
let tokens =
self.generate_tokens(stk, ctx, opt, txn, FilteringStage::Querying, content).await?;
// We extract the term ids
let mut list = Vec::with_capacity(tokens.list().len());
let mut unique_tokens = HashSet::new();
@ -98,6 +100,7 @@ impl Analyzer {
pub(in crate::idx) async fn extract_indexing_terms(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -105,7 +108,7 @@ impl Analyzer {
content: Value,
) -> Result<TermsSet, Error> {
let mut tv = Vec::new();
self.analyze_value(ctx, opt, txn, content, FilteringStage::Indexing, &mut tv).await?;
self.analyze_value(stk, ctx, opt, txn, content, FilteringStage::Indexing, &mut tv).await?;
let mut set = HashSet::new();
let mut has_unknown_terms = false;
let mut tx = txn.lock().await;
@ -130,6 +133,7 @@ impl Analyzer {
/// It will create new term ids for non already existing terms.
pub(super) async fn extract_terms_with_frequencies(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -140,8 +144,16 @@ impl Analyzer {
// Let's first collect all the inputs, and collect the tokens.
// We need to store them because everything after is zero-copy
let mut inputs = vec![];
self.analyze_content(ctx, opt, txn, field_content, FilteringStage::Indexing, &mut inputs)
.await?;
self.analyze_content(
stk,
ctx,
opt,
txn,
field_content,
FilteringStage::Indexing,
&mut inputs,
)
.await?;
// We then collect every unique terms and count the frequency
let mut tf: HashMap<&str, TermFrequency> = HashMap::new();
for tks in &inputs {
@ -171,6 +183,7 @@ impl Analyzer {
/// It will create new term ids for non already existing terms.
pub(super) async fn extract_terms_with_frequencies_with_offsets(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -181,7 +194,8 @@ impl Analyzer {
// Let's first collect all the inputs, and collect the tokens.
// We need to store them because everything after is zero-copy
let mut inputs = Vec::with_capacity(content.len());
self.analyze_content(ctx, opt, txn, content, FilteringStage::Indexing, &mut inputs).await?;
self.analyze_content(stk, ctx, opt, txn, content, FilteringStage::Indexing, &mut inputs)
.await?;
// We then collect every unique terms and count the frequency and extract the offsets
let mut tfos: HashMap<&str, Vec<Offset>> = HashMap::new();
for (i, tks) in inputs.iter().enumerate() {
@ -210,10 +224,11 @@ impl Analyzer {
Ok((dl, tfid, osid))
}
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
/// Was marked recursive
#[allow(clippy::too_many_arguments)]
async fn analyze_content(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -222,15 +237,16 @@ impl Analyzer {
tks: &mut Vec<Tokens>,
) -> Result<(), Error> {
for v in content {
self.analyze_value(ctx, opt, txn, v, stage, tks).await?;
self.analyze_value(stk, ctx, opt, txn, v, stage, tks).await?;
}
Ok(())
}
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
/// Was marked recursive
#[allow(clippy::too_many_arguments)]
async fn analyze_value(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -239,21 +255,23 @@ impl Analyzer {
tks: &mut Vec<Tokens>,
) -> Result<(), Error> {
match val {
Value::Strand(s) => tks.push(self.generate_tokens(ctx, opt, txn, stage, s.0).await?),
Value::Strand(s) => {
tks.push(self.generate_tokens(stk, ctx, opt, txn, stage, s.0).await?)
}
Value::Number(n) => {
tks.push(self.generate_tokens(ctx, opt, txn, stage, n.to_string()).await?)
tks.push(self.generate_tokens(stk, ctx, opt, txn, stage, n.to_string()).await?)
}
Value::Bool(b) => {
tks.push(self.generate_tokens(ctx, opt, txn, stage, b.to_string()).await?)
tks.push(self.generate_tokens(stk, ctx, opt, txn, stage, b.to_string()).await?)
}
Value::Array(a) => {
for v in a.0 {
self.analyze_value(ctx, opt, txn, v, stage, tks).await?;
stk.run(|stk| self.analyze_value(stk, ctx, opt, txn, v, stage, tks)).await?;
}
}
Value::Object(o) => {
for (_, v) in o.0 {
self.analyze_value(ctx, opt, txn, v, stage, tks).await?;
stk.run(|stk| self.analyze_value(stk, ctx, opt, txn, v, stage, tks)).await?;
}
}
_ => {}
@ -263,6 +281,7 @@ impl Analyzer {
async fn generate_tokens(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -271,7 +290,7 @@ impl Analyzer {
) -> Result<Tokens, Error> {
if let Some(function_name) = self.function.clone() {
let fns = Function::Custom(function_name.clone(), vec![Value::Strand(Strand(input))]);
let val = fns.compute(ctx, opt, txn, None).await?;
let val = fns.compute(stk, ctx, opt, txn, None).await?;
if let Value::Strand(val) = val {
input = val.0;
} else {
@ -293,12 +312,13 @@ impl Analyzer {
/// Used for exposing the analyzer as the native function `search::analyze`
pub(crate) async fn analyze(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
input: String,
) -> Result<Value, Error> {
self.generate_tokens(ctx, opt, txn, FilteringStage::Indexing, input).await?.try_into()
self.generate_tokens(stk, ctx, opt, txn, FilteringStage::Indexing, input).await?.try_into()
}
}
@ -328,15 +348,24 @@ mod tests {
};
let a: Analyzer = az.into();
a.generate_tokens(
&Context::default(),
&Options::default(),
&txn,
FilteringStage::Indexing,
input.to_string(),
)
.await
.unwrap()
let mut stack = reblessive::TreeStack::new();
let ctx = Context::default();
let opts = Options::default();
stack
.enter(|stk| {
a.generate_tokens(
stk,
&ctx,
&opts,
&txn,
FilteringStage::Indexing,
input.to_string(),
)
})
.finish()
.await
.unwrap()
}
pub(super) async fn test_analyzer(def: &str, input: &str, expected: &[&str]) {

View file

@ -28,6 +28,7 @@ use crate::sql::index::SearchParams;
use crate::sql::scoring::Scoring;
use crate::sql::statements::DefineAnalyzerStatement;
use crate::sql::{Idiom, Object, Thing, Value};
use reblessive::tree::Stk;
use revision::revisioned;
use roaring::treemap::IntoIter;
use roaring::RoaringTreemap;
@ -231,6 +232,7 @@ impl FtIndex {
pub(crate) async fn index_document(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -248,13 +250,13 @@ impl FtIndex {
let (doc_length, terms_and_frequencies, offsets) = if self.highlighting {
let (dl, tf, ofs) = self
.analyzer
.extract_terms_with_frequencies_with_offsets(ctx, opt, txn, &mut t, content)
.extract_terms_with_frequencies_with_offsets(stk, ctx, opt, txn, &mut t, content)
.await?;
(dl, tf, Some(ofs))
} else {
let (dl, tf) = self
.analyzer
.extract_terms_with_frequencies(ctx, opt, txn, &mut t, content)
.extract_terms_with_frequencies(stk, ctx, opt, txn, &mut t, content)
.await?;
(dl, tf, None)
};
@ -336,13 +338,15 @@ impl FtIndex {
pub(super) async fn extract_querying_terms(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
query_string: String,
) -> Result<(TermsList, TermsSet), Error> {
let t = self.terms.read().await;
let res = self.analyzer.extract_querying_terms(ctx, opt, txn, &t, query_string).await?;
let res =
self.analyzer.extract_querying_terms(stk, ctx, opt, txn, &t, query_string).await?;
Ok(res)
}
@ -513,6 +517,7 @@ mod tests {
use crate::sql::{Array, Statement, Thing, Value};
use crate::syn;
use futures::lock::Mutex;
use reblessive::tree::Stk;
use std::collections::HashMap;
use std::sync::Arc;
use test_log::test;
@ -540,6 +545,7 @@ mod tests {
}
async fn search(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -547,7 +553,7 @@ mod tests {
qs: &str,
) -> (Option<HitsIterator>, BM25Scorer) {
let (term_list, _) =
fti.extract_querying_terms(ctx, opt, txn, qs.to_string()).await.unwrap();
fti.extract_querying_terms(stk, ctx, opt, txn, qs.to_string()).await.unwrap();
let mut tx = txn.lock().await;
let td = Arc::new(fti.get_terms_docs(&mut tx, &term_list).await.unwrap());
drop(tx);
@ -605,6 +611,7 @@ mod tests {
let Statement::Define(DefineStatement::Analyzer(az)) = q.0 .0.pop().unwrap() else {
panic!()
};
let mut stack = reblessive::TreeStack::new();
let btree_order = 5;
@ -612,84 +619,115 @@ mod tests {
let doc2: Thing = ("t", "doc2").into();
let doc3: Thing = ("t", "doc3").into();
{
// Add one document
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, false).await;
fti.index_document(&ctx, &opt, &txn, &doc1, vec![Value::from("hello the world")])
stack
.enter(|stk| async {
// Add one document
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, false).await;
fti.index_document(
stk,
&ctx,
&opt,
&txn,
&doc1,
vec![Value::from("hello the world")],
)
.await
.unwrap();
finish(&txn, fti).await;
}
finish(&txn, fti).await;
})
.finish()
.await;
{
// Add two documents
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, false).await;
fti.index_document(&ctx, &opt, &txn, &doc2, vec![Value::from("a yellow hello")])
stack
.enter(|stk| async {
// Add two documents
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, false).await;
fti.index_document(
stk,
&ctx,
&opt,
&txn,
&doc2,
vec![Value::from("a yellow hello")],
)
.await
.unwrap();
fti.index_document(&ctx, &opt, &txn, &doc3, vec![Value::from("foo bar")])
.await
.unwrap();
finish(&txn, fti).await;
}
fti.index_document(stk, &ctx, &opt, &txn, &doc3, vec![Value::from("foo bar")])
.await
.unwrap();
finish(&txn, fti).await;
})
.finish()
.await;
{
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, false).await;
// Check the statistics
let statistics = fti.statistics(&txn).await.unwrap();
assert_eq!(statistics.terms.keys_count, 7);
assert_eq!(statistics.postings.keys_count, 8);
assert_eq!(statistics.doc_ids.keys_count, 3);
assert_eq!(statistics.doc_lengths.keys_count, 3);
stack
.enter(|stk| async {
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, false).await;
// Check the statistics
let statistics = fti.statistics(&txn).await.unwrap();
assert_eq!(statistics.terms.keys_count, 7);
assert_eq!(statistics.postings.keys_count, 8);
assert_eq!(statistics.doc_ids.keys_count, 3);
assert_eq!(statistics.doc_lengths.keys_count, 3);
// Search & score
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "hello").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(-0.4859746)), (&doc2, Some(-0.4859746))])
// Search & score
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "hello").await;
check_hits(
&txn,
hits,
scr,
vec![(&doc1, Some(-0.4859746)), (&doc2, Some(-0.4859746))],
)
.await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "world").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.4859746))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "world").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.4859746))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "yellow").await;
check_hits(&txn, hits, scr, vec![(&doc2, Some(0.4859746))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "yellow").await;
check_hits(&txn, hits, scr, vec![(&doc2, Some(0.4859746))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "foo").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "foo").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "bar").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "bar").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
let (hits, _) = search(&ctx, &opt, &txn, &fti, "dummy").await;
assert!(hits.is_none());
}
let (hits, _) = search(stk, &ctx, &opt, &txn, &fti, "dummy").await;
assert!(hits.is_none());
})
.finish()
.await;
{
// Reindex one document
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, false).await;
fti.index_document(&ctx, &opt, &txn, &doc3, vec![Value::from("nobar foo")])
.await
.unwrap();
finish(&txn, fti).await;
stack
.enter(|stk| async {
// Reindex one document
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, false).await;
fti.index_document(stk, &ctx, &opt, &txn, &doc3, vec![Value::from("nobar foo")])
.await
.unwrap();
finish(&txn, fti).await;
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, false).await;
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, false).await;
// We can still find 'foo'
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "foo").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
// We can still find 'foo'
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "foo").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
// We can't anymore find 'bar'
let (hits, _) = search(&ctx, &opt, &txn, &fti, "bar").await;
assert!(hits.is_none());
// We can't anymore find 'bar'
let (hits, _) = search(stk, &ctx, &opt, &txn, &fti, "bar").await;
assert!(hits.is_none());
// We can now find 'nobar'
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "nobar").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
}
// We can now find 'nobar'
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "nobar").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.56902087))]).await;
})
.finish()
.await;
{
// Remove documents
@ -701,14 +739,17 @@ mod tests {
finish(&txn, fti).await;
}
{
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, false).await;
let (hits, _) = search(&ctx, &opt, &txn, &fti, "hello").await;
assert!(hits.is_none());
let (hits, _) = search(&ctx, &opt, &txn, &fti, "foo").await;
assert!(hits.is_none());
}
stack
.enter(|stk| async {
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, false).await;
let (hits, _) = search(stk, &ctx, &opt, &txn, &fti, "hello").await;
assert!(hits.is_none());
let (hits, _) = search(stk, &ctx, &opt, &txn, &fti, "foo").await;
assert!(hits.is_none());
})
.finish()
.await;
}
async fn test_ft_index_bm_25(hl: bool) {
@ -722,6 +763,7 @@ mod tests {
let Statement::Define(DefineStatement::Analyzer(az)) = q.0 .0.pop().unwrap() else {
panic!()
};
let mut stack = reblessive::TreeStack::new();
let doc1: Thing = ("t", "doc1").into();
let doc2: Thing = ("t", "doc2").into();
@ -729,106 +771,116 @@ mod tests {
let doc4: Thing = ("t", "doc4").into();
let btree_order = 5;
{
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, hl).await;
fti.index_document(
&ctx,
&opt,
&txn,
&doc1,
vec![Value::from("the quick brown fox jumped over the lazy dog")],
)
.await
.unwrap();
fti.index_document(
&ctx,
&opt,
&txn,
&doc2,
vec![Value::from("the fast fox jumped over the lazy dog")],
)
.await
.unwrap();
fti.index_document(
&ctx,
&opt,
&txn,
&doc3,
vec![Value::from("the dog sat there and did nothing")],
)
.await
.unwrap();
fti.index_document(
&ctx,
&opt,
&txn,
&doc4,
vec![Value::from("the other animals sat there watching")],
)
.await
.unwrap();
finish(&txn, fti).await;
}
{
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, hl).await;
let statistics = fti.statistics(&txn).await.unwrap();
assert_eq!(statistics.terms.keys_count, 17);
assert_eq!(statistics.postings.keys_count, 28);
assert_eq!(statistics.doc_ids.keys_count, 4);
assert_eq!(statistics.doc_lengths.keys_count, 4);
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "the").await;
check_hits(
&txn,
hits,
scr,
vec![
(&doc1, Some(-3.4388628)),
(&doc2, Some(-3.621457)),
(&doc3, Some(-2.258829)),
(&doc4, Some(-2.393017)),
],
)
stack
.enter(|stk| async {
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, btree_order, hl).await;
fti.index_document(
stk,
&ctx,
&opt,
&txn,
&doc1,
vec![Value::from("the quick brown fox jumped over the lazy dog")],
)
.await
.unwrap();
fti.index_document(
stk,
&ctx,
&opt,
&txn,
&doc2,
vec![Value::from("the fast fox jumped over the lazy dog")],
)
.await
.unwrap();
fti.index_document(
stk,
&ctx,
&opt,
&txn,
&doc3,
vec![Value::from("the dog sat there and did nothing")],
)
.await
.unwrap();
fti.index_document(
stk,
&ctx,
&opt,
&txn,
&doc4,
vec![Value::from("the other animals sat there watching")],
)
.await
.unwrap();
finish(&txn, fti).await;
})
.finish()
.await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "dog").await;
check_hits(
&txn,
hits,
scr,
vec![
(&doc1, Some(-0.7832165)),
(&doc2, Some(-0.8248031)),
(&doc3, Some(-0.87105393)),
],
)
stack
.enter(|stk| async {
let (ctx, opt, txn, fti) =
tx_fti(&ds, TransactionType::Read, &az, btree_order, hl).await;
let statistics = fti.statistics(&txn).await.unwrap();
assert_eq!(statistics.terms.keys_count, 17);
assert_eq!(statistics.postings.keys_count, 28);
assert_eq!(statistics.doc_ids.keys_count, 4);
assert_eq!(statistics.doc_lengths.keys_count, 4);
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "the").await;
check_hits(
&txn,
hits,
scr,
vec![
(&doc1, Some(-3.4388628)),
(&doc2, Some(-3.621457)),
(&doc3, Some(-2.258829)),
(&doc4, Some(-2.393017)),
],
)
.await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "dog").await;
check_hits(
&txn,
hits,
scr,
vec![
(&doc1, Some(-0.7832165)),
(&doc2, Some(-0.8248031)),
(&doc3, Some(-0.87105393)),
],
)
.await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "fox").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "over").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "lazy").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "jumped").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "nothing").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.87105393))]).await;
let (hits, scr) = search(stk, &ctx, &opt, &txn, &fti, "animals").await;
check_hits(&txn, hits, scr, vec![(&doc4, Some(0.92279965))]).await;
let (hits, _) = search(stk, &ctx, &opt, &txn, &fti, "dummy").await;
assert!(hits.is_none());
})
.finish()
.await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "fox").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "over").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "lazy").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "jumped").await;
check_hits(&txn, hits, scr, vec![(&doc1, Some(0.0)), (&doc2, Some(0.0))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "nothing").await;
check_hits(&txn, hits, scr, vec![(&doc3, Some(0.87105393))]).await;
let (hits, scr) = search(&ctx, &opt, &txn, &fti, "animals").await;
check_hits(&txn, hits, scr, vec![(&doc4, Some(0.92279965))]).await;
let (hits, _) = search(&ctx, &opt, &txn, &fti, "dummy").await;
assert!(hits.is_none());
}
}
}
@ -846,10 +898,16 @@ mod tests {
let btree_order = 5;
let doc1: Thing = ("t", "doc1").into();
let content1 = Value::from(Array::from(vec!["Enter a search term", "Welcome", "Docusaurus blogging features are powered by the blog plugin.", "Simply add Markdown files (or folders) to the blog directory.", "blog", "Regular blog authors can be added to authors.yml.", "authors.yml", "The blog post date can be extracted from filenames, such as:", "2019-05-30-welcome.md", "2019-05-30-welcome/index.md", "A blog post folder can be convenient to co-locate blog post images:", "The blog supports tags as well!", "And if you don't want a blog: just delete this directory, and use blog: false in your Docusaurus config.", "blog: false", "MDX Blog Post", "Blog posts support Docusaurus Markdown features, such as MDX.", "Use the power of React to create interactive blog posts.", "Long Blog Post", "This is the summary of a very long blog post,", "Use a <!-- truncate --> comment to limit blog post size in the list view.", "<!--", "truncate", "-->", "First Blog Post", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet"]));
let mut stack = reblessive::TreeStack::new();
let start = std::time::Instant::now();
while start.elapsed().as_secs() < 3 {
remove_insert_task(ds.as_ref(), &az, btree_order, &doc1, &content1).await;
stack
.enter(|stk| {
remove_insert_task(stk, ds.as_ref(), &az, btree_order, &doc1, &content1)
})
.finish()
.await;
}
}
#[test(tokio::test)]
@ -866,6 +924,7 @@ mod tests {
}
async fn remove_insert_task(
stk: &mut Stk,
ds: &Datastore,
az: &DefineAnalyzerStatement,
btree_order: u32,
@ -875,13 +934,14 @@ mod tests {
let (ctx, opt, txn, mut fti) =
tx_fti(ds, TransactionType::Write, az, btree_order, false).await;
fti.remove_document(&txn, rid).await.unwrap();
fti.index_document(&ctx, &opt, &txn, rid, vec![content.clone()]).await.unwrap();
fti.index_document(stk, &ctx, &opt, &txn, rid, vec![content.clone()]).await.unwrap();
finish(&txn, fti).await;
}
#[test(tokio::test)]
async fn remove_insert_sequence() {
let ds = Datastore::new("memory").await.unwrap();
let mut stack = reblessive::TreeStack::new();
let mut q = syn::parse("DEFINE ANALYZER test TOKENIZERS blank;").unwrap();
let Statement::Define(DefineStatement::Analyzer(az)) = q.0 .0.pop().unwrap() else {
panic!()
@ -894,7 +954,13 @@ mod tests {
{
let (ctx, opt, txn, mut fti) =
tx_fti(&ds, TransactionType::Write, &az, 5, false).await;
fti.index_document(&ctx, &opt, &txn, &doc, vec![content.clone()]).await.unwrap();
stack
.enter(|stk| {
fti.index_document(stk, &ctx, &opt, &txn, &doc, vec![content.clone()])
})
.finish()
.await
.unwrap();
finish(&txn, fti).await;
}

View file

@ -25,6 +25,7 @@ use crate::kvs::{Key, TransactionType};
use crate::sql::index::{Distance, Index};
use crate::sql::statements::DefineIndexStatement;
use crate::sql::{Array, Expression, Idiom, Number, Object, Table, Thing, Value};
use reblessive::tree::Stk;
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::Arc;
use tokio::sync::RwLock;
@ -77,6 +78,7 @@ impl IteratorEntry {
}
impl InnerQueryExecutor {
pub(super) async fn new(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -101,7 +103,7 @@ impl InnerQueryExecutor {
let mut ft_entry = None;
if let Some(ft) = ft_map.get(&ix_ref) {
if ft_entry.is_none() {
ft_entry = FtEntry::new(ctx, opt, txn, ft, io).await?;
ft_entry = FtEntry::new(stk, ctx, opt, txn, ft, io).await?;
}
} else {
let ikb = IndexKeyBase::new(opt, idx_def);
@ -116,7 +118,7 @@ impl InnerQueryExecutor {
)
.await?;
if ft_entry.is_none() {
ft_entry = FtEntry::new(ctx, opt, txn, &ft, io).await?;
ft_entry = FtEntry::new(stk, ctx, opt, txn, &ft, io).await?;
}
ft_map.insert(ix_ref, ft);
}
@ -182,8 +184,10 @@ impl InnerQueryExecutor {
}
impl QueryExecutor {
#[allow(clippy::too_many_arguments)]
pub(crate) async fn knn(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -204,7 +208,7 @@ impl QueryExecutor {
Ok(Value::Bool(false))
} else {
if let Some((p, id, val, dist)) = self.0.knn_entries.get(exp) {
let v: Vec<Number> = id.compute(ctx, opt, txn, doc).await?.try_into()?;
let v: Vec<Number> = id.compute(stk, ctx, opt, txn, doc).await?.try_into()?;
let dist = dist.compute(&v, val.as_ref())?;
p.add(dist, thg).await;
}
@ -423,6 +427,7 @@ impl QueryExecutor {
#[allow(clippy::too_many_arguments)]
pub(crate) async fn matches(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -437,7 +442,7 @@ impl QueryExecutor {
return self.matches_with_doc_id(txn, thg, ft).await;
}
}
return self.matches_with_value(ctx, opt, txn, ft, l, r).await;
return self.matches_with_value(stk, ctx, opt, txn, ft, l, r).await;
}
// If no previous case were successful, we end up with a user error
@ -475,8 +480,10 @@ impl QueryExecutor {
Ok(false)
}
#[allow(clippy::too_many_arguments)]
async fn matches_with_value(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -496,7 +503,7 @@ impl QueryExecutor {
};
let terms = ft.0.terms.read().await;
// Extract the terms set from the record
let t = ft.0.analyzer.extract_indexing_terms(ctx, opt, txn, &terms, v).await?;
let t = ft.0.analyzer.extract_indexing_terms(stk, ctx, opt, txn, &terms, v).await?;
Ok(ft.0.query_terms_set.is_subset(&t))
}
@ -602,6 +609,7 @@ struct Inner {
impl FtEntry {
async fn new(
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -610,7 +618,7 @@ impl FtEntry {
) -> Result<Option<Self>, Error> {
if let Matches(qs, _) = io.op() {
let (terms_list, terms_set) =
ft.extract_querying_terms(ctx, opt, txn, qs.to_owned()).await?;
ft.extract_querying_terms(stk, ctx, opt, txn, qs.to_owned()).await?;
let mut tx = txn.lock().await;
let terms_docs = Arc::new(ft.get_terms_docs(&mut tx, &terms_list).await?);
Ok(Some(Self(Arc::new(Inner {

View file

@ -14,6 +14,7 @@ use crate::idx::planner::plan::{Plan, PlanBuilder};
use crate::idx::planner::tree::Tree;
use crate::sql::with::With;
use crate::sql::{Cond, Expression, Table, Thing};
use reblessive::tree::Stk;
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc;
@ -46,6 +47,7 @@ impl<'a> QueryPlanner<'a> {
pub(crate) async fn add_iterables(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
txn: &Transaction,
t: Table,
@ -53,10 +55,11 @@ impl<'a> QueryPlanner<'a> {
) -> Result<(), Error> {
let mut is_table_iterator = false;
let mut is_knn = false;
match Tree::build(ctx, self.opt, txn, &t, self.cond, self.with).await? {
match Tree::build(stk, ctx, self.opt, txn, &t, self.cond, self.with).await? {
Some(tree) => {
is_knn = is_knn || !tree.knn_expressions.is_empty();
let mut exe = InnerQueryExecutor::new(
stk,
ctx,
self.opt,
txn,

View file

@ -9,7 +9,7 @@ use crate::sql::statements::{DefineFieldStatement, DefineIndexStatement};
use crate::sql::{
Array, Cond, Expression, Idiom, Kind, Number, Operator, Part, Subquery, Table, Value, With,
};
use async_recursion::async_recursion;
use reblessive::tree::Stk;
use std::collections::HashMap;
use std::sync::Arc;
@ -24,6 +24,7 @@ impl Tree {
/// Traverse all the conditions and extract every expression
/// that can be resolved by an index.
pub(super) async fn build<'a>(
stk: &mut Stk,
ctx: &'a Context<'_>,
opt: &'a Options,
txn: &'a Transaction,
@ -33,7 +34,7 @@ impl Tree {
) -> Result<Option<Self>, Error> {
let mut b = TreeBuilder::new(ctx, opt, txn, table, with);
if let Some(cond) = cond {
let root = b.eval_value(0, &cond.0).await?;
let root = b.eval_value(stk, 0, &cond.0).await?;
Ok(Some(Self {
root,
index_map: b.index_map,
@ -115,12 +116,16 @@ impl<'a> TreeBuilder<'a> {
Ok(())
}
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
async fn eval_value(&mut self, group: GroupRef, v: &Value) -> Result<Node, Error> {
/// Was marked recursive
async fn eval_value(
&mut self,
stk: &mut Stk,
group: GroupRef,
v: &Value,
) -> Result<Node, Error> {
match v {
Value::Expression(e) => self.eval_expression(group, e).await,
Value::Idiom(i) => self.eval_idiom(group, i).await,
Value::Expression(e) => self.eval_expression(stk, group, e).await,
Value::Idiom(i) => self.eval_idiom(stk, group, i).await,
Value::Strand(_)
| Value::Number(_)
| Value::Bool(_)
@ -130,25 +135,30 @@ impl<'a> TreeBuilder<'a> {
| Value::Constant(_)
| Value::Geometry(_)
| Value::Datetime(_) => Ok(Node::Computed(Arc::new(v.to_owned()))),
Value::Array(a) => self.eval_array(a).await,
Value::Subquery(s) => self.eval_subquery(s).await,
Value::Array(a) => self.eval_array(stk, a).await,
Value::Subquery(s) => self.eval_subquery(stk, s).await,
Value::Param(p) => {
let v = p.compute(self.ctx, self.opt, self.txn, None).await?;
self.eval_value(group, &v).await
let v = stk.run(|stk| p.compute(stk, self.ctx, self.opt, self.txn, None)).await?;
stk.run(|stk| self.eval_value(stk, group, &v)).await
}
_ => Ok(Node::Unsupported(format!("Unsupported value: {}", v))),
}
}
async fn eval_array(&mut self, a: &Array) -> Result<Node, Error> {
async fn eval_array(&mut self, stk: &mut Stk, a: &Array) -> Result<Node, Error> {
let mut values = Vec::with_capacity(a.len());
for v in &a.0 {
values.push(v.compute(self.ctx, self.opt, self.txn, None).await?);
values.push(stk.run(|stk| v.compute(stk, self.ctx, self.opt, self.txn, None)).await?);
}
Ok(Node::Computed(Arc::new(Value::Array(Array::from(values)))))
}
async fn eval_idiom(&mut self, group: GroupRef, i: &Idiom) -> Result<Node, Error> {
async fn eval_idiom(
&mut self,
stk: &mut Stk,
group: GroupRef,
i: &Idiom,
) -> Result<Node, Error> {
// Check if the idiom has already been resolved
if let Some(node) = self.resolved_idioms.get(i).cloned() {
return Ok(node);
@ -157,8 +167,8 @@ impl<'a> TreeBuilder<'a> {
// Compute the idiom value if it is a param
if let Some(Part::Start(x)) = i.0.first() {
if x.is_param() {
let v = i.compute(self.ctx, self.opt, self.txn, None).await?;
return self.eval_value(group, &v).await;
let v = stk.run(|stk| i.compute(stk, self.ctx, self.opt, self.txn, None)).await?;
return stk.run(|stk| self.eval_value(stk, group, &v)).await;
}
}
@ -258,7 +268,12 @@ impl<'a> TreeBuilder<'a> {
Ok(None)
}
async fn eval_expression(&mut self, group: GroupRef, e: &Expression) -> Result<Node, Error> {
async fn eval_expression(
&mut self,
stk: &mut Stk,
group: GroupRef,
e: &Expression,
) -> Result<Node, Error> {
match e {
Expression::Unary {
..
@ -273,8 +288,8 @@ impl<'a> TreeBuilder<'a> {
return Ok(re.into());
}
let exp = Arc::new(e.clone());
let left = Arc::new(self.eval_value(group, l).await?);
let right = Arc::new(self.eval_value(group, r).await?);
let left = Arc::new(stk.run(|stk| self.eval_value(stk, group, l)).await?);
let right = Arc::new(stk.run(|stk| self.eval_value(stk, group, r)).await?);
let mut io = None;
if let Some((id, local_irs, remote_irs)) = left.is_indexed_field() {
io = self.lookup_index_options(
@ -464,10 +479,10 @@ impl<'a> TreeBuilder<'a> {
}
}
async fn eval_subquery(&mut self, s: &Subquery) -> Result<Node, Error> {
async fn eval_subquery(&mut self, stk: &mut Stk, s: &Subquery) -> Result<Node, Error> {
self.group_sequence += 1;
match s {
Subquery::Value(v) => self.eval_value(self.group_sequence, v).await,
Subquery::Value(v) => stk.run(|stk| self.eval_value(stk, self.group_sequence, v)).await,
_ => Ok(Node::Unsupported(format!("Unsupported subquery: {}", s))),
}
}

View file

@ -7,7 +7,7 @@ use std::fmt::{Debug, Display, Formatter};
use std::io::Cursor;
use std::sync::Arc;
use async_recursion::async_recursion;
use reblessive::tree::Stk;
use revision::revisioned;
use roaring::RoaringTreemap;
use serde::{Deserialize, Serialize};
@ -72,6 +72,7 @@ impl MTreeIndex {
}
pub(crate) async fn index_document(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
rid: &Thing,
content: Vec<Value>,
@ -84,7 +85,7 @@ impl MTreeIndex {
for v in content {
// Extract the vector
let vector = self.extract_vector(v)?.into();
mtree.insert(tx, &mut self.store, vector, doc_id).await?;
mtree.insert(stk, tx, &mut self.store, vector, doc_id).await?;
}
Ok(())
}
@ -153,6 +154,7 @@ impl MTreeIndex {
pub(crate) async fn remove_document(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
rid: &Thing,
content: Vec<Value>,
@ -162,7 +164,7 @@ impl MTreeIndex {
for v in content {
// Extract the vector
let vector = self.extract_vector(v)?.into();
mtree.delete(tx, &mut self.store, vector, doc_id).await?;
mtree.delete(stk, tx, &mut self.store, vector, doc_id).await?;
}
}
Ok(())
@ -388,6 +390,7 @@ impl MTree {
pub async fn insert(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
store: &mut MTreeStore,
obj: SharedVector,
@ -403,7 +406,7 @@ impl MTree {
let node = store.get_node_mut(tx, root_id).await?;
// Otherwise, we insert the object with possibly mutating the tree
if let InsertionResult::PromotedEntries(o1, p1, o2, p2) =
self.insert_at_node(tx, store, node, &None, obj, id).await?
self.insert_at_node(stk, tx, store, node, &None, obj, id).await?
{
self.create_new_internal_root(store, o1, p1, o2, p2).await?;
}
@ -493,10 +496,11 @@ impl MTree {
Ok(false)
}
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(? Send))]
/// Was marked recursive
#[allow(clippy::too_many_arguments)]
async fn insert_at_node(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
store: &mut MTreeStore,
node: MStoredNode,
@ -514,6 +518,7 @@ impl MTree {
// Else
MTreeNode::Internal(n) => {
self.insert_node_internal(
stk,
tx,
store,
node.id,
@ -531,6 +536,7 @@ impl MTree {
#[allow(clippy::too_many_arguments)]
async fn insert_node_internal(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
store: &mut MTreeStore,
node_id: NodeId,
@ -544,8 +550,13 @@ impl MTree {
let (best_entry_obj, mut best_entry) = self.find_closest(&node, &object)?;
let best_node = store.get_node_mut(tx, best_entry.node).await?;
// Insert(Oi, child(ObestSubstree), ObestSubtree);
match self
.insert_at_node(tx, store, best_node, &Some(best_entry_obj.clone()), object, doc_id)
let best_entry_obj_op = Some(best_entry_obj.clone());
let this = &mut *self;
match stk
.run(|stk| async {
this.insert_at_node(stk, tx, store, best_node, &best_entry_obj_op, object, doc_id)
.await
})
.await?
{
// If (entry returned)
@ -838,6 +849,7 @@ impl MTree {
async fn delete(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
store: &mut MTreeStore,
object: SharedVector,
@ -847,7 +859,7 @@ impl MTree {
if let Some(root_id) = self.state.root {
let root_node = store.get_node_mut(tx, root_id).await?;
if let DeletionResult::Underflown(sn, n_updated) = self
.delete_at_node(tx, store, root_node, &None, object, doc_id, &mut deleted)
.delete_at_node(stk, tx, store, root_node, &None, object, doc_id, &mut deleted)
.await?
{
match &sn.n {
@ -879,11 +891,11 @@ impl MTree {
Ok(deleted)
}
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(? Send))]
/// Was marked recursive
#[allow(clippy::too_many_arguments)]
async fn delete_at_node(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
store: &mut MTreeStore,
node: MStoredNode,
@ -913,6 +925,7 @@ impl MTree {
// Else
MTreeNode::Internal(n) => {
self.delete_node_internal(
stk,
tx,
store,
node.id,
@ -931,6 +944,7 @@ impl MTree {
#[allow(clippy::too_many_arguments)]
async fn delete_node_internal(
&mut self,
stk: &mut Stk,
tx: &mut Transaction,
store: &mut MTreeStore,
node_id: NodeId,
@ -964,8 +978,20 @@ impl MTree {
let on_node = store.get_node_mut(tx, on_entry.node).await?;
#[cfg(debug_assertions)]
let d_id = on_node.id;
match self
.delete_at_node(tx, store, on_node, &Some(on_obj.clone()), od.clone(), id, deleted)
let on_obj_op = Some(on_obj.clone());
match stk
.run(|stk| {
self.delete_at_node(
stk,
tx,
store,
on_node,
&on_obj_op,
od.clone(),
id,
deleted,
)
})
.await?
{
DeletionResult::NotFound => {
@ -1614,6 +1640,7 @@ impl VersionedSerdeState for MState {}
mod tests {
use rand::prelude::StdRng;
use rand::{Rng, SeedableRng};
use reblessive::tree::Stk;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use crate::err::Error;
@ -1683,6 +1710,8 @@ mod tests {
async fn test_mtree_insertions() -> Result<(), Error> {
const CACHE_SIZE: usize = 20;
let mut stack = reblessive::tree::TreeStack::new();
let mut t = MTree::new(MState::new(3), Distance::Euclidean);
let ds = Datastore::new("memory").await?;
@ -1698,7 +1727,7 @@ mod tests {
// Insert single element
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec1.clone(), 1).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec1.clone(), 1)).finish().await?;
assert_eq!(t.state.root, Some(0));
check_leaf_write(&mut tx, &mut st, 0, |m| {
assert_eq!(m.len(), 1);
@ -1721,7 +1750,7 @@ mod tests {
let vec2 = new_vec(2, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec2.clone(), 2).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec2.clone(), 2)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
// vec1 knn
@ -1752,7 +1781,7 @@ mod tests {
// insert new doc to existing vector
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec2.clone(), 3).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec2.clone(), 3)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
// vec2 knn
@ -1776,7 +1805,7 @@ mod tests {
let vec3 = new_vec(3, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec3.clone(), 3).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec3.clone(), 3)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
// vec3 knn
@ -1801,7 +1830,7 @@ mod tests {
let vec4 = new_vec(4, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec4.clone(), 4).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec4.clone(), 4)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
// vec4 knn
@ -1837,7 +1866,7 @@ mod tests {
let vec6 = new_vec(6, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec6.clone(), 6).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec6.clone(), 6)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
// vec6 knn
@ -1876,7 +1905,7 @@ mod tests {
let vec8 = new_vec(8, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec8.clone(), 8).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec8.clone(), 8)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
{
@ -1915,7 +1944,7 @@ mod tests {
let vec9 = new_vec(9, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec9.clone(), 9).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec9.clone(), 9)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
{
@ -1955,7 +1984,7 @@ mod tests {
let vec10 = new_vec(10, VectorType::F64, 1);
{
let (mut st, mut tx) = new_operation(&ds, &t, TransactionType::Write, CACHE_SIZE).await;
t.insert(&mut tx, &mut st, vec10.clone(), 10).await?;
stack.enter(|stk| t.insert(stk, &mut tx, &mut st, vec10.clone(), 10)).finish().await?;
finish_operation(&mut t, tx, st, true).await?;
}
{
@ -2038,6 +2067,7 @@ mod tests {
}
async fn insert_collection_one_by_one(
stk: &mut Stk,
ds: &Datastore,
t: &mut MTree,
collection: &TestCollection,
@ -2049,7 +2079,7 @@ mod tests {
{
let (mut st, mut tx) =
new_operation(ds, t, TransactionType::Write, cache_size).await;
t.insert(&mut tx, &mut st, obj.clone(), *doc_id).await?;
t.insert(stk, &mut tx, &mut st, obj.clone(), *doc_id).await?;
finish_operation(t, tx, st, true).await?;
map.insert(*doc_id, obj.clone());
}
@ -2065,6 +2095,7 @@ mod tests {
}
async fn insert_collection_batch(
stk: &mut Stk,
ds: &Datastore,
t: &mut MTree,
collection: &TestCollection,
@ -2074,7 +2105,7 @@ mod tests {
{
let (mut st, mut tx) = new_operation(ds, t, TransactionType::Write, cache_size).await;
for (doc_id, obj) in collection.as_ref() {
t.insert(&mut tx, &mut st, obj.clone(), *doc_id).await?;
t.insert(stk, &mut tx, &mut st, obj.clone(), *doc_id).await?;
map.insert(*doc_id, obj.clone());
}
finish_operation(t, tx, st, true).await?;
@ -2087,6 +2118,7 @@ mod tests {
}
async fn delete_collection(
stk: &mut Stk,
ds: &Datastore,
t: &mut MTree,
collection: &TestCollection,
@ -2098,7 +2130,7 @@ mod tests {
debug!("### Remove {} {:?}", doc_id, obj);
let (mut st, mut tx) =
new_operation(ds, t, TransactionType::Write, cache_size).await;
let deleted = t.delete(&mut tx, &mut st, obj.clone(), *doc_id).await?;
let deleted = t.delete(stk, &mut tx, &mut st, obj.clone(), *doc_id).await?;
finish_operation(t, tx, st, true).await?;
deleted
};
@ -2195,6 +2227,7 @@ mod tests {
}
async fn test_mtree_collection(
stk: &mut Stk,
capacities: &[u16],
vector_type: VectorType,
collection: TestCollection,
@ -2220,9 +2253,9 @@ mod tests {
let mut t = MTree::new(MState::new(*capacity), distance.clone());
let map = if collection.as_ref().len() < 1000 {
insert_collection_one_by_one(&ds, &mut t, &collection, cache_size).await?
insert_collection_one_by_one(stk, &ds, &mut t, &collection, cache_size).await?
} else {
insert_collection_batch(&ds, &mut t, &collection, cache_size).await?
insert_collection_batch(stk, &ds, &mut t, &collection, cache_size).await?
};
if check_find {
find_collection(&ds, &mut t, &collection, cache_size).await?;
@ -2231,7 +2264,7 @@ mod tests {
check_full_knn(&ds, &mut t, &map, cache_size).await?;
}
if check_delete {
delete_collection(&ds, &mut t, &collection, cache_size).await?;
delete_collection(stk, &ds, &mut t, &collection, cache_size).await?;
}
}
}
@ -2288,170 +2321,245 @@ mod tests {
#[test(tokio::test)]
#[ignore]
async fn test_mtree_unique_xs() -> Result<(), Error> {
for vt in
[VectorType::F64, VectorType::F32, VectorType::I64, VectorType::I32, VectorType::I16]
{
for i in 0..30 {
test_mtree_collection(
&[3, 40],
vt,
TestCollection::new_unique(i, vt, 2),
true,
true,
true,
100,
)
.await?;
}
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [
VectorType::F64,
VectorType::F32,
VectorType::I64,
VectorType::I32,
VectorType::I16,
] {
for i in 0..30 {
test_mtree_collection(
stk,
&[3, 40],
vt,
TestCollection::new_unique(i, vt, 2),
true,
true,
true,
100,
)
.await?;
}
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
#[ignore]
async fn test_mtree_unique_xs_full_cache() -> Result<(), Error> {
for vt in
[VectorType::F64, VectorType::F32, VectorType::I64, VectorType::I32, VectorType::I16]
{
for i in 0..30 {
test_mtree_collection(
&[3, 40],
vt,
TestCollection::new_unique(i, vt, 2),
true,
true,
true,
0,
)
.await?;
}
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [
VectorType::F64,
VectorType::F32,
VectorType::I64,
VectorType::I32,
VectorType::I16,
] {
for i in 0..30 {
test_mtree_collection(
stk,
&[3, 40],
vt,
TestCollection::new_unique(i, vt, 2),
true,
true,
true,
0,
)
.await?;
}
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
async fn test_mtree_unique_small() -> Result<(), Error> {
for vt in [VectorType::F64, VectorType::I64] {
test_mtree_collection(
&[10, 20],
vt,
TestCollection::new_unique(150, vt, 3),
true,
true,
false,
0,
)
.await?;
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [VectorType::F64, VectorType::I64] {
test_mtree_collection(
stk,
&[10, 20],
vt,
TestCollection::new_unique(150, vt, 3),
true,
true,
false,
0,
)
.await?;
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
async fn test_mtree_unique_normal() -> Result<(), Error> {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
&[40],
vt,
TestCollection::new_unique(1000, vt, 10),
false,
true,
false,
100,
)
.await?;
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
stk,
&[40],
vt,
TestCollection::new_unique(1000, vt, 10),
false,
true,
false,
100,
)
.await?;
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
async fn test_mtree_unique_normal_full_cache() -> Result<(), Error> {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
&[40],
vt,
TestCollection::new_unique(1000, vt, 10),
false,
true,
false,
0,
)
.await?;
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
stk,
&[40],
vt,
TestCollection::new_unique(1000, vt, 10),
false,
true,
false,
0,
)
.await?;
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
async fn test_mtree_unique_normal_root_cache() -> Result<(), Error> {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
&[40],
vt,
TestCollection::new_unique(1000, vt, 10),
false,
true,
false,
1,
)
.await?;
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
stk,
&[40],
vt,
TestCollection::new_unique(1000, vt, 10),
false,
true,
false,
1,
)
.await?;
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
#[ignore]
async fn test_mtree_random_xs() -> Result<(), Error> {
for vt in
[VectorType::F64, VectorType::F32, VectorType::I64, VectorType::I32, VectorType::I16]
{
for i in 0..30 {
// 10, 40
test_mtree_collection(
&[3, 40],
vt,
TestCollection::new_random(i, vt, 1),
true,
true,
true,
0,
)
.await?;
}
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [
VectorType::F64,
VectorType::F32,
VectorType::I64,
VectorType::I32,
VectorType::I16,
] {
for i in 0..30 {
// 10, 40
test_mtree_collection(
stk,
&[3, 40],
vt,
TestCollection::new_random(i, vt, 1),
true,
true,
true,
0,
)
.await?;
}
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
async fn test_mtree_random_small() -> Result<(), Error> {
for vt in [VectorType::F64, VectorType::I64] {
test_mtree_collection(
&[10, 20],
vt,
TestCollection::new_random(150, vt, 3),
true,
true,
false,
0,
)
.await?;
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [VectorType::F64, VectorType::I64] {
test_mtree_collection(
stk,
&[10, 20],
vt,
TestCollection::new_random(150, vt, 3),
true,
true,
false,
0,
)
.await?;
}
Ok(())
})
.finish()
.await
}
#[test(tokio::test)]
async fn test_mtree_random_normal() -> Result<(), Error> {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
&[40],
vt,
TestCollection::new_random(1000, vt, 10),
false,
true,
false,
0,
)
.await?;
}
Ok(())
let mut stack = reblessive::tree::TreeStack::new();
stack
.enter(|stk| async {
for vt in [VectorType::F32, VectorType::I32] {
test_mtree_collection(
stk,
&[40],
vt,
TestCollection::new_random(1000, vt, 10),
false,
true,
false,
0,
)
.await?;
}
Ok(())
})
.finish()
.await
}
fn check_leaf_vec(

View file

@ -26,6 +26,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
use channel::{Receiver, Sender};
use futures::{lock::Mutex, Future};
use reblessive::{tree::Stk, TreeStack};
use tokio::sync::RwLock;
use tracing::instrument;
use tracing::trace;
@ -33,6 +34,7 @@ use tracing::trace;
#[cfg(target_arch = "wasm32")]
use wasmtimer::std::{SystemTime, UNIX_EPOCH};
use super::tx::Transaction;
use crate::cf;
use crate::cf::{ChangeSet, TableMutation};
use crate::ctx::Context;
@ -63,8 +65,6 @@ use crate::sql::{self, statements::DefineUserStatement, Base, Query, Uuid, Value
use crate::syn;
use crate::vs::{conv, Oracle, Versionstamp};
use super::tx::Transaction;
// If there are an infinite number of heartbeats, then we want to go batch-by-batch spread over several checks
const HEARTBEAT_BATCH_SIZE: u32 = 1000;
const LQ_CHANNEL_SIZE: usize = 100;
@ -934,7 +934,11 @@ impl Datastore {
}
/// Poll change feeds for live query notifications
pub async fn process_lq_notifications(&self, opt: &Options) -> Result<(), Error> {
pub async fn process_lq_notifications(
&self,
stk: &mut Stk,
opt: &Options,
) -> Result<(), Error> {
// Runtime feature gate, as it is not production-ready
if !FFLAGS.change_feed_live_queries.enabled() {
return Ok(());
@ -994,8 +998,14 @@ impl Datastore {
.join("\n")
);
for change_set in change_sets {
self.process_change_set_for_notifications(tx.clone(), opt, change_set, &lq_pairs)
.await?;
self.process_change_set_for_notifications(
stk,
tx.clone(),
opt,
change_set,
&lq_pairs,
)
.await?;
}
}
trace!("Finished process lq successfully");
@ -1004,6 +1014,7 @@ impl Datastore {
async fn process_change_set_for_notifications(
&self,
stk: &mut Stk,
tx: Arc<Mutex<Transaction>>,
opt: &Options,
change_set: ChangeSet,
@ -1043,6 +1054,7 @@ impl Datastore {
// for the current state we only forward
let (sender, receiver) = channel::bounded(notification_capacity);
doc.check_lqs_and_send_notifications(
stk,
opt,
&Statement::Live(&lq_value.stm),
&tx,
@ -1541,6 +1553,9 @@ impl Datastore {
if sess.expired() {
return Err(Error::ExpiredSession);
}
let mut stack = TreeStack::new();
// Check if anonymous actors can compute values when auth is enabled
// TODO(sgirones): Check this as part of the authorisation layer
if self.auth_enabled && !self.capabilities.allows_guest_access() {
@ -1579,7 +1594,7 @@ impl Datastore {
// Start a new transaction
let txn = self.transaction(val.writeable().into(), Optimistic).await?.enclose();
// Compute the value
let res = val.compute(&ctx, &opt, &txn, None).await;
let res = stack.enter(|stk| val.compute(stk, &ctx, &opt, &txn, None)).finish().await;
// Store any data
match (res.is_ok(), val.writeable()) {
// If the compute was successful, then commit if writeable
@ -1624,6 +1639,8 @@ impl Datastore {
if sess.expired() {
return Err(Error::ExpiredSession);
}
let mut stack = TreeStack::new();
// Create a new query options
let opt = Options::default()
.with_id(self.id.0)
@ -1652,7 +1669,7 @@ impl Datastore {
// Start a new transaction
let txn = self.transaction(val.writeable().into(), Optimistic).await?.enclose();
// Compute the value
let res = val.compute(&ctx, &opt, &txn, None).await;
let res = stack.enter(|stk| val.compute(stk, &ctx, &opt, &txn, None)).finish().await;
// Store any data
match (res.is_ok(), val.writeable()) {
// If the compute was successful, then commit if writeable
@ -1781,3 +1798,56 @@ async fn find_required_cfs_to_catch_up(
tx.cancel().await?;
Ok(tracked_cfs_updates)
}
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
pub async fn very_deep_query() -> Result<(), Error> {
use crate::kvs::Datastore;
use crate::sql::{Expression, Future, Number, Operator, Value};
use reblessive::{Stack, Stk};
// build query manually to bypass query limits.
let mut stack = Stack::new();
async fn build_query(stk: &mut Stk, depth: usize) -> Value {
if depth == 0 {
Value::Expression(Box::new(Expression::Binary {
l: Value::Number(Number::Int(1)),
o: Operator::Add,
r: Value::Number(Number::Int(1)),
}))
} else {
let q = stk.run(|stk| build_query(stk, depth - 1)).await;
Value::Future(Box::new(Future::from(q)))
}
}
let val = stack.enter(|stk| build_query(stk, 1000)).finish();
let dbs = Datastore::new("memory").await.unwrap().with_capabilities(Capabilities::all());
let opt = Options::default()
.with_id(dbs.id.0)
.with_ns(Some("test".into()))
.with_db(Some("test".into()))
.with_live(false)
.with_strict(false)
.with_auth_enabled(false)
.with_max_computation_depth(u32::MAX)
.with_futures(true);
// Create a default context
let mut ctx = Context::default();
// Set context capabilities
ctx.add_capabilities(dbs.capabilities.clone());
// Start a new transaction
let txn = dbs.transaction(val.writeable().into(), Optimistic).await?.enclose();
// Compute the value
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.compute(stk, &ctx, &opt, &txn, None)).finish().await.unwrap();
assert_eq!(res, Value::Number(Number::Int(2)));
Ok(())
}
}

View file

@ -1,4 +1,3 @@
use crate::{lazy_env_parse, lazy_env_parse_or_else};
use once_cell::sync::Lazy;
pub static ROCKSDB_THREAD_COUNT: Lazy<i32> =

View file

@ -106,7 +106,10 @@ async fn expired_nodes_get_live_queries_archived() {
.with_id(old_node);
let opt = Options::new_with_sender(&opt, sender);
let tx = Arc::new(Mutex::new(test.db.transaction(Write, Optimistic).await.unwrap()));
let res = lq.compute(&ctx, &opt, &tx, None).await.unwrap();
let res = {
let mut stack = reblessive::tree::TreeStack::new();
stack.enter(|stk| lq.compute(stk, &ctx, &opt, &tx, None)).finish().await.unwrap()
};
match res {
Value::Uuid(_) => {}
_ => {
@ -138,6 +141,7 @@ async fn expired_nodes_get_live_queries_archived() {
#[serial]
async fn single_live_queries_are_garbage_collected() {
// Test parameters
let mut stack = reblessive::tree::TreeStack::new();
let ctx = context::Context::background();
let node_id = Uuid::parse_str("b1a08614-a826-4581-938d-bea17f00e253").unwrap();
let time = Timestamp {
@ -178,8 +182,9 @@ async fn single_live_queries_are_garbage_collected() {
session: Some(Value::None),
auth: Some(Auth::for_root(Role::Owner)),
};
live_st
.compute(&ctx, &options, &tx, None)
stack
.enter(|stk| live_st.compute(stk, &ctx, &options, &tx, None))
.finish()
.await
.map_err(|e| format!("Error computing live statement: {:?} {:?}", live_st, e))
.unwrap();
@ -195,8 +200,9 @@ async fn single_live_queries_are_garbage_collected() {
session: Some(Value::None),
auth: Some(Auth::for_root(Role::Owner)),
};
live_st
.compute(&ctx, &options, &tx, None)
stack
.enter(|stk| live_st.compute(stk, &ctx, &options, &tx, None))
.finish()
.await
.map_err(|e| format!("Error computing live statement: {:?} {:?}", live_st, e))
.unwrap();
@ -222,6 +228,7 @@ async fn single_live_queries_are_garbage_collected() {
#[serial]
async fn bootstrap_does_not_error_on_missing_live_queries() {
// Test parameters
let mut stack = reblessive::tree::TreeStack::new();
let ctx = context::Context::background();
let old_node_id = Uuid::parse_str("5f644f02-7c1a-4f8b-babd-bd9e92c1836a").unwrap();
let t1 = Timestamp {
@ -265,8 +272,9 @@ async fn bootstrap_does_not_error_on_missing_live_queries() {
session: Some(Value::None),
auth: Some(Auth::for_root(Role::Owner)),
};
live_st
.compute(&ctx, &options, &tx, None)
stack
.enter(|stk| live_st.compute(stk, &ctx, &options, &tx, None))
.finish()
.await
.map_err(|e| format!("Error computing live statement: {:?} {:?}", live_st, e))
.unwrap();

View file

@ -6,6 +6,7 @@ use crate::sql::{
fmt::{pretty_indent, Fmt, Pretty},
Number, Operation, Value,
};
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
@ -133,6 +134,7 @@ impl Array {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -140,7 +142,7 @@ impl Array {
) -> Result<Value, Error> {
let mut x = Self::with_capacity(self.len());
for v in self.iter() {
match v.compute(ctx, opt, txn, doc).await {
match v.compute(stk, ctx, opt, txn, doc).await {
Ok(v) => x.push(v),
Err(e) => return Err(e),
};

View file

@ -10,6 +10,7 @@ use crate::sql::statements::{
RemoveStatement, SelectStatement, SetStatement, ThrowStatement, UpdateStatement,
};
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@ -46,6 +47,7 @@ impl Block {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -57,12 +59,12 @@ impl Block {
for (i, v) in self.iter().enumerate() {
match v {
Entry::Set(v) => {
let val = v.compute(&ctx, opt, txn, doc).await?;
let val = v.compute(stk, &ctx, opt, txn, doc).await?;
ctx.add_value(v.name.to_owned(), val);
}
Entry::Throw(v) => {
// Always errors immediately
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Break(v) => {
// Always errors immediately
@ -73,46 +75,46 @@ impl Block {
v.compute(&ctx, opt, txn, doc).await?;
}
Entry::Foreach(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Ifelse(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Select(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Create(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Update(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Delete(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Relate(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Insert(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Define(v) => {
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
Entry::Remove(v) => {
v.compute(&ctx, opt, txn, doc).await?;
}
Entry::Output(v) => {
// Return the RETURN value
return v.compute(&ctx, opt, txn, doc).await;
return v.compute(stk, &ctx, opt, txn, doc).await;
}
Entry::Value(v) => {
if i == self.len() - 1 {
// If the last entry then return the value
return v.compute(&ctx, opt, txn, doc).await;
return v.compute(stk, &ctx, opt, txn, doc).await;
} else {
// Otherwise just process the value
v.compute(&ctx, opt, txn, doc).await?;
v.compute(stk, &ctx, opt, txn, doc).await?;
}
}
}

View file

@ -3,7 +3,7 @@ use crate::dbs::{Options, Transaction};
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{Idiom, Kind, Value};
use async_recursion::async_recursion;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@ -33,17 +33,17 @@ impl Cast {
}
impl Cast {
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
/// Was marked recursively
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&'async_recursion CursorDoc<'_>>,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Compute the value to be cast and convert it
self.1.compute(ctx, opt, txn, doc).await?.convert_to(&self.0)
stk.run(|stk| self.1.compute(stk, ctx, opt, txn, doc)).await?.convert_to(&self.0)
}
}

View file

@ -5,6 +5,7 @@ use crate::sql::fmt::Fmt;
use crate::sql::idiom::Idiom;
use crate::sql::operator::Operator;
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
@ -36,6 +37,7 @@ impl Data {
/// Fetch the 'id' field if one has been specified
pub(crate) async fn rid(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -43,20 +45,20 @@ impl Data {
match self {
Self::MergeExpression(v) => {
// This MERGE expression has an 'id' field
Ok(v.compute(ctx, opt, txn, None).await?.rid().some())
Ok(v.compute(stk, ctx, opt, txn, None).await?.rid().some())
}
Self::ReplaceExpression(v) => {
// This REPLACE expression has an 'id' field
Ok(v.compute(ctx, opt, txn, None).await?.rid().some())
Ok(v.compute(stk, ctx, opt, txn, None).await?.rid().some())
}
Self::ContentExpression(v) => {
// This CONTENT expression has an 'id' field
Ok(v.compute(ctx, opt, txn, None).await?.rid().some())
Ok(v.compute(stk, ctx, opt, txn, None).await?.rid().some())
}
Self::SetExpression(v) => match v.iter().find(|f| f.0.is_id()) {
Some((_, _, v)) => {
// This SET expression has an 'id' field
Ok(v.compute(ctx, opt, txn, None).await?.some())
Ok(v.compute(stk, ctx, opt, txn, None).await?.some())
}
// This SET expression had no 'id' field
_ => Ok(None),

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::fnc;
use crate::sql::operator::Operator;
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -98,6 +99,7 @@ impl Expression {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -108,7 +110,7 @@ impl Expression {
o,
v,
} => {
let operand = v.compute(ctx, opt, txn, doc).await?;
let operand = v.compute(stk, ctx, opt, txn, doc).await?;
return match o {
Operator::Neg => fnc::operate::neg(operand),
// TODO: Check if it is a number?
@ -124,7 +126,7 @@ impl Expression {
} => (l, o, r),
};
let l = l.compute(ctx, opt, txn, doc).await?;
let l = l.compute(stk, ctx, opt, txn, doc).await?;
match o {
Operator::Or => {
if l.is_truthy() {
@ -148,7 +150,7 @@ impl Expression {
}
_ => {} // Continue
}
let r = r.compute(ctx, opt, txn, doc).await?;
let r = r.compute(stk, ctx, opt, txn, doc).await?;
match o {
Operator::Or => fnc::operate::or(l, r),
Operator::And => fnc::operate::and(l, r),
@ -185,8 +187,10 @@ impl Expression {
Operator::NoneInside => fnc::operate::inside_none(&l, &r),
Operator::Outside => fnc::operate::outside(&l, &r),
Operator::Intersects => fnc::operate::intersects(&l, &r),
Operator::Matches(_) => fnc::operate::matches(ctx, opt, txn, doc, self, l, r).await,
Operator::Knn(_, _) => fnc::operate::knn(ctx, opt, txn, doc, self).await,
Operator::Matches(_) => {
fnc::operate::matches(stk, ctx, opt, txn, doc, self, l, r).await
}
Operator::Knn(_, _) => fnc::operate::knn(stk, ctx, opt, txn, doc, self).await,
_ => unreachable!(),
}
}

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::sql::statements::info::InfoStructure;
use crate::sql::{fmt::Fmt, Idiom, Part, Value};
use crate::syn;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
@ -75,6 +76,7 @@ impl Fields {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -82,15 +84,16 @@ impl Fields {
group: bool,
) -> Result<Value, Error> {
if let Some(doc) = doc {
self.compute_value(ctx, opt, txn, doc, group).await
self.compute_value(stk, ctx, opt, txn, doc, group).await
} else {
let doc = (&Value::None).into();
self.compute_value(ctx, opt, txn, &doc, group).await
self.compute_value(stk, ctx, opt, txn, &doc, group).await
}
}
async fn compute_value(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -101,7 +104,7 @@ impl Fields {
let opt = &opt.new_with_futures(true);
// Process the desired output
let mut out = match self.is_all() {
true => doc.doc.compute(ctx, opt, txn, Some(doc)).await?,
true => doc.doc.compute(stk, ctx, opt, txn, Some(doc)).await?,
false => Value::base(),
};
for v in self.other() {
@ -120,13 +123,13 @@ impl Fields {
Value::Function(f) if group && f.is_aggregate() => {
let x = match f.args().len() {
// If no function arguments, then compute the result
0 => f.compute(ctx, opt, txn, Some(doc)).await?,
0 => f.compute(stk, ctx, opt, txn, Some(doc)).await?,
// If arguments, then pass the first value through
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?,
_ => f.args()[0].compute(stk, ctx, opt, txn, Some(doc)).await?,
};
// Check if this is a single VALUE field expression
match self.single().is_some() {
false => out.set(ctx, opt, txn, name.as_ref(), x).await?,
false => out.set(stk, ctx, opt, txn, name.as_ref(), x).await?,
true => out = x,
}
}
@ -143,9 +146,9 @@ impl Fields {
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, Some(doc), v)
.get(stk, ctx, opt, txn, Some(doc), v)
.await?
.compute(ctx, opt, txn, Some(doc))
.compute(stk, ctx, opt, txn, Some(doc))
.await?
.flatten();
// Add the result to the temporary store
@ -157,13 +160,13 @@ impl Fields {
// This is an alias expression part
Some(a) => {
if let Some(i) = alias {
out.set(ctx, opt, txn, i, x.clone()).await?;
out.set(stk, ctx, opt, txn, i, x.clone()).await?;
}
out.set(ctx, opt, txn, a, x).await?;
out.set(stk, ctx, opt, txn, a, x).await?;
}
// This is the end of the expression
None => {
out.set(ctx, opt, txn, alias.as_ref().unwrap_or(v), x)
out.set(stk, ctx, opt, txn, alias.as_ref().unwrap_or(v), x)
.await?
}
}
@ -172,14 +175,14 @@ impl Fields {
// This expression is a variable fields expression
Value::Function(f) if f.name() == Some("type::fields") => {
// Process the function using variable field projections
let expr = expr.compute(ctx, opt, txn, Some(doc)).await?;
let expr = expr.compute(stk, ctx, opt, txn, Some(doc)).await?;
// Check if this is a single VALUE field expression
match self.single().is_some() {
false => {
// Get the first argument which is guaranteed to exist
let args = match f.args().first().unwrap() {
Value::Param(v) => {
v.compute(ctx, opt, txn, Some(doc)).await?
v.compute(stk, ctx, opt, txn, Some(doc)).await?
}
v => v.to_owned(),
};
@ -192,7 +195,7 @@ impl Fields {
// This value is always a string, so we can convert it
let name = syn::idiom(&name.to_raw_string())?;
// Check if this is a single VALUE field expression
out.set(ctx, opt, txn, name.as_ref(), expr).await?
out.set(stk, ctx, opt, txn, name.as_ref(), expr).await?
}
}
true => out = expr,
@ -201,14 +204,14 @@ impl Fields {
// This expression is a variable field expression
Value::Function(f) if f.name() == Some("type::field") => {
// Process the function using variable field projections
let expr = expr.compute(ctx, opt, txn, Some(doc)).await?;
let expr = expr.compute(stk, ctx, opt, txn, Some(doc)).await?;
// Check if this is a single VALUE field expression
match self.single().is_some() {
false => {
// Get the first argument which is guaranteed to exist
let name = match f.args().first().unwrap() {
Value::Param(v) => {
v.compute(ctx, opt, txn, Some(doc)).await?
v.compute(stk, ctx, opt, txn, Some(doc)).await?
}
v => v.to_owned(),
};
@ -220,17 +223,17 @@ impl Fields {
Cow::Owned(syn::idiom(&name.to_raw_string())?)
};
// Add the projected field to the output document
out.set(ctx, opt, txn, name.as_ref(), expr).await?
out.set(stk, ctx, opt, txn, name.as_ref(), expr).await?
}
true => out = expr,
}
}
// This expression is a normal field expression
_ => {
let expr = expr.compute(ctx, opt, txn, Some(doc)).await?;
let expr = expr.compute(stk, ctx, opt, txn, Some(doc)).await?;
// Check if this is a single VALUE field expression
match self.single().is_some() {
false => out.set(ctx, opt, txn, name.as_ref(), expr).await?,
false => out.set(stk, ctx, opt, txn, name.as_ref(), expr).await?,
true => out = expr,
}
}

View file

@ -9,8 +9,8 @@ use crate::sql::idiom::Idiom;
use crate::sql::script::Script;
use crate::sql::value::Value;
use crate::sql::Permission;
use async_recursion::async_recursion;
use futures::future::try_join_all;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@ -177,14 +177,15 @@ impl Function {
impl Function {
/// Process this type returning a computed simple Value
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
///
/// Was marked recursive
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&'async_recursion CursorDoc<'_>>,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Ensure futures are run
let opt = &opt.new_with_futures(true);
@ -194,9 +195,15 @@ impl Function {
// Check this function is allowed
ctx.check_allowed_function(s)?;
// Compute the function arguments
let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?;
let a = stk
.scope(|scope| {
try_join_all(
x.iter().map(|v| scope.run(|stk| v.compute(stk, ctx, opt, txn, doc))),
)
})
.await?;
// Run the normal function
fnc::run(ctx, opt, txn, doc, s, a).await
fnc::run(stk, ctx, opt, txn, doc, s, a).await
}
Self::Custom(s, x) => {
// Check that a database is set to prevent a panic
@ -225,7 +232,8 @@ impl Function {
// Disable permissions
let opt = &opt.new_with_perms(false);
// Process the PERMISSION clause
if !e.compute(ctx, opt, txn, doc).await?.is_truthy() {
if !stk.run(|stk| e.compute(stk, ctx, opt, txn, doc)).await?.is_truthy()
{
return Err(Error::FunctionPermissions {
name: s.to_owned(),
});
@ -254,7 +262,13 @@ impl Function {
});
}
// Compute the function arguments
let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?;
let a = stk
.scope(|scope| {
try_join_all(
x.iter().map(|v| scope.run(|stk| v.compute(stk, ctx, opt, txn, doc))),
)
})
.await?;
// Duplicate context
let mut ctx = Context::new(ctx);
// Process the function arguments
@ -262,7 +276,7 @@ impl Function {
ctx.add_value(name.to_raw(), val.coerce_to(kind)?);
}
// Run the custom function
val.block.compute(&ctx, opt, txn, doc).await
stk.run(|stk| val.block.compute(stk, &ctx, opt, txn, doc)).await
}
#[allow(unused_variables)]
Self::Script(s, x) => {
@ -271,7 +285,14 @@ impl Function {
// Check if scripting is allowed
ctx.check_allowed_scripting()?;
// Compute the function arguments
let a = try_join_all(x.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?;
let a = stk
.scope(|scope| {
try_join_all(
x.iter()
.map(|v| scope.run(|stk| v.compute(stk, ctx, opt, txn, doc))),
)
})
.await?;
// Run the script function
fnc::script::run(ctx, opt, txn, doc, s, a).await
}

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::block::Block;
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -27,6 +28,7 @@ impl Future {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -34,7 +36,7 @@ impl Future {
) -> Result<Value, Error> {
// Process the future if enabled
match opt.futures {
true => self.0.compute(ctx, opt, txn, doc).await?.ok(),
true => stk.run(|stk| self.0.compute(stk, ctx, opt, txn, doc)).await?.ok(),
false => Ok(self.clone().into()),
}
}

View file

@ -5,6 +5,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{escape::escape_rid, Array, Number, Object, Strand, Thing, Uuid, Value};
use nanoid::nanoid;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@ -182,6 +183,7 @@ impl Id {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -190,11 +192,11 @@ impl Id {
match self {
Id::Number(v) => Ok(Id::Number(*v)),
Id::String(v) => Ok(Id::String(v.clone())),
Id::Array(v) => match v.compute(ctx, opt, txn, doc).await? {
Id::Array(v) => match v.compute(stk, ctx, opt, txn, doc).await? {
Value::Array(v) => Ok(Id::Array(v)),
_ => unreachable!(),
},
Id::Object(v) => match v.compute(ctx, opt, txn, doc).await? {
Id::Object(v) => match v.compute(stk, ctx, opt, txn, doc).await? {
Value::Object(v) => Ok(Id::Object(v)),
_ => unreachable!(),
},

View file

@ -10,6 +10,7 @@ use crate::sql::{
Part, Value,
};
use md5::{Digest, Md5};
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
@ -156,6 +157,7 @@ impl Idiom {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -164,18 +166,22 @@ impl Idiom {
match self.first() {
// The starting part is a value
Some(Part::Start(v)) => {
v.compute(ctx, opt, txn, doc)
v.compute(stk, ctx, opt, txn, doc)
.await?
.get(ctx, opt, txn, doc, self.as_ref().next())
.get(stk, ctx, opt, txn, doc, self.as_ref().next())
.await?
.compute(ctx, opt, txn, doc)
.compute(stk, ctx, opt, txn, doc)
.await
}
// Otherwise use the current document
_ => match doc {
// There is a current document
Some(v) => {
v.doc.get(ctx, opt, txn, doc, self).await?.compute(ctx, opt, txn, doc).await
v.doc
.get(stk, ctx, opt, txn, doc, self)
.await?
.compute(stk, ctx, opt, txn, doc)
.await
}
// There isn't any document
None => Ok(Value::None),

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::number::Number;
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -17,12 +18,13 @@ pub struct Limit(pub Value);
impl Limit {
pub(crate) async fn process(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<usize, Error> {
match self.0.compute(ctx, opt, txn, doc).await {
match self.0.compute(stk, ctx, opt, txn, doc).await {
// This is a valid limiting number
Ok(Value::Number(Number::Int(v))) if v >= 0 => Ok(v as usize),
// An invalid value was specified

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::value::Value;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -53,6 +54,7 @@ impl Model {
#[cfg(feature = "ml")]
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -93,7 +95,7 @@ impl Model {
// Disable permissions
let opt = &opt.new_with_perms(false);
// Process the PERMISSION clause
if !e.compute(ctx, opt, txn, doc).await?.is_truthy() {
if !stk.run(|stk| e.compute(stk, ctx, opt, txn, doc)).await?.is_truthy() {
return Err(Error::FunctionPermissions {
name: self.name.to_owned(),
});
@ -102,8 +104,13 @@ impl Model {
}
}
// Compute the function arguments
let mut args =
try_join_all(self.args.iter().map(|v| v.compute(ctx, opt, txn, doc))).await?;
let mut args = stk
.scope(|stk| {
try_join_all(
self.args.iter().map(|v| stk.run(|stk| v.compute(stk, ctx, opt, txn, doc))),
)
})
.await?;
// Check the minimum argument length
if args.len() != 1 {
return Err(Error::InvalidArguments {
@ -214,6 +221,7 @@ impl Model {
#[cfg(not(feature = "ml"))]
pub(crate) async fn compute(
&self,
_stk: &mut Stk,
_ctx: &Context<'_>,
_opt: &Options,
_txn: &Transaction,

View file

@ -7,6 +7,7 @@ use crate::sql::{
fmt::{is_pretty, pretty_indent, Fmt, Pretty},
Operation, Thing, Value,
};
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@ -216,6 +217,7 @@ impl Object {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -223,7 +225,7 @@ impl Object {
) -> Result<Value, Error> {
let mut x = BTreeMap::new();
for (k, v) in self.iter() {
match v.compute(ctx, opt, txn, doc).await {
match v.compute(stk, ctx, opt, txn, doc).await {
Ok(v) => x.insert(k.clone(), v),
Err(e) => return Err(e),
};

View file

@ -6,6 +6,7 @@ use crate::{
iam::Action,
sql::{ident::Ident, value::Value, Permission},
};
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::{fmt, ops::Deref, str};
@ -48,6 +49,7 @@ impl Param {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -58,14 +60,14 @@ impl Param {
// This is a special param
"this" | "self" => match doc {
// The base document exists
Some(v) => v.doc.compute(ctx, opt, txn, doc).await,
Some(v) => v.doc.compute(stk, ctx, opt, txn, doc).await,
// The base document does not exist
None => Ok(Value::None),
},
// This is a normal param
v => match ctx.value(v) {
// The param has been set locally
Some(v) => v.compute(ctx, opt, txn, doc).await,
Some(v) => v.compute(stk, ctx, opt, txn, doc).await,
// The param has not been set locally
None => {
// Check that a database is set to prevent a panic
@ -93,7 +95,7 @@ impl Param {
// Disable permissions
let opt = &opt.new_with_perms(false);
// Process the PERMISSION clause
if !e.compute(ctx, opt, txn, doc).await?.is_truthy() {
if !e.compute(stk, ctx, opt, txn, doc).await?.is_truthy() {
return Err(Error::ParamPermissions {
name: v.to_owned(),
});
@ -102,7 +104,7 @@ impl Param {
}
}
// Return the computed value
val.value.compute(ctx, opt, txn, doc).await
val.value.compute(stk, ctx, opt, txn, doc).await
}
// The param has not been set globally
Err(_) => Ok(Value::None),

View file

@ -11,6 +11,7 @@ use crate::sql::Part;
use crate::sql::Thing;
use crate::sql::{strand::no_nul_bytes, Id, Value};
use crate::syn;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@ -158,6 +159,7 @@ impl Range {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -166,13 +168,13 @@ impl Range {
Ok(Value::Range(Box::new(Range {
tb: self.tb.clone(),
beg: match &self.beg {
Bound::Included(id) => Bound::Included(id.compute(ctx, opt, txn, doc).await?),
Bound::Excluded(id) => Bound::Excluded(id.compute(ctx, opt, txn, doc).await?),
Bound::Included(id) => Bound::Included(id.compute(stk, ctx, opt, txn, doc).await?),
Bound::Excluded(id) => Bound::Excluded(id.compute(stk, ctx, opt, txn, doc).await?),
Bound::Unbounded => Bound::Unbounded,
},
end: match &self.end {
Bound::Included(id) => Bound::Included(id.compute(ctx, opt, txn, doc).await?),
Bound::Excluded(id) => Bound::Excluded(id.compute(ctx, opt, txn, doc).await?),
Bound::Included(id) => Bound::Included(id.compute(stk, ctx, opt, txn, doc).await?),
Bound::Excluded(id) => Bound::Excluded(id.compute(stk, ctx, opt, txn, doc).await?),
Bound::Unbounded => Bound::Unbounded,
},
})))

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::number::Number;
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -17,12 +18,13 @@ pub struct Start(pub Value);
impl Start {
pub(crate) async fn process(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<usize, Error> {
match self.0.compute(ctx, opt, txn, doc).await {
match self.0.compute(stk, ctx, opt, txn, doc).await {
// This is a valid starting number
Ok(Value::Number(Number::Int(v))) if v >= 0 => Ok(v as usize),
// An invalid value was specified

View file

@ -14,6 +14,7 @@ use crate::sql::{
value::Value,
};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::{
@ -132,6 +133,7 @@ impl Statement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -141,29 +143,29 @@ impl Statement {
Self::Analyze(v) => v.compute(ctx, opt, txn, doc).await,
Self::Break(v) => v.compute(ctx, opt, txn, doc).await,
Self::Continue(v) => v.compute(ctx, opt, txn, doc).await,
Self::Create(v) => v.compute(ctx, opt, txn, doc).await,
Self::Delete(v) => v.compute(ctx, opt, txn, doc).await,
Self::Define(v) => v.compute(ctx, opt, txn, doc).await,
Self::Foreach(v) => v.compute(ctx, opt, txn, doc).await,
Self::Ifelse(v) => v.compute(ctx, opt, txn, doc).await,
Self::Create(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Delete(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Define(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Foreach(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Ifelse(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Info(v) => v.compute(ctx, opt, txn, doc).await,
Self::Insert(v) => v.compute(ctx, opt, txn, doc).await,
Self::Kill(v) => v.compute(ctx, opt, txn, doc).await,
Self::Live(v) => v.compute(ctx, opt, txn, doc).await,
Self::Output(v) => v.compute(ctx, opt, txn, doc).await,
Self::Relate(v) => v.compute(ctx, opt, txn, doc).await,
Self::Insert(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Kill(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Live(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Output(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Relate(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Remove(v) => v.compute(ctx, opt, txn, doc).await,
Self::Select(v) => v.compute(ctx, opt, txn, doc).await,
Self::Set(v) => v.compute(ctx, opt, txn, doc).await,
Self::Select(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Set(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Show(v) => v.compute(ctx, opt, txn, doc).await,
Self::Sleep(v) => v.compute(ctx, opt, txn, doc).await,
Self::Throw(v) => v.compute(ctx, opt, txn, doc).await,
Self::Update(v) => v.compute(ctx, opt, txn, doc).await,
Self::Throw(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Update(v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Value(v) => {
// Ensure futures are processed
let opt = &opt.new_with_futures(true);
// Process the output value
v.compute(ctx, opt, txn, doc).await
v.compute(stk, ctx, opt, txn, doc).await
}
_ => unreachable!(),
}

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{Data, Output, Timeout, Value, Values};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -30,6 +31,7 @@ impl CreateStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -45,8 +47,8 @@ impl CreateStatement {
let opt = &opt.new_with_futures(false);
// Loop over the create targets
for w in self.what.0.iter() {
let v = w.compute(ctx, opt, txn, doc).await?;
i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e {
let v = w.compute(stk, ctx, opt, txn, doc).await?;
i.prepare(stk, ctx, opt, txn, &stm, v).await.map_err(|e| match e {
Error::InvalidStatementTarget {
value: v,
} => Error::CreateStatement {
@ -56,7 +58,7 @@ impl CreateStatement {
})?;
}
// Output the results
match i.output(ctx, opt, txn, &stm).await? {
match i.output(stk, ctx, opt, txn, &stm).await? {
// This is a single record result
Value::Array(mut a) if self.only => match a.len() {
// There was exactly one result

View file

@ -8,6 +8,7 @@ use crate::sql::{
statements::UpdateStatement, Base, Ident, Idioms, Index, Object, Strand, Value, Values,
};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
@ -31,6 +32,7 @@ impl DefineIndexStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -79,7 +81,7 @@ impl DefineIndexStatement {
what: Values(vec![Value::Table(self.what.clone().into())]),
..UpdateStatement::default()
};
stm.compute(ctx, opt, txn, doc).await?;
stm.compute(stk, ctx, opt, txn, doc).await?;
// Ok all good
Ok(Value::None)
}

View file

@ -33,6 +33,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::value::Value;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
@ -65,6 +66,7 @@ impl DefineStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -76,11 +78,11 @@ impl DefineStatement {
Self::Function(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Token(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Scope(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Param(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Table(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Param(ref v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Table(ref v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Event(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Field(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Index(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Index(ref v) => v.compute(stk, ctx, opt, txn, doc).await,
Self::Analyzer(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::User(ref v) => v.compute(ctx, opt, txn, doc).await,
Self::Model(ref v) => v.compute(ctx, opt, txn, doc).await,

View file

@ -7,6 +7,7 @@ use crate::sql::fmt::{is_pretty, pretty_indent};
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Base, Ident, Object, Permission, Strand, Value};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Write};
@ -28,6 +29,7 @@ impl DefineParamStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -53,7 +55,7 @@ impl DefineParamStatement {
key,
DefineParamStatement {
// Compute the param
value: self.value.compute(ctx, opt, txn, doc).await?,
value: self.value.compute(stk, ctx, opt, txn, doc).await?,
// Don't persist the "IF NOT EXISTS" clause to schema
if_not_exists: false,
..self.clone()

View file

@ -14,6 +14,7 @@ use std::sync::Arc;
use crate::sql::statements::info::InfoStructure;
use crate::sql::{Idiom, Kind, Part, Table, TableType};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Write};
@ -42,6 +43,7 @@ pub struct DefineTableStatement {
impl DefineTableStatement {
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -131,7 +133,7 @@ impl DefineTableStatement {
what: Values(vec![Value::Table(v.clone())]),
..UpdateStatement::default()
};
stm.compute(ctx, opt, txn, doc).await?;
stm.compute(stk, ctx, opt, txn, doc).await?;
}
} else if dt.changefeed.is_some() {
run.record_table_change(opt.ns(), opt.db(), self.name.0.as_str(), &dt);

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{Cond, Output, Timeout, Value, Values};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -30,6 +31,7 @@ impl DeleteStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -45,8 +47,8 @@ impl DeleteStatement {
let opt = &opt.new_with_futures(false).with_projections(false);
// Loop over the delete targets
for w in self.what.0.iter() {
let v = w.compute(ctx, opt, txn, doc).await?;
i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e {
let v = w.compute(stk, ctx, opt, txn, doc).await?;
i.prepare(stk, ctx, opt, txn, &stm, v).await.map_err(|e| match e {
Error::InvalidStatementTarget {
value: v,
} => Error::DeleteStatement {
@ -56,7 +58,7 @@ impl DeleteStatement {
})?;
}
// Output the results
match i.output(ctx, opt, txn, &stm).await? {
match i.output(stk, ctx, opt, txn, &stm).await? {
// This is a single record result
Value::Array(mut a) if self.only => match a.len() {
// There was exactly one result

View file

@ -3,8 +3,8 @@ use crate::dbs::{Options, Transaction};
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{block::Entry, Block, Param, Value};
use async_recursion::async_recursion;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
@ -25,17 +25,18 @@ impl ForeachStatement {
self.range.writeable() || self.block.writeable()
}
/// Process this type returning a computed simple Value
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
///
/// Was marked recursive
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&'async_recursion CursorDoc<'_>>,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Check the loop data
match &self.range.compute(ctx, opt, txn, doc).await? {
match &self.range.compute(stk, ctx, opt, txn, doc).await? {
Value::Array(arr) => {
// Loop over the values
'foreach: for v in arr.iter() {
@ -43,35 +44,54 @@ impl ForeachStatement {
let mut ctx = Context::new(ctx);
// Set the current parameter
let key = self.param.0.to_raw();
let val = v.compute(&ctx, opt, txn, doc).await?;
let val = stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await?;
ctx.add_value(key, val);
// Loop over the code block statements
for v in self.block.iter() {
// Compute each block entry
let res = match v {
Entry::Set(v) => {
let val = v.compute(&ctx, opt, txn, doc).await?;
let val =
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await?;
ctx.add_value(v.name.to_owned(), val);
Ok(Value::None)
}
Entry::Value(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Value(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Break(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Continue(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Foreach(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Ifelse(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Select(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Create(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Update(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Delete(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Relate(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Insert(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Define(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Foreach(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Ifelse(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Select(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Create(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Update(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Delete(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Relate(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Insert(v) => {
stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await
}
Entry::Define(v) => v.compute(stk, &ctx, opt, txn, doc).await,
Entry::Remove(v) => v.compute(&ctx, opt, txn, doc).await,
Entry::Output(v) => {
return v.compute(&ctx, opt, txn, doc).await;
return stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await;
}
Entry::Throw(v) => {
return v.compute(&ctx, opt, txn, doc).await;
return stk.run(|stk| v.compute(stk, &ctx, opt, txn, doc)).await;
}
};
// Catch any special errors

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::sql::fmt::{fmt_separated_by, is_pretty, pretty_indent, Fmt, Pretty};
use crate::sql::Value;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Write};
@ -39,19 +40,20 @@ impl IfelseStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
for (ref cond, ref then) in &self.exprs {
let v = cond.compute(ctx, opt, txn, doc).await?;
let v = cond.compute(stk, ctx, opt, txn, doc).await?;
if v.is_truthy() {
return then.compute(ctx, opt, txn, doc).await;
return then.compute(stk, ctx, opt, txn, doc).await;
}
}
match self.close {
Some(ref v) => v.compute(ctx, opt, txn, doc).await,
Some(ref v) => v.compute(stk, ctx, opt, txn, doc).await,
None => Ok(Value::None),
}
}

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{Data, Output, Timeout, Value};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -30,6 +31,7 @@ impl InsertStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -42,7 +44,7 @@ impl InsertStatement {
// Ensure futures are stored
let opt = &opt.new_with_futures(false).with_projections(false);
// Parse the expression
match self.into.compute(ctx, opt, txn, doc).await? {
match self.into.compute(stk, ctx, opt, txn, doc).await? {
Value::Table(into) => match &self.data {
// Check if this is a traditional statement
Data::ValuesExpression(v) => {
@ -51,8 +53,8 @@ impl InsertStatement {
let mut o = Value::base();
// Set each field from the expression
for (k, v) in v.iter() {
let v = v.compute(ctx, opt, txn, None).await?;
o.set(ctx, opt, txn, k, v).await?;
let v = v.compute(stk, ctx, opt, txn, None).await?;
o.set(stk, ctx, opt, txn, k, v).await?;
}
// Specify the new table record id
let id = o.rid().generate(&into, true)?;
@ -62,7 +64,7 @@ impl InsertStatement {
}
// Check if this is a modern statement
Data::SingleExpression(v) => {
let v = v.compute(ctx, opt, txn, doc).await?;
let v = v.compute(stk, ctx, opt, txn, doc).await?;
match v {
Value::Array(v) => {
for v in v {
@ -96,7 +98,7 @@ impl InsertStatement {
// Assign the statement
let stm = Statement::from(self);
// Output the results
i.output(ctx, opt, txn, &stm).await
i.output(stk, ctx, opt, txn, &stm).await
}
}

View file

@ -1,6 +1,7 @@
use std::fmt;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
@ -27,6 +28,7 @@ impl KillStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -39,7 +41,7 @@ impl KillStatement {
// Resolve live query id
let live_query_id = match &self.id {
Value::Uuid(id) => *id,
Value::Param(param) => match param.compute(ctx, opt, txn, None).await? {
Value::Param(param) => match param.compute(stk, ctx, opt, txn, None).await? {
Value::Uuid(id) => id,
Value::Strand(id) => match uuid::Uuid::try_parse(&id) {
Ok(id) => Uuid(id),
@ -153,7 +155,10 @@ mod test {
let ds = Datastore::new("memory").await.unwrap();
let tx =
ds.transaction(TransactionType::Write, LockType::Optimistic).await.unwrap().enclose();
res.compute(&ctx, &opt, &tx, None).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
stack.enter(|stk| res.compute(stk, &ctx, &opt, &tx, None)).finish().await.unwrap();
let mut tx = tx.lock().await;
tx.commit().await.unwrap();

View file

@ -9,6 +9,7 @@ use crate::sql::statements::info::InfoStructure;
use crate::sql::{Cond, Fetchs, Fields, Object, Table, Uuid, Value};
use derive::Store;
use futures::lock::MutexGuard;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -76,6 +77,7 @@ impl LiveStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -103,7 +105,7 @@ impl LiveStatement {
match FFLAGS.change_feed_live_queries.enabled() {
true => {
let mut run = txn.lock().await;
match stm.what.compute(ctx, opt, txn, doc).await? {
match stm.what.compute(stk, ctx, opt, txn, doc).await? {
Value::Table(tb) => {
let ns = opt.ns().to_string();
let db = opt.db().to_string();
@ -128,7 +130,7 @@ impl LiveStatement {
// Claim transaction
let mut run = txn.lock().await;
// Process the live query table
match stm.what.compute(ctx, opt, txn, doc).await? {
match stm.what.compute(stk, ctx, opt, txn, doc).await? {
Value::Table(tb) => {
// Store the current Node ID
stm.node = nid.into();

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::sql::fetch::Fetchs;
use crate::sql::value::Value;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -26,6 +27,7 @@ impl OutputStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -34,11 +36,11 @@ impl OutputStatement {
// Ensure futures are processed
let opt = &opt.new_with_futures(true);
// Process the output value
let mut val = self.what.compute(ctx, opt, txn, doc).await?;
let mut val = self.what.compute(stk, ctx, opt, txn, doc).await?;
// Fetch any
if let Some(fetchs) = &self.fetch {
for fetch in fetchs.iter() {
val.fetch(ctx, opt, txn, fetch).await?;
val.fetch(stk, ctx, opt, txn, fetch).await?;
}
}
//

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{Data, Output, Timeout, Value};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -33,6 +34,7 @@ impl RelateStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -47,7 +49,7 @@ impl RelateStatement {
// Loop over the from targets
let from = {
let mut out = Vec::new();
match self.from.compute(ctx, opt, txn, doc).await? {
match self.from.compute(stk, ctx, opt, txn, doc).await? {
Value::Thing(v) => out.push(v),
Value::Array(v) => {
for v in v {
@ -89,7 +91,7 @@ impl RelateStatement {
// Loop over the with targets
let with = {
let mut out = Vec::new();
match self.with.compute(ctx, opt, txn, doc).await? {
match self.with.compute(stk, ctx, opt, txn, doc).await? {
Value::Thing(v) => out.push(v),
Value::Array(v) => {
for v in v {
@ -132,14 +134,14 @@ impl RelateStatement {
for w in with.iter() {
let f = f.clone();
let w = w.clone();
match &self.kind.compute(ctx, opt, txn, doc).await? {
match &self.kind.compute(stk, ctx, opt, txn, doc).await? {
// The relation has a specific record id
Value::Thing(id) => i.ingest(Iterable::Relatable(f, id.to_owned(), w)),
// The relation does not have a specific record id
Value::Table(tb) => match &self.data {
// There is a data clause so check for a record id
Some(data) => {
let id = match data.rid(ctx, opt, txn).await? {
let id = match data.rid(stk, ctx, opt, txn).await? {
Some(id) => id.generate(tb, false)?,
None => tb.generate(),
};
@ -160,7 +162,7 @@ impl RelateStatement {
// Assign the statement
let stm = Statement::from(self);
// Output the results
match i.output(ctx, opt, txn, &stm).await? {
match i.output(stk, ctx, opt, txn, &stm).await? {
// This is a single record result
Value::Array(mut a) if self.only => match a.len() {
// There was exactly one result

View file

@ -8,6 +8,7 @@ use crate::sql::{
Value, Values, Version, With,
};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -57,6 +58,7 @@ impl SelectStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -72,7 +74,7 @@ impl SelectStatement {
let mut planner = QueryPlanner::new(opt, &self.with, &self.cond);
// Used for ONLY: is the limit 1?
let limit_is_one_or_zero = match &self.limit {
Some(l) => l.process(ctx, opt, txn, doc).await? <= 1,
Some(l) => l.process(stk, ctx, opt, txn, doc).await? <= 1,
_ => false,
};
// Fail for multiple targets without a limit
@ -81,14 +83,14 @@ impl SelectStatement {
}
// Loop over the select targets
for w in self.what.0.iter() {
let v = w.compute(ctx, opt, txn, doc).await?;
let v = w.compute(stk, ctx, opt, txn, doc).await?;
match v {
Value::Table(t) => {
if self.only && !limit_is_one_or_zero {
return Err(Error::SingleOnlyOutput);
}
planner.add_iterables(ctx, txn, t, &mut i).await?;
planner.add_iterables(stk, ctx, txn, t, &mut i).await?;
}
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
Value::Range(v) => {
@ -122,7 +124,7 @@ impl SelectStatement {
for v in v {
match v {
Value::Table(t) => {
planner.add_iterables(ctx, txn, t, &mut i).await?;
planner.add_iterables(stk, ctx, txn, t, &mut i).await?;
}
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
@ -147,7 +149,7 @@ impl SelectStatement {
ctx.set_query_planner(&planner);
}
// Output the results
match i.output(&ctx, opt, txn, &stm).await? {
match i.output(stk, &ctx, opt, txn, &stm).await? {
// This is a single record result
Value::Array(mut a) if self.only => match a.len() {
// There were no results

View file

@ -5,6 +5,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::Value;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -26,6 +27,7 @@ impl SetStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -34,7 +36,7 @@ impl SetStatement {
// Check if the variable is a protected variable
match PROTECTED_PARAM_NAMES.contains(&self.name.as_str()) {
// The variable isn't protected and can be stored
false => self.what.compute(ctx, opt, txn, doc).await,
false => self.what.compute(stk, ctx, opt, txn, doc).await,
// The user tried to set a protected variable
true => Err(Error::InvalidParam {
// Move the parameter name, as we no longer need it

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::Value;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -24,12 +25,13 @@ impl ThrowStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
Err(Error::Thrown(self.error.compute(ctx, opt, txn, doc).await?.to_raw_string()))
Err(Error::Thrown(self.error.compute(stk, ctx, opt, txn, doc).await?.to_raw_string()))
}
}

View file

@ -4,6 +4,7 @@ use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::{Cond, Data, Output, Timeout, Value, Values};
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -31,6 +32,7 @@ impl UpdateStatement {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -46,8 +48,8 @@ impl UpdateStatement {
let opt = &opt.new_with_futures(false).with_projections(false);
// Loop over the update targets
for w in self.what.0.iter() {
let v = w.compute(ctx, opt, txn, doc).await?;
i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e {
let v = w.compute(stk, ctx, opt, txn, doc).await?;
i.prepare(stk, ctx, opt, txn, &stm, v).await.map_err(|e| match e {
Error::InvalidStatementTarget {
value: v,
} => Error::UpdateStatement {
@ -57,7 +59,7 @@ impl UpdateStatement {
})?;
}
// Output the results
match i.output(ctx, opt, txn, &stm).await? {
match i.output(stk, ctx, opt, txn, &stm).await? {
// This is a single record result
Value::Array(mut a) if self.only => match a.len() {
// There was exactly one result

View file

@ -7,6 +7,7 @@ use crate::sql::statements::{
OutputStatement, RelateStatement, RemoveStatement, SelectStatement, UpdateStatement,
};
use crate::sql::value::Value;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@ -61,6 +62,7 @@ impl Subquery {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -74,17 +76,17 @@ impl Subquery {
}
// Process the subquery
match self {
Self::Value(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Ifelse(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Output(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Define(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Value(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Ifelse(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Output(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Define(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Remove(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Select(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Create(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Update(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Delete(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Relate(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Insert(ref v) => v.compute(&ctx, opt, txn, doc).await,
Self::Select(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Create(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Update(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Delete(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Relate(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
Self::Insert(ref v) => v.compute(stk, &ctx, opt, txn, doc).await,
}
}
}

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::sql::{escape::escape_rid, id::Id, Strand, Value};
use crate::syn;
use derive::Store;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -101,6 +102,7 @@ impl Thing {
/// Process this type returning a computed simple Value
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -108,7 +110,7 @@ impl Thing {
) -> Result<Value, Error> {
Ok(Value::Thing(Thing {
tb: self.tb.clone(),
id: self.id.compute(ctx, opt, txn, doc).await?,
id: self.id.compute(stk, ctx, opt, txn, doc).await?,
}))
}
}

View file

@ -4,29 +4,31 @@ use crate::err::Error;
use crate::sql::number::Number;
use crate::sql::part::Part;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl Value {
/// Asynchronous method for decrementing a field in a `Value`
pub(crate) async fn decrement(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
path: &[Part],
val: Value,
) -> Result<(), Error> {
match self.get(ctx, opt, txn, None, path).await? {
match self.get(stk, ctx, opt, txn, None, path).await? {
Value::Number(v) => match val {
Value::Number(x) => self.set(ctx, opt, txn, path, Value::from(v - x)).await,
Value::Number(x) => self.set(stk, ctx, opt, txn, path, Value::from(v - x)).await,
_ => Ok(()),
},
Value::Array(v) => match val {
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(v - x)).await,
x => self.set(ctx, opt, txn, path, Value::from(v - x)).await,
Value::Array(x) => self.set(stk, ctx, opt, txn, path, Value::from(v - x)).await,
x => self.set(stk, ctx, opt, txn, path, Value::from(v - x)).await,
},
Value::None => match val {
Value::Number(x) => {
self.set(ctx, opt, txn, path, Value::from(Number::from(0) - x)).await
self.set(stk, ctx, opt, txn, path, Value::from(Number::from(0) - x)).await
}
_ => Ok(()),
},
@ -49,7 +51,12 @@ mod tests {
let idi = Idiom::parse("other");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 100, other: -10 }");
val.decrement(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.decrement(stk, &ctx, &opt, &txn, &idi, Value::from(10)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -59,7 +66,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 90 }");
val.decrement(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.decrement(stk, &ctx, &opt, &txn, &idi, Value::from(10)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -69,7 +81,12 @@ mod tests {
let idi = Idiom::parse("test[1]");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 190, 300] }");
val.decrement(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.decrement(stk, &ctx, &opt, &txn, &idi, Value::from(10)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -79,7 +96,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 300] }");
val.decrement(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.decrement(stk, &ctx, &opt, &txn, &idi, Value::from(200)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -89,7 +111,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [200] }");
val.decrement(&ctx, &opt, &txn, &idi, Value::parse("[100, 300]")).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.decrement(stk, &ctx, &opt, &txn, &idi, Value::parse("[100,300]")))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
}

View file

@ -6,15 +6,16 @@ use crate::sql::array::Abolish;
use crate::sql::part::Next;
use crate::sql::part::Part;
use crate::sql::value::Value;
use async_recursion::async_recursion;
use reblessive::tree::Stk;
use std::collections::HashSet;
impl Value {
/// Asynchronous method for deleting a field from a `Value`
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
///
/// Was marked recursive
pub(crate) async fn del(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -31,7 +32,9 @@ impl Value {
Ok(())
}
_ => match v.get_mut(f.as_str()) {
Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await,
Some(v) if v.is_some() => {
stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await
}
_ => Ok(()),
},
},
@ -41,18 +44,22 @@ impl Value {
Ok(())
}
_ => match v.get_mut(&i.to_string()) {
Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await,
Some(v) if v.is_some() => {
stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await
}
_ => Ok(()),
},
},
Part::Value(x) => match x.compute(ctx, opt, txn, None).await? {
Part::Value(x) => match x.compute(stk, ctx, opt, txn, None).await? {
Value::Strand(f) => match path.len() {
1 => {
v.remove(f.as_str());
Ok(())
}
_ => match v.get_mut(f.as_str()) {
Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await,
Some(v) if v.is_some() => {
stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await
}
_ => Ok(()),
},
},
@ -69,8 +76,13 @@ impl Value {
}
_ => {
let path = path.next();
let futs = v.iter_mut().map(|v| v.del(ctx, opt, txn, path));
try_join_all_buffered(futs).await?;
stk.scope(|scope| {
let futs = v
.iter_mut()
.map(|v| scope.run(|stk| v.del(stk, ctx, opt, txn, path)));
try_join_all_buffered(futs)
})
.await?;
Ok(())
}
},
@ -83,7 +95,7 @@ impl Value {
Ok(())
}
_ => match v.first_mut() {
Some(v) => v.del(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
},
@ -96,7 +108,7 @@ impl Value {
Ok(())
}
_ => match v.last_mut() {
Some(v) => v.del(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
},
@ -108,7 +120,7 @@ impl Value {
Ok(())
}
_ => match v.get_mut(i.to_usize()) {
Some(v) => v.del(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
},
@ -119,7 +131,7 @@ impl Value {
let mut m = HashSet::new();
for (i, v) in v.iter().enumerate() {
let cur = v.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
if w.compute(stk, ctx, opt, txn, Some(&cur)).await?.is_truthy() {
m.insert(i);
};
}
@ -133,7 +145,8 @@ impl Value {
// Store the elements and positions to update
for (i, o) in v.iter_mut().enumerate() {
let cur = o.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
if w.compute(stk, ctx, opt, txn, Some(&cur)).await?.is_truthy()
{
a.push(o.clone());
p.push(i);
}
@ -141,7 +154,7 @@ impl Value {
// Convert the matched elements array to a value
let mut a = Value::from(a);
// Set the new value on the matches elements
a.del(ctx, opt, txn, path.next()).await?;
stk.run(|stk| a.del(stk, ctx, opt, txn, path.next())).await?;
// Push the new values into the original array
for (i, p) in p.into_iter().enumerate().rev() {
match a.pick(&[Part::Index(i.into())]) {
@ -157,15 +170,16 @@ impl Value {
let path = path.next();
for v in v.iter_mut() {
let cur = v.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
v.del(ctx, opt, txn, path).await?;
if w.compute(stk, ctx, opt, txn, Some(&cur)).await?.is_truthy()
{
stk.run(|stk| v.del(stk, ctx, opt, txn, path)).await?;
}
}
Ok(())
}
},
},
Part::Value(x) => match x.compute(ctx, opt, txn, None).await? {
Part::Value(x) => match x.compute(stk, ctx, opt, txn, None).await? {
Value::Number(i) => match path.len() {
1 => {
if v.len() > i.to_usize() {
@ -174,15 +188,22 @@ impl Value {
Ok(())
}
_ => match v.get_mut(i.to_usize()) {
Some(v) => v.del(ctx, opt, txn, path.next()).await,
Some(v) => {
stk.run(|stk| v.del(stk, ctx, opt, txn, path.next())).await
}
None => Ok(()),
},
},
_ => Ok(()),
},
_ => {
let futs = v.iter_mut().map(|v| v.del(ctx, opt, txn, path));
try_join_all_buffered(futs).await?;
stk.scope(|scope| {
let futs = v
.iter_mut()
.map(|v| scope.run(|stk| v.del(stk, ctx, opt, txn, path)));
try_join_all_buffered(futs)
})
.await?;
Ok(())
}
},
@ -209,7 +230,8 @@ mod tests {
let idi = Idiom::default();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null, something: 123 } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -219,7 +241,8 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -229,7 +252,8 @@ mod tests {
let idi = Idiom::parse("test.something");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -239,7 +263,8 @@ mod tests {
let idi = Idiom::parse("test.something.wrong");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null, something: 123 } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -249,7 +274,8 @@ mod tests {
let idi = Idiom::parse("test.other.something");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null, something: 123 } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -259,7 +285,8 @@ mod tests {
let idi = Idiom::parse("test.something[1]");
let mut val = Value::parse("{ test: { something: [123, 456, 789] } }");
let res = Value::parse("{ test: { something: [123, 789] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -271,7 +298,8 @@ mod tests {
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
);
let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -283,7 +311,8 @@ mod tests {
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
);
let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -295,7 +324,8 @@ mod tests {
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
);
let res = Value::parse("{ test: { something: [{ name: 'A' }, { name: 'B' }] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -307,7 +337,8 @@ mod tests {
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
);
let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }, { name: 'B' }] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -319,7 +350,8 @@ mod tests {
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
);
let res = Value::parse("{ test: { something: [{ name: 'A', age: 34 }] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -331,7 +363,8 @@ mod tests {
"{ test: { something: [{ name: 'A', age: 34 }, { name: 'B', age: 36 }] } }",
);
let res = Value::parse("{ test: { something: [{ name: 'B', age: 36 }] } }");
val.del(&ctx, &opt, &txn, &idi).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack.enter(|stk| val.del(stk, &ctx, &opt, &txn, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
}

View file

@ -4,24 +4,28 @@ use crate::err::Error;
use crate::sql::array::Uniq;
use crate::sql::part::Part;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl Value {
pub(crate) async fn extend(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
path: &[Part],
val: Value,
) -> Result<(), Error> {
match self.get(ctx, opt, txn, None, path).await? {
match self.get(stk, ctx, opt, txn, None, path).await? {
Value::Array(v) => match val {
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await,
x => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await,
Value::Array(x) => {
self.set(stk, ctx, opt, txn, path, Value::from((v + x).uniq())).await
}
x => self.set(stk, ctx, opt, txn, path, Value::from((v + x).uniq())).await,
},
Value::None => match val {
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(x)).await,
x => self.set(ctx, opt, txn, path, Value::from(vec![x])).await,
Value::Array(x) => self.set(stk, ctx, opt, txn, path, Value::from(x)).await,
x => self.set(stk, ctx, opt, txn, path, Value::from(vec![x])).await,
},
_ => Ok(()),
}
@ -42,7 +46,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 200, 300] }");
val.extend(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.extend(stk, &ctx, &opt, &txn, &idi, Value::from(200)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -52,7 +61,14 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 200, 300, 400, 500] }");
val.extend(&ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]")).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| {
val.extend(stk, &ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]"))
})
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
}

View file

@ -7,14 +7,14 @@ use crate::sql::part::Next;
use crate::sql::part::Part;
use crate::sql::statements::select::SelectStatement;
use crate::sql::value::{Value, Values};
use async_recursion::async_recursion;
use futures::future::try_join_all;
use reblessive::tree::Stk;
impl Value {
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
/// Was marked recursive
pub(crate) async fn fetch(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -26,53 +26,66 @@ impl Value {
// Current path part is an object
Value::Object(v) => match p {
Part::Graph(_) => match v.rid() {
Some(v) => Value::Thing(v).fetch(ctx, opt, txn, path.next()).await,
Some(v) => {
let mut v = Value::Thing(v);
stk.run(|stk| v.fetch(stk, ctx, opt, txn, path.next())).await
}
None => Ok(()),
},
Part::Field(f) => match v.get_mut(f as &str) {
Some(v) => v.fetch(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
Part::Index(i) => match v.get_mut(&i.to_string()) {
Some(v) => v.fetch(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
Part::All => self.fetch(ctx, opt, txn, path.next()).await,
Part::All => stk.run(|stk| self.fetch(stk, ctx, opt, txn, path.next())).await,
_ => Ok(()),
},
// Current path part is an array
Value::Array(v) => match p {
Part::All => {
let path = path.next();
let futs = v.iter_mut().map(|v| v.fetch(ctx, opt, txn, path));
try_join_all(futs).await?;
stk.scope(|scope| {
let futs = v
.iter_mut()
.map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, txn, path)));
try_join_all(futs)
})
.await?;
Ok(())
}
Part::First => match v.first_mut() {
Some(v) => v.fetch(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
Part::Last => match v.last_mut() {
Some(v) => v.fetch(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
Part::Index(i) => match v.get_mut(i.to_usize()) {
Some(v) => v.fetch(ctx, opt, txn, path.next()).await,
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, txn, path.next())).await,
None => Ok(()),
},
Part::Where(w) => {
let path = path.next();
for v in v.iter_mut() {
let cur = v.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
v.fetch(ctx, opt, txn, path).await?;
if w.compute(stk, ctx, opt, txn, Some(&cur)).await?.is_truthy() {
stk.run(|stk| v.fetch(stk, ctx, opt, txn, path)).await?;
}
}
Ok(())
}
_ => {
let futs = v.iter_mut().map(|v| v.fetch(ctx, opt, txn, path));
try_join_all(futs).await?;
stk.scope(|scope| {
let futs = v
.iter_mut()
.map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, txn, path)));
try_join_all(futs)
})
.await?;
Ok(())
}
},
@ -95,10 +108,10 @@ impl Value {
..SelectStatement::default()
};
*self = stm
.compute(ctx, opt, txn, None)
.compute(stk, ctx, opt, txn, None)
.await?
.all()
.get(ctx, opt, txn, None, path.next())
.get(stk, ctx, opt, txn, None, path.next())
.await?
.flatten()
.ok()?;
@ -111,7 +124,7 @@ impl Value {
what: Values(vec![Value::from(val)]),
..SelectStatement::default()
};
*self = stm.compute(ctx, opt, txn, None).await?.first();
*self = stm.compute(stk, ctx, opt, txn, None).await?.first();
Ok(())
}
}
@ -123,8 +136,13 @@ impl Value {
None => match self {
// Current path part is an array
Value::Array(v) => {
let futs = v.iter_mut().map(|v| v.fetch(ctx, opt, txn, path));
try_join_all(futs).await?;
stk.scope(|scope| {
let futs = v
.iter_mut()
.map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, txn, path)));
try_join_all(futs)
})
.await?;
Ok(())
}
// Current path part is a thing
@ -137,7 +155,7 @@ impl Value {
what: Values(vec![Value::from(val)]),
..SelectStatement::default()
};
*self = stm.compute(ctx, opt, txn, None).await?.first();
*self = stm.compute(stk, ctx, opt, txn, None).await?.first();
Ok(())
}
// Ignore everything else

View file

@ -13,22 +13,23 @@ use crate::sql::paths::ID;
use crate::sql::statements::select::SelectStatement;
use crate::sql::thing::Thing;
use crate::sql::value::{Value, Values};
use async_recursion::async_recursion;
use reblessive::tree::Stk;
impl Value {
/// Asynchronous method for getting a local or remote field from a `Value`
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
///
/// Was marked recursive
pub(crate) async fn get(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&'async_recursion CursorDoc<'_>>,
doc: Option<&CursorDoc<'_>>,
path: &[Part],
) -> Result<Self, Error> {
// Limit recursion depth.
if path.len() > (*MAX_COMPUTATION_DEPTH).into() {
if path.len() > (*MAX_COMPUTATION_DEPTH).try_into().unwrap_or(usize::MAX) {
return Err(Error::ComputationDepthExceeded);
}
match path.first() {
@ -38,15 +39,18 @@ impl Value {
Value::Geometry(v) => match p {
// If this is the 'type' field then continue
Part::Field(f) if f.is_type() => {
Value::from(v.as_type()).get(ctx, opt, txn, doc, path.next()).await
let v = Value::from(v.as_type());
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
// If this is the 'coordinates' field then continue
Part::Field(f) if f.is_coordinates() && v.is_geometry() => {
v.as_coordinates().get(ctx, opt, txn, doc, path.next()).await
let v = v.as_coordinates();
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
// If this is the 'geometries' field then continue
Part::Field(f) if f.is_geometries() && v.is_collection() => {
v.as_coordinates().get(ctx, opt, txn, doc, path.next()).await
let v = v.as_coordinates();
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
// Otherwise return none
_ => Ok(Value::None),
@ -62,9 +66,9 @@ impl Value {
// Ensure the future is processed
let fut = &opt.new_with_futures(true);
// Get the future return value
let val = v.compute(ctx, fut, txn, doc).await?;
let val = v.compute(stk, ctx, fut, txn, doc).await?;
// Fetch the embedded field
val.get(ctx, opt, txn, doc, path).await
stk.run(|stk| val.get(stk, ctx, opt, txn, doc, path)).await
}
}
}
@ -75,34 +79,49 @@ impl Value {
Some(Value::Thing(Thing {
id: Id::Object(v),
..
})) => Value::Object(v.clone()).get(ctx, opt, txn, doc, path.next()).await,
})) => {
let v = Value::Object(v.clone());
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
Some(Value::Thing(Thing {
id: Id::Array(v),
..
})) => Value::Array(v.clone()).get(ctx, opt, txn, doc, path.next()).await,
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
})) => {
let v = Value::Array(v.clone());
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await,
None => Ok(Value::None),
},
Part::Graph(_) => match v.rid() {
Some(v) => Value::Thing(v).get(ctx, opt, txn, doc, path).await,
Some(v) => {
let v = Value::Thing(v);
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path)).await
}
None => Ok(Value::None),
},
Part::Field(f) => match v.get(f.as_str()) {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await,
None => Ok(Value::None),
},
Part::Index(i) => match v.get(&i.to_string()) {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await,
None => Ok(Value::None),
},
Part::Value(x) => match x.compute(ctx, opt, txn, doc).await? {
Value::Strand(f) => match v.get(f.as_str()) {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
None => Ok(Value::None),
},
_ => Ok(Value::None),
},
Part::All => self.get(ctx, opt, txn, doc, path.next()).await,
Part::Value(x) => {
match stk.run(|stk| x.compute(stk, ctx, opt, txn, doc)).await? {
Value::Strand(f) => match v.get(f.as_str()) {
Some(v) => {
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
None => Ok(Value::None),
},
_ => Ok(Value::None),
}
}
Part::All => {
stk.run(|stk| self.get(stk, ctx, opt, txn, doc, path.next())).await
}
_ => Ok(Value::None),
},
// Current value at path is an array
@ -110,42 +129,62 @@ impl Value {
// Current path is an `*` part
Part::All | Part::Flatten => {
let path = path.next();
let futs = v.iter().map(|v| v.get(ctx, opt, txn, doc, path));
try_join_all_buffered(futs).await.map(Into::into)
stk.scope(|scope| {
let futs = v
.iter()
.map(|v| scope.run(|stk| v.get(stk, ctx, opt, txn, doc, path)));
try_join_all_buffered(futs)
})
.await
.map(Into::into)
}
Part::First => match v.first() {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await,
None => Ok(Value::None),
},
Part::Last => match v.last() {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await,
None => Ok(Value::None),
},
Part::Index(i) => match v.get(i.to_usize()) {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await,
None => Ok(Value::None),
},
Part::Where(w) => {
let mut a = Vec::new();
for v in v.iter() {
let cur = v.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
if stk
.run(|stk| w.compute(stk, ctx, opt, txn, Some(&cur)))
.await?
.is_truthy()
{
a.push(v.clone());
}
}
Value::from(a).get(ctx, opt, txn, doc, path.next()).await
let v = Value::from(a);
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
Part::Value(x) => match x.compute(ctx, opt, txn, doc).await? {
Value::Number(i) => match v.get(i.to_usize()) {
Some(v) => v.get(ctx, opt, txn, doc, path.next()).await,
None => Ok(Value::None),
},
_ => Ok(Value::None),
},
_ => {
let futs = v.iter().map(|v| v.get(ctx, opt, txn, doc, path));
try_join_all_buffered(futs).await.map(Into::into)
Part::Value(x) => {
match stk.run(|stk| x.compute(stk, ctx, opt, txn, doc)).await? {
Value::Number(i) => match v.get(i.to_usize()) {
Some(v) => {
stk.run(|stk| v.get(stk, ctx, opt, txn, doc, path.next())).await
}
None => Ok(Value::None),
},
_ => Ok(Value::None),
}
}
_ => stk
.scope(|scope| {
let futs = v
.iter()
.map(|v| scope.run(|stk| v.get(stk, ctx, opt, txn, doc, path)));
try_join_all_buffered(futs)
})
.await
.map(Into::into),
},
// Current value at path is an edges
Value::Edges(v) => {
@ -162,11 +201,9 @@ impl Value {
what: Values(vec![Value::from(val)]),
..SelectStatement::default()
};
stm.compute(ctx, opt, txn, None)
.await?
.first()
.get(ctx, opt, txn, None, path)
.await
let v =
stk.run(|stk| stm.compute(stk, ctx, opt, txn, None)).await?.first();
stk.run(|stk| v.get(stk, ctx, opt, txn, None, path)).await
}
}
}
@ -193,22 +230,26 @@ impl Value {
..SelectStatement::default()
};
match path.len() {
1 => stm
.compute(ctx, opt, txn, None)
.await?
.all()
.get(ctx, opt, txn, None, ID.as_ref())
.await?
.flatten()
.ok(),
_ => stm
.compute(ctx, opt, txn, None)
.await?
.all()
.get(ctx, opt, txn, None, path.next())
.await?
.flatten()
.ok(),
1 => {
let v = stk
.run(|stk| stm.compute(stk, ctx, opt, txn, None))
.await?
.all();
stk.run(|stk| v.get(stk, ctx, opt, txn, None, ID.as_ref()))
.await?
.flatten()
.ok()
}
_ => {
let v = stk
.run(|stk| stm.compute(stk, ctx, opt, txn, None))
.await?
.all();
stk.run(|stk| v.get(stk, ctx, opt, txn, None, path.next()))
.await?
.flatten()
.ok()
}
}
}
// This is a remote field expression
@ -218,18 +259,18 @@ impl Value {
what: Values(vec![Value::from(val)]),
..SelectStatement::default()
};
stm.compute(ctx, opt, txn, None)
let v = stk
.run(|stk| stm.compute(stk, ctx, opt, txn, None))
.await?
.first()
.get(ctx, opt, txn, None, path)
.await
.first();
stk.run(|stk| v.get(stk, ctx, opt, txn, None, path)).await
}
},
}
}
v => {
if matches!(p, Part::Flatten) {
v.get(ctx, opt, txn, None, path.next()).await
stk.run(|stk| v.get(stk, ctx, opt, txn, None, path.next())).await
} else {
// Ignore everything else
Ok(Value::None)
@ -255,7 +296,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::default();
let val = Value::parse("{ test: { other: null, something: 123 } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, val);
}
@ -264,7 +307,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something");
let val = Value::parse("{ test: { other: null, something: 123 } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(123));
}
@ -278,7 +323,9 @@ mod tests {
"{ test: ".repeat(depth),
"}".repeat(depth)
));
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(123));
}
@ -288,7 +335,12 @@ mod tests {
let depth = 2000;
let idi = Idiom::parse(&format!("{}something", "test.".repeat(depth)));
let val = Value::parse("{}"); // A deep enough object cannot be parsed.
let err = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap_err();
let mut stack = reblessive::tree::TreeStack::new();
let err = stack
.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi))
.finish()
.await
.unwrap_err();
assert!(
matches!(err, Error::ComputationDepthExceeded),
"expected computation depth exceeded, got {:?}",
@ -301,7 +353,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.other");
let val = Value::parse("{ test: { other: test:tobie, something: 123 } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(
res,
Value::from(Thing {
@ -316,7 +370,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[1]");
let val = Value::parse("{ test: { something: [123, 456, 789] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(456));
}
@ -325,7 +381,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[1]");
let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(
res,
Value::from(Thing {
@ -340,7 +398,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[1].age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(36));
}
@ -349,7 +409,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[*].age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(vec![34, 36]));
}
@ -358,7 +420,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something.age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(vec![34, 36]));
}
@ -367,7 +431,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[WHERE age > 35].age");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(res, Value::from(vec![36]));
}
@ -376,7 +442,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[WHERE age > 35]");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(
res,
Value::from(vec![Value::from(map! {
@ -390,7 +458,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[WHERE age > 30][0]");
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(
res,
Value::from(map! {
@ -404,7 +474,9 @@ mod tests {
let (ctx, opt, txn) = mock().await;
let idi = Idiom::parse("test.something[WHERE age > 35]");
let val = Value::parse("{ test: <future> { { something: [{ age: 34 }, { age: 36 }] } } }");
let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res =
stack.enter(|stk| val.get(stk, &ctx, &opt, &txn, None, &idi)).finish().await.unwrap();
assert_eq!(
res,
Value::from(vec![Value::from(map! {
@ -420,7 +492,12 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35]");
let val = Value::parse("{ test: <future> { { something: something } } }");
let cur = (&doc).into();
let res = val.get(&ctx, &opt, &txn, Some(&cur), &idi).await.unwrap();
let mut stack = reblessive::tree::TreeStack::new();
let res = stack
.enter(|stk| val.get(stk, &ctx, &opt, &txn, Some(&cur), &idi))
.finish()
.await
.unwrap();
assert_eq!(
res,
Value::from(vec![Value::from(map! {

View file

@ -4,32 +4,34 @@ use crate::err::Error;
use crate::sql::number::Number;
use crate::sql::part::Part;
use crate::sql::value::Value;
use reblessive::tree::Stk;
impl Value {
/// Asynchronous method for incrementing a field in a `Value`
pub(crate) async fn increment(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
path: &[Part],
val: Value,
) -> Result<(), Error> {
match self.get(ctx, opt, txn, None, path).await? {
match self.get(stk, ctx, opt, txn, None, path).await? {
Value::Number(v) => match val {
Value::Number(x) => self.set(ctx, opt, txn, path, Value::from(v + x)).await,
Value::Number(x) => self.set(stk, ctx, opt, txn, path, Value::from(v + x)).await,
_ => Ok(()),
},
Value::Array(v) => match val {
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(v + x)).await,
x => self.set(ctx, opt, txn, path, Value::from(v + x)).await,
Value::Array(x) => self.set(stk, ctx, opt, txn, path, Value::from(v + x)).await,
x => self.set(stk, ctx, opt, txn, path, Value::from(v + x)).await,
},
Value::None => match val {
Value::Number(x) => {
self.set(ctx, opt, txn, path, Value::from(Number::from(0) + x)).await
self.set(stk, ctx, opt, txn, path, Value::from(Number::from(0) + x)).await
}
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(x)).await,
x => self.set(ctx, opt, txn, path, Value::from(vec![x])).await,
Value::Array(x) => self.set(stk, ctx, opt, txn, path, Value::from(x)).await,
x => self.set(stk, ctx, opt, txn, path, Value::from(vec![x])).await,
},
_ => Ok(()),
}
@ -50,7 +52,12 @@ mod tests {
let idi = Idiom::parse("other");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 100, other: +10 }");
val.increment(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.increment(stk, &ctx, &opt, &txn, &idi, Value::from(10)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -60,7 +67,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: 100 }");
let res = Value::parse("{ test: 110 }");
val.increment(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.increment(stk, &ctx, &opt, &txn, &idi, Value::from(10)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -70,7 +82,12 @@ mod tests {
let idi = Idiom::parse("test[1]");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 210, 300] }");
val.increment(&ctx, &opt, &txn, &idi, Value::from(10)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.increment(stk, &ctx, &opt, &txn, &idi, Value::from(10)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -80,7 +97,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 200, 300, 200] }");
val.increment(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.increment(stk, &ctx, &opt, &txn, &idi, Value::from(200)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -90,7 +112,14 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: [100, 200, 300] }");
let res = Value::parse("{ test: [100, 200, 300, 100, 300, 400, 500] }");
val.increment(&ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]")).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| {
val.increment(stk, &ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]"))
})
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
}

View file

@ -5,14 +5,15 @@ use crate::exe::try_join_all_buffered;
use crate::sql::part::Next;
use crate::sql::part::Part;
use crate::sql::value::Value;
use async_recursion::async_recursion;
use reblessive::tree::Stk;
impl Value {
/// Asynchronous method for setting a field on a `Value`
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
///
/// Was marked recursive
pub(crate) async fn set(
&mut self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
@ -25,64 +26,81 @@ impl Value {
// Current value at path is an object
Value::Object(v) => match p {
Part::Graph(g) => match v.get_mut(g.to_raw().as_str()) {
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) if v.is_some() => {
stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await
}
_ => {
let mut obj = Value::base();
obj.set(ctx, opt, txn, path.next(), val).await?;
stk.run(|stk| obj.set(stk, ctx, opt, txn, path.next(), val)).await?;
v.insert(g.to_raw(), obj);
Ok(())
}
},
Part::Field(f) => match v.get_mut(f.as_str()) {
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) if v.is_some() => {
stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await
}
_ => {
let mut obj = Value::base();
obj.set(ctx, opt, txn, path.next(), val).await?;
stk.run(|stk| obj.set(stk, ctx, opt, txn, path.next(), val)).await?;
v.insert(f.to_raw(), obj);
Ok(())
}
},
Part::Index(i) => match v.get_mut(&i.to_string()) {
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) if v.is_some() => {
stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await
}
_ => {
let mut obj = Value::base();
obj.set(ctx, opt, txn, path.next(), val).await?;
stk.run(|stk| obj.set(stk, ctx, opt, txn, path.next(), val)).await?;
v.insert(i.to_string(), obj);
Ok(())
}
},
Part::Value(x) => match x.compute(ctx, opt, txn, None).await? {
Value::Strand(f) => match v.get_mut(f.as_str()) {
Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
_ => {
let mut obj = Value::base();
obj.set(ctx, opt, txn, path.next(), val).await?;
v.insert(f.to_string(), obj);
Ok(())
}
},
_ => Ok(()),
},
Part::Value(x) => {
match stk.run(|stk| x.compute(stk, ctx, opt, txn, None)).await? {
Value::Strand(f) => match v.get_mut(f.as_str()) {
Some(v) if v.is_some() => {
stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await
}
_ => {
let mut obj = Value::base();
stk.run(|stk| obj.set(stk, ctx, opt, txn, path.next(), val))
.await?;
v.insert(f.to_string(), obj);
Ok(())
}
},
_ => Ok(()),
}
}
_ => Ok(()),
},
// Current value at path is an array
Value::Array(v) => match p {
Part::All => {
let path = path.next();
let futs = v.iter_mut().map(|v| v.set(ctx, opt, txn, path, val.clone()));
try_join_all_buffered(futs).await?;
stk.scope(|scope| {
let futs = v.iter_mut().map(|v| {
scope.run(|stk| v.set(stk, ctx, opt, txn, path, val.clone()))
});
try_join_all_buffered(futs)
})
.await?;
Ok(())
}
Part::First => match v.first_mut() {
Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await,
None => Ok(()),
},
Part::Last => match v.last_mut() {
Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await,
None => Ok(()),
},
Part::Index(i) => match v.get_mut(i.to_usize()) {
Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await,
None => Ok(()),
},
Part::Where(w) => match path.next().first() {
@ -92,7 +110,7 @@ impl Value {
// Store the elements and positions to update
for (i, o) in v.iter_mut().enumerate() {
let cur = o.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
if w.compute(stk, ctx, opt, txn, Some(&cur)).await?.is_truthy() {
a.push(o.clone());
p.push(i);
}
@ -100,7 +118,8 @@ impl Value {
// Convert the matched elements array to a value
let mut a = Value::from(a);
// Set the new value on the matches elements
a.set(ctx, opt, txn, path.next(), val.clone()).await?;
stk.run(|stk| a.set(stk, ctx, opt, txn, path.next(), val.clone()))
.await?;
// Push the new values into the original array
for (i, p) in p.into_iter().enumerate() {
v[p] = a.pick(&[Part::Index(i.into())]);
@ -111,35 +130,44 @@ impl Value {
let path = path.next();
for v in v.iter_mut() {
let cur = v.into();
if w.compute(ctx, opt, txn, Some(&cur)).await?.is_truthy() {
v.set(ctx, opt, txn, path, val.clone()).await?;
if w.compute(stk, ctx, opt, txn, Some(&cur)).await?.is_truthy() {
stk.run(|stk| v.set(stk, ctx, opt, txn, path, val.clone()))
.await?;
}
}
Ok(())
}
},
Part::Value(x) => match x.compute(ctx, opt, txn, None).await? {
Part::Value(x) => match x.compute(stk, ctx, opt, txn, None).await? {
Value::Number(i) => match v.get_mut(i.to_usize()) {
Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
Some(v) => {
stk.run(|stk| v.set(stk, ctx, opt, txn, path.next(), val)).await
}
None => Ok(()),
},
_ => Ok(()),
},
_ => {
let futs = v.iter_mut().map(|v| v.set(ctx, opt, txn, path, val.clone()));
try_join_all_buffered(futs).await?;
stk.scope(|scope| {
let futs = v.iter_mut().map(|v| {
scope.run(|stk| v.set(stk, ctx, opt, txn, path, val.clone()))
});
try_join_all_buffered(futs)
})
.await?;
Ok(())
}
},
// Current value at path is empty
Value::Null => {
*self = Value::base();
self.set(ctx, opt, txn, path, val).await
stk.run(|stk| self.set(stk, ctx, opt, txn, path, val)).await
}
// Current value at path is empty
Value::None => {
*self = Value::base();
self.set(ctx, opt, txn, path, val).await
stk.run(|stk| self.set(stk, ctx, opt, txn, path, val)).await
}
// Ignore everything else
_ => Ok(()),
@ -167,7 +195,12 @@ mod tests {
let idi = Idiom::default();
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("999");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -177,7 +210,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::None;
let res = Value::parse("{ test: 999 }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -187,7 +225,12 @@ mod tests {
let idi = Idiom::parse("test.something");
let mut val = Value::None;
let res = Value::parse("{ test: { something: 999 } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -197,7 +240,12 @@ mod tests {
let idi = Idiom::parse("test");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: 999 }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -207,7 +255,12 @@ mod tests {
let idi = Idiom::parse("test.something");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null, something: 999 } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -217,7 +270,12 @@ mod tests {
let idi = Idiom::parse("test.something.allow");
let mut val = Value::parse("{ test: { other: null } }");
let res = Value::parse("{ test: { other: null, something: { allow: 999 } } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -227,7 +285,12 @@ mod tests {
let idi = Idiom::parse("test.something.wrong");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: null, something: 123 } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -237,7 +300,12 @@ mod tests {
let idi = Idiom::parse("test.other.something");
let mut val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::parse("{ test: { other: { something: 999 }, something: 123 } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -247,7 +315,12 @@ mod tests {
let idi = Idiom::parse("test.something[1]");
let mut val = Value::parse("{ test: { something: [123, 456, 789] } }");
let res = Value::parse("{ test: { something: [123, 999, 789] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(999)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(999)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -257,7 +330,12 @@ mod tests {
let idi = Idiom::parse("test.something[1].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, { age: 21 }] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -267,7 +345,12 @@ mod tests {
let idi = Idiom::parse("test.something[*].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 21 }] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -277,7 +360,12 @@ mod tests {
let idi = Idiom::parse("test.something.age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 21 }] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -287,7 +375,12 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, { age: 21 }] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -297,7 +390,12 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 35]");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 34 }, 21] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -307,7 +405,12 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 30][0]");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [21, { age: 36 }] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
@ -317,7 +420,12 @@ mod tests {
let idi = Idiom::parse("test.something[WHERE age > 30][0].age");
let mut val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
let res = Value::parse("{ test: { something: [{ age: 21 }, { age: 36 }] } }");
val.set(&ctx, &opt, &txn, &idi, Value::from(21)).await.unwrap();
let mut stack = reblessive::TreeStack::new();
stack
.enter(|stk| val.set(stk, &ctx, &opt, &txn, &idi, Value::from(21)))
.finish()
.await
.unwrap();
assert_eq!(res, val);
}
}

View file

@ -15,10 +15,10 @@ use crate::sql::{
Geometry, Idiom, Kind, Mock, Number, Object, Operation, Param, Part, Query, Range, Regex,
Strand, Subquery, Table, Thing, Uuid,
};
use async_recursion::async_recursion;
use chrono::{DateTime, Utc};
use derive::Store;
use geo::Point;
use reblessive::tree::Stk;
use revision::revisioned;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
@ -2619,33 +2619,34 @@ impl Value {
}
}
/// Process this type returning a computed simple Value
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
///
/// Is used recursively.
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&'async_recursion CursorDoc<'_>>,
doc: Option<&CursorDoc<'_>>,
) -> Result<Value, Error> {
// Prevent infinite recursion due to casting, expressions, etc.
let opt = &opt.dive(1)?;
match self {
Value::Cast(v) => v.compute(ctx, opt, txn, doc).await,
Value::Thing(v) => v.compute(ctx, opt, txn, doc).await,
Value::Block(v) => v.compute(ctx, opt, txn, doc).await,
Value::Range(v) => v.compute(ctx, opt, txn, doc).await,
Value::Param(v) => v.compute(ctx, opt, txn, doc).await,
Value::Idiom(v) => v.compute(ctx, opt, txn, doc).await,
Value::Array(v) => v.compute(ctx, opt, txn, doc).await,
Value::Object(v) => v.compute(ctx, opt, txn, doc).await,
Value::Future(v) => v.compute(ctx, opt, txn, doc).await,
Value::Cast(v) => v.compute(stk, ctx, opt, txn, doc).await,
Value::Thing(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Block(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Range(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Param(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Idiom(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Array(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Object(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Future(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Constant(v) => v.compute(ctx, opt, txn, doc).await,
Value::Function(v) => v.compute(ctx, opt, txn, doc).await,
Value::Model(v) => v.compute(ctx, opt, txn, doc).await,
Value::Subquery(v) => v.compute(ctx, opt, txn, doc).await,
Value::Expression(v) => v.compute(ctx, opt, txn, doc).await,
Value::Function(v) => v.compute(stk, ctx, opt, txn, doc).await,
Value::Model(v) => v.compute(stk, ctx, opt, txn, doc).await,
Value::Subquery(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
Value::Expression(v) => stk.run(|stk| v.compute(stk, ctx, opt, txn, doc)).await,
_ => Ok(self.to_owned()),
}
}

View file

@ -110,6 +110,7 @@ tokio-util = { version = "0.7.10", optional = true, features = ["compat"] }
tracing = "0.1.40"
trice = { version = "0.4.0", optional = true }
url = "2.5.0"
reblessive = { version = "0.3.3", features = ["tree"] }
[dev-dependencies]
criterion = { version = "0.5.1", features = ["async_tokio"] }

View file

@ -1,6 +1,7 @@
use flume::Sender;
use futures::StreamExt;
use futures_concurrency::stream::Merge;
use reblessive::TreeStack;
#[cfg(target_arch = "wasm32")]
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@ -125,6 +126,8 @@ fn live_query_change_feed(opt: &EngineOptions, dbs: Arc<Datastore>) -> (FutureTa
let (tx, rx) = flume::bounded(1);
let _fut = spawn_future(async move {
let mut stack = TreeStack::new();
let _lifecycle = crate::dbs::LoggingLifecycle::new("live query agent task".to_string());
if !FFLAGS.change_feed_live_queries.enabled() {
// TODO verify test fails since return without completion
@ -144,7 +147,9 @@ fn live_query_change_feed(opt: &EngineOptions, dbs: Arc<Datastore>) -> (FutureTa
let opt = Options::default();
while let Some(Some(_)) = streams.next().await {
if let Err(e) = dbs.process_lq_notifications(&opt).await {
if let Err(e) =
stack.enter(|stk| dbs.process_lq_notifications(stk, &opt)).finish().await
{
error!("Error running node agent tick: {}", e);
break;
}

View file

@ -487,7 +487,7 @@ async fn define_statement_event_check_doc_always_populated() -> Result<(), Error
}
]"#,
);
assert_eq!(tmp, val);
assert_eq!(tmp, val, "{tmp} != {val}");
//
Ok(())
}

View file

@ -62,6 +62,7 @@ async fn live_query_sends_registered_lq_details() -> Result<(), Error> {
DEFINE TABLE lq_test_123 CHANGEFEED 10m INCLUDE ORIGINAL;
LIVE SELECT * FROM lq_test_123;
";
let mut stack = reblessive::tree::TreeStack::new();
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test").with_rt(true);
let res = &mut dbs.execute(sql, &ses, None).await?;
@ -86,7 +87,8 @@ async fn live_query_sends_registered_lq_details() -> Result<(), Error> {
let result = res.remove(0);
assert!(result.result.is_ok());
dbs.process_lq_notifications(&Default::default()).await?;
let def = Default::default();
stack.enter(|stk| dbs.process_lq_notifications(stk, &def)).finish().await?;
let notifications_chan = dbs.notifications().unwrap();

View file

@ -211,10 +211,6 @@ criteria = "safe-to-deploy"
version = "1.0.3"
criteria = "safe-to-deploy"
[[exemptions.atomic-waker]]
version = "1.1.2"
criteria = "safe-to-deploy"
[[exemptions.axum]]
version = "0.6.20"
criteria = "safe-to-deploy"
@ -1392,7 +1388,7 @@ version = "0.11.3"
criteria = "safe-to-run"
[[exemptions.reblessive]]
version = "0.3.2"
version = "0.3.3"
criteria = "safe-to-deploy"
[[exemptions.redox_syscall]]