Ensure aggregate functions are computed in GROUP BY clauses

This commit is contained in:
Tobie Morgan Hitchcock 2022-07-16 23:20:50 +01:00
parent c9d9b041f7
commit c9a5b66d9c
2 changed files with 25 additions and 2 deletions

View file

@ -27,10 +27,12 @@ impl<'a> Document<'a> {
Output::Diff => Ok(self.initial.diff(&self.current, Idiom::default()).into()), Output::Diff => Ok(self.initial.diff(&self.current, Idiom::default()).into()),
Output::After => self.current.compute(ctx, opt, txn, Some(&self.current)).await, 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::Before => self.initial.compute(ctx, opt, txn, Some(&self.initial)).await,
Output::Fields(v) => v.compute(ctx, opt, txn, Some(&self.current)).await, Output::Fields(v) => v.compute(ctx, opt, txn, Some(&self.current), false).await,
}, },
None => match stm { None => match stm {
Statement::Select(s) => s.expr.compute(ctx, opt, txn, Some(&self.current)).await, Statement::Select(s) => {
s.expr.compute(ctx, opt, txn, Some(&self.current), s.group.is_some()).await
}
Statement::Create(_) => { Statement::Create(_) => {
self.current.compute(ctx, opt, txn, Some(&self.current)).await self.current.compute(ctx, opt, txn, Some(&self.current)).await
} }

View file

@ -66,6 +66,7 @@ impl Fields {
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
doc: Option<&Value>, doc: Option<&Value>,
group: bool,
) -> Result<Value, Error> { ) -> Result<Value, Error> {
// Ensure futures are run // Ensure futures are run
let opt = &opt.futures(true); let opt = &opt.futures(true);
@ -80,6 +81,16 @@ impl Fields {
match v { match v {
Field::All => (), Field::All => (),
Field::Alone(v) => match v { 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 // This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => { Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here // Store the different output yields here
@ -117,6 +128,16 @@ impl Fields {
} }
}, },
Field::Alias(v, i) => match v { 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 // This expression is a multi-output graph traversal
Value::Idiom(v) if v.is_multi_yield() => { Value::Idiom(v) if v.is_multi_yield() => {
// Store the different output yields here // Store the different output yields here