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::fmt::Fmt; 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; use nom::multi::separated_list1; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter, Write}; use std::ops::Deref; #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] pub struct Fields(pub Vec); impl Fields { pub fn all(&self) -> bool { self.0.iter().any(|v| matches!(v, Field::All)) } pub fn other(&self) -> impl Iterator { self.0.iter().filter(|v| !matches!(v, Field::All)) } pub fn single(&self) -> Option { match self.0.len() { 1 => match self.0.first() { Some(Field::All) => None, Some(Field::Alone(e)) => Some(e.to_idiom()), Some(Field::Alias(_, i)) => Some(i.to_owned()), _ => None, }, _ => None, } } } impl Deref for Fields { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl IntoIterator for Fields { type Item = Field; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Display for Fields { fn fmt(&self, f: &mut Formatter) -> fmt::Result { Display::fmt(&Fmt::comma_separated(&self.0), f) } } impl Fields { pub(crate) async fn compute( &self, ctx: &Context<'_>, opt: &Options, txn: &Transaction, doc: Option<&Value>, group: bool, ) -> Result { // 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 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?, }; 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 => doc, }; // Continue fetching the next idiom part let x = x .get(ctx, opt, txn, 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?; 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 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?, }; 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 => doc, }; // Continue fetching the next idiom part let x = x .get(ctx, opt, txn, 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?, } } } _ => { 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))) } #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)] pub enum Field { All, Alone(Value), Alias(Value, Idiom), } impl Default for Field { fn default() -> Self { Self::All } } 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}"), } } } pub fn field(i: &str) -> IResult<&str, Field> { alt((all, alias, alone))(i) } pub fn all(i: &str) -> IResult<&str, Field> { let (i, _) = tag_no_case("*")(i)?; Ok((i, Field::All)) } pub fn alone(i: &str) -> IResult<&str, Field> { let (i, f) = value(i)?; Ok((i, Field::Alone(f))) } pub fn alias(i: &str) -> IResult<&str, Field> { let (i, f) = 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))) } #[cfg(test)] mod tests { use super::*; #[test] fn field_all() { let sql = "*"; let res = fields(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("*", format!("{}", out)); } #[test] fn field_single() { let sql = "field"; let res = fields(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("field", format!("{}", out)); } #[test] fn field_multiple() { let sql = "field, other.field"; let res = fields(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("field, other.field", format!("{}", out)); } #[test] fn field_aliases() { let sql = "field AS one, other.field AS two"; let res = fields(sql); assert!(res.is_ok()); let out = res.unwrap().1; assert_eq!("field AS one, other.field AS two", format!("{}", out)); } }