Consolidate field expression code into single location

This commit is contained in:
Tobie Morgan Hitchcock 2022-07-10 23:11:28 +01:00
parent b0bcecf5da
commit db7ed18119
2 changed files with 106 additions and 201 deletions
lib/src

View file

@ -4,10 +4,8 @@ use crate::dbs::Statement;
use crate::dbs::Transaction;
use crate::doc::Document;
use crate::err::Error;
use crate::sql::field::Field;
use crate::sql::idiom::Idiom;
use crate::sql::output::Output;
use crate::sql::part::Part;
use crate::sql::permission::Permission;
use crate::sql::value::Value;
@ -29,207 +27,10 @@ impl<'a> Document<'a> {
Output::Diff => Ok(self.initial.diff(&self.current, Idiom::default()).into()),
Output::After => self.current.compute(ctx, opt, txn, Some(&self.current)).await,
Output::Before => self.initial.compute(ctx, opt, txn, Some(&self.initial)).await,
Output::Fields(v) => {
let mut out = match v.all() {
true => self.current.compute(ctx, opt, txn, Some(&self.current)).await?,
false => Value::base(),
};
for v in v.other() {
match v {
Field::All => (),
Field::Alone(v) => match v {
// This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here
let mut res: Vec<(&[Part], Value)> = Vec::new();
// Split the expression by each output alias
for v in v.split_inclusive(Idiom::split_multi_yield) {
// Use the last fetched value for each fetch
let x = match res.last() {
Some((_, r)) => r,
None => self.current.as_ref(),
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, v)
.await?
.compute(ctx, opt, txn, Some(&self.current))
.await?;
// Add the result to the temporary store
res.push((v, x));
}
// Assign each fetched yield to the output
for (p, x) in res {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(i) => out.set(ctx, opt, txn, i, x).await?,
// This is the end of the expression
None => out.set(ctx, opt, txn, v, x).await?,
}
}
}
// This expression is a normal field expression
_ => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?
}
},
Field::Alias(v, i) => match v {
// This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here
let mut res: Vec<(&[Part], Value)> = Vec::new();
// Split the expression by each output alias
for v in v.split_inclusive(Idiom::split_multi_yield) {
// Use the last fetched value for each fetch
let x = match res.last() {
Some((_, r)) => r,
None => self.current.as_ref(),
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, v)
.await?
.compute(ctx, opt, txn, Some(&self.current))
.await?;
// Add the result to the temporary store
res.push((v, x));
}
// Assign each fetched yield to the output
for (p, x) in res {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(i) => out.set(ctx, opt, txn, i, x).await?,
// This is the end of the expression
None => out.set(ctx, opt, txn, i, x).await?,
}
}
}
_ => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, i, x).await?
}
},
}
}
Ok(out)
}
Output::Fields(v) => v.compute(ctx, opt, txn, Some(&self.current)).await,
},
None => match stm {
Statement::Select(s) => {
let mut out = match s.expr.all() {
true => self.current.compute(ctx, opt, txn, Some(&self.current)).await?,
false => Value::base(),
};
for v in s.expr.other() {
match v {
Field::All => (),
Field::Alone(v) => match v {
// This expression is a grouped aggregate function
Value::Function(f) if s.group.is_some() && f.is_aggregate() => {
let x = match f.args().len() {
// If no function arguments, then compute the result
0 => f.compute(ctx, opt, txn, Some(&self.current)).await?,
// If arguments, then pass the first value through
_ => {
f.args()[0]
.compute(ctx, opt, txn, Some(&self.current))
.await?
}
};
out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
}
// This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here
let mut res: Vec<(&[Part], Value)> = Vec::new();
// Split the expression by each output alias
for v in v.split_inclusive(Idiom::split_multi_yield) {
// Use the last fetched value for each fetch
let x = match res.last() {
Some((_, r)) => r,
None => self.current.as_ref(),
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, v)
.await?
.compute(ctx, opt, txn, Some(&self.current))
.await?;
// Add the result to the temporary store
res.push((v, x));
}
// Assign each fetched yield to the output
for (p, x) in res {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(i) => out.set(ctx, opt, txn, i, x).await?,
// This is the end of the expression
None => out.set(ctx, opt, txn, v, x).await?,
}
}
}
// This expression is a normal field expression
_ => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
}
},
Field::Alias(v, i) => match v {
// This expression is a grouped aggregate function
Value::Function(f) if s.group.is_some() && f.is_aggregate() => {
let x = match f.args().len() {
// If no function arguments, then compute the result
0 => f.compute(ctx, opt, txn, Some(&self.current)).await?,
// If arguments, then pass the first value through
_ => {
f.args()[0]
.compute(ctx, opt, txn, Some(&self.current))
.await?
}
};
out.set(ctx, opt, txn, i, x).await?;
}
// This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here
let mut res: Vec<(&[Part], Value)> = Vec::new();
// Split the expression by each output alias
for v in v.split_inclusive(Idiom::split_multi_yield) {
// Use the last fetched value for each fetch
let x = match res.last() {
Some((_, r)) => r,
None => self.current.as_ref(),
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, v)
.await?
.compute(ctx, opt, txn, Some(&self.current))
.await?;
// Add the result to the temporary store
res.push((v, x));
}
// Assign each fetched yield to the output
for (p, x) in res {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(i) => out.set(ctx, opt, txn, i, x).await?,
// This is the end of the expression
None => out.set(ctx, opt, txn, i, x).await?,
}
}
}
// This expression is a normal field expression
_ => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, i, x).await?;
}
},
}
}
Ok(out)
}
Statement::Select(s) => s.expr.compute(ctx, opt, txn, Some(&self.current)).await,
Statement::Create(_) => {
self.current.compute(ctx, opt, txn, Some(&self.current)).await
}

View file

@ -1,7 +1,12 @@
use crate::ctx::Context;
use crate::dbs::Options;
use crate::dbs::Transaction;
use crate::err::Error;
use crate::sql::comment::shouldbespace;
use crate::sql::common::commas;
use crate::sql::error::IResult;
use crate::sql::idiom::{idiom, Idiom};
use crate::sql::part::Part;
use crate::sql::value::{value, Value};
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
@ -54,6 +59,105 @@ impl fmt::Display for Fields {
}
}
impl Fields {
pub(crate) async fn compute(
&self,
ctx: &Context<'_>,
opt: &Options,
txn: &Transaction,
doc: Option<&Value>,
) -> Result<Value, Error> {
// Ensure futures are run
let opt = &opt.futures(true);
//
let doc = doc.unwrap_or(&Value::None);
// Process the desired output
let mut out = match self.all() {
true => doc.compute(ctx, opt, txn, Some(doc)).await?,
false => Value::base(),
};
for v in self.other() {
match v {
Field::All => (),
Field::Alone(v) => match v {
// This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here
let mut res: Vec<(&[Part], Value)> = Vec::new();
// Split the expression by each output alias
for v in v.split_inclusive(Idiom::split_multi_yield) {
// Use the last fetched value for each fetch
let x = match res.last() {
Some((_, r)) => r,
None => doc,
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, v)
.await?
.compute(ctx, opt, txn, Some(doc))
.await?;
// Add the result to the temporary store
res.push((v, x));
}
// Assign each fetched yield to the output
for (p, x) in res {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(i) => out.set(ctx, opt, txn, i, x).await?,
// This is the end of the expression
None => out.set(ctx, opt, txn, v, x).await?,
}
}
}
// This expression is a normal field expression
_ => {
let x = v.compute(ctx, opt, txn, Some(doc)).await?;
out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
}
},
Field::Alias(v, i) => match v {
// This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here
let mut res: Vec<(&[Part], Value)> = Vec::new();
// Split the expression by each output alias
for v in v.split_inclusive(Idiom::split_multi_yield) {
// Use the last fetched value for each fetch
let x = match res.last() {
Some((_, r)) => r,
None => doc,
};
// Continue fetching the next idiom part
let x = x
.get(ctx, opt, txn, v)
.await?
.compute(ctx, opt, txn, Some(doc))
.await?;
// Add the result to the temporary store
res.push((v, x));
}
// Assign each fetched yield to the output
for (p, x) in res {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(i) => out.set(ctx, opt, txn, i, x).await?,
// This is the end of the expression
None => out.set(ctx, opt, txn, i, x).await?,
}
}
}
_ => {
let x = v.compute(ctx, opt, txn, Some(doc)).await?;
out.set(ctx, opt, txn, i, x).await?;
}
},
}
}
Ok(out)
}
}
pub fn fields(i: &str) -> IResult<&str, Fields> {
let (i, v) = separated_list1(commas, field)(i)?;
Ok((i, Fields(v)))