Refactor - consolidate Field
enum to reduce redundancy. (#2104)
This commit is contained in:
parent
98c5bafd37
commit
5ebf4ee963
7 changed files with 271 additions and 325 deletions
|
@ -14,6 +14,7 @@ use crate::sql::table::Table;
|
||||||
use crate::sql::thing::Thing;
|
use crate::sql::thing::Thing;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -219,36 +220,31 @@ impl Iterator {
|
||||||
let vals = Value::from(vals);
|
let vals = Value::from(vals);
|
||||||
// Loop over each group clause
|
// Loop over each group clause
|
||||||
for field in fields.other() {
|
for field in fields.other() {
|
||||||
// Process it if it is a normal field
|
// Process the field
|
||||||
if let Field::Alone(v) = field {
|
if let Field::Single {
|
||||||
match v {
|
expr,
|
||||||
|
alias,
|
||||||
|
} = field
|
||||||
|
{
|
||||||
|
let idiom = alias
|
||||||
|
.as_ref()
|
||||||
|
.map(Cow::Borrowed)
|
||||||
|
.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
|
||||||
|
match expr {
|
||||||
Value::Function(f) if f.is_aggregate() => {
|
Value::Function(f) if f.is_aggregate() => {
|
||||||
let x = vals
|
let x =
|
||||||
.all()
|
vals.all().get(ctx, opt, txn, None, idiom.as_ref()).await?;
|
||||||
.get(ctx, opt, txn, None, v.to_idiom().as_ref())
|
|
||||||
.await?;
|
|
||||||
let x = f.aggregate(x).compute(ctx, opt, txn, None).await?;
|
let x = f.aggregate(x).compute(ctx, opt, txn, None).await?;
|
||||||
obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
|
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let x = vals.first();
|
let x = vals.first();
|
||||||
let x = v.compute(ctx, opt, txn, Some(&x)).await?;
|
let x = if let Some(alias) = alias {
|
||||||
obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
|
alias.compute(ctx, opt, txn, Some(&x)).await?
|
||||||
}
|
} else {
|
||||||
}
|
expr.compute(ctx, opt, txn, Some(&x)).await?
|
||||||
}
|
};
|
||||||
// Process it if it is a aliased field
|
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
|
||||||
if let Field::Alias(v, i) = field {
|
|
||||||
match v {
|
|
||||||
Value::Function(f) if f.is_aggregate() => {
|
|
||||||
let x = vals.all().get(ctx, opt, txn, None, i).await?;
|
|
||||||
let x = f.aggregate(x).compute(ctx, opt, txn, None).await?;
|
|
||||||
obj.set(ctx, opt, txn, i, x).await?;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let x = vals.first();
|
|
||||||
let x = i.compute(ctx, opt, txn, Some(&x)).await?;
|
|
||||||
obj.set(ctx, opt, txn, i, x).await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,67 +261,40 @@ impl<'a> Document<'a> {
|
||||||
};
|
};
|
||||||
//
|
//
|
||||||
for field in exp.other() {
|
for field in exp.other() {
|
||||||
// Process it if it is a normal field
|
// Process the field
|
||||||
if let Field::Alone(v) = field {
|
if let Field::Single {
|
||||||
match v {
|
expr,
|
||||||
|
alias,
|
||||||
|
} = field
|
||||||
|
{
|
||||||
|
let idiom = alias.clone().unwrap_or_else(|| expr.to_idiom());
|
||||||
|
match expr {
|
||||||
Value::Function(f) if f.is_rolling() => match f.name() {
|
Value::Function(f) if f.is_rolling() => match f.name() {
|
||||||
"count" => {
|
"count" => {
|
||||||
let val = f.compute(ctx, opt, txn, doc).await?;
|
let val = f.compute(ctx, opt, txn, doc).await?;
|
||||||
self.chg(&mut ops, &act, v.to_idiom(), val);
|
self.chg(&mut ops, &act, idiom, val);
|
||||||
}
|
}
|
||||||
"math::sum" => {
|
"math::sum" => {
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||||
self.chg(&mut ops, &act, v.to_idiom(), val);
|
self.chg(&mut ops, &act, idiom, val);
|
||||||
}
|
}
|
||||||
"math::min" => {
|
"math::min" => {
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||||
self.min(&mut ops, &act, v.to_idiom(), val);
|
self.min(&mut ops, &act, idiom, val);
|
||||||
}
|
}
|
||||||
"math::max" => {
|
"math::max" => {
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||||
self.max(&mut ops, &act, v.to_idiom(), val);
|
self.max(&mut ops, &act, idiom, val);
|
||||||
}
|
}
|
||||||
"math::mean" => {
|
"math::mean" => {
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
||||||
self.mean(&mut ops, &act, v.to_idiom(), val);
|
self.mean(&mut ops, &act, idiom, val);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let val = v.compute(ctx, opt, txn, doc).await?;
|
let val = expr.compute(ctx, opt, txn, doc).await?;
|
||||||
self.set(&mut ops, v.to_idiom(), val);
|
self.set(&mut ops, idiom, val);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Process it if it is a aliased field
|
|
||||||
if let Field::Alias(v, i) = field {
|
|
||||||
match v {
|
|
||||||
Value::Function(f) if f.is_rolling() => match f.name() {
|
|
||||||
"count" => {
|
|
||||||
let val = f.compute(ctx, opt, txn, doc).await?;
|
|
||||||
self.chg(&mut ops, &act, i.to_owned(), val);
|
|
||||||
}
|
|
||||||
"math::sum" => {
|
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
|
||||||
self.chg(&mut ops, &act, i.to_owned(), val);
|
|
||||||
}
|
|
||||||
"math::min" => {
|
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
|
||||||
self.min(&mut ops, &act, i.to_owned(), val);
|
|
||||||
}
|
|
||||||
"math::max" => {
|
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
|
||||||
self.max(&mut ops, &act, i.to_owned(), val);
|
|
||||||
}
|
|
||||||
"math::mean" => {
|
|
||||||
let val = f.args()[0].compute(ctx, opt, txn, doc).await?;
|
|
||||||
self.mean(&mut ops, &act, i.to_owned(), val);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let val = v.compute(ctx, opt, txn, doc).await?;
|
|
||||||
self.set(&mut ops, i.to_owned(), val);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt::{self, Display, Formatter, Write};
|
use std::fmt::{self, Display, Formatter, Write};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
@ -91,122 +92,79 @@ impl Fields {
|
||||||
for v in self.other() {
|
for v in self.other() {
|
||||||
match v {
|
match v {
|
||||||
Field::All => (),
|
Field::All => (),
|
||||||
Field::Alone(v) => match v {
|
Field::Single {
|
||||||
// This expression is a grouped aggregate function
|
expr,
|
||||||
Value::Function(f) if group && f.is_aggregate() => {
|
alias,
|
||||||
let x = match f.args().len() {
|
} => {
|
||||||
// If no function arguments, then compute the result
|
let idiom = alias
|
||||||
0 => f.compute(ctx, opt, txn, Some(doc)).await?,
|
.as_ref()
|
||||||
// If arguments, then pass the first value through
|
.map(Cow::Borrowed)
|
||||||
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?,
|
.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
|
||||||
};
|
match expr {
|
||||||
// Check if this is a single VALUE field expression
|
// This expression is a grouped aggregate function
|
||||||
match self.single().is_some() {
|
Value::Function(f) if group && f.is_aggregate() => {
|
||||||
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?,
|
let x = match f.args().len() {
|
||||||
true => out = x,
|
// If no function arguments, then compute the result
|
||||||
}
|
0 => f.compute(ctx, opt, txn, Some(doc)).await?,
|
||||||
}
|
// If arguments, then pass the first value through
|
||||||
// This expression is a multi-output graph traversal
|
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?,
|
||||||
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
|
// Check if this is a single VALUE field expression
|
||||||
let x = x
|
match self.single().is_some() {
|
||||||
.get(ctx, opt, txn, Some(doc), v)
|
false => out.set(ctx, opt, txn, idiom.as_ref(), x).await?,
|
||||||
.await?
|
true => out = x,
|
||||||
.compute(ctx, opt, txn, Some(doc))
|
|
||||||
.await?
|
|
||||||
.flatten();
|
|
||||||
// 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(a) => out.set(ctx, opt, txn, a, x).await?,
|
|
||||||
// This is the end of the expression
|
|
||||||
None => out.set(ctx, opt, txn, v, x).await?,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// This expression is a multi-output graph traversal
|
||||||
// This expression is a normal field expression
|
Value::Idiom(v) if v.is_multi_yield() => {
|
||||||
_ => {
|
// Store the different output yields here
|
||||||
let x = v.compute(ctx, opt, txn, Some(doc)).await?;
|
let mut res: Vec<(&[Part], Value)> = Vec::new();
|
||||||
// Check if this is a single VALUE field expression
|
// Split the expression by each output alias
|
||||||
match self.single().is_some() {
|
for v in v.split_inclusive(Idiom::split_multi_yield) {
|
||||||
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?,
|
// Use the last fetched value for each fetch
|
||||||
true => out = x,
|
let x = match res.last() {
|
||||||
}
|
Some((_, r)) => r,
|
||||||
}
|
None => doc,
|
||||||
},
|
};
|
||||||
Field::Alias(v, i) => match v {
|
// Continue fetching the next idiom part
|
||||||
// This expression is a grouped aggregate function
|
let x = x
|
||||||
Value::Function(f) if group && f.is_aggregate() => {
|
.get(ctx, opt, txn, Some(doc), v)
|
||||||
let x = match f.args().len() {
|
.await?
|
||||||
// If no function arguments, then compute the result
|
.compute(ctx, opt, txn, Some(doc))
|
||||||
0 => f.compute(ctx, opt, txn, Some(doc)).await?,
|
.await?
|
||||||
// If arguments, then pass the first value through
|
.flatten();
|
||||||
_ => f.args()[0].compute(ctx, opt, txn, Some(doc)).await?,
|
// Add the result to the temporary store
|
||||||
};
|
res.push((v, x));
|
||||||
// Check if this is a single VALUE field expression
|
}
|
||||||
match self.single().is_some() {
|
// Assign each fetched yield to the output
|
||||||
false => out.set(ctx, opt, txn, i, x).await?,
|
for (p, x) in res {
|
||||||
true => out = x,
|
match p.last().unwrap().alias() {
|
||||||
}
|
// This is an alias expression part
|
||||||
}
|
Some(a) => {
|
||||||
// This expression is a multi-output graph traversal
|
if let Some(i) = alias {
|
||||||
Value::Idiom(v) if v.is_multi_yield() => {
|
out.set(ctx, opt, txn, i, x.clone()).await?;
|
||||||
// Store the different output yields here
|
}
|
||||||
let mut res: Vec<(&[Part], Value)> = Vec::new();
|
out.set(ctx, opt, txn, a, x).await?;
|
||||||
// Split the expression by each output alias
|
}
|
||||||
for v in v.split_inclusive(Idiom::split_multi_yield) {
|
// This is the end of the expression
|
||||||
// Use the last fetched value for each fetch
|
None => {
|
||||||
let x = match res.last() {
|
out.set(ctx, opt, txn, alias.as_ref().unwrap_or(v), x)
|
||||||
Some((_, r)) => r,
|
.await?
|
||||||
None => doc,
|
}
|
||||||
};
|
|
||||||
// Continue fetching the next idiom part
|
|
||||||
let x = x
|
|
||||||
.get(ctx, opt, txn, Some(doc), v)
|
|
||||||
.await?
|
|
||||||
.compute(ctx, opt, txn, Some(doc))
|
|
||||||
.await?
|
|
||||||
.flatten();
|
|
||||||
// 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(a) => {
|
|
||||||
let v = x.clone();
|
|
||||||
out.set(ctx, opt, txn, a, x).await?;
|
|
||||||
out.set(ctx, opt, txn, i, v).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 = expr.compute(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, idiom.as_ref(), x).await?,
|
||||||
|
true => out = x,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This expression is a normal field expression
|
}
|
||||||
_ => {
|
|
||||||
let x = v.compute(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, i, x).await?,
|
|
||||||
true => out = x,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
|
@ -230,25 +188,35 @@ fn field_many(i: &str) -> IResult<&str, Fields> {
|
||||||
Ok((i, Fields(v, false)))
|
Ok((i, Fields(v, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||||
pub enum Field {
|
pub enum Field {
|
||||||
|
/// The `*` in `SELECT * FROM ...`
|
||||||
|
#[default]
|
||||||
All,
|
All,
|
||||||
Alone(Value),
|
/// The 'rating' in `SELECT rating FROM ...`
|
||||||
Alias(Value, Idiom),
|
Single {
|
||||||
}
|
expr: Value,
|
||||||
|
/// The `quality` in `SELECT rating AS quality FROM ...`
|
||||||
impl Default for Field {
|
alias: Option<Idiom>,
|
||||||
fn default() -> Self {
|
},
|
||||||
Self::All
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Field {
|
impl Display for Field {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::All => f.write_char('*'),
|
Self::All => f.write_char('*'),
|
||||||
Self::Alone(e) => Display::fmt(e, f),
|
Self::Single {
|
||||||
Self::Alias(e, a) => write!(f, "{e} AS {a}"),
|
expr,
|
||||||
|
alias,
|
||||||
|
} => {
|
||||||
|
Display::fmt(expr, f)?;
|
||||||
|
if let Some(alias) = alias {
|
||||||
|
f.write_str(" AS ")?;
|
||||||
|
Display::fmt(alias, f)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,17 +231,29 @@ pub fn all(i: &str) -> IResult<&str, Field> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alone(i: &str) -> IResult<&str, Field> {
|
pub fn alone(i: &str) -> IResult<&str, Field> {
|
||||||
let (i, f) = value(i)?;
|
let (i, expr) = value(i)?;
|
||||||
Ok((i, Field::Alone(f)))
|
Ok((
|
||||||
|
i,
|
||||||
|
Field::Single {
|
||||||
|
expr,
|
||||||
|
alias: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alias(i: &str) -> IResult<&str, Field> {
|
pub fn alias(i: &str) -> IResult<&str, Field> {
|
||||||
let (i, f) = value(i)?;
|
let (i, expr) = value(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, _) = tag_no_case("AS")(i)?;
|
let (i, _) = tag_no_case("AS")(i)?;
|
||||||
let (i, _) = shouldbespace(i)?;
|
let (i, _) = shouldbespace(i)?;
|
||||||
let (i, a) = idiom(i)?;
|
let (i, alias) = idiom(i)?;
|
||||||
Ok((i, Field::Alias(f, a)))
|
Ok((
|
||||||
|
i,
|
||||||
|
Field::Single {
|
||||||
|
expr,
|
||||||
|
alias: Some(alias),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -4,43 +4,51 @@ use crate::sql::group::Groups;
|
||||||
use crate::sql::order::Orders;
|
use crate::sql::order::Orders;
|
||||||
use crate::sql::split::Splits;
|
use crate::sql::split::Splits;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
use crate::sql::Idiom;
|
||||||
use nom::Err;
|
use nom::Err;
|
||||||
use nom::Err::Failure;
|
use nom::Err::Failure;
|
||||||
|
|
||||||
|
/// Check to see whether the expression is in the SELECT clause
|
||||||
|
fn contains_idiom(fields: &Fields, idiom: &Idiom) -> bool {
|
||||||
|
fields.iter().any(|field| {
|
||||||
|
match field {
|
||||||
|
// There is a SELECT * expression, so presume everything is ok
|
||||||
|
Field::All => true,
|
||||||
|
// Check each field
|
||||||
|
Field::Single {
|
||||||
|
expr,
|
||||||
|
alias,
|
||||||
|
} => {
|
||||||
|
if let Some(i) = alias {
|
||||||
|
// This field is aliased, so check the alias name
|
||||||
|
i.as_ref() == idiom.as_ref()
|
||||||
|
} else {
|
||||||
|
// This field is not aliased, so check the field value
|
||||||
|
match expr {
|
||||||
|
// Use raw idiom (TODO: should this use `simplify`?)
|
||||||
|
Value::Idiom(i) => i.as_ref() == idiom.as_ref(),
|
||||||
|
// Check the expression
|
||||||
|
v => v.to_idiom().as_ref() == idiom.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_split_on_fields<'a>(
|
pub fn check_split_on_fields<'a>(
|
||||||
i: &'a str,
|
i: &'a str,
|
||||||
fields: &Fields,
|
fields: &Fields,
|
||||||
splits: &Option<Splits>,
|
splits: &Option<Splits>,
|
||||||
) -> Result<(), Err<Error<&'a str>>> {
|
) -> Result<(), Err<Error<&'a str>>> {
|
||||||
// Check to see if a ORDER BY clause has been defined
|
// Check to see if a SPLIT ON clause has been defined
|
||||||
if let Some(splits) = splits {
|
if let Some(splits) = splits {
|
||||||
// Loop over each of the expressions in the SPLIT ON clause
|
// Loop over each of the expressions in the SPLIT ON clause
|
||||||
'outer: for split in splits.iter() {
|
for split in splits.iter() {
|
||||||
// Loop over each of the expressions in the SELECT clause
|
if !contains_idiom(fields, &split.0) {
|
||||||
for field in fields.iter() {
|
// If the expression isn't specified in the SELECT clause, then error
|
||||||
// Check to see whether the expression is in the SELECT clause
|
return Err(Failure(Error::Split(i, split.to_string())));
|
||||||
match field {
|
|
||||||
// There is a SELECT * expression, so presume everything is ok
|
|
||||||
Field::All => break 'outer,
|
|
||||||
// This field is aliased, so check the alias name
|
|
||||||
Field::Alias(_, i) if i.as_ref() == split.as_ref() => continue 'outer,
|
|
||||||
// This field is not aliased, so check the field value
|
|
||||||
Field::Alone(v) => {
|
|
||||||
match v {
|
|
||||||
// If the expression in the SELECT clause is a field, check if it exists in the SPLIT ON clause
|
|
||||||
Value::Idiom(i) if i.as_ref() == split.as_ref() => continue 'outer,
|
|
||||||
// Otherwise check if the expression itself exists in the SPLIT ON clause
|
|
||||||
v if v.to_idiom().as_ref() == split.as_ref() => continue 'outer,
|
|
||||||
// If not, then this query should fail
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If not, then this query should fail
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the expression isn't specified in the SELECT clause, then error
|
|
||||||
return Err(Failure(Error::Split(i, split.to_string())));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This query is ok to run
|
// This query is ok to run
|
||||||
|
@ -55,32 +63,11 @@ pub fn check_order_by_fields<'a>(
|
||||||
// Check to see if a ORDER BY clause has been defined
|
// Check to see if a ORDER BY clause has been defined
|
||||||
if let Some(orders) = orders {
|
if let Some(orders) = orders {
|
||||||
// Loop over each of the expressions in the ORDER BY clause
|
// Loop over each of the expressions in the ORDER BY clause
|
||||||
'outer: for order in orders.iter() {
|
for order in orders.iter() {
|
||||||
// Loop over each of the expressions in the SELECT clause
|
if !contains_idiom(fields, &*order) {
|
||||||
for field in fields.iter() {
|
// If the expression isn't specified in the SELECT clause, then error
|
||||||
// Check to see whether the expression is in the SELECT clause
|
return Err(Failure(Error::Order(i, order.to_string())));
|
||||||
match field {
|
|
||||||
// There is a SELECT * expression, so presume everything is ok
|
|
||||||
Field::All => break 'outer,
|
|
||||||
// This field is aliased, so check the alias name
|
|
||||||
Field::Alias(_, i) if i.as_ref() == order.as_ref() => continue 'outer,
|
|
||||||
// This field is not aliased, so check the field value
|
|
||||||
Field::Alone(v) => {
|
|
||||||
match v {
|
|
||||||
// If the expression in the SELECT clause is a field, check if it exists in the ORDER BY clause
|
|
||||||
Value::Idiom(i) if i.as_ref() == order.as_ref() => continue 'outer,
|
|
||||||
// Otherwise check if the expression itself exists in the ORDER BY clause
|
|
||||||
v if v.to_idiom().as_ref() == order.as_ref() => continue 'outer,
|
|
||||||
// If not, then this query should fail
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If not, then this query should fail
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the expression isn't specified in the SELECT clause, then error
|
|
||||||
return Err(Failure(Error::Order(i, order.to_string())));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This query is ok to run
|
// This query is ok to run
|
||||||
|
@ -95,54 +82,43 @@ pub fn check_group_by_fields<'a>(
|
||||||
// Check to see if a GROUP BY clause has been defined
|
// Check to see if a GROUP BY clause has been defined
|
||||||
if let Some(groups) = groups {
|
if let Some(groups) = groups {
|
||||||
// Loop over each of the expressions in the GROUP BY clause
|
// Loop over each of the expressions in the GROUP BY clause
|
||||||
'outer: for group in groups.iter() {
|
for group in groups.iter() {
|
||||||
// Loop over each of the expressions in the SELECT clause
|
if !contains_idiom(fields, &group.0) {
|
||||||
for field in fields.iter() {
|
// If the expression isn't specified in the SELECT clause, then error
|
||||||
// Check to see whether the expression is in the SELECT clause
|
return Err(Failure(Error::Group(i, group.to_string())));
|
||||||
match field {
|
|
||||||
// This field is aliased, so check the alias name
|
|
||||||
Field::Alias(_, i) if i.as_ref() == group.as_ref() => continue 'outer,
|
|
||||||
// This field is not aliased, so check the field value
|
|
||||||
Field::Alone(v) => {
|
|
||||||
match v {
|
|
||||||
// If the expression in the SELECT clause is a field, check if it exists in the GROUP BY clause
|
|
||||||
Value::Idiom(i) if i.as_ref() == group.as_ref() => continue 'outer,
|
|
||||||
// Otherwise check if the expression itself exists in the GROUP BY clause
|
|
||||||
v if v.to_idiom().as_ref() == group.as_ref() => continue 'outer,
|
|
||||||
// If not, then this query should fail
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If not, then this query should fail
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the expression isn't specified in the SELECT clause, then error
|
|
||||||
return Err(Failure(Error::Group(i, group.to_string())));
|
|
||||||
}
|
}
|
||||||
// Check if this is a GROUP ALL clause or a GROUP BY clause
|
// Check if this is a GROUP ALL clause or a GROUP BY clause
|
||||||
if groups.len() > 0 {
|
if !groups.is_empty() {
|
||||||
// Loop over each of the expressions in the SELECT clause
|
// Loop over each of the expressions in the SELECT clause
|
||||||
'outer: for field in fields.iter() {
|
'outer: for field in fields.iter() {
|
||||||
// Loop over each of the expressions in the GROUP BY clause
|
// Loop over each of the expressions in the GROUP BY clause
|
||||||
for group in groups.iter() {
|
for group in groups.iter() {
|
||||||
// Check to see whether the expression is in the SELECT clause
|
// Check to see whether the expression is in the GROUP BY clause or is an aggregate
|
||||||
match field {
|
match field {
|
||||||
// This field is aliased, so check the alias name
|
Field::Single {
|
||||||
Field::Alias(_, i) if i.as_ref() == group.as_ref() => continue 'outer,
|
expr,
|
||||||
// Otherwise, check the type of the field value
|
alias,
|
||||||
Field::Alias(v, _) | Field::Alone(v) => match v {
|
} => {
|
||||||
// If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY
|
if alias.as_ref().map(|i| i.as_ref() == group.as_ref()).unwrap_or(false)
|
||||||
Value::Idiom(i) if i == &group.0 => continue 'outer,
|
{
|
||||||
// If the expression in the SELECT clause is a function, check to see if it is an aggregate function
|
// This field is aliased, and the alias name matched
|
||||||
Value::Function(f) if f.is_aggregate() => continue 'outer,
|
continue 'outer;
|
||||||
// Otherwise check if the expression itself exists in the GROUP BY clause
|
} else {
|
||||||
v if v.to_idiom() == group.0 => continue 'outer,
|
match expr {
|
||||||
// Check if this is a static value which can be used in the GROUP BY clause
|
// If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY
|
||||||
v if v.is_static() => continue 'outer,
|
Value::Idiom(i) if i == &group.0 => continue 'outer,
|
||||||
// If not, then this query should fail
|
// If the expression in the SELECT clause is a function, check to see if it is an aggregate function
|
||||||
_ => (),
|
Value::Function(f) if f.is_aggregate() => continue 'outer,
|
||||||
},
|
// Otherwise check if the expression itself exists in the GROUP BY clause
|
||||||
|
v if v.to_idiom() == group.0 => continue 'outer,
|
||||||
|
// Check if this is a static value which can be used in the GROUP BY clause
|
||||||
|
v if v.is_static() => continue 'outer,
|
||||||
|
// If not, then this query should fail
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,10 @@ impl SelectStatement {
|
||||||
pub(crate) fn writeable(&self) -> bool {
|
pub(crate) fn writeable(&self) -> bool {
|
||||||
if self.expr.iter().any(|v| match v {
|
if self.expr.iter().any(|v| match v {
|
||||||
Field::All => false,
|
Field::All => false,
|
||||||
Field::Alone(v) => v.writeable(),
|
Field::Single {
|
||||||
Field::Alias(v, _) => v.writeable(),
|
expr,
|
||||||
|
..
|
||||||
|
} => expr.writeable(),
|
||||||
}) {
|
}) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@ impl ser::Serializer for Serializer {
|
||||||
type SerializeSeq = Impossible<Field, Error>;
|
type SerializeSeq = Impossible<Field, Error>;
|
||||||
type SerializeTuple = Impossible<Field, Error>;
|
type SerializeTuple = Impossible<Field, Error>;
|
||||||
type SerializeTupleStruct = Impossible<Field, Error>;
|
type SerializeTupleStruct = Impossible<Field, Error>;
|
||||||
type SerializeTupleVariant = SerializeValueIdiomTuple;
|
type SerializeTupleVariant = Impossible<Field, Error>;
|
||||||
type SerializeMap = Impossible<Field, Error>;
|
type SerializeMap = Impossible<Field, Error>;
|
||||||
type SerializeStruct = Impossible<Field, Error>;
|
type SerializeStruct = Impossible<Field, Error>;
|
||||||
type SerializeStructVariant = Impossible<Field, Error>;
|
type SerializeStructVariant = SerializeValueIdiomTuple;
|
||||||
|
|
||||||
const EXPECTED: &'static str = "an enum `Field`";
|
const EXPECTED: &'static str = "an enum `Field`";
|
||||||
|
|
||||||
|
@ -39,77 +39,89 @@ impl ser::Serializer for Serializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn serialize_struct_variant(
|
||||||
fn serialize_newtype_variant<T>(
|
|
||||||
self,
|
|
||||||
name: &'static str,
|
|
||||||
_variant_index: u32,
|
|
||||||
variant: &'static str,
|
|
||||||
value: &T,
|
|
||||||
) -> Result<Self::Ok, Error>
|
|
||||||
where
|
|
||||||
T: ?Sized + Serialize,
|
|
||||||
{
|
|
||||||
match variant {
|
|
||||||
"Alone" => Ok(Field::Alone(value.serialize(ser::value::Serializer.wrap())?)),
|
|
||||||
variant => {
|
|
||||||
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn serialize_tuple_variant(
|
|
||||||
self,
|
self,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
_variant_index: u32,
|
_variant_index: u32,
|
||||||
variant: &'static str,
|
variant: &'static str,
|
||||||
_len: usize,
|
_len: usize,
|
||||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||||
match variant {
|
match variant {
|
||||||
"Alias" => Ok(SerializeValueIdiomTuple::default()),
|
"Single" => Ok(SerializeValueIdiomTuple::default()),
|
||||||
variant => Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))),
|
variant => Err(Error::custom(format!("unexpected struct variant `{name}::{variant}`"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(super) struct SerializeValueIdiomTuple {
|
pub(super) struct SerializeValueIdiomTuple {
|
||||||
index: usize,
|
|
||||||
value: Option<Value>,
|
value: Option<Value>,
|
||||||
idiom: Option<Idiom>,
|
idiom: Option<Option<Idiom>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl serde::ser::SerializeTupleVariant for SerializeValueIdiomTuple {
|
impl serde::ser::SerializeStructVariant for SerializeValueIdiomTuple {
|
||||||
type Ok = Field;
|
type Ok = Field;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
|
||||||
where
|
where
|
||||||
T: Serialize + ?Sized,
|
T: Serialize + ?Sized,
|
||||||
{
|
{
|
||||||
match self.index {
|
match key {
|
||||||
0 => {
|
"expr" => {
|
||||||
self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
|
self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
|
||||||
}
|
}
|
||||||
1 => {
|
"alias" => {
|
||||||
self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?));
|
self.idiom = Some(value.serialize(SerializeOptionIdiom.wrap())?);
|
||||||
}
|
}
|
||||||
index => {
|
key => {
|
||||||
return Err(Error::custom(format!("unexpected `Field::Alias` index `{index}`")));
|
return Err(Error::custom(format!("unexpected `Field::Single` field `{key}`")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.index += 1;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||||
match (self.value, self.idiom) {
|
match (self.value, self.idiom) {
|
||||||
(Some(value), Some(idiom)) => Ok(Field::Alias(value, idiom)),
|
(Some(expr), Some(alias)) => Ok(Field::Single {
|
||||||
_ => Err(Error::custom("`Field::Alias` missing required value(s)")),
|
expr,
|
||||||
|
alias,
|
||||||
|
}),
|
||||||
|
_ => Err(Error::custom("`Field::Single` missing required value(s)")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct SerializeOptionIdiom;
|
||||||
|
|
||||||
|
impl ser::Serializer for SerializeOptionIdiom {
|
||||||
|
type Ok = Option<Idiom>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
type SerializeSeq = Impossible<Self::Ok, Error>;
|
||||||
|
type SerializeTuple = Impossible<Self::Ok, Error>;
|
||||||
|
type SerializeTupleStruct = Impossible<Self::Ok, Error>;
|
||||||
|
type SerializeTupleVariant = Impossible<Self::Ok, Error>;
|
||||||
|
type SerializeMap = Impossible<Self::Ok, Error>;
|
||||||
|
type SerializeStruct = Impossible<Self::Ok, Error>;
|
||||||
|
type SerializeStructVariant = Impossible<Self::Ok, Error>;
|
||||||
|
|
||||||
|
const EXPECTED: &'static str = "an enum `Option<Idiom>`";
|
||||||
|
|
||||||
|
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
let idiom = Idiom(value.serialize(ser::part::vec::Serializer.wrap())?);
|
||||||
|
Ok(Some(idiom))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -124,14 +136,20 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alone() {
|
fn alone() {
|
||||||
let field = Field::Alone(Default::default());
|
let field = Field::Single {
|
||||||
|
expr: Default::default(),
|
||||||
|
alias: None,
|
||||||
|
};
|
||||||
let serialized = field.serialize(Serializer.wrap()).unwrap();
|
let serialized = field.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(field, serialized);
|
assert_eq!(field, serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alias() {
|
fn alias() {
|
||||||
let field = Field::Alias(Default::default(), Default::default());
|
let field = Field::Single {
|
||||||
|
expr: Default::default(),
|
||||||
|
alias: Some(Default::default()),
|
||||||
|
};
|
||||||
let serialized = field.serialize(Serializer.wrap()).unwrap();
|
let serialized = field.serialize(Serializer.wrap()).unwrap();
|
||||||
assert_eq!(field, serialized);
|
assert_eq!(field, serialized);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2225,6 +2225,7 @@ impl Value {
|
||||||
Value::None => true,
|
Value::None => true,
|
||||||
Value::Null => true,
|
Value::Null => true,
|
||||||
Value::Bool(_) => true,
|
Value::Bool(_) => true,
|
||||||
|
Value::Bytes(_) => true,
|
||||||
Value::Uuid(_) => true,
|
Value::Uuid(_) => true,
|
||||||
Value::Number(_) => true,
|
Value::Number(_) => true,
|
||||||
Value::Strand(_) => true,
|
Value::Strand(_) => true,
|
||||||
|
|
Loading…
Reference in a new issue