Skip to main content

alopex_sql/executor/query/
aggregate.rs

1use std::cmp::Ordering;
2use std::collections::{HashMap, HashSet};
3use std::sync::Arc;
4use std::sync::atomic::{AtomicU64, AtomicUsize};
5
6use crate::catalog::ColumnMetadata;
7use crate::executor::evaluator::EvalContext;
8use crate::executor::memory::{MemoryPolicy, MemoryTracker, map_core_memory_error};
9use crate::executor::{EvaluationError, ExecutorError, Result};
10use crate::planner::aggregate_expr::{AggregateExpr, AggregateFunction};
11use crate::planner::typed_expr::TypedExpr;
12use crate::planner::types::ResolvedType;
13use crate::storage::SqlValue;
14use alopex_core::sql::stream::ByteSized;
15
16use super::{Row, RowIterator, iterator::VecIterator};
17
18/// Byte-encoded group key for hash-based aggregation.
19pub type GroupKeyBytes = Vec<u8>;
20
21fn encode_group_value(value: &SqlValue, buf: &mut Vec<u8>) -> Result<()> {
22    buf.push(value.type_tag());
23    match value {
24        SqlValue::Null => Ok(()),
25        SqlValue::Integer(v) => {
26            buf.extend_from_slice(&v.to_le_bytes());
27            Ok(())
28        }
29        SqlValue::BigInt(v) => {
30            buf.extend_from_slice(&v.to_le_bytes());
31            Ok(())
32        }
33        SqlValue::Float(v) => {
34            buf.extend_from_slice(&v.to_bits().to_le_bytes());
35            Ok(())
36        }
37        SqlValue::Double(v) => {
38            buf.extend_from_slice(&v.to_bits().to_le_bytes());
39            Ok(())
40        }
41        SqlValue::Text(s) => {
42            let len = u32::try_from(s.len()).map_err(|_| ExecutorError::InvalidOperation {
43                operation: "aggregate".into(),
44                reason: "text length exceeds u32::MAX".into(),
45            })?;
46            buf.extend_from_slice(&len.to_le_bytes());
47            buf.extend_from_slice(s.as_bytes());
48            Ok(())
49        }
50        SqlValue::Blob(bytes) => {
51            let len = u32::try_from(bytes.len()).map_err(|_| ExecutorError::InvalidOperation {
52                operation: "aggregate".into(),
53                reason: "blob length exceeds u32::MAX".into(),
54            })?;
55            buf.extend_from_slice(&len.to_le_bytes());
56            buf.extend_from_slice(bytes);
57            Ok(())
58        }
59        SqlValue::Boolean(b) => {
60            buf.push(u8::from(*b));
61            Ok(())
62        }
63        SqlValue::Timestamp(v) => {
64            buf.extend_from_slice(&v.to_le_bytes());
65            Ok(())
66        }
67        SqlValue::Date(v) => {
68            buf.extend_from_slice(&v.to_le_bytes());
69            Ok(())
70        }
71        SqlValue::Time(v) => {
72            buf.extend_from_slice(&v.to_le_bytes());
73            Ok(())
74        }
75        SqlValue::Interval {
76            months,
77            days,
78            micros,
79        } => {
80            buf.extend_from_slice(&months.to_le_bytes());
81            buf.extend_from_slice(&days.to_le_bytes());
82            buf.extend_from_slice(&micros.to_le_bytes());
83            Ok(())
84        }
85        SqlValue::Decimal(value) => {
86            buf.extend_from_slice(&value.coefficient.to_le_bytes());
87            buf.push(value.scale);
88            Ok(())
89        }
90        SqlValue::Json(value) => {
91            let bytes = value.as_str().as_bytes();
92            let len = u32::try_from(bytes.len()).map_err(|_| ExecutorError::InvalidOperation {
93                operation: "aggregate".into(),
94                reason: "JSON length exceeds u32::MAX".into(),
95            })?;
96            buf.extend_from_slice(&len.to_le_bytes());
97            buf.extend_from_slice(bytes);
98            Ok(())
99        }
100        SqlValue::Vector(values) => {
101            let len = u32::try_from(values.len()).map_err(|_| ExecutorError::InvalidOperation {
102                operation: "aggregate".into(),
103                reason: "vector length exceeds u32::MAX".into(),
104            })?;
105            buf.extend_from_slice(&len.to_le_bytes());
106            for f in values {
107                buf.extend_from_slice(&f.to_bits().to_le_bytes());
108            }
109            Ok(())
110        }
111        SqlValue::Array(values) => {
112            buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
113            for value in values {
114                encode_group_value(value, buf)?;
115            }
116            Ok(())
117        }
118        SqlValue::Map(values) => {
119            buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
120            for (key, value) in values {
121                encode_group_value(key, buf)?;
122                encode_group_value(value, buf)?;
123            }
124            Ok(())
125        }
126        SqlValue::Struct(values) => {
127            buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
128            for (name, value) in values {
129                buf.extend_from_slice(&(name.len() as u32).to_le_bytes());
130                buf.extend_from_slice(name.as_bytes());
131                encode_group_value(value, buf)?;
132            }
133            Ok(())
134        }
135    }
136}
137
138/// Encode group key values into a deterministic byte sequence.
139pub fn encode_group_key(values: &[SqlValue]) -> Result<GroupKeyBytes> {
140    let mut buf = Vec::new();
141    for value in values {
142        encode_group_value(value, &mut buf)?;
143    }
144    Ok(buf)
145}
146
147/// Accumulator interface for aggregate function execution.
148pub trait Accumulator: Send {
149    /// Update the accumulator with a new value (None for COUNT(*) rows).
150    fn update(&mut self, value: Option<SqlValue>) -> Result<()>;
151    /// Update all aggregate inputs. Two-argument aggregates override this;
152    /// existing single-input accumulators keep their established path.
153    fn update_values(&mut self, values: &[SqlValue]) -> Result<()> {
154        self.update(values.first().cloned())
155    }
156    /// Update with the row's aggregate-local sort key values (issue #148).
157    ///
158    /// Order-insensitive accumulators ignore the keys and defer to
159    /// [`Accumulator::update`]; order-sensitive accumulators buffer
160    /// `(keys, value)` pairs and sort in [`Accumulator::finalize`].
161    fn update_ordered(&mut self, value: Option<SqlValue>, _sort_keys: &[SqlValue]) -> Result<()> {
162        self.update(value)
163    }
164    fn update_ordered_values(&mut self, values: &[SqlValue], sort_keys: &[SqlValue]) -> Result<()> {
165        self.update_ordered(values.first().cloned(), sort_keys)
166    }
167    /// Return the serializable partial aggregate state.
168    fn state(&self) -> Result<Vec<SqlValue>>;
169    /// Merge a partial state produced by an accumulator of the same function.
170    fn merge(&mut self, state: &[SqlValue]) -> Result<()>;
171    /// Finalize the accumulator and return the resulting SqlValue.
172    fn finalize(&self) -> Result<SqlValue>;
173    /// Clone the accumulator as a trait object.
174    fn clone_box(&self) -> Box<dyn Accumulator>;
175    /// Estimated bytes retained by dynamically allocated accumulator state.
176    ///
177    /// Window execution uses this to charge the shared operator memory budget
178    /// while an accumulator is alive.
179    fn retained_bytes(&self) -> u64 {
180        0
181    }
182}
183
184impl Clone for Box<dyn Accumulator> {
185    fn clone(&self) -> Self {
186        self.clone_box()
187    }
188}
189
190fn invalid_aggregate_state(function: &str, reason: impl Into<String>) -> ExecutorError {
191    ExecutorError::InvalidOperation {
192        operation: function.into(),
193        reason: reason.into(),
194    }
195}
196
197fn expect_state_arity(function: &str, state: &[SqlValue], expected: usize) -> Result<()> {
198    if state.len() == expected {
199        Ok(())
200    } else {
201        Err(invalid_aggregate_state(
202            function,
203            format!("expected {expected} state value(s), got {}", state.len()),
204        ))
205    }
206}
207
208fn state_bigint(function: &str, value: &SqlValue, index: usize) -> Result<i64> {
209    match value {
210        SqlValue::BigInt(v) => Ok(*v),
211        other => Err(invalid_aggregate_state(
212            function,
213            format!(
214                "state value {index} expected BigInt, got {}",
215                other.type_name()
216            ),
217        )),
218    }
219}
220
221fn state_double(function: &str, value: &SqlValue, index: usize) -> Result<f64> {
222    match value {
223        SqlValue::Double(v) => Ok(*v),
224        other => Err(invalid_aggregate_state(
225            function,
226            format!(
227                "state value {index} expected Double, got {}",
228                other.type_name()
229            ),
230        )),
231    }
232}
233
234fn state_text<'a>(function: &str, value: &'a SqlValue, index: usize) -> Result<&'a str> {
235    match value {
236        SqlValue::Text(v) => Ok(v),
237        other => Err(invalid_aggregate_state(
238            function,
239            format!(
240                "state value {index} expected Text, got {}",
241                other.type_name()
242            ),
243        )),
244    }
245}
246
247fn distinct_allows(
248    distinct_values: &mut Option<HashSet<Vec<u8>>>,
249    value: &SqlValue,
250) -> Result<bool> {
251    if value.is_null() {
252        return Ok(false);
253    }
254    let Some(distinct) = distinct_values else {
255        return Ok(true);
256    };
257    let encoded = encode_group_key(std::slice::from_ref(value))?;
258    Ok(distinct.insert(encoded))
259}
260
261fn estimated_distinct_retained_bytes(distinct_values: &Option<HashSet<Vec<u8>>>) -> u64 {
262    let Some(values) = distinct_values else {
263        return 0;
264    };
265    // Charge every allocated hash bucket conservatively as a stored Vec plus
266    // hash/control metadata, then add each encoded key's owned allocation.
267    let bucket_bytes = values
268        .capacity()
269        .saturating_mul(std::mem::size_of::<Vec<u8>>() + std::mem::size_of::<u64>() + 1);
270    values.iter().fold(
271        u64::try_from(bucket_bytes).unwrap_or(u64::MAX),
272        |total, value| total.saturating_add(u64::try_from(value.capacity()).unwrap_or(u64::MAX)),
273    )
274}
275
276fn estimated_string_collection_bytes(
277    values: &[String],
278    values_capacity: usize,
279    separator_capacity: usize,
280) -> u64 {
281    let slots = values_capacity.saturating_mul(std::mem::size_of::<String>());
282    values
283        .iter()
284        .fold(u64::try_from(slots).unwrap_or(u64::MAX), |total, value| {
285            total.saturating_add(u64::try_from(value.capacity()).unwrap_or(u64::MAX))
286        })
287        .saturating_add(u64::try_from(separator_capacity).unwrap_or(u64::MAX))
288}
289
290/// Accumulator for COUNT / COUNT(DISTINCT).
291#[derive(Debug, Clone)]
292pub struct CountAccumulator {
293    count: usize,
294    distinct_values: Option<HashSet<Vec<u8>>>,
295}
296
297impl CountAccumulator {
298    /// Create a new count accumulator.
299    pub fn new(distinct: bool) -> Self {
300        Self {
301            count: 0,
302            distinct_values: if distinct { Some(HashSet::new()) } else { None },
303        }
304    }
305}
306
307impl Accumulator for CountAccumulator {
308    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
309        match (&mut self.distinct_values, value) {
310            (Some(distinct), Some(value)) => {
311                if value.is_null() {
312                    return Ok(());
313                }
314                let encoded = encode_group_key(std::slice::from_ref(&value))?;
315                if distinct.insert(encoded) {
316                    self.count += 1;
317                }
318            }
319            (Some(_), None) => {
320                self.count += 1;
321            }
322            (None, Some(value)) => {
323                if !value.is_null() {
324                    self.count += 1;
325                }
326            }
327            (None, None) => {
328                self.count += 1;
329            }
330        }
331        Ok(())
332    }
333
334    fn finalize(&self) -> Result<SqlValue> {
335        Ok(SqlValue::BigInt(self.count as i64))
336    }
337
338    fn state(&self) -> Result<Vec<SqlValue>> {
339        Ok(vec![SqlValue::BigInt(self.count as i64)])
340    }
341
342    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
343        expect_state_arity("count", state, 1)?;
344        let count = state_bigint("count", &state[0], 0)?;
345        if count < 0 {
346            return Err(invalid_aggregate_state(
347                "count",
348                "state count must be non-negative",
349            ));
350        }
351        self.count = self.count.saturating_add(count as usize);
352        Ok(())
353    }
354
355    fn clone_box(&self) -> Box<dyn Accumulator> {
356        Box::new(self.clone())
357    }
358
359    fn retained_bytes(&self) -> u64 {
360        estimated_distinct_retained_bytes(&self.distinct_values)
361    }
362}
363
364/// Accumulator for SUM.
365#[derive(Debug, Clone)]
366pub struct SumAccumulator {
367    sum: Option<SqlValue>,
368    result_type: ResolvedType,
369    distinct_values: Option<HashSet<Vec<u8>>>,
370}
371
372impl SumAccumulator {
373    /// Create a new sum accumulator.
374    pub fn new() -> Self {
375        Self::with_distinct(false)
376    }
377
378    pub fn with_distinct(distinct: bool) -> Self {
379        Self::with_distinct_for_type(distinct, ResolvedType::Double)
380    }
381
382    pub fn with_distinct_for_type(distinct: bool, result_type: ResolvedType) -> Self {
383        Self {
384            sum: None,
385            result_type,
386            distinct_values: if distinct { Some(HashSet::new()) } else { None },
387        }
388    }
389
390    fn add_value(&mut self, value: SqlValue) -> Result<()> {
391        let next = match &self.result_type {
392            ResolvedType::Integer => {
393                let SqlValue::Integer(value) = value else {
394                    return sum_type_mismatch("Integer", &value);
395                };
396                let sum = match self.sum.as_ref() {
397                    None => value,
398                    Some(SqlValue::Integer(current)) => current
399                        .checked_add(value)
400                        .ok_or(ExecutorError::Evaluation(EvaluationError::Overflow))?,
401                    Some(other) => return sum_type_mismatch("Integer", other),
402                };
403                SqlValue::Integer(sum)
404            }
405            ResolvedType::BigInt => {
406                let value = match value {
407                    SqlValue::Integer(value) => i64::from(value),
408                    SqlValue::BigInt(value) => value,
409                    other => return sum_type_mismatch("BigInt", &other),
410                };
411                let sum = match self.sum.as_ref() {
412                    None => value,
413                    Some(SqlValue::BigInt(current)) => current
414                        .checked_add(value)
415                        .ok_or(ExecutorError::Evaluation(EvaluationError::Overflow))?,
416                    Some(other) => return sum_type_mismatch("BigInt", other),
417                };
418                SqlValue::BigInt(sum)
419            }
420            ResolvedType::Decimal { precision, scale } => {
421                let SqlValue::Decimal(value) = value else {
422                    return sum_type_mismatch("Decimal", &value);
423                };
424                let value = value
425                    .rescale(*scale)
426                    .ok_or(ExecutorError::Evaluation(EvaluationError::Overflow))?;
427                let coefficient = match self.sum.as_ref() {
428                    None => value.coefficient,
429                    Some(SqlValue::Decimal(current)) => current
430                        .coefficient
431                        .checked_add(value.coefficient)
432                        .ok_or(ExecutorError::Evaluation(EvaluationError::Overflow))?,
433                    Some(other) => return sum_type_mismatch("Decimal", other),
434                };
435                let sum = crate::storage::DecimalValue::new(coefficient, *scale);
436                if !sum.fits_precision(*precision) {
437                    return Err(ExecutorError::Evaluation(EvaluationError::Overflow));
438                }
439                SqlValue::Decimal(sum)
440            }
441            _ => {
442                let value = numeric_to_f64(&value)?;
443                let sum = match self.sum.as_ref() {
444                    None => value,
445                    Some(SqlValue::Double(current)) => *current + value,
446                    Some(other) => return sum_type_mismatch("Double", other),
447                };
448                SqlValue::Double(sum)
449            }
450        };
451        self.sum = Some(next);
452        Ok(())
453    }
454}
455
456impl Default for SumAccumulator {
457    fn default() -> Self {
458        Self::new()
459    }
460}
461
462impl Accumulator for SumAccumulator {
463    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
464        let Some(value) = value else {
465            return Ok(());
466        };
467        if value.is_null() {
468            return Ok(());
469        }
470        if !distinct_allows(&mut self.distinct_values, &value)? {
471            return Ok(());
472        }
473        self.add_value(value)
474    }
475
476    fn finalize(&self) -> Result<SqlValue> {
477        Ok(self.sum.clone().unwrap_or(SqlValue::Null))
478    }
479
480    fn state(&self) -> Result<Vec<SqlValue>> {
481        Ok(vec![self.sum.clone().unwrap_or(SqlValue::Null)])
482    }
483
484    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
485        expect_state_arity("sum", state, 1)?;
486        if state[0].is_null() {
487            return Ok(());
488        }
489        self.add_value(state[0].clone())
490    }
491
492    fn clone_box(&self) -> Box<dyn Accumulator> {
493        Box::new(self.clone())
494    }
495
496    fn retained_bytes(&self) -> u64 {
497        estimated_distinct_retained_bytes(&self.distinct_values).saturating_add(
498            self.sum
499                .as_ref()
500                .map(ByteSized::estimated_bytes)
501                .unwrap_or(0),
502        )
503    }
504}
505
506/// Accumulator for TOTAL (SUM that returns 0.0 on empty/all-NULL input).
507#[derive(Debug, Clone)]
508pub struct TotalAccumulator {
509    sum: Option<f64>,
510}
511
512impl TotalAccumulator {
513    /// Create a new total accumulator.
514    pub fn new() -> Self {
515        Self { sum: None }
516    }
517}
518
519impl Default for TotalAccumulator {
520    fn default() -> Self {
521        Self::new()
522    }
523}
524
525impl Accumulator for TotalAccumulator {
526    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
527        let Some(value) = value else {
528            return Ok(());
529        };
530        if value.is_null() {
531            return Ok(());
532        }
533        let numeric = numeric_to_f64(&value)?;
534        self.sum = Some(self.sum.unwrap_or(0.0) + numeric);
535        Ok(())
536    }
537
538    fn finalize(&self) -> Result<SqlValue> {
539        Ok(SqlValue::Double(self.sum.unwrap_or(0.0)))
540    }
541
542    fn state(&self) -> Result<Vec<SqlValue>> {
543        Ok(vec![SqlValue::Double(self.sum.unwrap_or(0.0))])
544    }
545
546    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
547        expect_state_arity("total", state, 1)?;
548        let value = state_double("total", &state[0], 0)?;
549        self.sum = Some(self.sum.unwrap_or(0.0) + value);
550        Ok(())
551    }
552
553    fn clone_box(&self) -> Box<dyn Accumulator> {
554        Box::new(self.clone())
555    }
556}
557
558/// Accumulator for AVG.
559#[derive(Debug, Clone)]
560pub struct AvgAccumulator {
561    sum: SumAccumulator,
562    result_type: ResolvedType,
563    count: usize,
564    distinct_values: Option<HashSet<Vec<u8>>>,
565}
566
567impl AvgAccumulator {
568    /// Create a new average accumulator.
569    pub fn new() -> Self {
570        Self::with_distinct(false)
571    }
572
573    pub fn with_distinct(distinct: bool) -> Self {
574        Self::with_distinct_for_type(distinct, ResolvedType::Double)
575    }
576
577    pub fn with_distinct_for_type(distinct: bool, result_type: ResolvedType) -> Self {
578        Self {
579            sum: SumAccumulator::with_distinct_for_type(false, result_type.clone()),
580            result_type,
581            count: 0,
582            distinct_values: if distinct { Some(HashSet::new()) } else { None },
583        }
584    }
585}
586
587impl Default for AvgAccumulator {
588    fn default() -> Self {
589        Self::new()
590    }
591}
592
593impl Accumulator for AvgAccumulator {
594    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
595        let Some(value) = value else {
596            return Ok(());
597        };
598        if value.is_null() {
599            return Ok(());
600        }
601        if !distinct_allows(&mut self.distinct_values, &value)? {
602            return Ok(());
603        }
604        self.sum.add_value(value)?;
605        self.count += 1;
606        Ok(())
607    }
608
609    fn finalize(&self) -> Result<SqlValue> {
610        if self.count == 0 {
611            return Ok(SqlValue::Null);
612        }
613        match self.sum.finalize()? {
614            SqlValue::Decimal(sum) => {
615                let divisor = self.count as i128;
616                let quotient = sum.coefficient / divisor;
617                let remainder = sum.coefficient % divisor;
618                let rounded = if remainder.abs().saturating_mul(2) >= divisor {
619                    quotient
620                        .checked_add(sum.coefficient.signum())
621                        .ok_or(ExecutorError::Evaluation(EvaluationError::Overflow))?
622                } else {
623                    quotient
624                };
625                Ok(SqlValue::Decimal(crate::storage::DecimalValue::new(
626                    rounded, sum.scale,
627                )))
628            }
629            SqlValue::Double(sum) => Ok(SqlValue::Double(sum / self.count as f64)),
630            other => sum_type_mismatch(self.result_type.type_name(), &other),
631        }
632    }
633
634    fn state(&self) -> Result<Vec<SqlValue>> {
635        let sum = match self.sum.finalize()? {
636            SqlValue::Null => match self.result_type {
637                ResolvedType::Decimal { scale, .. } => {
638                    SqlValue::Decimal(crate::storage::DecimalValue::new(0, scale))
639                }
640                _ => SqlValue::Double(0.0),
641            },
642            value => value,
643        };
644        Ok(vec![sum, SqlValue::BigInt(self.count as i64)])
645    }
646
647    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
648        expect_state_arity("avg", state, 2)?;
649        let count = state_bigint("avg", &state[1], 1)?;
650        if count < 0 {
651            return Err(invalid_aggregate_state(
652                "avg",
653                "state count must be non-negative",
654            ));
655        }
656        if !state[0].is_null() {
657            self.sum.add_value(state[0].clone())?;
658        }
659        self.count = self.count.saturating_add(count as usize);
660        Ok(())
661    }
662
663    fn clone_box(&self) -> Box<dyn Accumulator> {
664        Box::new(self.clone())
665    }
666
667    fn retained_bytes(&self) -> u64 {
668        estimated_distinct_retained_bytes(&self.distinct_values)
669    }
670}
671
672fn numeric_to_f64(value: &SqlValue) -> Result<f64> {
673    match value {
674        SqlValue::Integer(v) => Ok(*v as f64),
675        SqlValue::BigInt(v) => Ok(*v as f64),
676        SqlValue::Float(v) => Ok(*v as f64),
677        SqlValue::Double(v) => Ok(*v),
678        _ => Err(ExecutorError::Evaluation(
679            crate::executor::EvaluationError::TypeMismatch {
680                expected: "numeric".into(),
681                actual: value.type_name().into(),
682            },
683        )),
684    }
685}
686
687fn sum_type_mismatch<T>(expected: &str, actual: &SqlValue) -> Result<T> {
688    Err(ExecutorError::Evaluation(EvaluationError::TypeMismatch {
689        expected: expected.into(),
690        actual: actual.type_name().into(),
691    }))
692}
693
694/// Accumulator for MIN / MAX.
695#[derive(Debug, Clone)]
696pub struct MinMaxAccumulator {
697    value: Option<SqlValue>,
698    is_min: bool,
699    distinct_values: Option<HashSet<Vec<u8>>>,
700}
701
702impl MinMaxAccumulator {
703    /// Create a new min/max accumulator.
704    pub fn new(is_min: bool) -> Self {
705        Self::with_distinct(is_min, false)
706    }
707
708    pub fn with_distinct(is_min: bool, distinct: bool) -> Self {
709        Self {
710            value: None,
711            is_min,
712            distinct_values: if distinct { Some(HashSet::new()) } else { None },
713        }
714    }
715}
716
717impl Accumulator for MinMaxAccumulator {
718    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
719        let Some(value) = value else {
720            return Ok(());
721        };
722        if value.is_null() {
723            return Ok(());
724        }
725        if !distinct_allows(&mut self.distinct_values, &value)? {
726            return Ok(());
727        }
728
729        match &self.value {
730            None => {
731                self.value = Some(value);
732            }
733            Some(current) => {
734                if std::mem::discriminant(current) != std::mem::discriminant(&value) {
735                    return Err(ExecutorError::Evaluation(
736                        crate::executor::EvaluationError::TypeMismatch {
737                            expected: current.type_name().into(),
738                            actual: value.type_name().into(),
739                        },
740                    ));
741                }
742                let ordering = value.partial_cmp(current).ok_or_else(|| {
743                    ExecutorError::Evaluation(crate::executor::EvaluationError::TypeMismatch {
744                        expected: current.type_name().into(),
745                        actual: value.type_name().into(),
746                    })
747                })?;
748                let should_replace = matches!(
749                    (self.is_min, ordering),
750                    (true, Ordering::Less) | (false, Ordering::Greater)
751                );
752                if should_replace {
753                    self.value = Some(value);
754                }
755            }
756        }
757        Ok(())
758    }
759
760    fn finalize(&self) -> Result<SqlValue> {
761        Ok(self.value.clone().unwrap_or(SqlValue::Null))
762    }
763
764    fn state(&self) -> Result<Vec<SqlValue>> {
765        Ok(vec![self.value.clone().unwrap_or(SqlValue::Null)])
766    }
767
768    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
769        expect_state_arity(if self.is_min { "min" } else { "max" }, state, 1)?;
770        if state[0].is_null() {
771            return Ok(());
772        }
773        self.update(Some(state[0].clone()))
774    }
775
776    fn clone_box(&self) -> Box<dyn Accumulator> {
777        Box::new(self.clone())
778    }
779
780    fn retained_bytes(&self) -> u64 {
781        estimated_distinct_retained_bytes(&self.distinct_values).saturating_add(
782            self.value
783                .as_ref()
784                .map(ByteSized::estimated_bytes)
785                .unwrap_or(0),
786        )
787    }
788}
789
790/// Compare aggregate-local sort keys under per-key `(asc, nulls_first)`
791/// specifications (issue #148).
792fn compare_ordered_keys(left: &[SqlValue], right: &[SqlValue], specs: &[(bool, bool)]) -> Ordering {
793    for (idx, (asc, nulls_first)) in specs.iter().enumerate() {
794        let left_value = left.get(idx).unwrap_or(&SqlValue::Null);
795        let right_value = right.get(idx).unwrap_or(&SqlValue::Null);
796        let cmp = super::iterator::compare_single(left_value, right_value, *asc, *nulls_first);
797        if cmp != Ordering::Equal {
798            return cmp;
799        }
800    }
801    Ordering::Equal
802}
803
804fn estimated_ordered_string_bytes(values: &[(Vec<SqlValue>, String)]) -> u64 {
805    values.iter().fold(0u64, |total, (keys, value)| {
806        let key_bytes: u64 = keys.iter().map(ByteSized::estimated_bytes).sum();
807        total
808            .saturating_add(key_bytes)
809            .saturating_add(u64::try_from(value.capacity()).unwrap_or(u64::MAX))
810    })
811}
812
813/// Accumulator for GROUP_CONCAT.
814#[derive(Debug, Clone)]
815pub struct GroupConcatAccumulator {
816    values: Vec<String>,
817    separator: String,
818    distinct_values: Option<HashSet<Vec<u8>>>,
819    /// Non-empty selects the ordered mode: pairs are buffered and stably
820    /// sorted at finalize (issue #148).
821    sort_specs: Vec<(bool, bool)>,
822    ordered_values: Vec<(Vec<SqlValue>, String)>,
823}
824
825impl GroupConcatAccumulator {
826    /// Create a new GROUP_CONCAT accumulator with the given separator.
827    pub fn new(separator: String) -> Self {
828        Self::with_distinct(separator, false)
829    }
830
831    pub fn with_distinct(separator: String, distinct: bool) -> Self {
832        Self::with_order(separator, distinct, Vec::new())
833    }
834
835    pub fn with_order(separator: String, distinct: bool, sort_specs: Vec<(bool, bool)>) -> Self {
836        Self {
837            values: Vec::new(),
838            separator,
839            distinct_values: if distinct { Some(HashSet::new()) } else { None },
840            sort_specs,
841            ordered_values: Vec::new(),
842        }
843    }
844}
845
846impl Accumulator for GroupConcatAccumulator {
847    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
848        if !self.sort_specs.is_empty() {
849            return Err(invalid_aggregate_state(
850                "group_concat",
851                "ordered aggregation requires the sort keys of every row",
852            ));
853        }
854        let Some(value) = value else {
855            return Ok(());
856        };
857        match value {
858            SqlValue::Null => Ok(()),
859            SqlValue::Text(text) => {
860                let value = SqlValue::Text(text.clone());
861                if !distinct_allows(&mut self.distinct_values, &value)? {
862                    return Ok(());
863                }
864                self.values.push(text);
865                Ok(())
866            }
867            other => Err(ExecutorError::Evaluation(
868                crate::executor::EvaluationError::TypeMismatch {
869                    expected: "Text".into(),
870                    actual: other.type_name().into(),
871                },
872            )),
873        }
874    }
875
876    fn update_ordered(&mut self, value: Option<SqlValue>, sort_keys: &[SqlValue]) -> Result<()> {
877        if self.sort_specs.is_empty() {
878            return self.update(value);
879        }
880        let Some(value) = value else {
881            return Ok(());
882        };
883        match value {
884            SqlValue::Null => Ok(()),
885            SqlValue::Text(text) => {
886                let value = SqlValue::Text(text.clone());
887                if !distinct_allows(&mut self.distinct_values, &value)? {
888                    return Ok(());
889                }
890                self.ordered_values.push((sort_keys.to_vec(), text));
891                Ok(())
892            }
893            other => Err(ExecutorError::Evaluation(
894                crate::executor::EvaluationError::TypeMismatch {
895                    expected: "Text".into(),
896                    actual: other.type_name().into(),
897                },
898            )),
899        }
900    }
901
902    fn finalize(&self) -> Result<SqlValue> {
903        if !self.sort_specs.is_empty() {
904            if self.ordered_values.is_empty() {
905                return Ok(SqlValue::Null);
906            }
907            let mut sorted = self.ordered_values.clone();
908            sorted.sort_by(|left, right| compare_ordered_keys(&left.0, &right.0, &self.sort_specs));
909            let joined = sorted
910                .into_iter()
911                .map(|(_, value)| value)
912                .collect::<Vec<_>>()
913                .join(&self.separator);
914            return Ok(SqlValue::Text(joined));
915        }
916        if self.values.is_empty() {
917            return Ok(SqlValue::Null);
918        }
919        Ok(SqlValue::Text(self.values.join(&self.separator)))
920    }
921
922    fn state(&self) -> Result<Vec<SqlValue>> {
923        if !self.sort_specs.is_empty() {
924            return Err(invalid_aggregate_state(
925                "group_concat",
926                "ordered aggregation cannot produce partial state",
927            ));
928        }
929        Ok(vec![
930            if self.values.is_empty() {
931                SqlValue::Null
932            } else {
933                SqlValue::Text(self.values.join(&self.separator))
934            },
935            SqlValue::Text(self.separator.clone()),
936        ])
937    }
938
939    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
940        if !self.sort_specs.is_empty() {
941            return Err(invalid_aggregate_state(
942                "group_concat",
943                "ordered aggregation cannot merge partial state",
944            ));
945        }
946        expect_state_arity("group_concat", state, 2)?;
947        let separator = state_text("group_concat", &state[1], 1)?;
948        if separator != self.separator {
949            return Err(invalid_aggregate_state(
950                "group_concat",
951                "state separator differs from accumulator separator",
952            ));
953        }
954        match &state[0] {
955            SqlValue::Null => Ok(()),
956            SqlValue::Text(text) => {
957                self.values.push(text.clone());
958                Ok(())
959            }
960            other => Err(invalid_aggregate_state(
961                "group_concat",
962                format!(
963                    "state value 0 expected Text or Null, got {}",
964                    other.type_name()
965                ),
966            )),
967        }
968    }
969
970    fn clone_box(&self) -> Box<dyn Accumulator> {
971        Box::new(self.clone())
972    }
973
974    fn retained_bytes(&self) -> u64 {
975        estimated_distinct_retained_bytes(&self.distinct_values)
976            .saturating_add(estimated_string_collection_bytes(
977                &self.values,
978                self.values.capacity(),
979                self.separator.capacity(),
980            ))
981            .saturating_add(estimated_ordered_string_bytes(&self.ordered_values))
982    }
983}
984
985/// Accumulator for STRING_AGG.
986#[derive(Debug, Clone)]
987pub struct StringAggAccumulator {
988    values: Vec<String>,
989    separator: String,
990    distinct_values: Option<HashSet<Vec<u8>>>,
991    /// Non-empty selects the ordered mode (see [`GroupConcatAccumulator`]).
992    sort_specs: Vec<(bool, bool)>,
993    ordered_values: Vec<(Vec<SqlValue>, String)>,
994}
995
996impl StringAggAccumulator {
997    /// Create a new string_agg accumulator.
998    pub fn new(separator: String) -> Self {
999        Self::with_distinct(separator, false)
1000    }
1001
1002    pub fn with_distinct(separator: String, distinct: bool) -> Self {
1003        Self::with_order(separator, distinct, Vec::new())
1004    }
1005
1006    pub fn with_order(separator: String, distinct: bool, sort_specs: Vec<(bool, bool)>) -> Self {
1007        Self {
1008            values: Vec::new(),
1009            separator,
1010            distinct_values: if distinct { Some(HashSet::new()) } else { None },
1011            sort_specs,
1012            ordered_values: Vec::new(),
1013        }
1014    }
1015}
1016
1017impl Accumulator for StringAggAccumulator {
1018    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1019        if !self.sort_specs.is_empty() {
1020            return Err(invalid_aggregate_state(
1021                "string_agg",
1022                "ordered aggregation requires the sort keys of every row",
1023            ));
1024        }
1025        let Some(value) = value else {
1026            return Ok(());
1027        };
1028        match value {
1029            SqlValue::Null => Ok(()),
1030            SqlValue::Text(s) => {
1031                let value = SqlValue::Text(s.clone());
1032                if !distinct_allows(&mut self.distinct_values, &value)? {
1033                    return Ok(());
1034                }
1035                self.values.push(s);
1036                Ok(())
1037            }
1038            other => Err(ExecutorError::Evaluation(
1039                crate::executor::EvaluationError::TypeMismatch {
1040                    expected: "Text".into(),
1041                    actual: other.type_name().into(),
1042                },
1043            )),
1044        }
1045    }
1046
1047    fn update_ordered(&mut self, value: Option<SqlValue>, sort_keys: &[SqlValue]) -> Result<()> {
1048        if self.sort_specs.is_empty() {
1049            return self.update(value);
1050        }
1051        let Some(value) = value else {
1052            return Ok(());
1053        };
1054        match value {
1055            SqlValue::Null => Ok(()),
1056            SqlValue::Text(s) => {
1057                let value = SqlValue::Text(s.clone());
1058                if !distinct_allows(&mut self.distinct_values, &value)? {
1059                    return Ok(());
1060                }
1061                self.ordered_values.push((sort_keys.to_vec(), s));
1062                Ok(())
1063            }
1064            other => Err(ExecutorError::Evaluation(
1065                crate::executor::EvaluationError::TypeMismatch {
1066                    expected: "Text".into(),
1067                    actual: other.type_name().into(),
1068                },
1069            )),
1070        }
1071    }
1072
1073    fn finalize(&self) -> Result<SqlValue> {
1074        if !self.sort_specs.is_empty() {
1075            if self.ordered_values.is_empty() {
1076                return Ok(SqlValue::Null);
1077            }
1078            let mut sorted = self.ordered_values.clone();
1079            sorted.sort_by(|left, right| compare_ordered_keys(&left.0, &right.0, &self.sort_specs));
1080            let joined = sorted
1081                .into_iter()
1082                .map(|(_, value)| value)
1083                .collect::<Vec<_>>()
1084                .join(&self.separator);
1085            return Ok(SqlValue::Text(joined));
1086        }
1087        if self.values.is_empty() {
1088            return Ok(SqlValue::Null);
1089        }
1090        Ok(SqlValue::Text(self.values.join(&self.separator)))
1091    }
1092
1093    fn state(&self) -> Result<Vec<SqlValue>> {
1094        if !self.sort_specs.is_empty() {
1095            return Err(invalid_aggregate_state(
1096                "string_agg",
1097                "ordered aggregation cannot produce partial state",
1098            ));
1099        }
1100        Ok(vec![
1101            if self.values.is_empty() {
1102                SqlValue::Null
1103            } else {
1104                SqlValue::Text(self.values.join(&self.separator))
1105            },
1106            SqlValue::Text(self.separator.clone()),
1107        ])
1108    }
1109
1110    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1111        if !self.sort_specs.is_empty() {
1112            return Err(invalid_aggregate_state(
1113                "string_agg",
1114                "ordered aggregation cannot merge partial state",
1115            ));
1116        }
1117        expect_state_arity("string_agg", state, 2)?;
1118        let separator = state_text("string_agg", &state[1], 1)?;
1119        if separator != self.separator {
1120            return Err(invalid_aggregate_state(
1121                "string_agg",
1122                "state separator differs from accumulator separator",
1123            ));
1124        }
1125        match &state[0] {
1126            SqlValue::Null => Ok(()),
1127            SqlValue::Text(text) => {
1128                self.values.push(text.clone());
1129                Ok(())
1130            }
1131            other => Err(invalid_aggregate_state(
1132                "string_agg",
1133                format!(
1134                    "state value 0 expected Text or Null, got {}",
1135                    other.type_name()
1136                ),
1137            )),
1138        }
1139    }
1140
1141    fn clone_box(&self) -> Box<dyn Accumulator> {
1142        Box::new(self.clone())
1143    }
1144
1145    fn retained_bytes(&self) -> u64 {
1146        estimated_distinct_retained_bytes(&self.distinct_values)
1147            .saturating_add(estimated_string_collection_bytes(
1148                &self.values,
1149                self.values.capacity(),
1150                self.separator.capacity(),
1151            ))
1152            .saturating_add(estimated_ordered_string_bytes(&self.ordered_values))
1153    }
1154}
1155
1156/// Accumulator for the ordered-set aggregate PERCENTILE_DISC (issue #148).
1157///
1158/// Buffers `(sort_keys, value)` pairs, sorts them at finalize, and returns the
1159/// first value whose cumulative distribution reaches the fraction:
1160/// `index = max(ceil(fraction * n) - 1, 0)` — PostgreSQL 16 semantics (D5).
1161/// NULL sort values are excluded; an empty group yields NULL. Partial state is
1162/// rejected: ordered-set aggregation always runs in Single mode (D11).
1163#[derive(Debug, Clone)]
1164pub struct PercentileDiscAccumulator {
1165    fraction: f64,
1166    sort_specs: Vec<(bool, bool)>,
1167    values: Vec<(Vec<SqlValue>, SqlValue)>,
1168}
1169
1170impl PercentileDiscAccumulator {
1171    pub fn new(fraction: f64, sort_specs: Vec<(bool, bool)>) -> Self {
1172        let sort_specs = if sort_specs.is_empty() {
1173            vec![(true, false)]
1174        } else {
1175            sort_specs
1176        };
1177        Self {
1178            fraction,
1179            sort_specs,
1180            values: Vec::new(),
1181        }
1182    }
1183}
1184
1185impl Accumulator for PercentileDiscAccumulator {
1186    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1187        // The sort value is the aggregated value, so it doubles as its own
1188        // key when a caller has no separate key row.
1189        let keys = match &value {
1190            Some(inner) if !inner.is_null() => vec![inner.clone()],
1191            _ => return Ok(()),
1192        };
1193        self.update_ordered(value, &keys)
1194    }
1195
1196    fn update_ordered(&mut self, value: Option<SqlValue>, sort_keys: &[SqlValue]) -> Result<()> {
1197        let Some(value) = value else {
1198            return Ok(());
1199        };
1200        if value.is_null() {
1201            return Ok(());
1202        }
1203        self.values.push((sort_keys.to_vec(), value));
1204        Ok(())
1205    }
1206
1207    fn finalize(&self) -> Result<SqlValue> {
1208        if self.values.is_empty() {
1209            return Ok(SqlValue::Null);
1210        }
1211        let mut sorted = self.values.clone();
1212        sorted.sort_by(|left, right| compare_ordered_keys(&left.0, &right.0, &self.sort_specs));
1213        let count = sorted.len();
1214        let index = (self.fraction * count as f64).ceil() as usize;
1215        let index = index.saturating_sub(1).min(count - 1);
1216        Ok(sorted[index].1.clone())
1217    }
1218
1219    fn state(&self) -> Result<Vec<SqlValue>> {
1220        Err(invalid_aggregate_state(
1221            "percentile_disc",
1222            "ordered-set aggregation cannot produce partial state",
1223        ))
1224    }
1225
1226    fn merge(&mut self, _state: &[SqlValue]) -> Result<()> {
1227        Err(invalid_aggregate_state(
1228            "percentile_disc",
1229            "ordered-set aggregation cannot merge partial state",
1230        ))
1231    }
1232
1233    fn clone_box(&self) -> Box<dyn Accumulator> {
1234        Box::new(self.clone())
1235    }
1236
1237    fn retained_bytes(&self) -> u64 {
1238        self.values.iter().fold(0u64, |total, (keys, value)| {
1239            let key_bytes: u64 = keys.iter().map(ByteSized::estimated_bytes).sum();
1240            total
1241                .saturating_add(key_bytes)
1242                .saturating_add(value.estimated_bytes())
1243        })
1244    }
1245}
1246
1247#[derive(Debug, Clone, Copy)]
1248enum StatisticsKind {
1249    Variance(bool),
1250    Stddev(bool),
1251    Covariance(bool),
1252    Corr,
1253    RegrCount,
1254    RegrAvgX,
1255    RegrAvgY,
1256    RegrSxx,
1257    RegrSyy,
1258    RegrSxy,
1259    RegrSlope,
1260    RegrIntercept,
1261    RegrR2,
1262}
1263
1264/// Numerically stable one-pass moments with Chan-compatible merge state.
1265#[derive(Debug, Clone)]
1266struct StatisticsAccumulator {
1267    kind: StatisticsKind,
1268    count: u64,
1269    mean_x: f64,
1270    mean_y: f64,
1271    m2_x: f64,
1272    m2_y: f64,
1273    co_moment: f64,
1274}
1275
1276impl StatisticsAccumulator {
1277    fn new(kind: StatisticsKind) -> Self {
1278        Self {
1279            kind,
1280            count: 0,
1281            mean_x: 0.0,
1282            mean_y: 0.0,
1283            m2_x: 0.0,
1284            m2_y: 0.0,
1285            co_moment: 0.0,
1286        }
1287    }
1288
1289    fn update_pair(&mut self, y: f64, x: f64) {
1290        self.count += 1;
1291        let count = self.count as f64;
1292        let dx = x - self.mean_x;
1293        let dy = y - self.mean_y;
1294        self.mean_x += dx / count;
1295        self.mean_y += dy / count;
1296        self.m2_x += dx * (x - self.mean_x);
1297        self.m2_y += dy * (y - self.mean_y);
1298        self.co_moment += dx * (y - self.mean_y);
1299    }
1300
1301    fn null_or(&self, value: f64) -> SqlValue {
1302        if self.count == 0 {
1303            SqlValue::Null
1304        } else {
1305            SqlValue::Double(value)
1306        }
1307    }
1308}
1309
1310impl Accumulator for StatisticsAccumulator {
1311    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1312        let Some(value) = value.filter(|value| !value.is_null()) else {
1313            return Ok(());
1314        };
1315        let value = numeric_to_f64(&value)?;
1316        self.update_pair(value, value);
1317        Ok(())
1318    }
1319
1320    fn update_values(&mut self, values: &[SqlValue]) -> Result<()> {
1321        if values.len() == 1 {
1322            return self.update(values.first().cloned());
1323        }
1324        let [y, x] = values else {
1325            return Err(invalid_aggregate_state(
1326                "statistics",
1327                format!("expected one or two inputs, got {}", values.len()),
1328            ));
1329        };
1330        if y.is_null() || x.is_null() {
1331            return Ok(());
1332        }
1333        self.update_pair(numeric_to_f64(y)?, numeric_to_f64(x)?);
1334        Ok(())
1335    }
1336
1337    fn state(&self) -> Result<Vec<SqlValue>> {
1338        Ok(vec![
1339            SqlValue::BigInt(self.count as i64),
1340            SqlValue::Double(self.mean_x),
1341            SqlValue::Double(self.mean_y),
1342            SqlValue::Double(self.m2_x),
1343            SqlValue::Double(self.m2_y),
1344            SqlValue::Double(self.co_moment),
1345        ])
1346    }
1347
1348    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1349        expect_state_arity("statistics", state, 6)?;
1350        let other_count = state_bigint("statistics", &state[0], 0)?;
1351        if other_count < 0 {
1352            return Err(invalid_aggregate_state(
1353                "statistics",
1354                "state count must be non-negative",
1355            ));
1356        }
1357        let other_count = other_count as u64;
1358        if other_count == 0 {
1359            return Ok(());
1360        }
1361        let other_mean_x = state_double("statistics", &state[1], 1)?;
1362        let other_mean_y = state_double("statistics", &state[2], 2)?;
1363        let other_m2_x = state_double("statistics", &state[3], 3)?;
1364        let other_m2_y = state_double("statistics", &state[4], 4)?;
1365        let other_co_moment = state_double("statistics", &state[5], 5)?;
1366        if self.count == 0 {
1367            self.count = other_count;
1368            self.mean_x = other_mean_x;
1369            self.mean_y = other_mean_y;
1370            self.m2_x = other_m2_x;
1371            self.m2_y = other_m2_y;
1372            self.co_moment = other_co_moment;
1373            return Ok(());
1374        }
1375        let total = self.count + other_count;
1376        let left = self.count as f64;
1377        let right = other_count as f64;
1378        let total_f = total as f64;
1379        let dx = other_mean_x - self.mean_x;
1380        let dy = other_mean_y - self.mean_y;
1381        self.m2_x += other_m2_x + dx * dx * left * right / total_f;
1382        self.m2_y += other_m2_y + dy * dy * left * right / total_f;
1383        self.co_moment += other_co_moment + dx * dy * left * right / total_f;
1384        self.mean_x += dx * right / total_f;
1385        self.mean_y += dy * right / total_f;
1386        self.count = total;
1387        Ok(())
1388    }
1389
1390    fn finalize(&self) -> Result<SqlValue> {
1391        let sample_divisor = || (self.count > 1).then(|| (self.count - 1) as f64);
1392        let population_divisor = || (self.count > 0).then_some(self.count as f64);
1393        let value = match self.kind {
1394            StatisticsKind::Variance(sample) | StatisticsKind::Stddev(sample) => {
1395                let divisor = if sample {
1396                    sample_divisor()
1397                } else {
1398                    population_divisor()
1399                };
1400                let Some(divisor) = divisor else {
1401                    return Ok(SqlValue::Null);
1402                };
1403                let variance = self.m2_x / divisor;
1404                if matches!(self.kind, StatisticsKind::Stddev(_)) {
1405                    variance.sqrt()
1406                } else {
1407                    variance
1408                }
1409            }
1410            StatisticsKind::Covariance(sample) => {
1411                let divisor = if sample {
1412                    sample_divisor()
1413                } else {
1414                    population_divisor()
1415                };
1416                let Some(divisor) = divisor else {
1417                    return Ok(SqlValue::Null);
1418                };
1419                self.co_moment / divisor
1420            }
1421            StatisticsKind::Corr => {
1422                let divisor = (self.m2_x * self.m2_y).sqrt();
1423                if self.count == 0 || divisor == 0.0 {
1424                    return Ok(SqlValue::Null);
1425                }
1426                self.co_moment / divisor
1427            }
1428            StatisticsKind::RegrCount => return Ok(SqlValue::BigInt(self.count as i64)),
1429            StatisticsKind::RegrAvgX => return Ok(self.null_or(self.mean_x)),
1430            StatisticsKind::RegrAvgY => return Ok(self.null_or(self.mean_y)),
1431            StatisticsKind::RegrSxx => return Ok(self.null_or(self.m2_x)),
1432            StatisticsKind::RegrSyy => return Ok(self.null_or(self.m2_y)),
1433            StatisticsKind::RegrSxy => return Ok(self.null_or(self.co_moment)),
1434            StatisticsKind::RegrSlope => {
1435                if self.count == 0 || self.m2_x == 0.0 {
1436                    return Ok(SqlValue::Null);
1437                }
1438                self.co_moment / self.m2_x
1439            }
1440            StatisticsKind::RegrIntercept => {
1441                if self.count == 0 || self.m2_x == 0.0 {
1442                    return Ok(SqlValue::Null);
1443                }
1444                self.mean_y - (self.co_moment / self.m2_x) * self.mean_x
1445            }
1446            StatisticsKind::RegrR2 => {
1447                if self.count == 0 || self.m2_x == 0.0 {
1448                    return Ok(SqlValue::Null);
1449                }
1450                if self.m2_y == 0.0 {
1451                    1.0
1452                } else {
1453                    self.co_moment * self.co_moment / (self.m2_x * self.m2_y)
1454                }
1455            }
1456        };
1457        Ok(SqlValue::Double(value))
1458    }
1459
1460    fn clone_box(&self) -> Box<dyn Accumulator> {
1461        Box::new(self.clone())
1462    }
1463}
1464
1465#[derive(Debug, Clone)]
1466struct PercentileContAccumulator {
1467    fraction: f64,
1468    ascending: bool,
1469    values: Vec<f64>,
1470}
1471
1472impl PercentileContAccumulator {
1473    fn new(fraction: f64, ascending: bool) -> Self {
1474        Self {
1475            fraction,
1476            ascending,
1477            values: Vec::new(),
1478        }
1479    }
1480}
1481
1482impl Accumulator for PercentileContAccumulator {
1483    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1484        let Some(value) = value.filter(|value| !value.is_null()) else {
1485            return Ok(());
1486        };
1487        self.values.push(numeric_to_f64(&value)?);
1488        Ok(())
1489    }
1490
1491    fn state(&self) -> Result<Vec<SqlValue>> {
1492        Err(invalid_aggregate_state(
1493            "percentile_cont",
1494            "ordered aggregation cannot produce partial state",
1495        ))
1496    }
1497
1498    fn merge(&mut self, _state: &[SqlValue]) -> Result<()> {
1499        Err(invalid_aggregate_state(
1500            "percentile_cont",
1501            "ordered aggregation cannot merge partial state",
1502        ))
1503    }
1504
1505    fn finalize(&self) -> Result<SqlValue> {
1506        if self.values.is_empty() {
1507            return Ok(SqlValue::Null);
1508        }
1509        let mut values = self.values.clone();
1510        values.sort_by(f64::total_cmp);
1511        if !self.ascending {
1512            values.reverse();
1513        }
1514        let position = self.fraction * (values.len() - 1) as f64;
1515        let lower = position.floor() as usize;
1516        let upper = position.ceil() as usize;
1517        if lower == upper {
1518            return Ok(SqlValue::Double(values[lower]));
1519        }
1520        let weight = position - lower as f64;
1521        Ok(SqlValue::Double(
1522            values[lower] + (values[upper] - values[lower]) * weight,
1523        ))
1524    }
1525
1526    fn clone_box(&self) -> Box<dyn Accumulator> {
1527        Box::new(self.clone())
1528    }
1529
1530    fn retained_bytes(&self) -> u64 {
1531        u64::try_from(
1532            self.values
1533                .capacity()
1534                .saturating_mul(std::mem::size_of::<f64>()),
1535        )
1536        .unwrap_or(u64::MAX)
1537    }
1538}
1539
1540#[derive(Debug, Clone)]
1541struct ModeAccumulator {
1542    sort_specs: Vec<(bool, bool)>,
1543    values: Vec<(Vec<SqlValue>, SqlValue)>,
1544}
1545
1546impl ModeAccumulator {
1547    fn new(sort_specs: Vec<(bool, bool)>) -> Self {
1548        Self {
1549            sort_specs: if sort_specs.is_empty() {
1550                vec![(true, false)]
1551            } else {
1552                sort_specs
1553            },
1554            values: Vec::new(),
1555        }
1556    }
1557}
1558
1559impl Accumulator for ModeAccumulator {
1560    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1561        let Some(value) = value.filter(|value| !value.is_null()) else {
1562            return Ok(());
1563        };
1564        self.values.push((vec![value.clone()], value));
1565        Ok(())
1566    }
1567
1568    fn update_ordered(&mut self, value: Option<SqlValue>, sort_keys: &[SqlValue]) -> Result<()> {
1569        let Some(value) = value.filter(|value| !value.is_null()) else {
1570            return Ok(());
1571        };
1572        self.values.push((sort_keys.to_vec(), value));
1573        Ok(())
1574    }
1575
1576    fn state(&self) -> Result<Vec<SqlValue>> {
1577        Err(invalid_aggregate_state(
1578            "mode",
1579            "ordered aggregation cannot produce partial state",
1580        ))
1581    }
1582
1583    fn merge(&mut self, _state: &[SqlValue]) -> Result<()> {
1584        Err(invalid_aggregate_state(
1585            "mode",
1586            "ordered aggregation cannot merge partial state",
1587        ))
1588    }
1589
1590    fn finalize(&self) -> Result<SqlValue> {
1591        if self.values.is_empty() {
1592            return Ok(SqlValue::Null);
1593        }
1594        let mut values = self.values.clone();
1595        values.sort_by(|left, right| compare_ordered_keys(&left.0, &right.0, &self.sort_specs));
1596        let mut best_start = 0;
1597        let mut best_count = 0;
1598        let mut start = 0;
1599        while start < values.len() {
1600            let mut end = start + 1;
1601            while end < values.len()
1602                && compare_ordered_keys(&values[start].0, &values[end].0, &self.sort_specs)
1603                    == Ordering::Equal
1604            {
1605                end += 1;
1606            }
1607            if end - start > best_count {
1608                best_start = start;
1609                best_count = end - start;
1610            }
1611            start = end;
1612        }
1613        Ok(values[best_start].1.clone())
1614    }
1615
1616    fn clone_box(&self) -> Box<dyn Accumulator> {
1617        Box::new(self.clone())
1618    }
1619
1620    fn retained_bytes(&self) -> u64 {
1621        self.values.iter().fold(0, |total, (keys, value)| {
1622            total
1623                .saturating_add(keys.iter().map(ByteSized::estimated_bytes).sum::<u64>())
1624                .saturating_add(value.estimated_bytes())
1625        })
1626    }
1627}
1628
1629#[derive(Debug, Clone, Copy)]
1630enum ValueKind {
1631    Any,
1632    First,
1633    Last,
1634    ArgMin,
1635    ArgMax,
1636}
1637
1638#[derive(Debug, Clone)]
1639struct ValueAccumulator {
1640    kind: ValueKind,
1641    sort_specs: Vec<(bool, bool)>,
1642    chosen: Option<(Vec<SqlValue>, SqlValue)>,
1643}
1644
1645impl ValueAccumulator {
1646    fn new(kind: ValueKind, sort_specs: Vec<(bool, bool)>) -> Self {
1647        Self {
1648            kind,
1649            sort_specs,
1650            chosen: None,
1651        }
1652    }
1653
1654    fn select(&mut self, keys: Vec<SqlValue>, value: SqlValue) {
1655        let replace = match &self.chosen {
1656            None => true,
1657            Some((current, _)) => match self.kind {
1658                ValueKind::Any | ValueKind::First => false,
1659                ValueKind::Last => true,
1660                ValueKind::ArgMin => {
1661                    compare_ordered_keys(&keys, current, &[(true, false)]) == Ordering::Less
1662                }
1663                ValueKind::ArgMax => {
1664                    compare_ordered_keys(&keys, current, &[(true, false)]) == Ordering::Greater
1665                }
1666            },
1667        };
1668        if replace {
1669            self.chosen = Some((keys, value));
1670        }
1671    }
1672}
1673
1674impl Accumulator for ValueAccumulator {
1675    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1676        let Some(value) = value else {
1677            return Ok(());
1678        };
1679        if matches!(self.kind, ValueKind::Any) && value.is_null() {
1680            return Ok(());
1681        }
1682        self.select(Vec::new(), value);
1683        Ok(())
1684    }
1685
1686    fn update_values(&mut self, values: &[SqlValue]) -> Result<()> {
1687        if matches!(self.kind, ValueKind::ArgMin | ValueKind::ArgMax) {
1688            let [value, key] = values else {
1689                return Err(invalid_aggregate_state(
1690                    "arg_min/max",
1691                    format!("expected two inputs, got {}", values.len()),
1692                ));
1693            };
1694            if value.is_null() || key.is_null() {
1695                return Ok(());
1696            }
1697            self.select(vec![key.clone()], value.clone());
1698            return Ok(());
1699        }
1700        self.update(values.first().cloned())
1701    }
1702
1703    fn update_ordered(&mut self, value: Option<SqlValue>, sort_keys: &[SqlValue]) -> Result<()> {
1704        let Some(value) = value else {
1705            return Ok(());
1706        };
1707        match &self.chosen {
1708            None => self.chosen = Some((sort_keys.to_vec(), value)),
1709            Some((keys, _)) => {
1710                let ordering = compare_ordered_keys(sort_keys, keys, &self.sort_specs);
1711                let replace = matches!(
1712                    (self.kind, ordering),
1713                    (ValueKind::First, Ordering::Less) | (ValueKind::Last, Ordering::Greater)
1714                );
1715                if replace {
1716                    self.chosen = Some((sort_keys.to_vec(), value));
1717                }
1718            }
1719        }
1720        Ok(())
1721    }
1722
1723    fn state(&self) -> Result<Vec<SqlValue>> {
1724        Ok(vec![
1725            self.chosen
1726                .as_ref()
1727                .map(|(_, value)| value.clone())
1728                .unwrap_or(SqlValue::Null),
1729        ])
1730    }
1731
1732    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1733        expect_state_arity("value aggregate", state, 1)?;
1734        self.update(Some(state[0].clone()))
1735    }
1736
1737    fn finalize(&self) -> Result<SqlValue> {
1738        Ok(self
1739            .chosen
1740            .as_ref()
1741            .map(|(_, value)| value.clone())
1742            .unwrap_or(SqlValue::Null))
1743    }
1744
1745    fn clone_box(&self) -> Box<dyn Accumulator> {
1746        Box::new(self.clone())
1747    }
1748
1749    fn retained_bytes(&self) -> u64 {
1750        self.chosen
1751            .as_ref()
1752            .map(|(keys, value)| {
1753                keys.iter()
1754                    .map(ByteSized::estimated_bytes)
1755                    .sum::<u64>()
1756                    .saturating_add(value.estimated_bytes())
1757            })
1758            .unwrap_or(0)
1759    }
1760}
1761
1762#[derive(Debug, Clone, Copy)]
1763enum BitKind {
1764    And,
1765    Or,
1766    Xor,
1767}
1768
1769#[derive(Debug, Clone)]
1770struct BitAccumulator {
1771    kind: BitKind,
1772    value: Option<SqlValue>,
1773}
1774
1775impl BitAccumulator {
1776    fn new(kind: BitKind) -> Self {
1777        Self { kind, value: None }
1778    }
1779}
1780
1781impl Accumulator for BitAccumulator {
1782    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1783        let Some(value) = value.filter(|value| !value.is_null()) else {
1784            return Ok(());
1785        };
1786        self.value = Some(match (self.value.take(), value) {
1787            (None, value) => value,
1788            (Some(SqlValue::Integer(left)), SqlValue::Integer(right)) => {
1789                SqlValue::Integer(match self.kind {
1790                    BitKind::And => left & right,
1791                    BitKind::Or => left | right,
1792                    BitKind::Xor => left ^ right,
1793                })
1794            }
1795            (Some(SqlValue::BigInt(left)), SqlValue::BigInt(right)) => {
1796                SqlValue::BigInt(match self.kind {
1797                    BitKind::And => left & right,
1798                    BitKind::Or => left | right,
1799                    BitKind::Xor => left ^ right,
1800                })
1801            }
1802            (Some(left), right) => {
1803                return Err(ExecutorError::Evaluation(EvaluationError::TypeMismatch {
1804                    expected: "matching integer types".into(),
1805                    actual: format!("{} vs {}", left.type_name(), right.type_name()),
1806                }));
1807            }
1808        });
1809        Ok(())
1810    }
1811
1812    fn state(&self) -> Result<Vec<SqlValue>> {
1813        Ok(vec![self.value.clone().unwrap_or(SqlValue::Null)])
1814    }
1815
1816    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1817        expect_state_arity("bit aggregate", state, 1)?;
1818        self.update(Some(state[0].clone()))
1819    }
1820
1821    fn finalize(&self) -> Result<SqlValue> {
1822        Ok(self.value.clone().unwrap_or(SqlValue::Null))
1823    }
1824
1825    fn clone_box(&self) -> Box<dyn Accumulator> {
1826        Box::new(self.clone())
1827    }
1828}
1829
1830#[derive(Debug, Clone)]
1831struct BoolAccumulator {
1832    and: bool,
1833    value: Option<bool>,
1834}
1835
1836impl BoolAccumulator {
1837    fn new(and: bool) -> Self {
1838        Self { and, value: None }
1839    }
1840}
1841
1842impl Accumulator for BoolAccumulator {
1843    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1844        match value {
1845            None | Some(SqlValue::Null) => Ok(()),
1846            Some(SqlValue::Boolean(value)) => {
1847                self.value = Some(match self.value {
1848                    None => value,
1849                    Some(current) if self.and => current && value,
1850                    Some(current) => current || value,
1851                });
1852                Ok(())
1853            }
1854            Some(other) => Err(ExecutorError::Evaluation(EvaluationError::TypeMismatch {
1855                expected: "Boolean".into(),
1856                actual: other.type_name().into(),
1857            })),
1858        }
1859    }
1860
1861    fn state(&self) -> Result<Vec<SqlValue>> {
1862        Ok(vec![
1863            self.value.map(SqlValue::Boolean).unwrap_or(SqlValue::Null),
1864        ])
1865    }
1866
1867    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1868        expect_state_arity("boolean aggregate", state, 1)?;
1869        self.update(Some(state[0].clone()))
1870    }
1871
1872    fn finalize(&self) -> Result<SqlValue> {
1873        Ok(self.value.map(SqlValue::Boolean).unwrap_or(SqlValue::Null))
1874    }
1875
1876    fn clone_box(&self) -> Box<dyn Accumulator> {
1877        Box::new(self.clone())
1878    }
1879}
1880
1881#[derive(Debug, Clone)]
1882struct ArrayAccumulator {
1883    values: Vec<SqlValue>,
1884    distinct: bool,
1885    seen: HashSet<Vec<u8>>,
1886}
1887
1888impl ArrayAccumulator {
1889    fn new(distinct: bool) -> Self {
1890        Self {
1891            values: Vec::new(),
1892            distinct,
1893            seen: HashSet::new(),
1894        }
1895    }
1896}
1897
1898impl Accumulator for ArrayAccumulator {
1899    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1900        let value = value.unwrap_or(SqlValue::Null);
1901        if !self.distinct
1902            || self
1903                .seen
1904                .insert(encode_group_key(std::slice::from_ref(&value))?)
1905        {
1906            self.values.push(value);
1907        }
1908        Ok(())
1909    }
1910
1911    fn state(&self) -> Result<Vec<SqlValue>> {
1912        Ok(vec![SqlValue::Array(self.values.clone())])
1913    }
1914
1915    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1916        expect_state_arity("ARRAY_AGG", state, 1)?;
1917        let SqlValue::Array(values) = &state[0] else {
1918            return Err(invalid_aggregate_state(
1919                "ARRAY_AGG",
1920                "partial state must be ARRAY",
1921            ));
1922        };
1923        for value in values {
1924            self.update(Some(value.clone()))?;
1925        }
1926        Ok(())
1927    }
1928
1929    fn finalize(&self) -> Result<SqlValue> {
1930        Ok(SqlValue::Array(self.values.clone()))
1931    }
1932
1933    fn clone_box(&self) -> Box<dyn Accumulator> {
1934        Box::new(self.clone())
1935    }
1936}
1937
1938#[derive(Debug, Clone)]
1939struct JsonArrayAccumulator {
1940    values: Vec<SqlValue>,
1941    distinct: bool,
1942    seen: HashSet<Vec<u8>>,
1943    native: bool,
1944}
1945
1946impl JsonArrayAccumulator {
1947    fn new(distinct: bool, native: bool) -> Self {
1948        Self {
1949            values: Vec::new(),
1950            distinct,
1951            seen: HashSet::new(),
1952            native,
1953        }
1954    }
1955}
1956
1957impl Accumulator for JsonArrayAccumulator {
1958    fn update(&mut self, value: Option<SqlValue>) -> Result<()> {
1959        let value = value.unwrap_or(SqlValue::Null);
1960        if !self.distinct
1961            || self
1962                .seen
1963                .insert(encode_group_key(std::slice::from_ref(&value))?)
1964        {
1965            self.values.push(value);
1966        }
1967        Ok(())
1968    }
1969
1970    fn state(&self) -> Result<Vec<SqlValue>> {
1971        Ok(vec![SqlValue::Text(
1972            crate::executor::evaluator::json::json_group_array(&self.values)?,
1973        )])
1974    }
1975
1976    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
1977        expect_state_arity("JSON_GROUP_ARRAY", state, 1)?;
1978        let SqlValue::Text(json) = &state[0] else {
1979            return Err(invalid_aggregate_state(
1980                "JSON_GROUP_ARRAY",
1981                "partial state must be TEXT",
1982            ));
1983        };
1984        let serde_json::Value::Array(values) =
1985            crate::executor::evaluator::json::parse_json("JSON_GROUP_ARRAY", json)?
1986        else {
1987            return Err(invalid_aggregate_state(
1988                "JSON_GROUP_ARRAY",
1989                "partial state must be a JSON array",
1990            ));
1991        };
1992        for value in values {
1993            self.update(Some(crate::executor::evaluator::json::json_to_sql(&value)?))?;
1994        }
1995        Ok(())
1996    }
1997
1998    fn finalize(&self) -> Result<SqlValue> {
1999        let value = crate::executor::evaluator::json::json_group_array(&self.values)?;
2000        if self.native {
2001            Ok(SqlValue::Json(
2002                crate::storage::JsonValue::parse(&value).expect("aggregate emits JSON"),
2003            ))
2004        } else {
2005            Ok(SqlValue::Text(value))
2006        }
2007    }
2008
2009    fn clone_box(&self) -> Box<dyn Accumulator> {
2010        Box::new(self.clone())
2011    }
2012    fn retained_bytes(&self) -> u64 {
2013        self.values.iter().map(ByteSized::estimated_bytes).sum()
2014    }
2015}
2016
2017#[derive(Debug, Clone)]
2018struct JsonObjectAccumulator {
2019    values: Vec<(String, SqlValue)>,
2020    distinct: bool,
2021    seen: HashSet<Vec<u8>>,
2022    native: bool,
2023}
2024
2025impl JsonObjectAccumulator {
2026    fn new(distinct: bool, native: bool) -> Self {
2027        Self {
2028            values: Vec::new(),
2029            distinct,
2030            seen: HashSet::new(),
2031            native,
2032        }
2033    }
2034}
2035
2036impl Accumulator for JsonObjectAccumulator {
2037    fn update(&mut self, _value: Option<SqlValue>) -> Result<()> {
2038        Err(invalid_aggregate_state(
2039            "JSON_GROUP_OBJECT",
2040            "requires two arguments",
2041        ))
2042    }
2043
2044    fn update_values(&mut self, values: &[SqlValue]) -> Result<()> {
2045        expect_state_arity("JSON_GROUP_OBJECT", values, 2)?;
2046        let key = match &values[0] {
2047            SqlValue::Null => return Ok(()),
2048            SqlValue::Text(key) => key.clone(),
2049            other => {
2050                return Err(invalid_aggregate_state(
2051                    "JSON_GROUP_OBJECT",
2052                    format!("object label must be TEXT, found {}", other.type_name()),
2053                ));
2054            }
2055        };
2056        if !self.distinct || self.seen.insert(encode_group_key(values)?) {
2057            self.values.push((key, values[1].clone()));
2058        }
2059        Ok(())
2060    }
2061
2062    fn state(&self) -> Result<Vec<SqlValue>> {
2063        let state = serde_json::to_string(&self.values).map_err(|error| {
2064            invalid_aggregate_state("JSON_GROUP_OBJECT", format!("cannot encode state: {error}"))
2065        })?;
2066        if state.len() > crate::executor::evaluator::json::MAX_JSON_BYTES {
2067            return Err(invalid_aggregate_state(
2068                "JSON_GROUP_OBJECT",
2069                "partial state exceeds 1048576 bytes",
2070            ));
2071        }
2072        Ok(vec![SqlValue::Text(state)])
2073    }
2074
2075    fn merge(&mut self, state: &[SqlValue]) -> Result<()> {
2076        expect_state_arity("JSON_GROUP_OBJECT", state, 1)?;
2077        let SqlValue::Text(state) = &state[0] else {
2078            return Err(invalid_aggregate_state(
2079                "JSON_GROUP_OBJECT",
2080                "partial state must be TEXT",
2081            ));
2082        };
2083        let values: Vec<(String, SqlValue)> = serde_json::from_str(state).map_err(|error| {
2084            invalid_aggregate_state("JSON_GROUP_OBJECT", format!("invalid state: {error}"))
2085        })?;
2086        for (key, value) in values {
2087            self.update_values(&[SqlValue::Text(key), value])?;
2088        }
2089        Ok(())
2090    }
2091
2092    fn finalize(&self) -> Result<SqlValue> {
2093        let value = crate::executor::evaluator::json::json_group_object(&self.values)?;
2094        if self.native {
2095            Ok(SqlValue::Json(
2096                crate::storage::JsonValue::parse(&value).expect("aggregate emits JSON"),
2097            ))
2098        } else {
2099            Ok(SqlValue::Text(value))
2100        }
2101    }
2102
2103    fn clone_box(&self) -> Box<dyn Accumulator> {
2104        Box::new(self.clone())
2105    }
2106    fn retained_bytes(&self) -> u64 {
2107        self.values
2108            .iter()
2109            .map(|(key, value)| key.capacity() as u64 + value.estimated_bytes())
2110            .sum()
2111    }
2112}
2113
2114/// Create a new accumulator instance for the aggregate function.
2115pub fn create_accumulator(function: &AggregateFunction, distinct: bool) -> Box<dyn Accumulator> {
2116    match function {
2117        AggregateFunction::Count => Box::new(CountAccumulator::new(distinct)),
2118        AggregateFunction::Sum => Box::new(SumAccumulator::with_distinct(distinct)),
2119        AggregateFunction::Total => Box::new(TotalAccumulator::new()),
2120        AggregateFunction::Avg => Box::new(AvgAccumulator::with_distinct(distinct)),
2121        AggregateFunction::Min => Box::new(MinMaxAccumulator::with_distinct(true, distinct)),
2122        AggregateFunction::Max => Box::new(MinMaxAccumulator::with_distinct(false, distinct)),
2123        AggregateFunction::GroupConcat { separator } => {
2124            let sep = separator.clone().unwrap_or_else(|| ",".to_string());
2125            Box::new(GroupConcatAccumulator::with_distinct(sep, distinct))
2126        }
2127        AggregateFunction::StringAgg { separator } => {
2128            let sep = separator.clone().unwrap_or_else(|| ",".to_string());
2129            Box::new(StringAggAccumulator::with_distinct(sep, distinct))
2130        }
2131        AggregateFunction::JsonGroupArray => Box::new(JsonArrayAccumulator::new(distinct, false)),
2132        AggregateFunction::ArrayAgg => Box::new(ArrayAccumulator::new(distinct)),
2133        AggregateFunction::JsonGroupObject => Box::new(JsonObjectAccumulator::new(distinct, false)),
2134        AggregateFunction::JsonbAgg => Box::new(JsonArrayAccumulator::new(distinct, true)),
2135        AggregateFunction::JsonbObjectAgg => Box::new(JsonObjectAccumulator::new(distinct, true)),
2136        AggregateFunction::PercentileDisc { fraction } => {
2137            Box::new(PercentileDiscAccumulator::new(*fraction, Vec::new()))
2138        }
2139        AggregateFunction::PercentileCont { fraction }
2140        | AggregateFunction::QuantileCont { fraction } => {
2141            Box::new(PercentileContAccumulator::new(*fraction, true))
2142        }
2143        AggregateFunction::Variance { sample } => Box::new(StatisticsAccumulator::new(
2144            StatisticsKind::Variance(*sample),
2145        )),
2146        AggregateFunction::Stddev { sample } => {
2147            Box::new(StatisticsAccumulator::new(StatisticsKind::Stddev(*sample)))
2148        }
2149        AggregateFunction::Covariance { sample } => Box::new(StatisticsAccumulator::new(
2150            StatisticsKind::Covariance(*sample),
2151        )),
2152        AggregateFunction::Corr => Box::new(StatisticsAccumulator::new(StatisticsKind::Corr)),
2153        AggregateFunction::Median => Box::new(PercentileContAccumulator::new(0.5, true)),
2154        AggregateFunction::Mode => Box::new(ModeAccumulator::new(Vec::new())),
2155        AggregateFunction::RegrCount => {
2156            Box::new(StatisticsAccumulator::new(StatisticsKind::RegrCount))
2157        }
2158        AggregateFunction::RegrAvgX => {
2159            Box::new(StatisticsAccumulator::new(StatisticsKind::RegrAvgX))
2160        }
2161        AggregateFunction::RegrAvgY => {
2162            Box::new(StatisticsAccumulator::new(StatisticsKind::RegrAvgY))
2163        }
2164        AggregateFunction::RegrSxx => Box::new(StatisticsAccumulator::new(StatisticsKind::RegrSxx)),
2165        AggregateFunction::RegrSyy => Box::new(StatisticsAccumulator::new(StatisticsKind::RegrSyy)),
2166        AggregateFunction::RegrSxy => Box::new(StatisticsAccumulator::new(StatisticsKind::RegrSxy)),
2167        AggregateFunction::RegrSlope => {
2168            Box::new(StatisticsAccumulator::new(StatisticsKind::RegrSlope))
2169        }
2170        AggregateFunction::RegrIntercept => {
2171            Box::new(StatisticsAccumulator::new(StatisticsKind::RegrIntercept))
2172        }
2173        AggregateFunction::RegrR2 => Box::new(StatisticsAccumulator::new(StatisticsKind::RegrR2)),
2174        AggregateFunction::AnyValue => Box::new(ValueAccumulator::new(ValueKind::Any, Vec::new())),
2175        AggregateFunction::First => Box::new(ValueAccumulator::new(ValueKind::First, Vec::new())),
2176        AggregateFunction::Last => Box::new(ValueAccumulator::new(ValueKind::Last, Vec::new())),
2177        AggregateFunction::ArgMin => Box::new(ValueAccumulator::new(ValueKind::ArgMin, Vec::new())),
2178        AggregateFunction::ArgMax => Box::new(ValueAccumulator::new(ValueKind::ArgMax, Vec::new())),
2179        AggregateFunction::BitAnd => Box::new(BitAccumulator::new(BitKind::And)),
2180        AggregateFunction::BitOr => Box::new(BitAccumulator::new(BitKind::Or)),
2181        AggregateFunction::BitXor => Box::new(BitAccumulator::new(BitKind::Xor)),
2182        AggregateFunction::BoolAnd => Box::new(BoolAccumulator::new(true)),
2183        AggregateFunction::BoolOr => Box::new(BoolAccumulator::new(false)),
2184    }
2185}
2186
2187fn aggregate_sort_specs(aggregate: &AggregateExpr) -> Vec<(bool, bool)> {
2188    aggregate
2189        .order_by
2190        .iter()
2191        .map(|sort| (sort.asc, sort.nulls_first))
2192        .collect()
2193}
2194
2195/// Create an accumulator using the aggregate expression's resolved result
2196/// type and its aggregate-local ordering (issue #148).
2197pub fn create_accumulator_for_aggregate(aggregate: &AggregateExpr) -> Box<dyn Accumulator> {
2198    match &aggregate.function {
2199        AggregateFunction::Sum => Box::new(SumAccumulator::with_distinct_for_type(
2200            aggregate.distinct,
2201            aggregate.result_type.clone(),
2202        )),
2203        AggregateFunction::Avg => Box::new(AvgAccumulator::with_distinct_for_type(
2204            aggregate.distinct,
2205            aggregate.result_type.clone(),
2206        )),
2207        AggregateFunction::GroupConcat { separator } if !aggregate.order_by.is_empty() => {
2208            let sep = separator.clone().unwrap_or_else(|| ",".to_string());
2209            Box::new(GroupConcatAccumulator::with_order(
2210                sep,
2211                aggregate.distinct,
2212                aggregate_sort_specs(aggregate),
2213            ))
2214        }
2215        AggregateFunction::StringAgg { separator } if !aggregate.order_by.is_empty() => {
2216            let sep = separator.clone().unwrap_or_else(|| ",".to_string());
2217            Box::new(StringAggAccumulator::with_order(
2218                sep,
2219                aggregate.distinct,
2220                aggregate_sort_specs(aggregate),
2221            ))
2222        }
2223        AggregateFunction::PercentileDisc { fraction } => Box::new(PercentileDiscAccumulator::new(
2224            *fraction,
2225            aggregate_sort_specs(aggregate),
2226        )),
2227        AggregateFunction::PercentileCont { fraction }
2228        | AggregateFunction::QuantileCont { fraction } => Box::new(PercentileContAccumulator::new(
2229            *fraction,
2230            aggregate_sort_specs(aggregate)
2231                .first()
2232                .is_none_or(|(ascending, _)| *ascending),
2233        )),
2234        AggregateFunction::Mode if !aggregate.order_by.is_empty() => {
2235            Box::new(ModeAccumulator::new(aggregate_sort_specs(aggregate)))
2236        }
2237        AggregateFunction::First if !aggregate.order_by.is_empty() => Box::new(
2238            ValueAccumulator::new(ValueKind::First, aggregate_sort_specs(aggregate)),
2239        ),
2240        AggregateFunction::Last if !aggregate.order_by.is_empty() => Box::new(
2241            ValueAccumulator::new(ValueKind::Last, aggregate_sort_specs(aggregate)),
2242        ),
2243        _ => create_accumulator(&aggregate.function, aggregate.distinct),
2244    }
2245}
2246
2247/// Returns whether an aggregate's partial state can be merged without
2248/// changing the current local SQL result. Floating-point, DISTINCT, and
2249/// order-sensitive aggregates must instead be replayed from ordered inputs.
2250/// A FILTER predicate does not affect this proof: it applies per input row
2251/// before the accumulator, so it commutes with Partial/Final splitting —
2252/// but the distributed catalog still classifies filtered aggregates as
2253/// local-only before any state reaches this kernel.
2254pub fn exact_partial_aggregate_is_proven(aggregate: &AggregateExpr) -> bool {
2255    !aggregate.distinct
2256        && aggregate.order_by.is_empty()
2257        && matches!(
2258            aggregate.function,
2259            AggregateFunction::Count | AggregateFunction::Min | AggregateFunction::Max
2260        )
2261}
2262
2263/// Merge only aggregate states whose merge rule is proven to preserve the
2264/// current local SQL result. This is the coordinator-side kernel used by the
2265/// distributed result assembler after every worker has acknowledged cleanup.
2266pub fn merge_exact_aggregate_states(
2267    aggregates: &[AggregateExpr],
2268    partial_rows: impl IntoIterator<Item = Vec<Vec<SqlValue>>>,
2269) -> Result<Vec<SqlValue>> {
2270    if let Some(aggregate) = aggregates
2271        .iter()
2272        .find(|aggregate| !exact_partial_aggregate_is_proven(aggregate))
2273    {
2274        return Err(ExecutorError::InvalidOperation {
2275            operation: "distributed aggregate merge".into(),
2276            reason: format!(
2277                "{:?} requires ordered input replay rather than an exact partial merge",
2278                aggregate.function
2279            ),
2280        });
2281    }
2282
2283    let mut accumulators = aggregates
2284        .iter()
2285        .map(create_accumulator_for_aggregate)
2286        .collect::<Vec<_>>();
2287    for states in partial_rows {
2288        if states.len() != accumulators.len() {
2289            return Err(ExecutorError::InvalidOperation {
2290                operation: "distributed aggregate merge".into(),
2291                reason: format!(
2292                    "partial state has {} aggregate(s), expected {}",
2293                    states.len(),
2294                    accumulators.len()
2295                ),
2296            });
2297        }
2298        for (accumulator, state) in accumulators.iter_mut().zip(states) {
2299            accumulator.merge(&state)?;
2300        }
2301    }
2302    accumulators
2303        .iter()
2304        .map(|accumulator| accumulator.finalize())
2305        .collect()
2306}
2307
2308const DEFAULT_GROUP_LIMIT: usize = 1_000_000;
2309const AGGREGATE_ACCUMULATOR_OVERHEAD_BYTES: u64 = 32;
2310
2311/// Aggregate execution mode.
2312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2313pub enum AggregateMode {
2314    /// Consume raw input rows and output aggregate partial state rows.
2315    Partial,
2316    /// Consume partial state rows and output final aggregate values.
2317    Final,
2318    /// Consume raw input rows and output final aggregate values in one pass.
2319    Single,
2320}
2321
2322struct AggregateGroup {
2323    key_values: Vec<SqlValue>,
2324    accumulators: Vec<Box<dyn Accumulator>>,
2325    /// Grouping-set mask emitted as the trailing `__grouping_id` output
2326    /// column; `None` outside grouping-sets mode (issue #149).
2327    grouping_id: Option<i64>,
2328}
2329
2330/// Iterator that performs hash-based aggregation over input rows.
2331pub struct AggregateIterator<'a> {
2332    input: Box<dyn RowIterator + 'a>,
2333    group_keys: Vec<TypedExpr>,
2334    aggregates: Vec<AggregateExpr>,
2335    having: Option<TypedExpr>,
2336    mode: AggregateMode,
2337    hash_table: Option<HashMap<GroupKeyBytes, AggregateGroup>>,
2338    result_rows: Vec<Row>,
2339    index: usize,
2340    schema: Vec<ColumnMetadata>,
2341    group_limit: usize,
2342    memory_tracker: Option<MemoryTracker>,
2343    shared_group_counter: Option<Arc<AtomicUsize>>,
2344    /// Expanded grouping-set masks over `group_keys` (issue #149, D9).
2345    /// Key 0 owns the most significant of the low `group_keys.len()` bits;
2346    /// a 1 bit excludes the key from the set (NULL placeholder output).
2347    grouping_sets: Option<Vec<u64>>,
2348}
2349
2350impl<'a> AggregateIterator<'a> {
2351    /// Create a new aggregate iterator with the default group limit.
2352    pub fn new(
2353        input: Box<dyn RowIterator + 'a>,
2354        group_keys: Vec<TypedExpr>,
2355        aggregates: Vec<AggregateExpr>,
2356        having: Option<TypedExpr>,
2357        schema: Vec<ColumnMetadata>,
2358    ) -> Self {
2359        Self {
2360            input,
2361            group_keys,
2362            aggregates,
2363            having,
2364            mode: AggregateMode::Single,
2365            hash_table: None,
2366            result_rows: Vec::new(),
2367            index: 0,
2368            schema,
2369            group_limit: DEFAULT_GROUP_LIMIT,
2370            memory_tracker: None,
2371            shared_group_counter: None,
2372            grouping_sets: None,
2373        }
2374    }
2375
2376    /// Override the maximum number of groups allowed during aggregation.
2377    pub fn with_group_limit(mut self, limit: usize) -> Self {
2378        self.group_limit = limit;
2379        self
2380    }
2381
2382    /// Enable single-pass GROUPING SETS aggregation (issue #149, D9).
2383    ///
2384    /// Each input row accumulates once per set under a set-id-prefixed key;
2385    /// the output rows gain a trailing `__grouping_id` BIGINT value and the
2386    /// group limit applies to the group total across every set (D6). Only
2387    /// `AggregateMode::Single` supports grouping sets — the planner keeps
2388    /// parallel/spill/streaming execution on the `None` path.
2389    pub fn with_grouping_sets(mut self, grouping_sets: Option<Vec<u64>>) -> Self {
2390        self.grouping_sets = grouping_sets;
2391        self
2392    }
2393
2394    /// Set the aggregate execution mode.
2395    pub fn with_mode(mut self, mode: AggregateMode) -> Self {
2396        self.mode = mode;
2397        self
2398    }
2399
2400    /// Attach a memory policy for enforcing in-flight aggregation limits.
2401    pub fn with_memory_policy(mut self, policy: Option<MemoryPolicy>) -> Self {
2402        self.memory_tracker = policy.map(MemoryTracker::new);
2403        self
2404    }
2405
2406    /// Attach a shared group counter used by parallel Partial aggregation.
2407    pub fn with_shared_group_counter(mut self, counter: Option<Arc<AtomicUsize>>) -> Self {
2408        self.shared_group_counter = counter;
2409        self
2410    }
2411
2412    fn build_hash_table(&mut self) -> Result<()> {
2413        let mut table: HashMap<GroupKeyBytes, AggregateGroup> = HashMap::new();
2414        let mut next_row_id = 0u64;
2415
2416        while let Some(result) = self.input.next_row() {
2417            let row = result?;
2418            let (key_values, key_bytes) = match self.mode {
2419                AggregateMode::Final => {
2420                    let key_values = row
2421                        .values
2422                        .get(..self.group_keys.len())
2423                        .ok_or_else(|| {
2424                            invalid_aggregate_state(
2425                                "aggregate",
2426                                "partial state row is missing group key values",
2427                            )
2428                        })?
2429                        .to_vec();
2430                    let key_bytes = encode_group_key(&key_values)?;
2431                    (key_values, key_bytes)
2432                }
2433                AggregateMode::Partial | AggregateMode::Single => {
2434                    let ctx = EvalContext::new(&row.values);
2435                    let mut key_values = Vec::with_capacity(self.group_keys.len());
2436                    for expr in &self.group_keys {
2437                        key_values.push(crate::executor::evaluator::evaluate(expr, &ctx)?);
2438                    }
2439                    let key_bytes = encode_group_key(&key_values)?;
2440                    (key_values, key_bytes)
2441                }
2442            };
2443
2444            // Grouping-sets mode accumulates every row once per set under a
2445            // set-id-prefixed key (issue #149, D3/D7): the prefix keeps
2446            // duplicate sets and per-set real-NULL groups distinct while the
2447            // masked key values become the NULL placeholder outputs.
2448            let variants: Vec<(Vec<SqlValue>, GroupKeyBytes, Option<i64>)> =
2449                if let Some(sets) = &self.grouping_sets {
2450                    let key_count = self.group_keys.len();
2451                    let mut variants = Vec::with_capacity(sets.len());
2452                    for (set_id, mask) in sets.iter().enumerate() {
2453                        let mut masked = key_values.clone();
2454                        for (position, value) in masked.iter_mut().enumerate() {
2455                            if (mask >> (key_count - 1 - position)) & 1 == 1 {
2456                                *value = SqlValue::Null;
2457                            }
2458                        }
2459                        let mut bytes = Vec::with_capacity(4 + key_bytes.len());
2460                        bytes.extend_from_slice(&(set_id as u32).to_le_bytes());
2461                        bytes.extend_from_slice(&encode_group_key(&masked)?);
2462                        variants.push((masked, bytes, Some(*mask as i64)));
2463                    }
2464                    variants
2465                } else {
2466                    vec![(key_values, key_bytes, None)]
2467                };
2468
2469            for (key_values, key_bytes, grouping_id) in variants {
2470                if !table.contains_key(&key_bytes) {
2471                    self.reserve_group_slot(table.len())?;
2472                    if let Some(tracker) = &mut self.memory_tracker {
2473                        tracker
2474                            .add_values(&key_values)
2475                            .map_err(map_core_memory_error)?;
2476                        tracker
2477                            .add_bytes(
2478                                self.aggregates.len() as u64 * AGGREGATE_ACCUMULATOR_OVERHEAD_BYTES,
2479                            )
2480                            .map_err(map_core_memory_error)?;
2481                    }
2482                    let accumulators = self
2483                        .aggregates
2484                        .iter()
2485                        .map(|agg| {
2486                            let mut aggregate = agg.clone();
2487                            aggregate.distinct =
2488                                matches!(self.mode, AggregateMode::Single) && agg.distinct;
2489                            create_accumulator_for_aggregate(&aggregate)
2490                        })
2491                        .collect::<Vec<_>>();
2492                    table.insert(
2493                        key_bytes.clone(),
2494                        AggregateGroup {
2495                            key_values: key_values.clone(),
2496                            accumulators,
2497                            grouping_id,
2498                        },
2499                    );
2500                }
2501
2502                if let Some(group) = table.get_mut(&key_bytes) {
2503                    match self.mode {
2504                        AggregateMode::Final => {
2505                            let mut offset = self.group_keys.len();
2506                            for (idx, agg) in self.aggregates.iter().enumerate() {
2507                                let arity = aggregate_state_types(agg).len();
2508                                let state = row.values.get(offset..offset + arity).ok_or_else(|| {
2509                                invalid_aggregate_state(
2510                                    "aggregate",
2511                                    format!(
2512                                        "partial state row is missing state values for aggregate {idx}"
2513                                    ),
2514                                )
2515                            })?;
2516                                group.accumulators[idx].merge(state)?;
2517                                offset += arity;
2518                            }
2519                            if offset != row.values.len() {
2520                                return Err(invalid_aggregate_state(
2521                                    "aggregate",
2522                                    format!(
2523                                        "partial state row has {} trailing value(s)",
2524                                        row.values.len() - offset
2525                                    ),
2526                                ));
2527                            }
2528                        }
2529                        AggregateMode::Partial | AggregateMode::Single => {
2530                            let ctx = EvalContext::new(&row.values);
2531                            for (idx, agg) in self.aggregates.iter().enumerate() {
2532                                // FILTER applies per input row before the
2533                                // accumulator (and before DISTINCT), in Partial
2534                                // and Single mode alike (issue #148, D1).
2535                                if !aggregate_filter_accepts(agg, &ctx)? {
2536                                    continue;
2537                                }
2538                                let values = evaluate_aggregate_values(agg, &ctx)?;
2539                                if let Some(tracker) = &mut self.memory_tracker
2540                                    && matches!(
2541                                        agg.function,
2542                                        AggregateFunction::GroupConcat { .. }
2543                                            | AggregateFunction::StringAgg { .. }
2544                                            | AggregateFunction::JsonGroupArray
2545                                            | AggregateFunction::JsonGroupObject
2546                                            | AggregateFunction::JsonbAgg
2547                                            | AggregateFunction::JsonbObjectAgg
2548                                            | AggregateFunction::PercentileDisc { .. }
2549                                            | AggregateFunction::PercentileCont { .. }
2550                                            | AggregateFunction::QuantileCont { .. }
2551                                            | AggregateFunction::Median
2552                                            | AggregateFunction::Mode
2553                                    )
2554                                    && let Some(value_ref) = values.first()
2555                                {
2556                                    tracker
2557                                        .add_value(value_ref)
2558                                        .map_err(map_core_memory_error)?;
2559                                }
2560                                if agg.order_by.is_empty() {
2561                                    group.accumulators[idx].update_values(&values)?;
2562                                } else {
2563                                    let keys = evaluate_sort_keys(agg, &ctx)?;
2564                                    group.accumulators[idx]
2565                                        .update_ordered_values(&values, &keys)?;
2566                                }
2567                            }
2568                        }
2569                    }
2570                }
2571            }
2572        }
2573
2574        if let Some(sets) = self.grouping_sets.clone() {
2575            // Sets that group over no key (their mask covers every key)
2576            // still emit exactly one row when the input is empty, matching
2577            // the global-aggregation contract below.
2578            let key_count = self.group_keys.len();
2579            let full_mask = if key_count == 0 {
2580                0
2581            } else {
2582                (1u64 << key_count) - 1
2583            };
2584            if table.is_empty() {
2585                for (set_id, mask) in sets.iter().enumerate() {
2586                    if *mask != full_mask {
2587                        continue;
2588                    }
2589                    if let Some(tracker) = &mut self.memory_tracker {
2590                        tracker
2591                            .add_bytes(
2592                                self.aggregates.len() as u64 * AGGREGATE_ACCUMULATOR_OVERHEAD_BYTES,
2593                            )
2594                            .map_err(map_core_memory_error)?;
2595                    }
2596                    let accumulators = self
2597                        .aggregates
2598                        .iter()
2599                        .map(|agg| {
2600                            let mut aggregate = agg.clone();
2601                            aggregate.distinct =
2602                                matches!(self.mode, AggregateMode::Single) && agg.distinct;
2603                            create_accumulator_for_aggregate(&aggregate)
2604                        })
2605                        .collect::<Vec<_>>();
2606                    let key_values = vec![SqlValue::Null; key_count];
2607                    let mut bytes = Vec::with_capacity(4);
2608                    bytes.extend_from_slice(&(set_id as u32).to_le_bytes());
2609                    bytes.extend_from_slice(&encode_group_key(&key_values)?);
2610                    table.insert(
2611                        bytes,
2612                        AggregateGroup {
2613                            key_values,
2614                            accumulators,
2615                            grouping_id: Some(*mask as i64),
2616                        },
2617                    );
2618                }
2619            }
2620        } else if table.is_empty() && self.group_keys.is_empty() {
2621            if let Some(tracker) = &mut self.memory_tracker {
2622                tracker
2623                    .add_bytes(self.aggregates.len() as u64 * AGGREGATE_ACCUMULATOR_OVERHEAD_BYTES)
2624                    .map_err(map_core_memory_error)?;
2625            }
2626            let accumulators = self
2627                .aggregates
2628                .iter()
2629                .map(|agg| {
2630                    let mut aggregate = agg.clone();
2631                    aggregate.distinct = matches!(self.mode, AggregateMode::Single) && agg.distinct;
2632                    create_accumulator_for_aggregate(&aggregate)
2633                })
2634                .collect::<Vec<_>>();
2635            table.insert(
2636                Vec::new(),
2637                AggregateGroup {
2638                    key_values: Vec::new(),
2639                    accumulators,
2640                    grouping_id: None,
2641                },
2642            );
2643        }
2644
2645        let mut rows = Vec::with_capacity(table.len());
2646        for group in table.values() {
2647            let mut values = Vec::with_capacity(self.group_keys.len() + self.aggregates.len() + 1);
2648            values.extend(group.key_values.iter().cloned());
2649            for acc in &group.accumulators {
2650                match self.mode {
2651                    AggregateMode::Partial => values.extend(acc.state()?),
2652                    AggregateMode::Final | AggregateMode::Single => values.push(acc.finalize()?),
2653                }
2654            }
2655            // The hidden __grouping_id column joins the row before HAVING so
2656            // GROUPING() predicates evaluate against it (issue #149, D11).
2657            if let Some(grouping_id) = group.grouping_id {
2658                values.push(SqlValue::BigInt(grouping_id));
2659            }
2660            let row = Row::new(next_row_id, values);
2661            next_row_id += 1;
2662            if let Some(tracker) = &mut self.memory_tracker {
2663                tracker
2664                    .add_row(&row.values)
2665                    .map_err(map_core_memory_error)?;
2666            }
2667
2668            if self.mode != AggregateMode::Partial
2669                && let Some(having) = &self.having
2670            {
2671                let ctx = EvalContext::new(&row.values);
2672                match crate::executor::evaluator::evaluate(having, &ctx)? {
2673                    SqlValue::Boolean(true) => rows.push(row),
2674                    SqlValue::Boolean(false) | SqlValue::Null => {}
2675                    other => {
2676                        return Err(ExecutorError::Evaluation(
2677                            crate::executor::EvaluationError::TypeMismatch {
2678                                expected: "Boolean".into(),
2679                                actual: other.type_name().into(),
2680                            },
2681                        ));
2682                    }
2683                }
2684            } else {
2685                rows.push(row);
2686            }
2687        }
2688
2689        self.hash_table = Some(table);
2690        self.result_rows = rows;
2691        Ok(())
2692    }
2693
2694    fn reserve_group_slot(&self, local_group_count: usize) -> Result<()> {
2695        let next_count = if let Some(counter) = &self.shared_group_counter {
2696            counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1
2697        } else {
2698            local_group_count + 1
2699        };
2700        if next_count > self.group_limit {
2701            return Err(ExecutorError::ResourceExhausted {
2702                message: format!(
2703                    "GROUP BY result exceeds memory limit (max groups: {})",
2704                    self.group_limit
2705                ),
2706            });
2707        }
2708        Ok(())
2709    }
2710}
2711
2712impl<'a> RowIterator for AggregateIterator<'a> {
2713    fn next_row(&mut self) -> Option<Result<Row>> {
2714        if self.hash_table.is_none()
2715            && let Err(err) = self.build_hash_table()
2716        {
2717            return Some(Err(err));
2718        }
2719
2720        if self.index >= self.result_rows.len() {
2721            return None;
2722        }
2723        let row = self.result_rows[self.index].clone();
2724        self.index += 1;
2725        Some(Ok(row))
2726    }
2727
2728    fn schema(&self) -> &[ColumnMetadata] {
2729        &self.schema
2730    }
2731}
2732
2733/// Iterator that performs streaming aggregation over sorted input.
2734pub struct StreamingAggregateIterator<'a> {
2735    input: Box<dyn RowIterator + 'a>,
2736    group_keys: Vec<TypedExpr>,
2737    aggregates: Vec<AggregateExpr>,
2738    having: Option<TypedExpr>,
2739    schema: Vec<ColumnMetadata>,
2740    current_key: Option<Vec<SqlValue>>,
2741    accumulators: Vec<Box<dyn Accumulator>>,
2742    pending_row: Option<Row>,
2743    finished: bool,
2744    next_row_id: u64,
2745    saw_row: bool,
2746}
2747
2748impl<'a> StreamingAggregateIterator<'a> {
2749    pub fn new(
2750        input: Box<dyn RowIterator + 'a>,
2751        group_keys: Vec<TypedExpr>,
2752        aggregates: Vec<AggregateExpr>,
2753        having: Option<TypedExpr>,
2754        schema: Vec<ColumnMetadata>,
2755    ) -> Self {
2756        Self {
2757            input,
2758            group_keys,
2759            aggregates,
2760            having,
2761            schema,
2762            current_key: None,
2763            accumulators: Vec::new(),
2764            pending_row: None,
2765            finished: false,
2766            next_row_id: 0,
2767            saw_row: false,
2768        }
2769    }
2770
2771    fn init_accumulators(&self) -> Vec<Box<dyn Accumulator>> {
2772        self.aggregates
2773            .iter()
2774            .map(create_accumulator_for_aggregate)
2775            .collect()
2776    }
2777
2778    fn update_accumulators(&mut self, ctx: &EvalContext<'_>) -> Result<()> {
2779        for (idx, agg) in self.aggregates.iter().enumerate() {
2780            if !aggregate_filter_accepts(agg, ctx)? {
2781                continue;
2782            }
2783            let values = evaluate_aggregate_values(agg, ctx)?;
2784            if agg.order_by.is_empty() {
2785                self.accumulators[idx].update_values(&values)?;
2786            } else {
2787                let keys = evaluate_sort_keys(agg, ctx)?;
2788                self.accumulators[idx].update_ordered_values(&values, &keys)?;
2789            }
2790        }
2791        Ok(())
2792    }
2793
2794    fn finalize_group(&mut self, key_values: &[SqlValue]) -> Result<Option<Row>> {
2795        let mut values = Vec::with_capacity(self.group_keys.len() + self.aggregates.len());
2796        values.extend(key_values.iter().cloned());
2797        for acc in &self.accumulators {
2798            values.push(acc.finalize()?);
2799        }
2800        let row = Row::new(self.next_row_id, values);
2801        self.next_row_id = self.next_row_id.saturating_add(1);
2802
2803        if let Some(having) = &self.having {
2804            let ctx = EvalContext::new(&row.values);
2805            match crate::executor::evaluator::evaluate(having, &ctx)? {
2806                SqlValue::Boolean(true) => Ok(Some(row)),
2807                SqlValue::Boolean(false) | SqlValue::Null => Ok(None),
2808                other => Err(ExecutorError::Evaluation(
2809                    crate::executor::EvaluationError::TypeMismatch {
2810                        expected: "Boolean".into(),
2811                        actual: other.type_name().into(),
2812                    },
2813                )),
2814            }
2815        } else {
2816            Ok(Some(row))
2817        }
2818    }
2819}
2820
2821impl<'a> RowIterator for StreamingAggregateIterator<'a> {
2822    fn next_row(&mut self) -> Option<Result<Row>> {
2823        if let Some(row) = self.pending_row.take() {
2824            return Some(Ok(row));
2825        }
2826        if self.finished {
2827            return None;
2828        }
2829
2830        loop {
2831            match self.input.next_row() {
2832                Some(Ok(row)) => {
2833                    self.saw_row = true;
2834                    let ctx = EvalContext::new(&row.values);
2835                    let mut key_values = Vec::with_capacity(self.group_keys.len());
2836                    for expr in &self.group_keys {
2837                        match crate::executor::evaluator::evaluate(expr, &ctx) {
2838                            Ok(value) => key_values.push(value),
2839                            Err(err) => return Some(Err(err)),
2840                        }
2841                    }
2842
2843                    match &self.current_key {
2844                        None => {
2845                            self.current_key = Some(key_values);
2846                            self.accumulators = self.init_accumulators();
2847                            if let Err(err) = self.update_accumulators(&ctx) {
2848                                return Some(Err(err));
2849                            }
2850                        }
2851                        Some(current_key) if *current_key == key_values => {
2852                            if let Err(err) = self.update_accumulators(&ctx) {
2853                                return Some(Err(err));
2854                            }
2855                        }
2856                        Some(_) => {
2857                            let current_key = self.current_key.clone().unwrap_or_default();
2858                            let output = match self.finalize_group(&current_key) {
2859                                Ok(value) => value,
2860                                Err(err) => return Some(Err(err)),
2861                            };
2862                            self.current_key = Some(key_values);
2863                            self.accumulators = self.init_accumulators();
2864                            if let Err(err) = self.update_accumulators(&ctx) {
2865                                return Some(Err(err));
2866                            }
2867                            if let Some(row) = output {
2868                                return Some(Ok(row));
2869                            }
2870                        }
2871                    }
2872                }
2873                Some(Err(err)) => return Some(Err(err)),
2874                None => {
2875                    self.finished = true;
2876                    if let Some(current_key) = self.current_key.take() {
2877                        return match self.finalize_group(&current_key) {
2878                            Ok(Some(row)) => Some(Ok(row)),
2879                            Ok(None) => None,
2880                            Err(err) => Some(Err(err)),
2881                        };
2882                    }
2883
2884                    if self.group_keys.is_empty() && !self.saw_row {
2885                        self.accumulators = self.init_accumulators();
2886                        return match self.finalize_group(&[]) {
2887                            Ok(Some(row)) => Some(Ok(row)),
2888                            Ok(None) => None,
2889                            Err(err) => Some(Err(err)),
2890                        };
2891                    }
2892
2893                    return None;
2894                }
2895            }
2896        }
2897    }
2898
2899    fn schema(&self) -> &[ColumnMetadata] {
2900        &self.schema
2901    }
2902}
2903
2904fn aggregate_state_types(agg: &AggregateExpr) -> Vec<ResolvedType> {
2905    match &agg.function {
2906        AggregateFunction::Count => vec![ResolvedType::BigInt],
2907        AggregateFunction::Sum => vec![agg.result_type.clone()],
2908        AggregateFunction::Total => vec![ResolvedType::Double],
2909        AggregateFunction::Avg => vec![agg.result_type.clone(), ResolvedType::BigInt],
2910        AggregateFunction::Min | AggregateFunction::Max => vec![agg.result_type.clone()],
2911        AggregateFunction::GroupConcat { .. } | AggregateFunction::StringAgg { .. } => {
2912            vec![ResolvedType::Text, ResolvedType::Text]
2913        }
2914        AggregateFunction::ArrayAgg => vec![agg.result_type.clone()],
2915        AggregateFunction::JsonGroupArray
2916        | AggregateFunction::JsonGroupObject
2917        | AggregateFunction::JsonbAgg
2918        | AggregateFunction::JsonbObjectAgg => {
2919            vec![ResolvedType::Text]
2920        }
2921        // Ordered-set aggregation never runs in Partial mode (D11); the
2922        // accumulator rejects state()/merge() with invalid_aggregate_state.
2923        AggregateFunction::PercentileDisc { .. } => vec![agg.result_type.clone()],
2924        AggregateFunction::PercentileCont { .. }
2925        | AggregateFunction::QuantileCont { .. }
2926        | AggregateFunction::Median
2927        | AggregateFunction::Mode => vec![agg.result_type.clone()],
2928        AggregateFunction::Variance { .. }
2929        | AggregateFunction::Stddev { .. }
2930        | AggregateFunction::Covariance { .. }
2931        | AggregateFunction::Corr
2932        | AggregateFunction::RegrCount
2933        | AggregateFunction::RegrAvgX
2934        | AggregateFunction::RegrAvgY
2935        | AggregateFunction::RegrSxx
2936        | AggregateFunction::RegrSyy
2937        | AggregateFunction::RegrSxy
2938        | AggregateFunction::RegrSlope
2939        | AggregateFunction::RegrIntercept
2940        | AggregateFunction::RegrR2 => vec![
2941            ResolvedType::BigInt,
2942            ResolvedType::Double,
2943            ResolvedType::Double,
2944            ResolvedType::Double,
2945            ResolvedType::Double,
2946            ResolvedType::Double,
2947        ],
2948        AggregateFunction::AnyValue
2949        | AggregateFunction::First
2950        | AggregateFunction::Last
2951        | AggregateFunction::ArgMin
2952        | AggregateFunction::ArgMax
2953        | AggregateFunction::BitAnd
2954        | AggregateFunction::BitOr
2955        | AggregateFunction::BitXor
2956        | AggregateFunction::BoolAnd
2957        | AggregateFunction::BoolOr => vec![agg.result_type.clone()],
2958    }
2959}
2960
2961/// Evaluate a FILTER (WHERE ...) predicate for one input row. Only TRUE
2962/// admits the row; FALSE and NULL (UNKNOWN) skip it (issue #148, D1).
2963fn aggregate_filter_accepts(agg: &AggregateExpr, ctx: &EvalContext<'_>) -> Result<bool> {
2964    let Some(filter) = &agg.filter else {
2965        return Ok(true);
2966    };
2967    match crate::executor::evaluator::evaluate(filter, ctx)? {
2968        SqlValue::Boolean(true) => Ok(true),
2969        SqlValue::Boolean(false) | SqlValue::Null => Ok(false),
2970        other => Err(ExecutorError::Evaluation(
2971            crate::executor::EvaluationError::TypeMismatch {
2972                expected: "Boolean".into(),
2973                actual: other.type_name().into(),
2974            },
2975        )),
2976    }
2977}
2978
2979/// Evaluate the aggregate-local sort keys of one input row.
2980fn evaluate_sort_keys(agg: &AggregateExpr, ctx: &EvalContext<'_>) -> Result<Vec<SqlValue>> {
2981    agg.order_by
2982        .iter()
2983        .map(|sort| crate::executor::evaluator::evaluate(&sort.expr, ctx))
2984        .collect()
2985}
2986
2987fn evaluate_aggregate_values(agg: &AggregateExpr, ctx: &EvalContext<'_>) -> Result<Vec<SqlValue>> {
2988    agg.arg
2989        .iter()
2990        .chain(&agg.extra_args)
2991        .map(|expr| crate::executor::evaluator::evaluate(expr, ctx))
2992        .collect()
2993}
2994
2995/// Build output schema for aggregate results.
2996pub fn build_aggregate_schema(
2997    group_keys: &[TypedExpr],
2998    aggregates: &[AggregateExpr],
2999) -> Vec<ColumnMetadata> {
3000    let mut schema = Vec::new();
3001    for (idx, key) in group_keys.iter().enumerate() {
3002        let name = match &key.kind {
3003            crate::planner::typed_expr::TypedExprKind::ColumnRef { column, .. } => column.clone(),
3004            _ => format!("group_{idx}"),
3005        };
3006        schema.push(ColumnMetadata::new(name, key.resolved_type.clone()));
3007    }
3008    for (idx, agg) in aggregates.iter().enumerate() {
3009        let name = match &agg.function {
3010            AggregateFunction::Count => format!("count_{idx}"),
3011            AggregateFunction::Sum => format!("sum_{idx}"),
3012            AggregateFunction::Total => format!("total_{idx}"),
3013            AggregateFunction::Avg => format!("avg_{idx}"),
3014            AggregateFunction::Min => format!("min_{idx}"),
3015            AggregateFunction::Max => format!("max_{idx}"),
3016            AggregateFunction::GroupConcat { .. } => format!("group_concat_{idx}"),
3017            AggregateFunction::StringAgg { .. } => format!("string_agg_{idx}"),
3018            AggregateFunction::ArrayAgg => format!("array_agg_{idx}"),
3019            AggregateFunction::JsonGroupArray => format!("json_group_array_{idx}"),
3020            AggregateFunction::JsonGroupObject => format!("json_group_object_{idx}"),
3021            AggregateFunction::JsonbAgg => format!("jsonb_agg_{idx}"),
3022            AggregateFunction::JsonbObjectAgg => format!("jsonb_object_agg_{idx}"),
3023            AggregateFunction::PercentileDisc { .. } => format!("percentile_disc_{idx}"),
3024            AggregateFunction::PercentileCont { .. } => format!("percentile_cont_{idx}"),
3025            AggregateFunction::QuantileCont { .. } => format!("quantile_cont_{idx}"),
3026            AggregateFunction::Variance { sample } => {
3027                format!("var_{}_{idx}", if *sample { "samp" } else { "pop" })
3028            }
3029            AggregateFunction::Stddev { sample } => {
3030                format!("stddev_{}_{idx}", if *sample { "samp" } else { "pop" })
3031            }
3032            AggregateFunction::Covariance { sample } => {
3033                format!("covar_{}_{idx}", if *sample { "samp" } else { "pop" })
3034            }
3035            AggregateFunction::Corr => format!("corr_{idx}"),
3036            AggregateFunction::Median => format!("median_{idx}"),
3037            AggregateFunction::Mode => format!("mode_{idx}"),
3038            AggregateFunction::RegrCount => format!("regr_count_{idx}"),
3039            AggregateFunction::RegrAvgX => format!("regr_avgx_{idx}"),
3040            AggregateFunction::RegrAvgY => format!("regr_avgy_{idx}"),
3041            AggregateFunction::RegrSxx => format!("regr_sxx_{idx}"),
3042            AggregateFunction::RegrSyy => format!("regr_syy_{idx}"),
3043            AggregateFunction::RegrSxy => format!("regr_sxy_{idx}"),
3044            AggregateFunction::RegrSlope => format!("regr_slope_{idx}"),
3045            AggregateFunction::RegrIntercept => format!("regr_intercept_{idx}"),
3046            AggregateFunction::RegrR2 => format!("regr_r2_{idx}"),
3047            AggregateFunction::AnyValue => format!("any_value_{idx}"),
3048            AggregateFunction::First => format!("first_{idx}"),
3049            AggregateFunction::Last => format!("last_{idx}"),
3050            AggregateFunction::ArgMin => format!("arg_min_{idx}"),
3051            AggregateFunction::ArgMax => format!("arg_max_{idx}"),
3052            AggregateFunction::BitAnd => format!("bit_and_{idx}"),
3053            AggregateFunction::BitOr => format!("bit_or_{idx}"),
3054            AggregateFunction::BitXor => format!("bit_xor_{idx}"),
3055            AggregateFunction::BoolAnd => format!("bool_and_{idx}"),
3056            AggregateFunction::BoolOr => format!("bool_or_{idx}"),
3057        };
3058        schema.push(ColumnMetadata::new(name, agg.result_type.clone()));
3059    }
3060    schema
3061}
3062
3063/// Build internal partial aggregate schema.
3064pub fn build_partial_aggregate_schema(
3065    group_keys: &[TypedExpr],
3066    aggregates: &[AggregateExpr],
3067) -> Vec<ColumnMetadata> {
3068    let mut schema = Vec::new();
3069    for (idx, key) in group_keys.iter().enumerate() {
3070        let name = match &key.kind {
3071            crate::planner::typed_expr::TypedExprKind::ColumnRef { column, .. } => column.clone(),
3072            _ => format!("group_{idx}"),
3073        };
3074        schema.push(ColumnMetadata::new(name, key.resolved_type.clone()));
3075    }
3076    for (agg_idx, agg) in aggregates.iter().enumerate() {
3077        for (state_idx, state_type) in aggregate_state_types(agg).into_iter().enumerate() {
3078            schema.push(ColumnMetadata::new(
3079                format!("__agg{agg_idx}_state{state_idx}"),
3080                state_type,
3081            ));
3082        }
3083    }
3084    schema
3085}
3086
3087/// Return true when aggregate execution must remain Single for correctness.
3088/// FILTER is applied per input row and therefore commutes with Partial/Final
3089/// splitting; ordered aggregates (aggregate-local ORDER BY and ordered-set
3090/// aggregates such as PERCENTILE_DISC) buffer whole groups and stay Single
3091/// (issue #148, D11).
3092pub fn should_use_single_for_parallel(parallelism: usize, aggregates: &[AggregateExpr]) -> bool {
3093    parallelism <= 1
3094        || aggregates.iter().any(|agg| {
3095            agg.distinct
3096                || !agg.order_by.is_empty()
3097                || matches!(agg.function, AggregateFunction::PercentileDisc { .. })
3098                || matches!(
3099                    agg.function,
3100                    AggregateFunction::PercentileCont { .. }
3101                        | AggregateFunction::QuantileCont { .. }
3102                        | AggregateFunction::Median
3103                        | AggregateFunction::Mode
3104                        | AggregateFunction::AnyValue
3105                        | AggregateFunction::First
3106                        | AggregateFunction::Last
3107                        | AggregateFunction::ArgMin
3108                        | AggregateFunction::ArgMax
3109                )
3110        })
3111}
3112
3113fn collect_iterator_rows(iter: &mut dyn RowIterator) -> Result<Vec<Row>> {
3114    let mut rows = Vec::new();
3115    while let Some(result) = iter.next_row() {
3116        rows.push(result?);
3117    }
3118    Ok(rows)
3119}
3120
3121struct ChainRowIterator<'a> {
3122    prefix: std::vec::IntoIter<Row>,
3123    tail: Box<dyn RowIterator + 'a>,
3124    schema: Vec<ColumnMetadata>,
3125}
3126
3127impl<'a> ChainRowIterator<'a> {
3128    fn new(prefix: Vec<Row>, tail: Box<dyn RowIterator + 'a>, schema: Vec<ColumnMetadata>) -> Self {
3129        Self {
3130            prefix: prefix.into_iter(),
3131            tail,
3132            schema,
3133        }
3134    }
3135}
3136
3137impl RowIterator for ChainRowIterator<'_> {
3138    fn next_row(&mut self) -> Option<Result<Row>> {
3139        if let Some(row) = self.prefix.next() {
3140            return Some(Ok(row));
3141        }
3142        self.tail.next_row()
3143    }
3144
3145    fn schema(&self) -> &[ColumnMetadata] {
3146        &self.schema
3147    }
3148}
3149
3150fn estimate_row_bytes(row: &Row) -> u64 {
3151    row.values.iter().map(ByteSized::estimated_bytes).sum()
3152}
3153
3154fn split_contiguous_partitions(rows: Vec<Row>, parallelism: usize) -> Vec<Vec<Row>> {
3155    let requested = parallelism.max(1);
3156    if rows.is_empty() {
3157        return (0..requested).map(|_| Vec::new()).collect();
3158    }
3159    let partitions = requested.min(rows.len());
3160    let total = rows.len();
3161    let mut tail = rows;
3162    let mut output = Vec::with_capacity(partitions);
3163    for partition in (0..partitions).rev() {
3164        let start = partition * total / partitions;
3165        output.push(tail.split_off(start));
3166    }
3167    output.reverse();
3168    output
3169}
3170
3171#[allow(clippy::too_many_arguments)]
3172fn execute_partial_partition(
3173    partition_index: usize,
3174    rows: Vec<Row>,
3175    input_schema: Vec<ColumnMetadata>,
3176    group_keys: Vec<TypedExpr>,
3177    aggregates: Vec<AggregateExpr>,
3178    partial_schema: Vec<ColumnMetadata>,
3179    group_limit: usize,
3180    shared_group_counter: Arc<AtomicUsize>,
3181    shared_memory_counter: Arc<AtomicU64>,
3182    memory_limit: Option<u64>,
3183) -> Result<(usize, Vec<Row>)> {
3184    let input = VecIterator::new(rows, input_schema);
3185    let mut iter = AggregateIterator::new(
3186        Box::new(input),
3187        group_keys,
3188        aggregates,
3189        None,
3190        partial_schema,
3191    )
3192    .with_mode(AggregateMode::Partial)
3193    .with_group_limit(group_limit)
3194    .with_shared_group_counter(Some(shared_group_counter));
3195    let rows = collect_iterator_rows(&mut iter)?;
3196    for row in &rows {
3197        let used = shared_memory_counter
3198            .fetch_add(estimate_row_bytes(row), std::sync::atomic::Ordering::SeqCst)
3199            .saturating_add(estimate_row_bytes(row));
3200        if let Some(limit) = memory_limit
3201            && used > limit
3202        {
3203            return Err(ExecutorError::ResourceExhausted {
3204                message: format!(
3205                    "parallel aggregate memory limit exceeded: {used} bytes (limit {limit})"
3206                ),
3207            });
3208        }
3209    }
3210    Ok((partition_index, rows))
3211}
3212
3213fn recv_partition_results(
3214    receiver: std::sync::mpsc::Receiver<Result<(usize, Vec<Row>)>>,
3215    expected: usize,
3216) -> Result<Vec<(usize, Vec<Row>)>> {
3217    let mut outputs = Vec::with_capacity(expected);
3218    for _ in 0..expected {
3219        let result = receiver
3220            .recv()
3221            .map_err(|err| ExecutorError::InvalidOperation {
3222                operation: "parallel aggregate".into(),
3223                reason: format!("partition worker failed to report result: {err}"),
3224            })?;
3225        outputs.push(result?);
3226    }
3227    outputs.sort_by_key(|(idx, _)| *idx);
3228    Ok(outputs)
3229}
3230
3231#[cfg(feature = "tokio")]
3232#[allow(clippy::too_many_arguments)]
3233fn run_partial_partitions(
3234    partitions: Vec<Vec<Row>>,
3235    input_schema: Vec<ColumnMetadata>,
3236    group_keys: Vec<TypedExpr>,
3237    aggregates: Vec<AggregateExpr>,
3238    partial_schema: Vec<ColumnMetadata>,
3239    group_limit: usize,
3240    shared_group_counter: Arc<AtomicUsize>,
3241    shared_memory_counter: Arc<AtomicU64>,
3242    memory_limit: Option<u64>,
3243) -> Result<Vec<(usize, Vec<Row>)>> {
3244    if let Ok(handle) = tokio::runtime::Handle::try_current() {
3245        let expected = partitions.len();
3246        let (sender, receiver) = std::sync::mpsc::channel();
3247        for (partition_index, rows) in partitions.into_iter().enumerate() {
3248            let sender = sender.clone();
3249            let input_schema = input_schema.clone();
3250            let group_keys = group_keys.clone();
3251            let aggregates = aggregates.clone();
3252            let partial_schema = partial_schema.clone();
3253            let shared_group_counter = Arc::clone(&shared_group_counter);
3254            let shared_memory_counter = Arc::clone(&shared_memory_counter);
3255            handle.spawn_blocking(move || {
3256                let result = execute_partial_partition(
3257                    partition_index,
3258                    rows,
3259                    input_schema,
3260                    group_keys,
3261                    aggregates,
3262                    partial_schema,
3263                    group_limit,
3264                    shared_group_counter,
3265                    shared_memory_counter,
3266                    memory_limit,
3267                );
3268                let _ = sender.send(result);
3269            });
3270        }
3271        drop(sender);
3272        return recv_partition_results(receiver, expected);
3273    }
3274
3275    run_partial_partitions_on_threads(
3276        partitions,
3277        input_schema,
3278        group_keys,
3279        aggregates,
3280        partial_schema,
3281        group_limit,
3282        shared_group_counter,
3283        shared_memory_counter,
3284        memory_limit,
3285    )
3286}
3287
3288#[cfg(not(feature = "tokio"))]
3289#[allow(clippy::too_many_arguments)]
3290fn run_partial_partitions(
3291    partitions: Vec<Vec<Row>>,
3292    input_schema: Vec<ColumnMetadata>,
3293    group_keys: Vec<TypedExpr>,
3294    aggregates: Vec<AggregateExpr>,
3295    partial_schema: Vec<ColumnMetadata>,
3296    group_limit: usize,
3297    shared_group_counter: Arc<AtomicUsize>,
3298    shared_memory_counter: Arc<AtomicU64>,
3299    memory_limit: Option<u64>,
3300) -> Result<Vec<(usize, Vec<Row>)>> {
3301    run_partial_partitions_on_threads(
3302        partitions,
3303        input_schema,
3304        group_keys,
3305        aggregates,
3306        partial_schema,
3307        group_limit,
3308        shared_group_counter,
3309        shared_memory_counter,
3310        memory_limit,
3311    )
3312}
3313
3314#[allow(clippy::too_many_arguments)]
3315fn run_partial_partitions_on_threads(
3316    partitions: Vec<Vec<Row>>,
3317    input_schema: Vec<ColumnMetadata>,
3318    group_keys: Vec<TypedExpr>,
3319    aggregates: Vec<AggregateExpr>,
3320    partial_schema: Vec<ColumnMetadata>,
3321    group_limit: usize,
3322    shared_group_counter: Arc<AtomicUsize>,
3323    shared_memory_counter: Arc<AtomicU64>,
3324    memory_limit: Option<u64>,
3325) -> Result<Vec<(usize, Vec<Row>)>> {
3326    let expected = partitions.len();
3327    let (sender, receiver) = std::sync::mpsc::channel();
3328    std::thread::scope(|scope| {
3329        for (partition_index, rows) in partitions.into_iter().enumerate() {
3330            let sender = sender.clone();
3331            let input_schema = input_schema.clone();
3332            let group_keys = group_keys.clone();
3333            let aggregates = aggregates.clone();
3334            let partial_schema = partial_schema.clone();
3335            let shared_group_counter = Arc::clone(&shared_group_counter);
3336            let shared_memory_counter = Arc::clone(&shared_memory_counter);
3337            scope.spawn(move || {
3338                let result = execute_partial_partition(
3339                    partition_index,
3340                    rows,
3341                    input_schema,
3342                    group_keys,
3343                    aggregates,
3344                    partial_schema,
3345                    group_limit,
3346                    shared_group_counter,
3347                    shared_memory_counter,
3348                    memory_limit,
3349                );
3350                let _ = sender.send(result);
3351            });
3352        }
3353    });
3354    drop(sender);
3355    recv_partition_results(receiver, expected)
3356}
3357
3358/// Execute a deterministic single-process parallel partial-to-final aggregate.
3359pub fn execute_parallel_aggregate_rows<'a>(
3360    input: Box<dyn RowIterator + 'a>,
3361    group_keys: Vec<TypedExpr>,
3362    aggregates: Vec<AggregateExpr>,
3363    having: Option<TypedExpr>,
3364    final_schema: Vec<ColumnMetadata>,
3365    parallelism: usize,
3366) -> Result<Vec<Row>> {
3367    execute_parallel_aggregate_rows_with_policy(
3368        input,
3369        group_keys,
3370        aggregates,
3371        having,
3372        final_schema,
3373        parallelism,
3374        None,
3375        DEFAULT_GROUP_LIMIT,
3376    )
3377}
3378
3379/// Execute a deterministic parallel aggregate with memory fallback controls.
3380#[allow(clippy::too_many_arguments)]
3381pub fn execute_parallel_aggregate_rows_with_policy<'a>(
3382    mut input: Box<dyn RowIterator + 'a>,
3383    group_keys: Vec<TypedExpr>,
3384    aggregates: Vec<AggregateExpr>,
3385    having: Option<TypedExpr>,
3386    final_schema: Vec<ColumnMetadata>,
3387    parallelism: usize,
3388    memory: Option<MemoryPolicy>,
3389    group_limit: usize,
3390) -> Result<Vec<Row>> {
3391    if parallelism <= 1 {
3392        return execute_single_aggregate_rows(
3393            input,
3394            group_keys,
3395            aggregates,
3396            having,
3397            final_schema,
3398            memory,
3399            group_limit,
3400        );
3401    }
3402
3403    let input_schema = input.schema().to_vec();
3404    let mut input_rows = Vec::new();
3405    let mut materialized_bytes = 0u64;
3406    let materialize_threshold = memory
3407        .as_ref()
3408        .and_then(MemoryPolicy::limit_bytes)
3409        .map(|limit| (limit / 2).max(1));
3410
3411    while let Some(result) = input.next_row() {
3412        let row = result?;
3413        let row_bytes = materialize_threshold.map(|_| estimate_row_bytes(&row));
3414        if let (Some(threshold), Some(row_bytes)) = (materialize_threshold, row_bytes)
3415            && materialized_bytes.saturating_add(row_bytes) > threshold
3416        {
3417            input_rows.push(row);
3418            let chained = ChainRowIterator::new(input_rows, input, input_schema.clone());
3419            return execute_single_aggregate_rows(
3420                Box::new(chained),
3421                group_keys,
3422                aggregates,
3423                having,
3424                final_schema,
3425                memory,
3426                group_limit,
3427            );
3428        }
3429        if let Some(row_bytes) = row_bytes {
3430            materialized_bytes = materialized_bytes.saturating_add(row_bytes);
3431        }
3432        input_rows.push(row);
3433    }
3434
3435    let fallback_rows = if memory.is_some() || group_limit < input_rows.len() {
3436        Some(input_rows.clone())
3437    } else {
3438        None
3439    };
3440    let result = execute_parallel_aggregate_rows_from_materialized(
3441        input_rows,
3442        input_schema.clone(),
3443        group_keys.clone(),
3444        aggregates.clone(),
3445        having.clone(),
3446        final_schema.clone(),
3447        parallelism,
3448        group_limit,
3449        materialized_bytes,
3450        memory.as_ref().and_then(MemoryPolicy::limit_bytes),
3451    );
3452    match result {
3453        Ok(rows) => Ok(rows),
3454        Err(ExecutorError::ResourceExhausted { .. }) => {
3455            if let Some(fallback_rows) = fallback_rows {
3456                execute_single_aggregate_rows(
3457                    Box::new(VecIterator::new(fallback_rows, input_schema)),
3458                    group_keys,
3459                    aggregates,
3460                    having,
3461                    final_schema,
3462                    memory,
3463                    group_limit,
3464                )
3465            } else {
3466                Err(ExecutorError::ResourceExhausted {
3467                    message: format!(
3468                        "parallel aggregate exceeded group limit {group_limit}; no fallback rows retained"
3469                    ),
3470                })
3471            }
3472        }
3473        Err(err) => Err(err),
3474    }
3475}
3476
3477#[allow(clippy::too_many_arguments)]
3478fn execute_parallel_aggregate_rows_from_materialized(
3479    input_rows: Vec<Row>,
3480    input_schema: Vec<ColumnMetadata>,
3481    group_keys: Vec<TypedExpr>,
3482    aggregates: Vec<AggregateExpr>,
3483    having: Option<TypedExpr>,
3484    final_schema: Vec<ColumnMetadata>,
3485    parallelism: usize,
3486    group_limit: usize,
3487    materialized_bytes: u64,
3488    memory_limit: Option<u64>,
3489) -> Result<Vec<Row>> {
3490    let partial_schema = build_partial_aggregate_schema(&group_keys, &aggregates);
3491    let partitions = split_contiguous_partitions(input_rows, parallelism);
3492    let shared_group_counter = Arc::new(AtomicUsize::new(0));
3493    let shared_memory_counter = Arc::new(AtomicU64::new(materialized_bytes));
3494    let partial_results = run_partial_partitions(
3495        partitions,
3496        input_schema,
3497        group_keys.clone(),
3498        aggregates.clone(),
3499        partial_schema.clone(),
3500        group_limit,
3501        shared_group_counter,
3502        shared_memory_counter,
3503        memory_limit,
3504    )?;
3505
3506    let partial_rows = partial_results
3507        .into_iter()
3508        .flat_map(|(_, rows)| rows)
3509        .collect::<Vec<_>>();
3510    let final_input = VecIterator::new(partial_rows, partial_schema);
3511    let mut final_iter = AggregateIterator::new(
3512        Box::new(final_input),
3513        group_keys,
3514        aggregates,
3515        having,
3516        final_schema,
3517    )
3518    .with_mode(AggregateMode::Final)
3519    .with_group_limit(group_limit);
3520    collect_iterator_rows(&mut final_iter)
3521}
3522
3523fn execute_single_aggregate_rows<'a>(
3524    input: Box<dyn RowIterator + 'a>,
3525    group_keys: Vec<TypedExpr>,
3526    aggregates: Vec<AggregateExpr>,
3527    having: Option<TypedExpr>,
3528    final_schema: Vec<ColumnMetadata>,
3529    memory: Option<MemoryPolicy>,
3530    group_limit: usize,
3531) -> Result<Vec<Row>> {
3532    let mut iter = AggregateIterator::new(input, group_keys, aggregates, having, final_schema)
3533        .with_group_limit(group_limit)
3534        .with_memory_policy(memory);
3535    collect_iterator_rows(&mut iter)
3536}
3537
3538#[cfg(test)]
3539mod tests {
3540    use super::*;
3541    use crate::ast::span::Span;
3542    use crate::executor::memory::SpillPolicy;
3543    use crate::planner::typed_expr::TypedExprKind;
3544
3545    fn apply_values(acc: &mut dyn Accumulator, values: &[Option<SqlValue>]) {
3546        for value in values {
3547            acc.update(value.clone()).unwrap();
3548        }
3549    }
3550
3551    fn single_result(
3552        make_accumulator: impl Fn() -> Box<dyn Accumulator>,
3553        partitions: &[Vec<Option<SqlValue>>],
3554    ) -> SqlValue {
3555        let mut acc = make_accumulator();
3556        for partition in partitions {
3557            apply_values(acc.as_mut(), partition);
3558        }
3559        acc.finalize().unwrap()
3560    }
3561
3562    fn merged_result(
3563        make_partial: impl Fn() -> Box<dyn Accumulator>,
3564        make_final: impl Fn() -> Box<dyn Accumulator>,
3565        partitions: &[Vec<Option<SqlValue>>],
3566        merge_order: &[usize],
3567    ) -> SqlValue {
3568        let states = partitions
3569            .iter()
3570            .map(|partition| {
3571                let mut acc = make_partial();
3572                apply_values(acc.as_mut(), partition);
3573                acc.state().unwrap()
3574            })
3575            .collect::<Vec<_>>();
3576
3577        let mut final_acc = make_final();
3578        for idx in merge_order {
3579            final_acc.merge(&states[*idx]).unwrap();
3580        }
3581        final_acc.finalize().unwrap()
3582    }
3583
3584    fn assert_single_equals_merged(
3585        make_accumulator: impl Fn() -> Box<dyn Accumulator> + Copy,
3586        partitions: Vec<Vec<Option<SqlValue>>>,
3587    ) {
3588        let merge_order = (0..partitions.len()).collect::<Vec<_>>();
3589        let single = single_result(make_accumulator, &partitions);
3590        let merged = merged_result(
3591            make_accumulator,
3592            make_accumulator,
3593            &partitions,
3594            &merge_order,
3595        );
3596        assert_eq!(single, merged);
3597    }
3598
3599    fn assert_merge_order_invariant(
3600        make_accumulator: impl Fn() -> Box<dyn Accumulator> + Copy,
3601        partitions: Vec<Vec<Option<SqlValue>>>,
3602        merge_orders: &[Vec<usize>],
3603    ) {
3604        let single = single_result(make_accumulator, &partitions);
3605        for order in merge_orders {
3606            let merged = merged_result(make_accumulator, make_accumulator, &partitions, order);
3607            assert_eq!(single, merged, "merge order {order:?}");
3608        }
3609    }
3610
3611    fn column_expr(index: usize, name: &str, resolved_type: ResolvedType) -> TypedExpr {
3612        TypedExpr {
3613            kind: TypedExprKind::ColumnRef {
3614                table: "t".into(),
3615                column: name.into(),
3616                column_index: index,
3617            },
3618            resolved_type,
3619            span: Span::default(),
3620        }
3621    }
3622
3623    fn sample_aggregate_schema() -> Vec<ColumnMetadata> {
3624        vec![
3625            ColumnMetadata::new("category", ResolvedType::Text),
3626            ColumnMetadata::new("price", ResolvedType::Double),
3627            ColumnMetadata::new("label", ResolvedType::Text),
3628        ]
3629    }
3630
3631    fn sample_aggregate_rows() -> Vec<Row> {
3632        vec![
3633            Row::new(
3634                0,
3635                vec![
3636                    SqlValue::Text("book".into()),
3637                    SqlValue::Double(10.0),
3638                    SqlValue::Text("a".into()),
3639                ],
3640            ),
3641            Row::new(
3642                1,
3643                vec![
3644                    SqlValue::Text("book".into()),
3645                    SqlValue::Double(15.0),
3646                    SqlValue::Text("b".into()),
3647                ],
3648            ),
3649            Row::new(
3650                2,
3651                vec![
3652                    SqlValue::Text("game".into()),
3653                    SqlValue::Double(20.0),
3654                    SqlValue::Text("c".into()),
3655                ],
3656            ),
3657            Row::new(
3658                3,
3659                vec![
3660                    SqlValue::Text("book".into()),
3661                    SqlValue::Null,
3662                    SqlValue::Text("a".into()),
3663                ],
3664            ),
3665            Row::new(
3666                4,
3667                vec![
3668                    SqlValue::Text("toy".into()),
3669                    SqlValue::Double(3.0),
3670                    SqlValue::Text("d".into()),
3671                ],
3672            ),
3673        ]
3674    }
3675
3676    fn sample_aggregates() -> Vec<AggregateExpr> {
3677        let price = column_expr(1, "price", ResolvedType::Double);
3678        let label = column_expr(2, "label", ResolvedType::Text);
3679        vec![
3680            AggregateExpr::count_star(),
3681            AggregateExpr::sum(price.clone()),
3682            AggregateExpr::avg(price),
3683            AggregateExpr {
3684                function: AggregateFunction::GroupConcat {
3685                    separator: Some("|".into()),
3686                },
3687                arg: Some(label),
3688                extra_args: Vec::new(),
3689                distinct: false,
3690                result_type: ResolvedType::Text,
3691                filter: None,
3692                order_by: Vec::new(),
3693            },
3694        ]
3695    }
3696
3697    fn collect_single_aggregate(
3698        group_keys: Vec<TypedExpr>,
3699        aggregates: Vec<AggregateExpr>,
3700    ) -> Vec<Vec<SqlValue>> {
3701        let input = VecIterator::new(sample_aggregate_rows(), sample_aggregate_schema());
3702        let schema = build_aggregate_schema(&group_keys, &aggregates);
3703        let mut iter =
3704            AggregateIterator::new(Box::new(input), group_keys, aggregates, None, schema);
3705        collect_iterator_rows(&mut iter)
3706            .unwrap()
3707            .into_iter()
3708            .map(|row| row.values)
3709            .collect()
3710    }
3711
3712    fn collect_parallel_aggregate(
3713        group_keys: Vec<TypedExpr>,
3714        aggregates: Vec<AggregateExpr>,
3715        parallelism: usize,
3716    ) -> Vec<Vec<SqlValue>> {
3717        let input = VecIterator::new(sample_aggregate_rows(), sample_aggregate_schema());
3718        let schema = build_aggregate_schema(&group_keys, &aggregates);
3719        execute_parallel_aggregate_rows(
3720            Box::new(input),
3721            group_keys,
3722            aggregates,
3723            None,
3724            schema,
3725            parallelism,
3726        )
3727        .unwrap()
3728        .into_iter()
3729        .map(|row| row.values)
3730        .collect()
3731    }
3732
3733    fn sort_rows(mut rows: Vec<Vec<SqlValue>>) -> Vec<Vec<SqlValue>> {
3734        rows.sort_by(|left, right| format!("{left:?}").cmp(&format!("{right:?}")));
3735        rows
3736    }
3737
3738    #[test]
3739    fn grouping_sets_accumulate_each_row_once_per_set() {
3740        let category = column_expr(0, "category", ResolvedType::Text);
3741        let group_keys = vec![category];
3742        let aggregates = vec![AggregateExpr::count_star()];
3743        let input = VecIterator::new(sample_aggregate_rows(), sample_aggregate_schema());
3744        let mut schema = build_aggregate_schema(&group_keys, &aggregates);
3745        schema.push(ColumnMetadata::new("__grouping_id", ResolvedType::BigInt));
3746        // Mask 0b0 keeps the category key; mask 0b1 is the grand total.
3747        let mut iter =
3748            AggregateIterator::new(Box::new(input), group_keys, aggregates, None, schema)
3749                .with_grouping_sets(Some(vec![0b0, 0b1]));
3750        let rows = sort_rows(
3751            collect_iterator_rows(&mut iter)
3752                .unwrap()
3753                .into_iter()
3754                .map(|row| row.values)
3755                .collect(),
3756        );
3757
3758        assert_eq!(
3759            rows,
3760            sort_rows(vec![
3761                vec![
3762                    SqlValue::Text("book".into()),
3763                    SqlValue::BigInt(3),
3764                    SqlValue::BigInt(0),
3765                ],
3766                vec![
3767                    SqlValue::Text("game".into()),
3768                    SqlValue::BigInt(1),
3769                    SqlValue::BigInt(0),
3770                ],
3771                vec![
3772                    SqlValue::Text("toy".into()),
3773                    SqlValue::BigInt(1),
3774                    SqlValue::BigInt(0),
3775                ],
3776                vec![SqlValue::Null, SqlValue::BigInt(5), SqlValue::BigInt(1)],
3777            ])
3778        );
3779    }
3780
3781    #[test]
3782    fn grouping_sets_group_limit_applies_across_all_sets() {
3783        let category = column_expr(0, "category", ResolvedType::Text);
3784        let group_keys = vec![category];
3785        let aggregates = vec![AggregateExpr::count_star()];
3786        let input = VecIterator::new(sample_aggregate_rows(), sample_aggregate_schema());
3787        let mut schema = build_aggregate_schema(&group_keys, &aggregates);
3788        schema.push(ColumnMetadata::new("__grouping_id", ResolvedType::BigInt));
3789        // Three category groups plus the grand total = 4 groups; a limit of
3790        // 3 must fail even though each single set stays within the limit
3791        // (issue #149, D6).
3792        let mut iter =
3793            AggregateIterator::new(Box::new(input), group_keys, aggregates, None, schema)
3794                .with_grouping_sets(Some(vec![0b0, 0b1]))
3795                .with_group_limit(3);
3796        let error = collect_iterator_rows(&mut iter).unwrap_err();
3797        assert!(matches!(error, ExecutorError::ResourceExhausted { .. }));
3798    }
3799
3800    #[test]
3801    fn partial_schema_uses_group_keys_and_state_columns() {
3802        let category = column_expr(0, "category", ResolvedType::Text);
3803        let price = column_expr(1, "price", ResolvedType::Double);
3804        let aggregates = vec![AggregateExpr::count_star(), AggregateExpr::avg(price)];
3805
3806        let schema = build_partial_aggregate_schema(&[category], &aggregates);
3807        let names = schema
3808            .iter()
3809            .map(|column| column.name.as_str())
3810            .collect::<Vec<_>>();
3811        assert_eq!(
3812            names,
3813            vec![
3814                "category",
3815                "__agg0_state0",
3816                "__agg1_state0",
3817                "__agg1_state1"
3818            ]
3819        );
3820        assert_eq!(schema[1].data_type, ResolvedType::BigInt);
3821        assert_eq!(schema[2].data_type, ResolvedType::Double);
3822        assert_eq!(schema[3].data_type, ResolvedType::BigInt);
3823    }
3824
3825    #[test]
3826    fn parallel_aggregate_matches_single_with_group_by() {
3827        let group_keys = vec![column_expr(0, "category", ResolvedType::Text)];
3828        let aggregates = sample_aggregates();
3829
3830        let single = sort_rows(collect_single_aggregate(
3831            group_keys.clone(),
3832            aggregates.clone(),
3833        ));
3834        let parallel = sort_rows(collect_parallel_aggregate(group_keys, aggregates, 3));
3835
3836        assert_eq!(parallel, single);
3837    }
3838
3839    #[test]
3840    fn parallel_aggregate_matches_single_without_group_by() {
3841        let aggregates = sample_aggregates();
3842
3843        let single = collect_single_aggregate(Vec::new(), aggregates.clone());
3844        let parallel = collect_parallel_aggregate(Vec::new(), aggregates, 4);
3845
3846        assert_eq!(parallel, single);
3847    }
3848
3849    #[test]
3850    fn distinct_aggregates_force_single_parallel_mode() {
3851        let price = column_expr(1, "price", ResolvedType::Double);
3852        let aggregates = vec![AggregateExpr {
3853            distinct: true,
3854            ..AggregateExpr::sum(price)
3855        }];
3856
3857        assert!(should_use_single_for_parallel(4, &aggregates));
3858        assert!(should_use_single_for_parallel(1, &sample_aggregates()));
3859        assert!(!should_use_single_for_parallel(2, &sample_aggregates()));
3860    }
3861
3862    #[test]
3863    fn parallel_group_counter_exhaustion_falls_back_to_single() {
3864        let schema = vec![ColumnMetadata::new("category", ResolvedType::Text)];
3865        let rows = vec![
3866            Row::new(0, vec![SqlValue::Text("a".into())]),
3867            Row::new(1, vec![SqlValue::Text("b".into())]),
3868            Row::new(2, vec![SqlValue::Text("a".into())]),
3869            Row::new(3, vec![SqlValue::Text("b".into())]),
3870        ];
3871        let group_keys = vec![column_expr(0, "category", ResolvedType::Text)];
3872        let aggregates = vec![AggregateExpr::count_star()];
3873        let final_schema = build_aggregate_schema(&group_keys, &aggregates);
3874
3875        let single_values = {
3876            let input = VecIterator::new(rows.clone(), schema.clone());
3877            execute_single_aggregate_rows(
3878                Box::new(input),
3879                group_keys.clone(),
3880                aggregates.clone(),
3881                None,
3882                final_schema.clone(),
3883                None,
3884                2,
3885            )
3886            .unwrap()
3887            .into_iter()
3888            .map(|row| row.values)
3889            .collect::<Vec<_>>()
3890        };
3891
3892        let parallel_values = execute_parallel_aggregate_rows_with_policy(
3893            Box::new(VecIterator::new(rows, schema)),
3894            group_keys,
3895            aggregates,
3896            None,
3897            final_schema,
3898            2,
3899            None,
3900            2,
3901        )
3902        .unwrap()
3903        .into_iter()
3904        .map(|row| row.values)
3905        .collect::<Vec<_>>();
3906
3907        assert_eq!(sort_rows(parallel_values), sort_rows(single_values));
3908    }
3909
3910    #[test]
3911    fn materialize_limit_exhaustion_falls_back_to_streaming_single() {
3912        let schema = vec![ColumnMetadata::new("payload", ResolvedType::Text)];
3913        let rows = (0..4)
3914            .map(|idx| Row::new(idx, vec![SqlValue::Text("x".repeat(40))]))
3915            .collect::<Vec<_>>();
3916        let aggregates = vec![AggregateExpr::count_star()];
3917        let final_schema = build_aggregate_schema(&[], &aggregates);
3918        let policy = MemoryPolicy::new(Some(100), SpillPolicy::FailFast);
3919
3920        let result = execute_parallel_aggregate_rows_with_policy(
3921            Box::new(VecIterator::new(rows, schema)),
3922            Vec::new(),
3923            aggregates,
3924            None,
3925            final_schema,
3926            4,
3927            Some(policy),
3928            DEFAULT_GROUP_LIMIT,
3929        )
3930        .unwrap();
3931
3932        assert_eq!(result.len(), 1);
3933        assert_eq!(result[0].values, vec![SqlValue::BigInt(4)]);
3934    }
3935
3936    #[test]
3937    fn streaming_aggregate_respects_distinct_accumulators() {
3938        let schema = sample_aggregate_schema();
3939        let rows = vec![
3940            Row::new(
3941                0,
3942                vec![
3943                    SqlValue::Text("book".into()),
3944                    SqlValue::Double(10.0),
3945                    SqlValue::Text("a".into()),
3946                ],
3947            ),
3948            Row::new(
3949                1,
3950                vec![
3951                    SqlValue::Text("book".into()),
3952                    SqlValue::Double(10.0),
3953                    SqlValue::Text("a".into()),
3954                ],
3955            ),
3956            Row::new(
3957                2,
3958                vec![
3959                    SqlValue::Text("book".into()),
3960                    SqlValue::Double(15.0),
3961                    SqlValue::Text("b".into()),
3962                ],
3963            ),
3964        ];
3965        let group_keys = vec![column_expr(0, "category", ResolvedType::Text)];
3966        let price = column_expr(1, "price", ResolvedType::Double);
3967        let label = column_expr(2, "label", ResolvedType::Text);
3968        let aggregates = vec![
3969            AggregateExpr {
3970                distinct: true,
3971                ..AggregateExpr::sum(price)
3972            },
3973            AggregateExpr {
3974                function: AggregateFunction::GroupConcat {
3975                    separator: Some("|".into()),
3976                },
3977                arg: Some(label),
3978                extra_args: Vec::new(),
3979                distinct: true,
3980                result_type: ResolvedType::Text,
3981                filter: None,
3982                order_by: Vec::new(),
3983            },
3984        ];
3985        let output_schema = build_aggregate_schema(&group_keys, &aggregates);
3986        let input = VecIterator::new(rows, schema);
3987        let mut iter = StreamingAggregateIterator::new(
3988            Box::new(input),
3989            group_keys,
3990            aggregates,
3991            None,
3992            output_schema,
3993        );
3994        let rows = collect_iterator_rows(&mut iter).unwrap();
3995
3996        assert_eq!(rows.len(), 1);
3997        assert_eq!(rows[0].values[1], SqlValue::Double(25.0));
3998        assert_eq!(rows[0].values[2], SqlValue::Text("a|b".into()));
3999    }
4000
4001    #[test]
4002    fn partial_state_matches_single_for_count_sum_total_avg_min_max() {
4003        assert_single_equals_merged(
4004            || Box::new(CountAccumulator::new(false)),
4005            vec![
4006                vec![
4007                    Some(SqlValue::Integer(1)),
4008                    Some(SqlValue::BigInt(2)),
4009                    Some(SqlValue::Text("x".into())),
4010                    Some(SqlValue::Null),
4011                ],
4012                vec![Some(SqlValue::Integer(3))],
4013            ],
4014        );
4015        assert_single_equals_merged(
4016            || Box::new(SumAccumulator::new()),
4017            vec![
4018                vec![
4019                    Some(SqlValue::Integer(1)),
4020                    Some(SqlValue::BigInt(2)),
4021                    Some(SqlValue::Float(3.5)),
4022                ],
4023                vec![Some(SqlValue::Double(4.5)), Some(SqlValue::Null)],
4024            ],
4025        );
4026        assert_single_equals_merged(
4027            || Box::new(TotalAccumulator::new()),
4028            vec![
4029                vec![Some(SqlValue::Integer(1)), Some(SqlValue::Null)],
4030                vec![Some(SqlValue::Double(2.5))],
4031            ],
4032        );
4033        assert_single_equals_merged(
4034            || Box::new(AvgAccumulator::new()),
4035            vec![
4036                vec![Some(SqlValue::Integer(2)), Some(SqlValue::Double(4.0))],
4037                vec![Some(SqlValue::Null), Some(SqlValue::Double(6.0))],
4038            ],
4039        );
4040        assert_single_equals_merged(
4041            || Box::new(MinMaxAccumulator::new(true)),
4042            vec![
4043                vec![Some(SqlValue::Integer(3)), Some(SqlValue::Integer(1))],
4044                vec![Some(SqlValue::Integer(2)), Some(SqlValue::Null)],
4045            ],
4046        );
4047        assert_single_equals_merged(
4048            || Box::new(MinMaxAccumulator::new(false)),
4049            vec![
4050                vec![
4051                    Some(SqlValue::Text("b".into())),
4052                    Some(SqlValue::Text("a".into())),
4053                ],
4054                vec![Some(SqlValue::Text("c".into())), Some(SqlValue::Null)],
4055            ],
4056        );
4057    }
4058
4059    #[test]
4060    fn partial_state_matches_single_for_ordered_string_aggregates() {
4061        assert_single_equals_merged(
4062            || Box::new(GroupConcatAccumulator::new("|".into())),
4063            vec![
4064                vec![Some(SqlValue::Text("a".into())), Some(SqlValue::Null)],
4065                vec![
4066                    Some(SqlValue::Text("b".into())),
4067                    Some(SqlValue::Text("c".into())),
4068                ],
4069            ],
4070        );
4071        assert_single_equals_merged(
4072            || Box::new(StringAggAccumulator::new("::".into())),
4073            vec![
4074                vec![Some(SqlValue::Text("a".into()))],
4075                vec![Some(SqlValue::Text("b".into())), Some(SqlValue::Null)],
4076            ],
4077        );
4078    }
4079
4080    #[test]
4081    fn partial_state_handles_empty_all_null_single_and_mixed_boundaries() {
4082        assert_single_equals_merged(
4083            || Box::new(CountAccumulator::new(false)),
4084            vec![vec![], vec![]],
4085        );
4086        assert_single_equals_merged(|| Box::new(SumAccumulator::new()), vec![vec![], vec![]]);
4087        assert_single_equals_merged(|| Box::new(TotalAccumulator::new()), vec![vec![], vec![]]);
4088        assert_single_equals_merged(|| Box::new(AvgAccumulator::new()), vec![vec![], vec![]]);
4089        assert_single_equals_merged(
4090            || Box::new(MinMaxAccumulator::new(true)),
4091            vec![vec![], vec![]],
4092        );
4093        assert_single_equals_merged(
4094            || Box::new(MinMaxAccumulator::new(false)),
4095            vec![vec![], vec![]],
4096        );
4097        assert_single_equals_merged(
4098            || Box::new(GroupConcatAccumulator::new(",".into())),
4099            vec![vec![], vec![]],
4100        );
4101        assert_single_equals_merged(
4102            || Box::new(StringAggAccumulator::new(",".into())),
4103            vec![vec![], vec![]],
4104        );
4105        assert_single_equals_merged(
4106            || Box::new(CountAccumulator::new(false)),
4107            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4108        );
4109        assert_single_equals_merged(
4110            || Box::new(SumAccumulator::new()),
4111            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4112        );
4113        assert_single_equals_merged(
4114            || Box::new(TotalAccumulator::new()),
4115            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4116        );
4117        assert_single_equals_merged(
4118            || Box::new(AvgAccumulator::new()),
4119            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4120        );
4121        assert_single_equals_merged(
4122            || Box::new(MinMaxAccumulator::new(true)),
4123            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4124        );
4125        assert_single_equals_merged(
4126            || Box::new(MinMaxAccumulator::new(false)),
4127            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4128        );
4129        assert_single_equals_merged(
4130            || Box::new(GroupConcatAccumulator::new(",".into())),
4131            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4132        );
4133        assert_single_equals_merged(
4134            || Box::new(StringAggAccumulator::new(",".into())),
4135            vec![vec![Some(SqlValue::Null)], vec![Some(SqlValue::Null)]],
4136        );
4137        assert_single_equals_merged(
4138            || Box::new(SumAccumulator::new()),
4139            vec![vec![Some(SqlValue::Integer(7))]],
4140        );
4141        assert_single_equals_merged(
4142            || Box::new(AvgAccumulator::new()),
4143            vec![
4144                vec![Some(SqlValue::Null)],
4145                vec![Some(SqlValue::Double(8.0))],
4146            ],
4147        );
4148    }
4149
4150    #[test]
4151    fn commutative_accumulators_are_merge_order_invariant() {
4152        let orders = vec![
4153            vec![1, 3, 0, 2],
4154            vec![3, 2, 1, 0],
4155            vec![0, 1, 2, 3],
4156            vec![2, 0, 3, 1],
4157        ];
4158        assert_merge_order_invariant(
4159            || Box::new(CountAccumulator::new(false)),
4160            vec![vec![Some(SqlValue::Integer(1))]],
4161            &[vec![0]],
4162        );
4163        assert_merge_order_invariant(
4164            || Box::new(SumAccumulator::new()),
4165            vec![
4166                vec![Some(SqlValue::Integer(1))],
4167                vec![Some(SqlValue::Integer(2))],
4168            ],
4169            &[vec![0, 1], vec![1, 0]],
4170        );
4171        assert_merge_order_invariant(
4172            || Box::new(AvgAccumulator::new()),
4173            vec![
4174                vec![Some(SqlValue::Integer(1))],
4175                vec![Some(SqlValue::Integer(2))],
4176                vec![Some(SqlValue::Integer(3))],
4177            ],
4178            &[vec![0, 1, 2], vec![2, 1, 0]],
4179        );
4180        let numeric_partitions = vec![
4181            vec![Some(SqlValue::Integer(1)), Some(SqlValue::Null)],
4182            vec![Some(SqlValue::BigInt(2))],
4183            vec![],
4184            vec![Some(SqlValue::Double(3.0))],
4185        ];
4186        assert_merge_order_invariant(
4187            || Box::new(CountAccumulator::new(false)),
4188            numeric_partitions.clone(),
4189            &orders,
4190        );
4191        assert_merge_order_invariant(
4192            || Box::new(SumAccumulator::new()),
4193            numeric_partitions.clone(),
4194            &orders,
4195        );
4196        assert_merge_order_invariant(
4197            || Box::new(TotalAccumulator::new()),
4198            numeric_partitions.clone(),
4199            &orders,
4200        );
4201        assert_merge_order_invariant(
4202            || Box::new(AvgAccumulator::new()),
4203            numeric_partitions.clone(),
4204            &orders,
4205        );
4206        let integer_partitions = vec![
4207            vec![Some(SqlValue::Integer(3)), Some(SqlValue::Null)],
4208            vec![Some(SqlValue::Integer(1))],
4209            vec![],
4210            vec![Some(SqlValue::Integer(2))],
4211        ];
4212        assert_merge_order_invariant(
4213            || Box::new(MinMaxAccumulator::new(true)),
4214            integer_partitions.clone(),
4215            &orders,
4216        );
4217        assert_merge_order_invariant(
4218            || Box::new(MinMaxAccumulator::new(false)),
4219            integer_partitions,
4220            &orders,
4221        );
4222    }
4223
4224    #[test]
4225    fn avg_partial_state_uses_sum_count_and_never_divides_by_zero_during_merge() {
4226        let empty = {
4227            let acc = AvgAccumulator::new();
4228            acc.state().unwrap()
4229        };
4230        assert_eq!(empty, vec![SqlValue::Double(0.0), SqlValue::BigInt(0)]);
4231
4232        let mut partial = AvgAccumulator::new();
4233        partial.update(Some(SqlValue::Integer(2))).unwrap();
4234        partial.update(Some(SqlValue::Double(4.0))).unwrap();
4235        assert_eq!(
4236            partial.state().unwrap(),
4237            vec![SqlValue::Double(6.0), SqlValue::BigInt(2)]
4238        );
4239
4240        let mut final_acc = AvgAccumulator::new();
4241        final_acc.merge(&empty).unwrap();
4242        assert_eq!(final_acc.finalize().unwrap(), SqlValue::Null);
4243        final_acc.merge(&partial.state().unwrap()).unwrap();
4244        assert_eq!(final_acc.finalize().unwrap(), SqlValue::Double(3.0));
4245    }
4246
4247    #[test]
4248    fn merge_rejects_invalid_state_contracts_without_panicking() {
4249        let mut count = CountAccumulator::new(false);
4250        assert!(count.merge(&[]).is_err());
4251        assert!(count.merge(&[SqlValue::Text("bad".into())]).is_err());
4252
4253        let mut avg = AvgAccumulator::new();
4254        assert!(avg.merge(&[SqlValue::Double(1.0)]).is_err());
4255        assert!(
4256            avg.merge(&[SqlValue::Double(1.0), SqlValue::Text("bad".into())])
4257                .is_err()
4258        );
4259
4260        let mut concat = GroupConcatAccumulator::new("|".into());
4261        assert!(
4262            concat
4263                .merge(&[SqlValue::Text("a".into()), SqlValue::Text(",".into())])
4264                .is_err()
4265        );
4266    }
4267
4268    #[test]
4269    fn count_accumulator_counts_rows_and_skips_nulls() {
4270        let mut acc = CountAccumulator::new(false);
4271        acc.update(None).unwrap();
4272        acc.update(Some(SqlValue::Null)).unwrap();
4273        acc.update(Some(SqlValue::Integer(1))).unwrap();
4274        assert_eq!(acc.finalize().unwrap(), SqlValue::BigInt(2));
4275    }
4276
4277    #[test]
4278    fn count_accumulator_distinct_deduplicates() {
4279        let mut acc = CountAccumulator::new(true);
4280        acc.update(Some(SqlValue::Integer(1))).unwrap();
4281        acc.update(Some(SqlValue::Integer(1))).unwrap();
4282        acc.update(Some(SqlValue::Integer(2))).unwrap();
4283        assert_eq!(acc.finalize().unwrap(), SqlValue::BigInt(2));
4284    }
4285
4286    #[test]
4287    fn count_distinct_uses_group_key_equality_boundaries() {
4288        let mut acc = CountAccumulator::new(true);
4289        let nan_a = f64::from_bits(0x7ff8_0000_0000_0001);
4290        let nan_b = f64::from_bits(0x7ff8_0000_0000_0002);
4291        for value in [
4292            SqlValue::Null,
4293            SqlValue::Null,
4294            SqlValue::Integer(1),
4295            SqlValue::Integer(1),
4296            SqlValue::Double(1.0),
4297            SqlValue::Double(-0.0),
4298            SqlValue::Double(0.0),
4299            SqlValue::Double(nan_a),
4300            SqlValue::Double(nan_a),
4301            SqlValue::Double(nan_b),
4302            SqlValue::Text("same".into()),
4303            SqlValue::Text("same".into()),
4304            SqlValue::Blob(vec![1, 2]),
4305            SqlValue::Blob(vec![1, 2]),
4306            SqlValue::Blob(vec![1, 3]),
4307        ] {
4308            acc.update(Some(value)).unwrap();
4309        }
4310        assert_eq!(acc.finalize().unwrap(), SqlValue::BigInt(9));
4311    }
4312
4313    #[test]
4314    fn distinct_non_count_accumulators_deduplicate_non_null_values() {
4315        let mut sum = SumAccumulator::with_distinct(true);
4316        for value in [
4317            SqlValue::Integer(1),
4318            SqlValue::Integer(1),
4319            SqlValue::Double(1.0),
4320            SqlValue::Integer(2),
4321            SqlValue::Null,
4322        ] {
4323            sum.update(Some(value)).unwrap();
4324        }
4325        assert_eq!(sum.finalize().unwrap(), SqlValue::Double(4.0));
4326
4327        let mut avg = AvgAccumulator::with_distinct(true);
4328        for value in [
4329            SqlValue::Integer(1),
4330            SqlValue::Integer(1),
4331            SqlValue::Integer(3),
4332            SqlValue::Null,
4333        ] {
4334            avg.update(Some(value)).unwrap();
4335        }
4336        assert_eq!(avg.finalize().unwrap(), SqlValue::Double(2.0));
4337
4338        let mut min = MinMaxAccumulator::with_distinct(true, true);
4339        let mut max = MinMaxAccumulator::with_distinct(false, true);
4340        for value in [
4341            SqlValue::Text("b".into()),
4342            SqlValue::Text("a".into()),
4343            SqlValue::Text("a".into()),
4344            SqlValue::Text("c".into()),
4345        ] {
4346            min.update(Some(value.clone())).unwrap();
4347            max.update(Some(value)).unwrap();
4348        }
4349        assert_eq!(min.finalize().unwrap(), SqlValue::Text("a".into()));
4350        assert_eq!(max.finalize().unwrap(), SqlValue::Text("c".into()));
4351
4352        let mut group_concat = GroupConcatAccumulator::with_distinct("|".into(), true);
4353        let mut string_agg = StringAggAccumulator::with_distinct(";".into(), true);
4354        for value in [
4355            SqlValue::Text("a".into()),
4356            SqlValue::Text("a".into()),
4357            SqlValue::Null,
4358            SqlValue::Text("b".into()),
4359        ] {
4360            group_concat.update(Some(value.clone())).unwrap();
4361            string_agg.update(Some(value)).unwrap();
4362        }
4363        assert_eq!(
4364            group_concat.finalize().unwrap(),
4365            SqlValue::Text("a|b".into())
4366        );
4367        assert_eq!(string_agg.finalize().unwrap(), SqlValue::Text("a;b".into()));
4368    }
4369
4370    #[test]
4371    fn sum_accumulator_aggregates_numeric_values() {
4372        let mut acc = SumAccumulator::new();
4373        acc.update(Some(SqlValue::Integer(2))).unwrap();
4374        acc.update(Some(SqlValue::Double(3.5))).unwrap();
4375        acc.update(Some(SqlValue::Null)).unwrap();
4376        assert_eq!(acc.finalize().unwrap(), SqlValue::Double(5.5));
4377    }
4378
4379    #[test]
4380    fn total_accumulator_returns_zero_for_empty() {
4381        let acc = TotalAccumulator::new();
4382        assert_eq!(acc.finalize().unwrap(), SqlValue::Double(0.0));
4383    }
4384
4385    #[test]
4386    fn total_accumulator_aggregates_numeric_values() {
4387        let mut acc = TotalAccumulator::new();
4388        acc.update(Some(SqlValue::Integer(2))).unwrap();
4389        acc.update(Some(SqlValue::Null)).unwrap();
4390        acc.update(Some(SqlValue::Double(1.5))).unwrap();
4391        assert_eq!(acc.finalize().unwrap(), SqlValue::Double(3.5));
4392    }
4393
4394    #[test]
4395    fn avg_accumulator_handles_empty_and_nulls() {
4396        let mut acc = AvgAccumulator::new();
4397        assert_eq!(acc.finalize().unwrap(), SqlValue::Null);
4398        acc.update(Some(SqlValue::Null)).unwrap();
4399        acc.update(Some(SqlValue::BigInt(4))).unwrap();
4400        acc.update(Some(SqlValue::Integer(2))).unwrap();
4401        assert_eq!(acc.finalize().unwrap(), SqlValue::Double(3.0));
4402    }
4403
4404    #[test]
4405    fn min_max_accumulator_tracks_extremes() {
4406        let mut min_acc = MinMaxAccumulator::new(true);
4407        let mut max_acc = MinMaxAccumulator::new(false);
4408        for value in [3, 1, 2] {
4409            min_acc.update(Some(SqlValue::Integer(value))).unwrap();
4410            max_acc.update(Some(SqlValue::Integer(value))).unwrap();
4411        }
4412        assert_eq!(min_acc.finalize().unwrap(), SqlValue::Integer(1));
4413        assert_eq!(max_acc.finalize().unwrap(), SqlValue::Integer(3));
4414    }
4415
4416    #[test]
4417    fn min_max_accumulator_rejects_type_mismatch() {
4418        let mut acc = MinMaxAccumulator::new(true);
4419        acc.update(Some(SqlValue::Integer(1))).unwrap();
4420        let err = acc.update(Some(SqlValue::Text("bad".into()))).unwrap_err();
4421        match err {
4422            ExecutorError::Evaluation(crate::executor::EvaluationError::TypeMismatch {
4423                ..
4424            }) => {}
4425            other => panic!("unexpected error {:?}", other),
4426        }
4427    }
4428
4429    #[test]
4430    fn group_concat_accumulator_joins_values() {
4431        let mut acc = GroupConcatAccumulator::new("|".into());
4432        acc.update(Some(SqlValue::Text("a".into()))).unwrap();
4433        acc.update(Some(SqlValue::Null)).unwrap();
4434        acc.update(Some(SqlValue::Text("b".into()))).unwrap();
4435        assert_eq!(acc.finalize().unwrap(), SqlValue::Text("a|b".into()));
4436    }
4437
4438    #[test]
4439    fn group_concat_accumulator_empty_returns_null() {
4440        let acc = GroupConcatAccumulator::new(",".into());
4441        assert_eq!(acc.finalize().unwrap(), SqlValue::Null);
4442    }
4443
4444    #[test]
4445    fn string_agg_accumulator_joins_values() {
4446        let mut acc = StringAggAccumulator::new("::".into());
4447        acc.update(Some(SqlValue::Text("a".into()))).unwrap();
4448        acc.update(Some(SqlValue::Null)).unwrap();
4449        acc.update(Some(SqlValue::Text("b".into()))).unwrap();
4450        assert_eq!(acc.finalize().unwrap(), SqlValue::Text("a::b".into()));
4451    }
4452
4453    #[test]
4454    fn string_agg_accumulator_empty_returns_null() {
4455        let acc = StringAggAccumulator::new(",".into());
4456        assert_eq!(acc.finalize().unwrap(), SqlValue::Null);
4457    }
4458
4459    #[test]
4460    fn encode_group_key_is_deterministic() {
4461        let values = vec![
4462            SqlValue::Integer(1),
4463            SqlValue::Text("a".into()),
4464            SqlValue::Null,
4465        ];
4466        let first = encode_group_key(&values).unwrap();
4467        let second = encode_group_key(&values).unwrap();
4468        assert_eq!(first, second);
4469    }
4470
4471    #[test]
4472    fn percentile_disc_accumulator_follows_postgres_selection_rule() {
4473        // values [1, 2, 2, 3]; index = max(ceil(f * n) - 1, 0)
4474        let values = [1i64, 2, 2, 3];
4475        for (fraction, expected) in [(0.0, 1i64), (0.25, 1), (0.5, 2), (0.75, 2), (1.0, 3)] {
4476            let mut acc = PercentileDiscAccumulator::new(fraction, vec![(true, false)]);
4477            for value in values {
4478                let value = SqlValue::Integer(value as i32);
4479                acc.update_ordered(Some(value.clone()), std::slice::from_ref(&value))
4480                    .unwrap();
4481            }
4482            // NULL sort values are excluded.
4483            acc.update_ordered(Some(SqlValue::Null), &[SqlValue::Null])
4484                .unwrap();
4485            assert_eq!(
4486                acc.finalize().unwrap(),
4487                SqlValue::Integer(expected as i32),
4488                "fraction {fraction}"
4489            );
4490        }
4491
4492        let empty = PercentileDiscAccumulator::new(0.5, vec![(true, false)]);
4493        assert_eq!(empty.finalize().unwrap(), SqlValue::Null);
4494
4495        let acc = PercentileDiscAccumulator::new(0.5, vec![(true, false)]);
4496        assert!(acc.state().is_err(), "ordered-set partial state is invalid");
4497    }
4498
4499    #[test]
4500    fn ordered_string_accumulators_sort_stably_and_reject_partial_state() {
4501        let mut acc = StringAggAccumulator::with_order(",".into(), false, vec![(false, false)]);
4502        for (key, value) in [(2, "b"), (1, "a"), (3, "c"), (2, "d")] {
4503            acc.update_ordered(
4504                Some(SqlValue::Text(value.into())),
4505                &[SqlValue::Integer(key)],
4506            )
4507            .unwrap();
4508        }
4509        // DESC by key; the two key=2 entries keep arrival order (stable sort).
4510        assert_eq!(acc.finalize().unwrap(), SqlValue::Text("c,b,d,a".into()));
4511        assert!(acc.state().is_err());
4512        assert!(acc.merge(&[]).is_err());
4513
4514        let mut concat = GroupConcatAccumulator::with_order("|".into(), true, vec![(true, false)]);
4515        for (key, value) in [(2, "x"), (1, "y"), (3, "x")] {
4516            concat
4517                .update_ordered(
4518                    Some(SqlValue::Text(value.into())),
4519                    &[SqlValue::Integer(key)],
4520                )
4521                .unwrap();
4522        }
4523        // DISTINCT drops the second "x"; ASC by key -> y,x.
4524        assert_eq!(concat.finalize().unwrap(), SqlValue::Text("y|x".into()));
4525    }
4526
4527    #[test]
4528    fn stable_moments_merge_and_preserve_sample_boundaries() {
4529        let mut whole = StatisticsAccumulator::new(StatisticsKind::Variance(true));
4530        let mut left = whole.clone();
4531        let mut right = whole.clone();
4532        for (index, value) in [1.0e12, 1.0e12 + 1.0, 1.0e12 + 2.0, 1.0e12 + 3.0]
4533            .into_iter()
4534            .enumerate()
4535        {
4536            whole.update(Some(SqlValue::Double(value))).unwrap();
4537            if index < 2 {
4538                left.update(Some(SqlValue::Double(value))).unwrap();
4539            } else {
4540                right.update(Some(SqlValue::Double(value))).unwrap();
4541            }
4542        }
4543        left.merge(&right.state().unwrap()).unwrap();
4544        assert_eq!(whole.finalize().unwrap(), left.finalize().unwrap());
4545        assert_eq!(whole.finalize().unwrap(), SqlValue::Double(5.0 / 3.0));
4546
4547        let empty = StatisticsAccumulator::new(StatisticsKind::Variance(true));
4548        assert_eq!(empty.finalize().unwrap(), SqlValue::Null);
4549        let mut singleton = empty.clone();
4550        singleton.update(Some(SqlValue::Integer(7))).unwrap();
4551        assert_eq!(singleton.finalize().unwrap(), SqlValue::Null);
4552        singleton.kind = StatisticsKind::Variance(false);
4553        assert_eq!(singleton.finalize().unwrap(), SqlValue::Double(0.0));
4554    }
4555
4556    #[test]
4557    fn continuous_percentiles_preserve_non_finite_endpoints() {
4558        for (fraction, expected) in [(0.0, f64::NEG_INFINITY), (1.0, f64::INFINITY)] {
4559            let mut acc = PercentileContAccumulator::new(fraction, true);
4560            acc.update(Some(SqlValue::Double(f64::NEG_INFINITY)))
4561                .unwrap();
4562            acc.update(Some(SqlValue::Double(0.0))).unwrap();
4563            acc.update(Some(SqlValue::Double(f64::INFINITY))).unwrap();
4564            assert_eq!(acc.finalize().unwrap(), SqlValue::Double(expected));
4565        }
4566
4567        let mut acc = PercentileContAccumulator::new(1.0, true);
4568        acc.update(Some(SqlValue::Double(f64::NAN))).unwrap();
4569        let SqlValue::Double(value) = acc.finalize().unwrap() else {
4570            panic!("continuous percentile must return DOUBLE");
4571        };
4572        assert!(value.is_nan());
4573    }
4574}