Implement reblessive into the executor. (#3803)
This commit is contained in:
parent
0462d6a395
commit
49ad32f45e
94 changed files with 2048 additions and 1114 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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!(),
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> =
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -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?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!(),
|
||||
},
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
})))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!(),
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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?;
|
||||
}
|
||||
}
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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]]
|
||||
|
|
Loading…
Reference in a new issue