Refactor - consolidate Field enum to reduce redundancy. (#2104)

This commit is contained in:
Finn Bear 2023-06-10 13:23:22 -07:00 committed by GitHub
parent 98c5bafd37
commit 5ebf4ee963
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 271 additions and 325 deletions

View file

@ -14,6 +14,7 @@ use crate::sql::table::Table;
use crate::sql::thing::Thing;
use crate::sql::value::Value;
use async_recursion::async_recursion;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::mem;
@ -219,36 +220,31 @@ impl Iterator {
let vals = Value::from(vals);
// Loop over each group clause
for field in fields.other() {
// Process it if it is a normal field
if let Field::Alone(v) = field {
match v {
// Process the field
if let Field::Single {
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() => {
let x = vals
.all()
.get(ctx, opt, txn, None, v.to_idiom().as_ref())
.await?;
let x =
vals.all().get(ctx, opt, txn, None, idiom.as_ref()).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 = v.compute(ctx, opt, txn, Some(&x)).await?;
obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
}
}
}
// Process it if it is a aliased field
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?;
let x = if let Some(alias) = alias {
alias.compute(ctx, opt, txn, Some(&x)).await?
} else {
expr.compute(ctx, opt, txn, Some(&x)).await?
};
obj.set(ctx, opt, txn, idiom.as_ref(), x).await?;
}
}
}

View file

@ -261,67 +261,40 @@ impl<'a> Document<'a> {
};
//
for field in exp.other() {
// Process it if it is a normal field
if let Field::Alone(v) = field {
match v {
// Process the field
if let Field::Single {
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() {
"count" => {
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" => {
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" => {
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" => {
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" => {
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!(),
},
_ => {
let val = v.compute(ctx, opt, txn, doc).await?;
self.set(&mut ops, v.to_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);
let val = expr.compute(ctx, opt, txn, doc).await?;
self.set(&mut ops, idiom, val);
}
}
}

View file

@ -14,6 +14,7 @@ use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::multi::separated_list1;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref;
@ -91,7 +92,15 @@ impl Fields {
for v in self.other() {
match v {
Field::All => (),
Field::Alone(v) => match v {
Field::Single {
expr,
alias,
} => {
let idiom = alias
.as_ref()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
match expr {
// This expression is a grouped aggregate function
Value::Function(f) if group && f.is_aggregate() => {
let x = match f.args().len() {
@ -102,63 +111,7 @@ impl Fields {
};
// Check if this is a single VALUE field expression
match self.single().is_some() {
false => out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?,
true => out = x,
}
}
// 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, 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) => 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 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, v.to_idiom().as_ref(), x).await?,
true => out = x,
}
}
},
Field::Alias(v, i) => match v {
// This expression is a grouped aggregate function
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?,
// If arguments, then pass the first value through
_ => f.args()[0].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?,
false => out.set(ctx, opt, txn, idiom.as_ref(), x).await?,
true => out = x,
}
}
@ -188,25 +141,30 @@ impl Fields {
match p.last().unwrap().alias() {
// This is an alias expression part
Some(a) => {
let v = x.clone();
if let Some(i) = alias {
out.set(ctx, opt, txn, i, x.clone()).await?;
}
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?,
None => {
out.set(ctx, opt, txn, alias.as_ref().unwrap_or(v), x)
.await?
}
}
}
}
// This expression is a normal field expression
_ => {
let x = v.compute(ctx, opt, txn, Some(doc)).await?;
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, i, x).await?,
false => out.set(ctx, opt, txn, idiom.as_ref(), x).await?,
true => out = x,
}
}
},
}
}
}
}
Ok(out)
@ -230,25 +188,35 @@ fn field_many(i: &str) -> IResult<&str, Fields> {
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 {
/// The `*` in `SELECT * FROM ...`
#[default]
All,
Alone(Value),
Alias(Value, Idiom),
}
impl Default for Field {
fn default() -> Self {
Self::All
}
/// The 'rating' in `SELECT rating FROM ...`
Single {
expr: Value,
/// The `quality` in `SELECT rating AS quality FROM ...`
alias: Option<Idiom>,
},
}
impl Display for Field {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::All => f.write_char('*'),
Self::Alone(e) => Display::fmt(e, f),
Self::Alias(e, a) => write!(f, "{e} AS {a}"),
Self::Single {
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> {
let (i, f) = value(i)?;
Ok((i, Field::Alone(f)))
let (i, expr) = value(i)?;
Ok((
i,
Field::Single {
expr,
alias: None,
},
))
}
pub fn alias(i: &str) -> IResult<&str, Field> {
let (i, f) = value(i)?;
let (i, expr) = value(i)?;
let (i, _) = shouldbespace(i)?;
let (i, _) = tag_no_case("AS")(i)?;
let (i, _) = shouldbespace(i)?;
let (i, a) = idiom(i)?;
Ok((i, Field::Alias(f, a)))
let (i, alias) = idiom(i)?;
Ok((
i,
Field::Single {
expr,
alias: Some(alias),
},
))
}
#[cfg(test)]

View file

@ -4,45 +4,53 @@ use crate::sql::group::Groups;
use crate::sql::order::Orders;
use crate::sql::split::Splits;
use crate::sql::value::Value;
use crate::sql::Idiom;
use nom::Err;
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>(
i: &'a str,
fields: &Fields,
splits: &Option<Splits>,
) -> 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 {
// Loop over each of the expressions in the SPLIT ON clause
'outer: for split in splits.iter() {
// Loop over each of the expressions in the SELECT clause
for field in fields.iter() {
// Check to see whether the expression is in the SELECT clause
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
_ => (),
}
}
for split in splits.iter() {
if !contains_idiom(fields, &split.0) {
// 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
Ok(())
}
@ -55,34 +63,13 @@ pub fn check_order_by_fields<'a>(
// Check to see if a ORDER BY clause has been defined
if let Some(orders) = orders {
// Loop over each of the expressions in the ORDER BY clause
'outer: for order in orders.iter() {
// Loop over each of the expressions in the SELECT clause
for field in fields.iter() {
// Check to see whether the expression is in the SELECT clause
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
_ => (),
}
}
for order in orders.iter() {
if !contains_idiom(fields, &*order) {
// 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
Ok(())
}
@ -95,43 +82,30 @@ pub fn check_group_by_fields<'a>(
// Check to see if a GROUP BY clause has been defined
if let Some(groups) = groups {
// Loop over each of the expressions in the GROUP BY clause
'outer: for group in groups.iter() {
// Loop over each of the expressions in the SELECT clause
for field in fields.iter() {
// Check to see whether the expression is in the SELECT clause
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
_ => (),
}
}
for group in groups.iter() {
if !contains_idiom(fields, &group.0) {
// 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
if groups.len() > 0 {
if !groups.is_empty() {
// Loop over each of the expressions in the SELECT clause
'outer: for field in fields.iter() {
// Loop over each of the expressions in the GROUP BY clause
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 {
// This field is aliased, so check the alias name
Field::Alias(_, i) if i.as_ref() == group.as_ref() => continue 'outer,
// Otherwise, check the type of the field value
Field::Alias(v, _) | Field::Alone(v) => match v {
Field::Single {
expr,
alias,
} => {
if alias.as_ref().map(|i| i.as_ref() == group.as_ref()).unwrap_or(false)
{
// This field is aliased, and the alias name matched
continue 'outer;
} else {
match expr {
// If the expression in the SELECT clause is a field, check to see if it exists in the GROUP BY
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
@ -142,7 +116,9 @@ pub fn check_group_by_fields<'a>(
v if v.is_static() => continue 'outer,
// If not, then this query should fail
_ => (),
},
}
}
}
_ => (),
}
}

View file

@ -50,8 +50,10 @@ impl SelectStatement {
pub(crate) fn writeable(&self) -> bool {
if self.expr.iter().any(|v| match v {
Field::All => false,
Field::Alone(v) => v.writeable(),
Field::Alias(v, _) => v.writeable(),
Field::Single {
expr,
..
} => expr.writeable(),
}) {
return true;
}

View file

@ -19,10 +19,10 @@ impl ser::Serializer for Serializer {
type SerializeSeq = Impossible<Field, Error>;
type SerializeTuple = Impossible<Field, Error>;
type SerializeTupleStruct = Impossible<Field, Error>;
type SerializeTupleVariant = SerializeValueIdiomTuple;
type SerializeTupleVariant = Impossible<Field, Error>;
type SerializeMap = Impossible<Field, Error>;
type SerializeStruct = Impossible<Field, Error>;
type SerializeStructVariant = Impossible<Field, Error>;
type SerializeStructVariant = SerializeValueIdiomTuple;
const EXPECTED: &'static str = "an enum `Field`";
@ -39,77 +39,89 @@ impl ser::Serializer for Serializer {
}
}
#[inline]
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(
fn serialize_struct_variant(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
) -> Result<Self::SerializeStructVariant, Self::Error> {
match variant {
"Alias" => Ok(SerializeValueIdiomTuple::default()),
variant => Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`"))),
"Single" => Ok(SerializeValueIdiomTuple::default()),
variant => Err(Error::custom(format!("unexpected struct variant `{name}::{variant}`"))),
}
}
}
#[derive(Default)]
pub(super) struct SerializeValueIdiomTuple {
index: usize,
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 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
T: Serialize + ?Sized,
{
match self.index {
0 => {
match key {
"expr" => {
self.value = Some(value.serialize(ser::value::Serializer.wrap())?);
}
1 => {
self.idiom = Some(Idiom(value.serialize(ser::part::vec::Serializer.wrap())?));
"alias" => {
self.idiom = Some(value.serialize(SerializeOptionIdiom.wrap())?);
}
index => {
return Err(Error::custom(format!("unexpected `Field::Alias` index `{index}`")));
key => {
return Err(Error::custom(format!("unexpected `Field::Single` field `{key}`")));
}
}
self.index += 1;
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
match (self.value, self.idiom) {
(Some(value), Some(idiom)) => Ok(Field::Alias(value, idiom)),
_ => Err(Error::custom("`Field::Alias` missing required value(s)")),
(Some(expr), Some(alias)) => Ok(Field::Single {
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)]
mod tests {
use super::*;
@ -124,14 +136,20 @@ mod tests {
#[test]
fn alone() {
let field = Field::Alone(Default::default());
let field = Field::Single {
expr: Default::default(),
alias: None,
};
let serialized = field.serialize(Serializer.wrap()).unwrap();
assert_eq!(field, serialized);
}
#[test]
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();
assert_eq!(field, serialized);
}

View file

@ -2225,6 +2225,7 @@ impl Value {
Value::None => true,
Value::Null => true,
Value::Bool(_) => true,
Value::Bytes(_) => true,
Value::Uuid(_) => true,
Value::Number(_) => true,
Value::Strand(_) => true,