1use crate::physical::common::{hash_value_into, store_value_in_vector};
3use crate::physical::types::OperatorResult;
4use akar_common::types::{PhysicalTypeID, Value};
5use akar_common::vector::{DataChunk, ValueVector};
6use akar_function::AggregateFunction;
7use akar_function::aggregate::AggValueState;
8use akar_parser::ast::Expression;
9use arrow::compute;
10use arrow::datatypes::{Float64Type, Int32Type, Int64Type};
11
12pub struct AggregateHashTable {
19 funcs: Vec<AggregateFunction>,
21 group_by_cols: Vec<u32>,
23 agg_expressions: Vec<Vec<Expression>>,
25}
26
27impl AggregateHashTable {
28 pub fn new(funcs: Vec<AggregateFunction>, group_by_cols: Vec<u32>, agg_expressions: Vec<Vec<Expression>>) -> Self {
29 Self {
30 funcs,
31 group_by_cols,
32 agg_expressions,
33 }
34 }
35
36 pub fn aggregate(&self, chunks: &[DataChunk]) -> OperatorResult {
38 let total_rows: usize = chunks.iter().map(|c| c.size).sum();
39
40 let mut col_indices = resolve_agg_col_indices(
42 &self.agg_expressions,
43 chunks.first().map(|c| c.field_names.as_slice()).unwrap_or(&[]),
44 );
45
46 if col_indices.is_empty() && !self.funcs.is_empty() {
50 col_indices = vec![Some(0); self.funcs.len()];
51 }
52
53 if total_rows == 0 && self.group_by_cols.is_empty() {
54 let mut fields = Vec::new();
57 for func in &self.funcs {
58 let state = AggValueState::new(func);
59 let result = state.finalize();
60 let phys_type = result.physical_type();
61 let mut v = ValueVector::new(phys_type, 1);
62 v.resize(1);
63 store_value_in_vector(&mut v, 0, &result)?;
64 fields.push(v);
65 }
66 return Ok(vec![{
67 let arrow_fields = fields
68 .iter()
69 .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
70 .collect::<Vec<_>>();
71 let arrow_field_types = fields.iter().map(|v| v.physical_type()).collect::<Vec<_>>();
72 DataChunk::new(arrow_fields, arrow_field_types)
73 }]);
74 }
75
76 if total_rows == 0 {
77 return self.empty_result();
78 }
79
80 if self.group_by_cols.is_empty() {
82 let all_count = self
83 .funcs
84 .iter()
85 .all(|f| matches!(f, AggregateFunction::Count | AggregateFunction::CountStar));
86 if all_count {
87 let mut fields = Vec::new();
88 for (i, func) in self.funcs.iter().enumerate() {
89 let mut total = 0u64;
90 for chunk in chunks {
91 match func {
92 AggregateFunction::CountStar => {
93 total += chunk.active_rows() as u64;
94 }
95 AggregateFunction::Count => {
96 if let Some(col_idx) = col_indices[i] {
97 if let Some(field) = chunk.fields.get(col_idx) {
98 if chunk.sel_vector.is_some() {
99 for row in chunk.iter_rows() {
100 if !field.is_null(row) {
101 total += 1;
102 }
103 }
104 } else {
105 total += (field.len() - field.null_count()) as u64;
106 }
107 }
108 } else {
109 total += chunk.active_rows() as u64;
110 }
111 }
112 _ => {}
113 }
114 }
115 let state = AggValueState::Count(total);
116 let result = state.finalize();
117 let phys_type = result.physical_type();
118 let mut v = ValueVector::new(phys_type, 1);
119 v.resize(1);
120 store_value_in_vector(&mut v, 0, &result)?;
121 fields.push(v);
122 }
123 return Ok(vec![{
124 let arrow_fields = fields
125 .iter()
126 .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
127 .collect::<Vec<_>>();
128 let arrow_field_types = fields.iter().map(|v| v.physical_type()).collect::<Vec<_>>();
129 DataChunk::new(arrow_fields, arrow_field_types)
130 }]);
131 }
132 }
133
134 if self.group_by_cols.is_empty() && chunks.iter().all(|c| c.sel_vector.is_none()) {
137 let all_scalar_agg = self.funcs.iter().all(|f| {
138 matches!(
139 f,
140 AggregateFunction::Sum | AggregateFunction::Min | AggregateFunction::Max | AggregateFunction::Avg
141 )
142 });
143 if all_scalar_agg {
144 let mut fields = Vec::new();
145 for (i, func) in self.funcs.iter().enumerate() {
146 if let Some(col_idx) = col_indices[i] {
147 let result_val = arrow_scalar_agg(func, chunks, col_idx);
148 let phys_type = result_val.physical_type();
149 let mut v = ValueVector::new(phys_type, 1);
150 v.resize(1);
151 store_value_in_vector(&mut v, 0, &result_val)?;
152 fields.push(v);
153 } else {
154 let mut v = ValueVector::new(PhysicalTypeID::Int64, 1);
155 v.resize(1);
156 v.set_null(0, true);
157 fields.push(v);
158 }
159 }
160 return Ok(vec![DataChunk::new(
161 fields
162 .iter()
163 .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
164 .collect(),
165 fields.iter().map(|v| v.physical_type()).collect(),
166 )]);
167 }
168 }
169
170 if total_rows > 1000 {
172 self.aggregate_parallel(chunks, &col_indices)
173 } else {
174 self.aggregate_sequential(chunks, &col_indices)
175 }
176 }
177
178 fn aggregate_parallel(&self, chunks: &[DataChunk], col_indices: &[Option<usize>]) -> OperatorResult {
180 use rayon::prelude::*;
181 let total_rows: usize = chunks.iter().map(|c| c.size).sum();
182 type LocalTable = hashbrown::HashMap<u64, Vec<(Value, Vec<AggValueState>)>>;
183
184 let group_cols = &self.group_by_cols;
186 let funcs = &self.funcs;
187
188 let results: Vec<LocalTable> = chunks
189 .par_iter()
190 .map(|chunk| {
191 let mut local: LocalTable = hashbrown::HashMap::with_capacity(chunk.size.max(16));
192 for row in chunk.iter_rows() {
193 let hash = hash_group_key(chunk, group_cols, row);
194 let bucket = local.entry(hash).or_default();
195 let entry = bucket.iter_mut().find(|(k, _)| keys_equal(k, chunk, group_cols, row));
196 if let Some((_, states)) = entry {
197 update_states_row(states, chunk, funcs, col_indices, row);
198 } else {
199 let key = build_group_key(chunk, group_cols, row);
200 let mut states = funcs.iter().map(AggValueState::new).collect::<Vec<_>>();
201 update_states_row(&mut states, chunk, funcs, col_indices, row);
202 bucket.push((key, states));
203 }
204 }
205 local
206 })
207 .collect();
208
209 let mut merged: LocalTable = hashbrown::HashMap::with_capacity(total_rows.max(16));
211 for local in results {
212 for (hash, bucket) in local {
213 let mbucket = merged.entry(hash).or_default();
214 for (key, states) in bucket {
215 let entry = mbucket.iter_mut().find(|(k, _)| *k == key);
216 if let Some((_, existing)) = entry {
217 for (i, s) in states.iter().enumerate() {
218 existing[i].merge(s);
219 }
220 } else {
221 mbucket.push((key, states));
222 }
223 }
224 }
225 }
226 self.build_output(&merged)
227 }
228
229 fn aggregate_sequential(&self, chunks: &[DataChunk], col_indices: &[Option<usize>]) -> OperatorResult {
231 let total_rows: usize = chunks.iter().map(|c| c.size).sum();
232 let mut groups: hashbrown::HashMap<u64, Vec<(Value, Vec<AggValueState>)>> =
233 hashbrown::HashMap::with_capacity(total_rows.max(16));
234 let group_cols = &self.group_by_cols;
235 let funcs = &self.funcs;
236
237 for chunk in chunks {
238 for row in chunk.iter_rows() {
239 let hash = hash_group_key(chunk, group_cols, row);
240 let bucket = groups.entry(hash).or_default();
241 let entry = bucket.iter_mut().find(|(k, _)| keys_equal(k, chunk, group_cols, row));
242 if let Some((_, states)) = entry {
243 update_states_row(states, chunk, funcs, col_indices, row);
244 } else {
245 let key = build_group_key(chunk, group_cols, row);
246 let mut states = funcs.iter().map(AggValueState::new).collect::<Vec<_>>();
247 update_states_row(&mut states, chunk, funcs, col_indices, row);
248 bucket.push((key, states));
249 }
250 }
251 }
252 self.build_output(&groups)
253 }
254
255 fn empty_result(&self) -> OperatorResult {
256 let num_cols = self.group_by_cols.len() + self.funcs.len();
257 Ok(vec![DataChunk::new(
258 Vec::with_capacity(num_cols),
259 Vec::with_capacity(num_cols),
260 )])
261 }
262
263 fn empty_scalar_result(&self) -> OperatorResult {
267 let mut fields = Vec::new();
268 for func in &self.funcs {
269 let state = AggValueState::new(func);
270 let result = state.finalize();
271 let phys_type = result.physical_type();
272 let mut v = ValueVector::new(phys_type, 1);
273 v.resize(1);
274 store_value_in_vector(&mut v, 0, &result)?;
275 fields.push(v);
276 }
277 Ok(vec![{
278 let arrow_fields = fields
279 .iter()
280 .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
281 .collect::<Vec<_>>();
282 let arrow_field_types = fields.iter().map(|v| v.physical_type()).collect::<Vec<_>>();
283 DataChunk::new(arrow_fields, arrow_field_types)
284 }])
285 }
286
287 pub fn build_output(&self, groups: &hashbrown::HashMap<u64, Vec<(Value, Vec<AggValueState>)>>) -> OperatorResult {
288 if groups.is_empty() {
289 if self.group_by_cols.is_empty() && !self.funcs.is_empty() {
292 return self.empty_scalar_result();
293 }
294 return self.empty_result();
295 }
296
297 let mut group_keys: Vec<Value> = Vec::new();
299 let mut agg_results: Vec<Vec<Value>> = (0..self.funcs.len()).map(|_| Vec::new()).collect();
300
301 for bucket in groups.values() {
302 for (key, states) in bucket {
303 group_keys.push(key.clone());
304 for (i, state) in states.iter().enumerate() {
305 agg_results[i].push(state.finalize());
306 }
307 }
308 }
309
310 if agg_results
314 .iter()
315 .any(|r| r.iter().any(|v| matches!(v, Value::List(_))))
316 {
317 return self.build_output_direct(&group_keys, &agg_results);
318 }
319
320 let num_group_cols = self.group_by_cols.len();
321 let num_rows = group_keys.len();
322 let mut output = Vec::with_capacity(num_group_cols + self.funcs.len());
323
324 if num_group_cols == 1 {
326 let first_val = &group_keys[0];
327 let phys_type = first_val.physical_type();
328 let mut v = ValueVector::new(phys_type, num_rows);
329 v.resize(num_rows);
330 for (row, key) in group_keys.iter().enumerate() {
331 if matches!(key, Value::Null) {
332 v.set_null(row, true);
333 } else {
334 store_value_in_vector(&mut v, row, key)?;
335 }
336 }
337 output.push(v);
338 } else {
339 for gc_idx in 0..num_group_cols {
340 let first_key = &group_keys[0];
341 let inner_val = match first_key {
342 Value::List(vals) => vals.get(gc_idx).cloned().unwrap_or(Value::Null),
343 _ => Value::Null,
344 };
345 let phys_type = inner_val.physical_type();
346 let mut v = ValueVector::new(phys_type, num_rows);
347 v.resize(num_rows);
348 for (row, key) in group_keys.iter().enumerate() {
349 let val = match key {
350 Value::List(vals) => vals.get(gc_idx).cloned().unwrap_or(Value::Null),
351 _ => Value::Null,
352 };
353 if matches!(val, Value::Null) {
354 v.set_null(row, true);
355 } else {
356 store_value_in_vector(&mut v, row, &val)?;
357 }
358 }
359 output.push(v);
360 }
361 }
362
363 for i in 0..self.funcs.len() {
365 let first_val = &agg_results[i][0];
366 let phys_type = first_val.physical_type();
367 let mut v = ValueVector::new(phys_type, num_rows);
368 v.resize(num_rows);
369 for (row, val) in agg_results[i].iter().enumerate() {
370 store_value_in_vector(&mut v, row, val)?;
371 }
372 output.push(v);
373 }
374
375 const CHUNK_SIZE: usize = 2048;
377 let mut chunks = Vec::new();
378
379 for chunk_start in (0..num_rows).step_by(CHUNK_SIZE) {
380 let chunk_end = (chunk_start + CHUNK_SIZE).min(num_rows);
381 let chunk_len = chunk_end - chunk_start;
382
383 let mut chunk_fields = Vec::with_capacity(output.len());
384 for field in &output {
385 let mut new_v = ValueVector::new(field.physical_type(), chunk_len);
386 new_v.resize(chunk_len);
387 for i in 0..chunk_len {
388 if field.is_null(chunk_start + i) {
389 new_v.set_null(i, true);
390 } else if let Some(val) = field.get_value(chunk_start + i) {
391 store_value_in_vector(&mut new_v, i, &val)?;
392 }
393 }
394 chunk_fields.push(new_v);
395 }
396 chunks.push({
397 let arrow_fields = chunk_fields
398 .iter()
399 .map(|v| akar_common::arrow_vector::ArrowVector::from_legacy(v).array)
400 .collect::<Vec<_>>();
401 let arrow_field_types = chunk_fields.iter().map(|v| v.physical_type()).collect::<Vec<_>>();
402 DataChunk::new(arrow_fields, arrow_field_types)
403 });
404 }
405
406 Ok(chunks)
407 }
408
409 fn build_output_direct(&self, group_keys: &[Value], agg_results: &[Vec<Value>]) -> OperatorResult {
414 let num_rows = group_keys.len();
415 let num_group_cols = self.group_by_cols.len();
416 let mut arrow_fields = Vec::with_capacity(num_group_cols + self.funcs.len());
417 let mut arrow_types = Vec::with_capacity(num_group_cols + self.funcs.len());
418
419 for gc_idx in 0..num_group_cols {
420 let first_key = &group_keys[0];
421 let inner_val = match first_key {
422 Value::List(vals) if num_group_cols > 1 => vals.get(gc_idx).cloned().unwrap_or(Value::Null),
423 _ => first_key.clone(),
424 };
425 let phys_type = inner_val.physical_type();
426 let mut v = ValueVector::new(phys_type, num_rows);
427 v.resize(num_rows);
428 for (row, key) in group_keys.iter().enumerate() {
429 let val = match key {
430 Value::List(vals) if num_group_cols > 1 => vals.get(gc_idx).cloned().unwrap_or(Value::Null),
431 _ => key.clone(),
432 };
433 if matches!(val, Value::Null) {
434 v.set_null(row, true);
435 } else {
436 store_value_in_vector(&mut v, row, &val)?;
437 }
438 }
439 arrow_fields.push(akar_common::arrow_vector::ArrowVector::from_legacy(&v).array);
440 arrow_types.push(phys_type);
441 }
442
443 for results in agg_results {
444 arrow_fields.push(akar_common::arrow_vector::arrow_array_from_values(results));
445 let phys = if results.iter().any(|v| matches!(v, Value::List(_))) {
446 PhysicalTypeID::List
447 } else {
448 results
449 .first()
450 .map(|v| v.physical_type())
451 .unwrap_or(PhysicalTypeID::Any)
452 };
453 arrow_types.push(phys);
454 }
455
456 Ok(vec![DataChunk::new(arrow_fields, arrow_types)])
457 }
458}
459
460pub fn hash_group_key(chunk: &DataChunk, group_cols: &[u32], row: usize) -> u64 {
464 use std::hash::Hash;
465 use std::hash::Hasher;
466 let mut hasher = ahash::AHasher::default();
467 for &gc in group_cols {
468 let col = gc as usize;
469 if col >= chunk.fields.len() {
470 0u8.hash(&mut hasher);
471 continue;
472 }
473 if chunk.is_null(col, row) {
474 0u8.hash(&mut hasher);
475 continue;
476 }
477 match chunk.field_types[col] {
478 PhysicalTypeID::Int64 => {
479 let v = chunk.get_i64(col, row).unwrap_or(0);
480 v.hash(&mut hasher);
481 }
482 PhysicalTypeID::Int32 => {
483 let v = chunk.get_i32(col, row).unwrap_or(0);
484 v.hash(&mut hasher);
485 }
486 PhysicalTypeID::Double => {
487 let v = chunk.get_f64(col, row).unwrap_or(0.0);
488 v.to_bits().hash(&mut hasher);
489 }
490 PhysicalTypeID::Bool => {
491 let v = chunk.get_bool(col, row).unwrap_or(false);
492 v.hash(&mut hasher);
493 }
494 PhysicalTypeID::String => {
495 if let Some(s) = chunk.get_string(col, row) {
496 s.hash(&mut hasher);
497 }
498 }
499 _ => {
500 if let Some(val) = chunk.get_value(col, row) {
502 hash_value_into(&val, &mut hasher);
503 }
504 }
505 }
506 }
507 hasher.finish()
508}
509
510pub fn keys_equal(stored: &Value, chunk: &DataChunk, group_cols: &[u32], row: usize) -> bool {
513 if group_cols.is_empty() {
514 return true;
519 }
520 if group_cols.len() == 1 {
521 let col = group_cols[0] as usize;
522 if col >= chunk.fields.len() {
523 return *stored == Value::Null;
524 }
525 if chunk.is_null(col, row) {
526 return *stored == Value::Null;
527 }
528 return match stored {
529 Value::Int64(v) => chunk.get_i64(col, row).is_some_and(|x| x == *v),
530 Value::Int32(v) => chunk.get_i32(col, row).is_some_and(|x| x == *v),
531 Value::Double(v) => chunk.get_f64(col, row).is_some_and(|x| x == *v),
532 Value::Bool(v) => chunk.get_bool(col, row).is_some_and(|x| x == *v),
533 Value::String(v) => chunk.get_string(col, row).is_some_and(|x| x == *v),
534 _ => {
535 let val = chunk.get_value(col, row).unwrap_or(Value::Null);
536 *stored == val
537 }
538 };
539 }
540 match stored {
541 Value::List(vals) if vals.len() == group_cols.len() => vals.iter().enumerate().all(|(i, v)| {
542 let col = group_cols[i] as usize;
543 if col >= chunk.fields.len() {
544 return *v == Value::Null;
545 }
546 if chunk.is_null(col, row) {
547 return *v == Value::Null;
548 }
549 match v {
550 Value::Int64(expected) => chunk.get_i64(col, row).is_some_and(|x| x == *expected),
551 Value::Int32(expected) => chunk.get_i32(col, row).is_some_and(|x| x == *expected),
552 Value::Double(expected) => chunk.get_f64(col, row).is_some_and(|x| x == *expected),
553 Value::Bool(expected) => chunk.get_bool(col, row).is_some_and(|x| x == *expected),
554 Value::String(expected) => chunk.get_string(col, row).is_some_and(|x| x == *expected),
555 _ => {
556 let val = chunk.get_value(col, row).unwrap_or(Value::Null);
557 *v == val
558 }
559 }
560 }),
561 _ => false,
562 }
563}
564
565pub fn build_group_key(chunk: &DataChunk, group_cols: &[u32], row: usize) -> Value {
567 if group_cols.is_empty() {
568 return Value::Null;
569 }
570 if group_cols.len() == 1 {
571 chunk
572 .fields
573 .get(group_cols[0] as usize)
574 .map(|_| chunk.get_value(group_cols[0] as usize, row))
575 .unwrap_or(Some(Value::Null))
576 .unwrap_or(Value::Null)
577 } else {
578 let vals: Vec<Value> = group_cols
579 .iter()
580 .map(|&gc| {
581 chunk
582 .fields
583 .get(gc as usize)
584 .map(|_| chunk.get_value(gc as usize, row))
585 .unwrap_or(Some(Value::Null))
586 .unwrap_or(Value::Null)
587 })
588 .collect();
589 Value::List(vals)
590 }
591}
592
593fn resolve_name_to_col(name: &str, field_names: &[String]) -> Option<usize> {
596 if let Some(idx) = field_names.iter().position(|n| n == name) {
597 return Some(idx);
598 }
599 if let Ok(idx) = name.parse::<usize>() {
600 if idx < field_names.len() {
601 return Some(idx);
602 }
603 }
604 let dot_name = format!(".{}", name);
605 if let Some(idx) = field_names.iter().position(|n| n.ends_with(&dot_name)) {
606 return Some(idx);
607 }
608 None
609}
610
611pub fn resolve_agg_col_indices(agg_expressions: &[Vec<Expression>], field_names: &[String]) -> Vec<Option<usize>> {
614 agg_expressions
615 .iter()
616 .map(|args| {
617 for expr in args {
618 match expr {
619 Expression::Variable(name) => {
620 if let Some(idx) = resolve_name_to_col(name, field_names) {
621 return Some(idx);
622 }
623 }
624 Expression::PropertyAccess(base, prop) => {
625 if let Expression::Variable(prefix) = base.as_ref() {
626 let qualified = format!("{}.{}", prefix, prop);
628 if let Some(idx) = field_names.iter().position(|n| n == &qualified) {
629 return Some(idx);
630 }
631 let dot_prop = format!(".{}", prop);
633 if let Some(idx) = field_names.iter().position(|n| n.ends_with(&dot_prop)) {
634 return Some(idx);
635 }
636 }
637 }
638 Expression::Star => return None,
639 _ => {}
640 }
641 }
642 None
643 })
644 .collect()
645}
646
647pub fn resolve_group_by_indices(group_by: &[Expression], field_names: &[String]) -> Vec<u32> {
649 group_by
650 .iter()
651 .map(|expr| match expr {
652 Expression::Variable(name) => resolve_name_to_col(name, field_names).unwrap_or(0) as u32,
653 Expression::PropertyAccess(base, prop) => {
654 if let Expression::Variable(prefix) = base.as_ref() {
655 let qualified = format!("{}.{}", prefix, prop);
656 if let Some(idx) = field_names.iter().position(|n| n == &qualified) {
657 return idx as u32;
658 }
659 let dot_prop = format!(".{}", prop);
660 if let Some(idx) = field_names.iter().position(|n| n.ends_with(&dot_prop)) {
661 return idx as u32;
662 }
663 }
664 0
665 }
666 _ => 0,
667 })
668 .collect()
669}
670
671pub fn update_states_row(
673 states: &mut [AggValueState],
674 chunk: &DataChunk,
675 funcs: &[AggregateFunction],
676 col_indices: &[Option<usize>],
677 row: usize,
678) {
679 for (i, state) in states.iter_mut().enumerate() {
680 if matches!(funcs[i], AggregateFunction::CountStar) {
681 if let AggValueState::Count(n) = state {
682 *n += 1;
683 }
684 continue;
685 }
686 if col_indices.get(i).copied().flatten().is_none() && matches!(funcs[i], AggregateFunction::Count) {
687 if let AggValueState::Count(n) = state {
688 *n += 1;
689 }
690 continue;
691 }
692 let col_idx = col_indices
693 .get(i)
694 .copied()
695 .flatten()
696 .unwrap_or_else(|| i.min(chunk.fields.len().saturating_sub(1)));
697 let val = chunk
698 .fields
699 .get(col_idx)
700 .map(|_| chunk.get_value(col_idx, row))
701 .unwrap_or(Some(Value::Null))
702 .unwrap_or(Value::Null);
703 state.update(&val);
704 }
705}
706
707pub fn update_states_row_distinct(
714 states: &mut [AggValueState],
715 seen: &mut [Vec<Value>],
716 chunk: &DataChunk,
717 funcs: &[AggregateFunction],
718 col_indices: &[Option<usize>],
719 distinct_flags: &[bool],
720 row: usize,
721) {
722 for (i, state) in states.iter_mut().enumerate() {
723 if matches!(funcs[i], AggregateFunction::CountStar) {
724 if let AggValueState::Count(n) = state {
725 *n += 1;
726 }
727 continue;
728 }
729 if col_indices.get(i).copied().flatten().is_none() && matches!(funcs[i], AggregateFunction::Count) {
730 if let AggValueState::Count(n) = state {
731 *n += 1;
732 }
733 continue;
734 }
735 let col_idx = col_indices
736 .get(i)
737 .copied()
738 .flatten()
739 .unwrap_or_else(|| i.min(chunk.fields.len().saturating_sub(1)));
740 let val = chunk
741 .fields
742 .get(col_idx)
743 .map(|_| chunk.get_value(col_idx, row))
744 .unwrap_or(Some(Value::Null))
745 .unwrap_or(Value::Null);
746 if distinct_flags[i] {
747 if matches!(val, Value::Null) {
750 continue;
751 }
752 let seen_list = &mut seen[i];
753 if seen_list.contains(&val) {
754 continue;
755 }
756 seen_list.push(val.clone());
757 }
758 state.update(&val);
759 }
760}
761
762fn arrow_scalar_agg(func: &AggregateFunction, chunks: &[DataChunk], col_idx: usize) -> Value {
765 let all_values: Vec<Value> = chunks
767 .iter()
768 .flat_map(|c| {
769 if col_idx >= c.fields.len() {
770 return Vec::new();
771 }
772 let field = &c.fields[col_idx];
773 let rows = if c.sel_vector.is_some() {
774 c.iter_rows().collect::<Vec<_>>()
775 } else {
776 (0..c.size).collect::<Vec<_>>()
777 };
778 rows.into_iter()
779 .filter_map(|row| {
780 if field.is_null(row) {
781 None
782 } else {
783 c.get_value(col_idx, row)
784 }
785 })
786 .collect::<Vec<_>>()
787 })
788 .collect();
789
790 if all_values.is_empty() {
791 return match func {
792 AggregateFunction::Sum | AggregateFunction::Avg => Value::Null,
793 AggregateFunction::Min | AggregateFunction::Max => Value::Null,
794 _ => Value::Null,
795 };
796 }
797
798 if let Some(arr) = values_to_prim_array(&all_values) {
800 return match func {
801 AggregateFunction::Sum => compute_sum(&arr),
802 AggregateFunction::Min => compute_min(&arr),
803 AggregateFunction::Max => compute_max(&arr),
804 AggregateFunction::Avg => {
805 let sum_val = compute_sum_f64(&arr);
806 let count = non_null_count(chunks, col_idx);
807 if count == 0 {
808 Value::Null
809 } else {
810 Value::Double(sum_val / count as f64)
811 }
812 }
813 _ => Value::Null,
814 };
815 }
816
817 let mut state = AggValueState::new(func);
819 for val in &all_values {
820 state.update(val);
821 }
822 state.finalize()
823}
824
825enum PrimArray {
826 I64(arrow::array::PrimitiveArray<Int64Type>),
827 I32(arrow::array::PrimitiveArray<Int32Type>),
828 F64(arrow::array::PrimitiveArray<Float64Type>),
829}
830
831fn values_to_prim_array(vals: &[Value]) -> Option<PrimArray> {
832 match &vals[0] {
833 Value::Int64(_) => {
834 let mut b = arrow::array::Int64Builder::with_capacity(vals.len());
835 for v in vals {
836 match v {
837 Value::Int64(n) => b.append_value(*n),
838 Value::Null => b.append_null(),
839 _ => return None,
840 }
841 }
842 Some(PrimArray::I64(b.finish()))
843 }
844 Value::Int32(_) => {
845 let mut b = arrow::array::Int32Builder::with_capacity(vals.len());
846 for v in vals {
847 match v {
848 Value::Int32(n) => b.append_value(*n),
849 Value::Null => b.append_null(),
850 _ => return None,
851 }
852 }
853 Some(PrimArray::I32(b.finish()))
854 }
855 Value::Double(_) => {
856 let mut b = arrow::array::Float64Builder::with_capacity(vals.len());
857 for v in vals {
858 match v {
859 Value::Double(n) => b.append_value(*n),
860 Value::Null => b.append_null(),
861 _ => return None,
862 }
863 }
864 Some(PrimArray::F64(b.finish()))
865 }
866 _ => None,
867 }
868}
869
870fn compute_sum(arr: &PrimArray) -> Value {
871 match arr {
872 PrimArray::I64(a) => compute::sum(a).map(Value::Int64).unwrap_or(Value::Null),
873 PrimArray::I32(a) => compute::sum(a).map(|v| Value::Int64(v as i64)).unwrap_or(Value::Null),
874 PrimArray::F64(a) => compute::sum(a).map(Value::Double).unwrap_or(Value::Null),
875 }
876}
877
878fn compute_sum_f64(arr: &PrimArray) -> f64 {
879 match arr {
880 PrimArray::I64(a) => compute::sum::<Int64Type>(a).unwrap_or(0) as f64,
881 PrimArray::I32(a) => compute::sum::<Int32Type>(a).unwrap_or(0) as f64,
882 PrimArray::F64(a) => compute::sum::<Float64Type>(a).unwrap_or(0.0),
883 }
884}
885
886fn compute_min(arr: &PrimArray) -> Value {
887 match arr {
888 PrimArray::I64(a) => compute::min(a).map(Value::Int64).unwrap_or(Value::Null),
889 PrimArray::I32(a) => compute::min(a).map(|v| Value::Int64(v as i64)).unwrap_or(Value::Null),
890 PrimArray::F64(a) => compute::min(a).map(Value::Double).unwrap_or(Value::Null),
891 }
892}
893
894fn compute_max(arr: &PrimArray) -> Value {
895 match arr {
896 PrimArray::I64(a) => compute::max(a).map(Value::Int64).unwrap_or(Value::Null),
897 PrimArray::I32(a) => compute::max(a).map(|v| Value::Int64(v as i64)).unwrap_or(Value::Null),
898 PrimArray::F64(a) => compute::max(a).map(Value::Double).unwrap_or(Value::Null),
899 }
900}
901
902fn non_null_count(chunks: &[DataChunk], col_idx: usize) -> u64 {
903 let mut count = 0u64;
904 for c in chunks {
905 if col_idx >= c.fields.len() {
906 continue;
907 }
908 let field = &c.fields[col_idx];
909 if c.sel_vector.is_some() {
910 for row in c.iter_rows() {
911 if !field.is_null(row) {
912 count += 1;
913 }
914 }
915 } else {
916 count += (field.len() - field.null_count()) as u64;
917 }
918 }
919 count
920}