uqa_execution/relational/
aggregate.rs1use super::{
10 compare_values, eval_scalar, Batch, DefaultExpressionEvaluator, ExecError, ExecResult,
11 PhysicalOperator, RowSchema, SQLParam, ScalarEvalContext, ScalarExpr, SortKey, Value,
12};
13use crate::ProjectedRow;
14
15mod adaptive;
16mod fold;
17mod partial;
18mod sort_fallback;
19
20pub(super) use fold::value_to_f64;
21#[cfg(test)]
22pub(super) use fold::{finalise_fold, AggFold};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum AggregateKind {
26 Count,
27 CountStar,
28 Sum,
29 Avg,
30 Min,
31 Max,
32}
33
34#[derive(Debug, Clone)]
35pub struct AggregateSpec {
36 pub kind: AggregateKind,
37 pub arg: Option<ScalarExpr>,
39 pub alias: String,
41 pub distinct: bool,
43}
44
45pub trait AggregateExecutor: Send {
50 fn consume(&mut self, batch: Batch) -> ExecResult<()>;
54
55 fn supports_projected_rows(&self) -> bool {
58 false
59 }
60
61 fn supports_storage_borrowed_rows(&self) -> bool {
63 false
64 }
65
66 fn consume_projected_row(&mut self, _row: &ProjectedRow<'_, '_>) -> ExecResult<()> {
69 Err(ExecError::Other(
70 "aggregate executor does not accept projected rows".into(),
71 ))
72 }
73
74 fn finish(&mut self) -> ExecResult<crate::spill::SpillBuffer>;
79}
80
81pub struct HashAggregate<'a> {
82 child: Box<dyn PhysicalOperator + 'a>,
83 group_keys: Vec<(String, ScalarExpr)>,
84 aggregates: Vec<AggregateSpec>,
85 params: Vec<SQLParam>,
86 schema: RowSchema,
87 executor: Option<Box<dyn AggregateExecutor + 'a>>,
88 work_mem_bytes: usize,
89 output: Option<crate::spill::SpillDrain>,
90 output_spilled: bool,
91}
92
93impl HashAggregate<'static> {
94 const DEFAULT_WORK_MEM_BYTES: usize = 64 * 1024 * 1024;
95
96 pub fn new(
97 child: Box<dyn PhysicalOperator>,
98 group_keys: Vec<(String, ScalarExpr)>,
99 aggregates: Vec<AggregateSpec>,
100 params: Vec<SQLParam>,
101 ) -> Self {
102 Self::new_with_work_mem(
103 child,
104 group_keys,
105 aggregates,
106 params,
107 Self::DEFAULT_WORK_MEM_BYTES,
108 )
109 }
110
111 pub fn new_with_work_mem(
112 child: Box<dyn PhysicalOperator>,
113 group_keys: Vec<(String, ScalarExpr)>,
114 aggregates: Vec<AggregateSpec>,
115 params: Vec<SQLParam>,
116 work_mem_bytes: usize,
117 ) -> Self {
118 let mut cols: Vec<String> = group_keys.iter().map(|(n, _)| n.clone()).collect();
119 for a in &aggregates {
120 cols.push(a.alias.clone());
121 }
122 let schema = RowSchema::new(cols);
123 Self {
124 child,
125 group_keys,
126 aggregates,
127 params,
128 schema,
129 executor: None,
130 work_mem_bytes,
131 output: None,
132 output_spilled: false,
133 }
134 }
135}
136
137impl<'a> HashAggregate<'a> {
138 pub fn with_executor(
142 child: Box<dyn PhysicalOperator + 'a>,
143 output_schema: Vec<String>,
144 executor: Box<dyn AggregateExecutor + 'a>,
145 ) -> Self {
146 let types = vec![None; output_schema.len()];
147 Self::with_typed_executor(child, output_schema, types, executor)
148 }
149
150 pub fn with_typed_executor(
151 child: Box<dyn PhysicalOperator + 'a>,
152 output_schema: Vec<String>,
153 output_types: Vec<Option<uqa_sql::ast::ColumnType>>,
154 executor: Box<dyn AggregateExecutor + 'a>,
155 ) -> Self {
156 Self {
157 child,
158 group_keys: Vec::new(),
159 aggregates: Vec::new(),
160 params: Vec::new(),
161 schema: RowSchema::with_types(output_schema, output_types),
162 executor: Some(executor),
163 work_mem_bytes: 0,
164 output: None,
165 output_spilled: false,
166 }
167 }
168
169 pub fn output_has_spilled(&self) -> bool {
172 self.output_spilled
173 }
174}
175
176impl PhysicalOperator for HashAggregate<'_> {
177 fn row_schema(&self) -> &RowSchema {
178 &self.schema
179 }
180
181 fn open(&mut self) -> ExecResult<()> {
182 self.child.open()?;
183 self.output_spilled = false;
184 if let Some(executor) = self.executor.as_mut() {
185 let consumed_directly = executor.supports_projected_rows()
186 && self.child.consume_into_aggregate(executor.as_mut())?;
187 if !consumed_directly {
188 while let Some(batch) = self.child.next()? {
189 executor.consume(batch)?;
190 }
191 }
192 let mut output = executor.finish()?;
193 self.output_spilled = output.has_spilled();
194 self.output = Some(output.drain()?);
195 return Ok(());
196 }
197 let mut output = if adaptive::supported(&self.group_keys, &self.aggregates) {
198 let mut aggregate = adaptive::AdaptiveBuiltinAggregate::new(
199 &self.group_keys,
200 &self.aggregates,
201 &self.params,
202 self.work_mem_bytes,
203 );
204 while let Some(batch) = self.child.next()? {
205 aggregate.consume(batch)?;
206 }
207 aggregate.finish(self.schema.clone())?
208 } else {
209 sort_fallback::execute(
210 self.child.as_mut(),
211 &self.group_keys,
212 &self.aggregates,
213 &self.params,
214 self.schema.clone(),
215 self.work_mem_bytes,
216 )?
217 };
218 self.output_spilled = output.has_spilled();
219 self.output = Some(output.drain()?);
220 Ok(())
221 }
222
223 fn next(&mut self) -> ExecResult<Option<Batch>> {
224 let Some(output) = self.output.as_mut() else {
225 return Ok(None);
226 };
227 output.next().transpose()
228 }
229
230 fn close(&mut self) -> ExecResult<()> {
231 self.output = None;
232 self.child.close()
233 }
234}
235
236