Implement expressions in index operators (#4756)
This commit is contained in:
parent
82636d43fa
commit
912cafd3c5
11 changed files with 629 additions and 313 deletions
|
@ -250,9 +250,10 @@ impl Fields {
|
|||
_ => {
|
||||
let expr = expr.compute(stk, ctx, opt, Some(doc)).await?;
|
||||
// Check if this is a single VALUE field expression
|
||||
match self.single().is_some() {
|
||||
false => out.set(stk, ctx, opt, name.as_ref(), expr).await?,
|
||||
true => out = expr,
|
||||
if self.single().is_some() {
|
||||
out = expr;
|
||||
} else {
|
||||
out.set(stk, ctx, opt, name.as_ref(), expr).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,9 +116,7 @@ impl Idiom {
|
|||
pub(crate) fn simplify(&self) -> Idiom {
|
||||
self.0
|
||||
.iter()
|
||||
.filter(|&p| {
|
||||
matches!(p, Part::Field(_) | Part::Start(_) | Part::Value(_) | Part::Graph(_))
|
||||
})
|
||||
.filter(|&p| matches!(p, Part::Field(_) | Part::Start(_) | Part::Graph(_)))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
|
|
|
@ -26,6 +26,84 @@ pub struct Range {
|
|||
pub end: Bound<Value>,
|
||||
}
|
||||
|
||||
impl Range {
|
||||
pub fn slice<'a, T>(&self, s: &'a [T]) -> Option<&'a [T]> {
|
||||
let r = match self.end {
|
||||
Bound::Included(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize();
|
||||
s.get(..=x)?
|
||||
}
|
||||
Bound::Excluded(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize();
|
||||
s.get(..x)?
|
||||
}
|
||||
Bound::Unbounded => s,
|
||||
};
|
||||
let r = match self.beg {
|
||||
Bound::Included(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize();
|
||||
r.get(x..)?
|
||||
}
|
||||
Bound::Excluded(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize().saturating_add(1);
|
||||
r.get(x..)?
|
||||
}
|
||||
Bound::Unbounded => r,
|
||||
};
|
||||
Some(r)
|
||||
}
|
||||
|
||||
pub fn slice_mut<'a, T>(&self, s: &'a mut [T]) -> Option<&'a mut [T]> {
|
||||
let r = match self.end {
|
||||
Bound::Included(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize();
|
||||
s.get_mut(..x)?
|
||||
}
|
||||
Bound::Excluded(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize();
|
||||
s.get_mut(..=x)?
|
||||
}
|
||||
Bound::Unbounded => s,
|
||||
};
|
||||
let r = match self.beg {
|
||||
Bound::Included(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize();
|
||||
r.get_mut(x..)?
|
||||
}
|
||||
Bound::Excluded(ref x) => {
|
||||
let Value::Number(ref x) = x else {
|
||||
return None;
|
||||
};
|
||||
let x = x.to_usize().saturating_add(1);
|
||||
r.get_mut(x..)?
|
||||
}
|
||||
Bound::Unbounded => r,
|
||||
};
|
||||
Some(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Range {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
|
|
@ -19,10 +19,12 @@ use std::sync::Arc;
|
|||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
pub struct SelectStatement {
|
||||
/// The foo,bar part in SELECT foo,bar FROM baz.
|
||||
pub expr: Fields,
|
||||
pub omit: Option<Idioms>,
|
||||
#[revision(start = 2)]
|
||||
pub only: bool,
|
||||
/// The baz part in SELECT foo,bar FROM baz.
|
||||
pub what: Values,
|
||||
pub with: Option<With>,
|
||||
pub cond: Option<Cond>,
|
||||
|
|
|
@ -11,7 +11,6 @@ use futures::future::try_join_all;
|
|||
use reblessive::tree::Stk;
|
||||
|
||||
impl Value {
|
||||
/// Was marked recursive
|
||||
pub(crate) async fn fetch(
|
||||
&mut self,
|
||||
stk: &mut Stk,
|
||||
|
@ -19,152 +18,222 @@ impl Value {
|
|||
opt: &Options,
|
||||
path: &[Part],
|
||||
) -> Result<(), Error> {
|
||||
match path.first() {
|
||||
// Get the current path part
|
||||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Graph(_) => match v.rid() {
|
||||
Some(v) => {
|
||||
let mut v = Value::Thing(v);
|
||||
stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await
|
||||
let mut this = self;
|
||||
let mut iter = path.iter();
|
||||
let mut prev = path;
|
||||
|
||||
// Loop over the path.
|
||||
// If the we just need to select a sub section of a value we update this to point to the
|
||||
// new subsection of the value. Otherwise we call into fetch again and then immediately
|
||||
// return.
|
||||
// If we encounter a idiom application which does not make sense, like `(1).foo` just
|
||||
// return Ok(())
|
||||
while let Some(p) = iter.next() {
|
||||
match p {
|
||||
Part::Graph(g) => match this {
|
||||
Value::Object(o) => {
|
||||
let Some(v) = o.rid() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut v = Value::Thing(v);
|
||||
return stk.run(|stk| v.fetch(stk, ctx, opt, iter.as_slice())).await;
|
||||
}
|
||||
Value::Thing(x) => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All], false),
|
||||
what: Values(vec![Value::from(Edges {
|
||||
from: x.clone(),
|
||||
dir: g.dir.clone(),
|
||||
what: g.what.clone(),
|
||||
})]),
|
||||
cond: g.cond.clone(),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
*this = stm
|
||||
.compute(stk, ctx, opt, None)
|
||||
.await?
|
||||
.all()
|
||||
.get(stk, ctx, opt, None, path.next())
|
||||
.await?
|
||||
.flatten()
|
||||
.ok()?;
|
||||
return Ok(());
|
||||
}
|
||||
Value::Array(x) => {
|
||||
// apply this path to every entry of the array.
|
||||
stk.scope(|scope| {
|
||||
let futs =
|
||||
x.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, prev)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
// break her to be comp
|
||||
_ => return Ok(()),
|
||||
},
|
||||
Part::Field(f) => match this {
|
||||
Value::Object(o) => {
|
||||
let Some(x) = o.get_mut(f.0.as_str()) else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
Value::Array(x) => {
|
||||
// apply this path to every entry of the array.
|
||||
stk.scope(|scope| {
|
||||
let futs =
|
||||
x.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, prev)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => break,
|
||||
},
|
||||
Part::Index(i) => match this {
|
||||
Value::Object(v) => {
|
||||
let Some(x) = v.get_mut(&i.to_string()) else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
Value::Array(v) => {
|
||||
let Some(x) = v.get_mut(i.to_usize()) else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
_ => break,
|
||||
},
|
||||
Part::Value(v) => {
|
||||
let v = v.compute(stk, ctx, opt, None).await?;
|
||||
match this {
|
||||
Value::Object(obj) => {
|
||||
let Some(x) = obj.get_mut(v.coerce_to_string()?.as_str()) else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Field(f) => match v.get_mut(f as &str) {
|
||||
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Index(i) => match v.get_mut(&i.to_string()) {
|
||||
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::All => stk.run(|stk| self.fetch(stk, ctx, opt, path.next())).await,
|
||||
Part::Destructure(p) => {
|
||||
Value::Array(array) => {
|
||||
if let Value::Range(x) = v {
|
||||
let Some(range) = x.slice(array) else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut range = Value::Array(range.to_vec().into());
|
||||
return stk
|
||||
.run(|stk| range.fetch(stk, ctx, opt, iter.as_slice()))
|
||||
.await;
|
||||
}
|
||||
let Some(x) = array.get_mut(v.coerce_to_u64()? as usize) else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
Part::Destructure(p) => match this {
|
||||
Value::Array(x) => {
|
||||
// apply this path to every entry of the array.
|
||||
stk.scope(|scope| {
|
||||
let futs =
|
||||
x.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, prev)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Value::Object(_) => {
|
||||
for p in p.iter() {
|
||||
let path = [(p.path().as_slice()), path].concat();
|
||||
stk.run(|stk| self.fetch(stk, ctx, opt, &path)).await?;
|
||||
let mut destructure_path = p.path();
|
||||
destructure_path.extend_from_slice(path);
|
||||
stk.run(|stk| this.fetch(stk, ctx, opt, &destructure_path)).await?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
Part::All => match this {
|
||||
Value::Object(_) => {
|
||||
continue;
|
||||
}
|
||||
Value::Array(x) => {
|
||||
let next_path = iter.as_slice();
|
||||
// no need to spawn all those futures if their is no more paths to
|
||||
// calculate
|
||||
if next_path.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
// Current path part is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => {
|
||||
let path = path.next();
|
||||
stk.scope(|scope| {
|
||||
let futs =
|
||||
v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path)));
|
||||
let futs = x
|
||||
.iter_mut()
|
||||
.map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, next_path)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
Part::First => match v.first_mut() {
|
||||
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Last => match v.last_mut() {
|
||||
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Index(i) => match v.get_mut(i.to_usize()) {
|
||||
Some(v) => stk.run(|stk| v.fetch(stk, ctx, opt, path.next())).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Where(w) => {
|
||||
let path = path.next();
|
||||
for v in v.iter_mut() {
|
||||
let cur = v.clone().into();
|
||||
if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() {
|
||||
stk.run(|stk| v.fetch(stk, ctx, opt, path)).await?;
|
||||
_ => break,
|
||||
},
|
||||
Part::First => match this {
|
||||
Value::Array(x) => {
|
||||
let Some(x) = x.first_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
Part::Last => match this {
|
||||
Value::Array(x) => {
|
||||
let Some(x) = x.last_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
this = x;
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
Part::Where(w) => match this {
|
||||
Value::Array(x) => {
|
||||
for v in x.iter_mut() {
|
||||
let doc = v.clone().into();
|
||||
if w.compute(stk, ctx, opt, Some(&doc)).await?.is_truthy() {
|
||||
stk.run(|stk| v.fetch(stk, ctx, opt, iter.as_slice())).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
stk.scope(|scope| {
|
||||
let futs =
|
||||
v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
// Current path part is a thing
|
||||
Value::Thing(v) => {
|
||||
// Clone the thing
|
||||
let val = v.clone();
|
||||
// Fetch the remote embedded record
|
||||
match p {
|
||||
// This is a graph traversal expression
|
||||
Part::Graph(g) => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All], false),
|
||||
what: Values(vec![Value::from(Edges {
|
||||
from: val,
|
||||
dir: g.dir.clone(),
|
||||
what: g.what.clone(),
|
||||
})]),
|
||||
cond: g.cond.clone(),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
*self = stm
|
||||
.compute(stk, ctx, opt, None)
|
||||
.await?
|
||||
.all()
|
||||
.get(stk, ctx, opt, None, path.next())
|
||||
.await?
|
||||
.flatten()
|
||||
.ok()?;
|
||||
Ok(())
|
||||
}
|
||||
// This is a remote field expression
|
||||
_ => {
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All], false),
|
||||
what: Values(vec![Value::from(val)]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
*self = stm.compute(stk, ctx, opt, None).await?.first();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ignore everything else
|
||||
_ => Ok(()),
|
||||
},
|
||||
// No more parts so get the value
|
||||
None => match self {
|
||||
// Current path part is an array
|
||||
Value::Array(v) => {
|
||||
stk.scope(|scope| {
|
||||
let futs =
|
||||
v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
// Current path part is a thing
|
||||
Value::Thing(v) => {
|
||||
// Clone the thing
|
||||
let val = v.clone();
|
||||
// Fetch the remote embedded record
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All], false),
|
||||
what: Values(vec![Value::from(val)]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
*self = stm.compute(stk, ctx, opt, None).await?.first();
|
||||
Ok(())
|
||||
}
|
||||
// Ignore everything else
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
prev = iter.as_slice();
|
||||
}
|
||||
|
||||
// If the final value is on of following types we still need to compute it.
|
||||
match this {
|
||||
Value::Array(v) => {
|
||||
stk.scope(|scope| {
|
||||
let futs = v.iter_mut().map(|v| scope.run(|stk| v.fetch(stk, ctx, opt, path)));
|
||||
try_join_all(futs)
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
Value::Thing(v) => {
|
||||
// Clone the thing
|
||||
let val = v.clone();
|
||||
// Fetch the remote embedded record
|
||||
let stm = SelectStatement {
|
||||
expr: Fields(vec![Field::All], false),
|
||||
what: Values(vec![Value::from(val)]),
|
||||
..SelectStatement::default()
|
||||
};
|
||||
*this = stm.compute(stk, ctx, opt, None).await?.first();
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,6 +233,21 @@ impl Value {
|
|||
Some(v) => stk.run(|stk| v.get(stk, ctx, opt, doc, path.next())).await,
|
||||
None => Ok(Value::None),
|
||||
},
|
||||
Value::Range(r) => {
|
||||
if let Some(range) = r.slice(v.as_slice()) {
|
||||
let path = path.next();
|
||||
stk.scope(|scope| {
|
||||
let futs = range
|
||||
.iter()
|
||||
.map(|v| scope.run(|stk| v.get(stk, ctx, opt, doc, path)));
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
} else {
|
||||
Ok(Value::None)
|
||||
}
|
||||
}
|
||||
_ => stk.run(|stk| Value::None.get(stk, ctx, opt, doc, path.next())).await,
|
||||
},
|
||||
Part::Method(name, args) => {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use std::collections::btree_map::Entry;
|
||||
|
||||
use crate::ctx::Context;
|
||||
use crate::dbs::Options;
|
||||
use crate::err::Error;
|
||||
use crate::exe::try_join_all_buffered;
|
||||
use crate::sql::part::Next;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Object;
|
||||
use reblessive::tree::Stk;
|
||||
|
||||
impl Value {
|
||||
|
@ -19,162 +21,254 @@ impl Value {
|
|||
path: &[Part],
|
||||
val: Value,
|
||||
) -> Result<(), Error> {
|
||||
match path.first() {
|
||||
// Get the current value at path
|
||||
Some(p) => match self {
|
||||
// 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() => {
|
||||
stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await
|
||||
}
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
stk.run(|stk| obj.set(stk, ctx, opt, 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() => {
|
||||
stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await
|
||||
}
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
stk.run(|stk| obj.set(stk, ctx, opt, 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() => {
|
||||
stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await
|
||||
}
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
stk.run(|stk| obj.set(stk, ctx, opt, path.next(), val)).await?;
|
||||
v.insert(i.to_string(), obj);
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
Part::Value(x) => match stk.run(|stk| x.compute(stk, ctx, opt, 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, path.next(), val)).await
|
||||
}
|
||||
_ => {
|
||||
let mut obj = Value::base();
|
||||
stk.run(|stk| obj.set(stk, ctx, opt, path.next(), val)).await?;
|
||||
v.insert(f.to_raw(), obj);
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
},
|
||||
// Current value at path is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => {
|
||||
let path = path.next();
|
||||
|
||||
stk.scope(|scope| {
|
||||
let futs = v
|
||||
.iter_mut()
|
||||
.map(|v| scope.run(|stk| v.set(stk, ctx, opt, path, val.clone())));
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
Part::First => match v.first_mut() {
|
||||
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Last => match v.last_mut() {
|
||||
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Index(i) => match v.get_mut(i.to_usize()) {
|
||||
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
Part::Where(w) => match path.next().first() {
|
||||
Some(Part::Index(_)) => {
|
||||
let mut a = Vec::new();
|
||||
let mut p = Vec::new();
|
||||
// Store the elements and positions to update
|
||||
for (i, o) in v.iter_mut().enumerate() {
|
||||
let cur = o.clone().into();
|
||||
if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() {
|
||||
a.push(o.clone());
|
||||
p.push(i);
|
||||
}
|
||||
}
|
||||
// Convert the matched elements array to a value
|
||||
let mut a = Value::from(a);
|
||||
// Set the new value on the matches elements
|
||||
stk.run(|stk| a.set(stk, ctx, opt, 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())]);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
let path = path.next();
|
||||
for v in v.iter_mut() {
|
||||
let cur = v.clone().into();
|
||||
if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() {
|
||||
stk.run(|stk| v.set(stk, ctx, opt, path, val.clone())).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
Part::Value(x) => match x.compute(stk, ctx, opt, None).await? {
|
||||
Value::Number(i) => match v.get_mut(i.to_usize()) {
|
||||
Some(v) => stk.run(|stk| v.set(stk, ctx, opt, path.next(), val)).await,
|
||||
None => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => {
|
||||
stk.scope(|scope| {
|
||||
let futs = v
|
||||
.iter_mut()
|
||||
.map(|v| scope.run(|stk| v.set(stk, ctx, opt, path, val.clone())));
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
// Current value at path is a record
|
||||
Value::Thing(_) => {
|
||||
*self = Value::base();
|
||||
stk.run(|stk| self.set(stk, ctx, opt, path, val)).await
|
||||
}
|
||||
// Current value at path is empty
|
||||
Value::Null => {
|
||||
*self = Value::base();
|
||||
stk.run(|stk| self.set(stk, ctx, opt, path, val)).await
|
||||
}
|
||||
// Current value at path is empty
|
||||
Value::None => {
|
||||
*self = Value::base();
|
||||
stk.run(|stk| self.set(stk, ctx, opt, path, val)).await
|
||||
}
|
||||
// Ignore everything else
|
||||
_ => Ok(()),
|
||||
},
|
||||
// No more parts so set the value
|
||||
None => {
|
||||
*self = val;
|
||||
Ok(())
|
||||
}
|
||||
if path.is_empty() {
|
||||
*self = val;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut iter = path.iter();
|
||||
let mut place = self;
|
||||
let mut prev = path;
|
||||
|
||||
// Index forward trying to find the location where to insert the value
|
||||
// Whenever we hit an existing path in the value we update place to point to the new value.
|
||||
// If we hit a dead end, we then assign the into that dead end. If any path is not yet
|
||||
// matched we use that to create an object to assign.
|
||||
while let Some(p) = iter.next() {
|
||||
match place {
|
||||
Value::Thing(_) | Value::Null | Value::None => {
|
||||
// any index is guaranteed to fail so just assign to this place.
|
||||
return Self::assign(stk, ctx, opt, place, val, prev).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match p {
|
||||
Part::Graph(g) => {
|
||||
match place {
|
||||
Value::Object(obj) => match obj.entry(g.to_raw()) {
|
||||
Entry::Vacant(x) => {
|
||||
let v = x.insert(Value::None);
|
||||
return Self::assign(stk, ctx, opt, v, val, iter.as_slice()).await;
|
||||
}
|
||||
Entry::Occupied(x) => {
|
||||
place = x.into_mut();
|
||||
}
|
||||
},
|
||||
Value::Array(arr) => {
|
||||
// Apply to all entries of the array
|
||||
stk.scope(|scope| {
|
||||
let futs = arr.iter_mut().map(|v| {
|
||||
scope.run(|stk| v.set(stk, ctx, opt, prev, val.clone()))
|
||||
});
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
}
|
||||
Part::Field(f) => {
|
||||
match place {
|
||||
Value::Object(obj) => match obj.entry(f.0.clone()) {
|
||||
Entry::Vacant(x) => {
|
||||
let v = x.insert(Value::None);
|
||||
return Self::assign(stk, ctx, opt, v, val, iter.as_slice()).await;
|
||||
}
|
||||
Entry::Occupied(x) => {
|
||||
place = x.into_mut();
|
||||
}
|
||||
},
|
||||
Value::Array(arr) => {
|
||||
// Apply to all entries of the array
|
||||
stk.scope(|scope| {
|
||||
let futs = arr.iter_mut().map(|v| {
|
||||
scope.run(|stk| v.set(stk, ctx, opt, prev, val.clone()))
|
||||
});
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
}
|
||||
Part::Index(f) => match place {
|
||||
Value::Object(obj) => match obj.entry(f.to_string()) {
|
||||
Entry::Vacant(x) => {
|
||||
let v = x.insert(Value::None);
|
||||
return Self::assign(stk, ctx, opt, v, val, prev).await;
|
||||
}
|
||||
Entry::Occupied(x) => {
|
||||
place = x.into_mut();
|
||||
}
|
||||
},
|
||||
Value::Array(arr) => {
|
||||
if let Some(x) = arr.get_mut(f.to_usize()) {
|
||||
place = x
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
Part::Value(x) => {
|
||||
let v = stk.run(|stk| x.compute(stk, ctx, opt, None)).await?;
|
||||
|
||||
match place {
|
||||
Value::Object(obj) => {
|
||||
let v = match v {
|
||||
Value::Strand(x) => x.0.clone(),
|
||||
x => x.to_string(),
|
||||
};
|
||||
|
||||
match obj.entry(v) {
|
||||
Entry::Vacant(x) => {
|
||||
let v = x.insert(Value::None);
|
||||
return Self::assign(stk, ctx, opt, v, val, prev).await;
|
||||
}
|
||||
Entry::Occupied(x) => {
|
||||
place = x.into_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
Value::Array(arr) => match v {
|
||||
Value::Range(x) => {
|
||||
if let Some(v) = x.slice_mut(arr) {
|
||||
let path = iter.as_slice();
|
||||
stk.scope(|scope| {
|
||||
let futs = v.iter_mut().map(|v| {
|
||||
scope.run(|stk| v.set(stk, ctx, opt, path, val.clone()))
|
||||
});
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Value::Number(i) => {
|
||||
if let Some(v) = arr.get_mut(i.to_usize()) {
|
||||
place = v;
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => return Ok(()),
|
||||
},
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
Part::First => {
|
||||
let Value::Array(arr) = place else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(x) = arr.first_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
place = x
|
||||
}
|
||||
Part::Last => {
|
||||
let Value::Array(arr) = place else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(x) = arr.last_mut() else {
|
||||
return Ok(());
|
||||
};
|
||||
place = x
|
||||
}
|
||||
Part::All => {
|
||||
let Value::Array(arr) = place else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let path = iter.as_slice();
|
||||
stk.scope(|scope| {
|
||||
let futs = arr
|
||||
.iter_mut()
|
||||
.map(|v| scope.run(|stk| v.set(stk, ctx, opt, path, val.clone())));
|
||||
try_join_all_buffered(futs)
|
||||
})
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
Part::Where(w) => {
|
||||
let Value::Array(arr) = place else {
|
||||
return Ok(());
|
||||
};
|
||||
if let Some(Part::Index(_)) = iter.as_slice().first() {
|
||||
let mut a = Vec::new();
|
||||
let mut p = Vec::new();
|
||||
// Store the elements and positions to update
|
||||
for (i, o) in arr.iter_mut().enumerate() {
|
||||
let cur = o.clone().into();
|
||||
if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() {
|
||||
a.push(o.clone());
|
||||
p.push(i);
|
||||
}
|
||||
}
|
||||
// Convert the matched elements array to a value
|
||||
let mut a = Value::from(a);
|
||||
// Set the new value on the matches elements
|
||||
stk.run(|stk| a.set(stk, ctx, opt, iter.as_slice(), val.clone())).await?;
|
||||
// Push the new values into the original array
|
||||
for (i, p) in p.into_iter().enumerate() {
|
||||
arr[p] = a.pick(&[Part::Index(i.into())]);
|
||||
}
|
||||
return Ok(());
|
||||
} else {
|
||||
for v in arr.iter_mut() {
|
||||
let cur = v.clone().into();
|
||||
if w.compute(stk, ctx, opt, Some(&cur)).await?.is_truthy() {
|
||||
stk.run(|stk| v.set(stk, ctx, opt, iter.as_slice(), val.clone()))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => return Ok(()),
|
||||
}
|
||||
prev = iter.as_slice();
|
||||
}
|
||||
|
||||
*place = val;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn assign(
|
||||
stk: &mut Stk,
|
||||
ctx: &Context,
|
||||
opt: &Options,
|
||||
place: &mut Value,
|
||||
mut val: Value,
|
||||
path: &[Part],
|
||||
) -> Result<(), Error> {
|
||||
for p in path.iter().rev() {
|
||||
let name = match p {
|
||||
Part::Graph(x) => x.to_raw(),
|
||||
Part::Field(f) => f.0.clone(),
|
||||
Part::Index(i) => i.to_string(),
|
||||
Part::Value(x) => {
|
||||
let v = stk.run(|stk| x.compute(stk, ctx, opt, None)).await?;
|
||||
match v {
|
||||
Value::Strand(x) => x.0,
|
||||
Value::Number(x) => x.to_string(),
|
||||
Value::Range(x) => x.to_string(),
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let mut object = Object::default();
|
||||
object.insert(name, val);
|
||||
val = object.into();
|
||||
}
|
||||
|
||||
*place = val;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -346,25 +346,18 @@ impl Parser<'_> {
|
|||
self.pop_peek();
|
||||
Part::Last
|
||||
}
|
||||
t!("+") | TokenKind::Digits | TokenKind::Glued(Glued::Number) => {
|
||||
Part::Index(self.next_token_value()?)
|
||||
}
|
||||
t!("-") => {
|
||||
if let TokenKind::Digits = self.peek_whitespace1().kind {
|
||||
unexpected!(self, peek,"$, * or a number", => "An index can't be negative.");
|
||||
}
|
||||
unexpected!(self, peek, "$, * or a number");
|
||||
}
|
||||
t!("?") | t!("WHERE") => {
|
||||
self.pop_peek();
|
||||
let value = ctx.run(|ctx| self.parse_value_field(ctx)).await?;
|
||||
Part::Where(value)
|
||||
}
|
||||
t!("$param") => Part::Value(Value::Param(self.next_token_value()?)),
|
||||
TokenKind::Qoute(_x) => Part::Value(Value::Strand(self.next_token_value()?)),
|
||||
_ => {
|
||||
let idiom = self.parse_basic_idiom(ctx).await?;
|
||||
Part::Value(Value::Idiom(idiom))
|
||||
let value = ctx.run(|ctx| self.parse_value_inherit(ctx)).await?;
|
||||
if let Value::Number(x) = value {
|
||||
Part::Index(x)
|
||||
} else {
|
||||
Part::Value(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
self.expect_closing_delimiter(t!("]"), start)?;
|
||||
|
|
|
@ -22,7 +22,7 @@ macro_rules! unexpected {
|
|||
$crate::syn::error::bail!("Unexpected whitespace, expected token {} to continue",$expected, @__found.span$( $($t)* )?)
|
||||
}
|
||||
x => {
|
||||
$crate::syn::error::bail!("Unexpected token {}, expected {}",x,$expected, @__found.span$( $($t)* )?)
|
||||
$crate::syn::error::bail!("Unexpected token `{}`, expected {}",x,$expected, @__found.span$( $($t)* )?)
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
|
|
@ -4,15 +4,37 @@ use reblessive::Stack;
|
|||
|
||||
use crate::{
|
||||
sql::{
|
||||
Array, Constant, Id, Idiom, Number, Object, Part, Query, Statement, Statements, Strand,
|
||||
Thing, Value,
|
||||
Array, Constant, Expression, Geometry, Id, Ident, Idiom, Number, Object, Operator, Part,
|
||||
Query, Statement, Statements, Strand, Thing, Value,
|
||||
},
|
||||
syn::parser::{mac::test_parse, Parser},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn parse_index_expression() {
|
||||
let value = test_parse!(parse_value_table, "a[1 + 1]").unwrap();
|
||||
let Value::Idiom(x) = value else {
|
||||
panic!("not the right value type");
|
||||
};
|
||||
assert_eq!(x.0[0], Part::Field(Ident("a".to_string())));
|
||||
assert_eq!(
|
||||
x.0[1],
|
||||
Part::Value(Value::Expression(Box::new(Expression::Binary {
|
||||
l: Value::Number(Number::Int(1)),
|
||||
o: Operator::Add,
|
||||
r: Value::Number(Number::Int(1)),
|
||||
})))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_coordinate() {
|
||||
test_parse!(parse_value_table, "(1.88, -18.0)").unwrap();
|
||||
let coord = test_parse!(parse_value_table, "(1.88, -18.0)").unwrap();
|
||||
let Value::Geometry(Geometry::Point(x)) = coord else {
|
||||
panic!("not the right value");
|
||||
};
|
||||
assert_eq!(x.x(), 1.88);
|
||||
assert_eq!(x.y(), -18.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -12,3 +12,47 @@ async fn idiom_chain_part_optional() -> Result<(), Error> {
|
|||
Test::new(sql).await?.expect_val("false")?.expect_val("None")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn idiom_index_expression() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
[1,2,3,4][1 + 1];
|
||||
"#;
|
||||
Test::new(sql).await?.expect_val("3")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn idiom_index_call() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
DEFINE FUNCTION fn::foo() {
|
||||
return 1 + 1;
|
||||
};
|
||||
RETURN [1,2,3,4][fn::foo()];
|
||||
"#;
|
||||
Test::new(sql).await?.expect_val("None")?.expect_val("3")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn idiom_index_range() -> Result<(), Error> {
|
||||
let sql = r#"
|
||||
[1,2,3,4][1..2];
|
||||
[1,2,3,4][1..=2];
|
||||
[1,2,3,4][1>..=2];
|
||||
[1,2,3,4][1>..];
|
||||
[1,2,3,4][1..];
|
||||
[1,2,3,4][..2];
|
||||
[1,2,3,4][..=2];
|
||||
"#;
|
||||
Test::new(sql)
|
||||
.await?
|
||||
.expect_val("[2]")?
|
||||
.expect_val("[2,3]")?
|
||||
.expect_val("[3]")?
|
||||
.expect_val("[3,4]")?
|
||||
.expect_val("[2,3,4]")?
|
||||
.expect_val("[1,2]")?
|
||||
.expect_val("[1,2,3]")?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue