1use super::*;
2
3impl<'host, H: AggregationHost + ?Sized> AggregationExecutor<'host, H> {
4 pub(super) fn storage_aggregate_expr_name(func_name: &str, fc: &FunctionCall) -> String {
5 if fc.arguments.is_empty() || matches!(fc.arguments.first(), Some(Expression::Star(_))) {
6 format!("{}(*)", func_name)
7 } else if let Some(Expression::Identifier(ident)) = fc.arguments.first() {
8 format!("{}({})", func_name, ident.value)
9 } else {
10 format!("{}(?)", func_name)
11 }
12 }
13
14 pub(super) fn storage_aggregate_from_call(
15 fc: &FunctionCall,
16 col_map: &FxHashMap<&str, usize>,
17 ) -> Option<(
18 String,
19 radixdb_storage::mvcc::version_store::AggregateOp,
20 usize,
21 )> {
22 use radixdb_storage::mvcc::version_store::AggregateOp;
23
24 if fc.filter.is_some() || fc.is_distinct || !fc.order_by.is_empty() {
25 return None;
26 }
27
28 let func_name = fc.function.to_uppercase();
29 let (op, col_idx) = match func_name.as_str() {
30 "COUNT" => {
31 if fc.arguments.is_empty()
32 || matches!(fc.arguments.first(), Some(Expression::Star(_)))
33 {
34 (AggregateOp::CountStar, 0)
35 } else if let Some(Expression::Identifier(ident)) = fc.arguments.first() {
36 let col_name = ident.value.to_lowercase();
37 (AggregateOp::Count, *col_map.get(col_name.as_str())?)
38 } else {
39 return None;
40 }
41 }
42 "SUM" => {
43 if let Some(Expression::Identifier(ident)) = fc.arguments.first() {
44 let col_name = ident.value.to_lowercase();
45 (AggregateOp::Sum, *col_map.get(col_name.as_str())?)
46 } else {
47 return None;
48 }
49 }
50 "AVG" => {
51 if let Some(Expression::Identifier(ident)) = fc.arguments.first() {
52 let col_name = ident.value.to_lowercase();
53 (AggregateOp::Avg, *col_map.get(col_name.as_str())?)
54 } else {
55 return None;
56 }
57 }
58 "MIN" => {
59 if let Some(Expression::Identifier(ident)) = fc.arguments.first() {
60 let col_name = ident.value.to_lowercase();
61 (AggregateOp::Min, *col_map.get(col_name.as_str())?)
62 } else {
63 return None;
64 }
65 }
66 "MAX" => {
67 if let Some(Expression::Identifier(ident)) = fc.arguments.first() {
68 let col_name = ident.value.to_lowercase();
69 (AggregateOp::Max, *col_map.get(col_name.as_str())?)
70 } else {
71 return None;
72 }
73 }
74 _ => return None,
75 };
76
77 Some((
78 Self::storage_aggregate_expr_name(&func_name, fc),
79 op,
80 col_idx,
81 ))
82 }
83
84 pub(super) fn collect_storage_aggregate_dependencies(
85 expr: &Expression,
86 col_map: &FxHashMap<&str, usize>,
87 out: &mut Vec<(
88 String,
89 radixdb_storage::mvcc::version_store::AggregateOp,
90 usize,
91 )>,
92 ) -> bool {
93 match expr {
94 Expression::FunctionCall(fc) => {
95 if is_aggregate_function(&fc.function) {
96 if let Some(dep) = Self::storage_aggregate_from_call(fc, col_map) {
97 out.push(dep);
98 true
99 } else {
100 false
101 }
102 } else {
103 fc.arguments
104 .iter()
105 .all(|arg| Self::collect_storage_aggregate_dependencies(arg, col_map, out))
106 && fc.filter.as_ref().is_none_or(|filter| {
107 Self::collect_storage_aggregate_dependencies(filter, col_map, out)
108 })
109 && fc.order_by.iter().all(|order| {
110 Self::collect_storage_aggregate_dependencies(
111 &order.expression,
112 col_map,
113 out,
114 )
115 })
116 }
117 }
118 Expression::Aliased(aliased) => {
119 Self::collect_storage_aggregate_dependencies(&aliased.expression, col_map, out)
120 }
121 Expression::Infix(infix) => {
122 Self::collect_storage_aggregate_dependencies(&infix.left, col_map, out)
123 && Self::collect_storage_aggregate_dependencies(&infix.right, col_map, out)
124 }
125 Expression::Prefix(prefix) => {
126 Self::collect_storage_aggregate_dependencies(&prefix.right, col_map, out)
127 }
128 Expression::Distinct(distinct) => {
129 Self::collect_storage_aggregate_dependencies(&distinct.expr, col_map, out)
130 }
131 Expression::In(in_expr) => {
132 Self::collect_storage_aggregate_dependencies(&in_expr.left, col_map, out)
133 && Self::collect_storage_aggregate_dependencies(&in_expr.right, col_map, out)
134 }
135 Expression::InHashSet(in_expr) => {
136 Self::collect_storage_aggregate_dependencies(&in_expr.column, col_map, out)
137 }
138 Expression::Between(between) => {
139 Self::collect_storage_aggregate_dependencies(&between.expr, col_map, out)
140 && Self::collect_storage_aggregate_dependencies(&between.lower, col_map, out)
141 && Self::collect_storage_aggregate_dependencies(&between.upper, col_map, out)
142 }
143 Expression::Like(like) => {
144 Self::collect_storage_aggregate_dependencies(&like.left, col_map, out)
145 && Self::collect_storage_aggregate_dependencies(&like.pattern, col_map, out)
146 && like.escape.as_ref().is_none_or(|escape| {
147 Self::collect_storage_aggregate_dependencies(escape, col_map, out)
148 })
149 }
150 Expression::List(list) => list
151 .elements
152 .iter()
153 .all(|item| Self::collect_storage_aggregate_dependencies(item, col_map, out)),
154 Expression::ExpressionList(list) => list
155 .expressions
156 .iter()
157 .all(|item| Self::collect_storage_aggregate_dependencies(item, col_map, out)),
158 Expression::Case(case) => {
159 case.value.as_ref().is_none_or(|value| {
160 Self::collect_storage_aggregate_dependencies(value, col_map, out)
161 }) && case.when_clauses.iter().all(|when| {
162 Self::collect_storage_aggregate_dependencies(&when.condition, col_map, out)
163 && Self::collect_storage_aggregate_dependencies(
164 &when.then_result,
165 col_map,
166 out,
167 )
168 }) && case.else_value.as_ref().is_none_or(|else_value| {
169 Self::collect_storage_aggregate_dependencies(else_value, col_map, out)
170 })
171 }
172 Expression::Cast(cast) => {
173 Self::collect_storage_aggregate_dependencies(&cast.expr, col_map, out)
174 }
175 Expression::AllAny(_)
176 | Expression::Exists(_)
177 | Expression::ScalarSubquery(_)
178 | Expression::Window(_)
179 | Expression::TableSource(_)
180 | Expression::JoinSource(_)
181 | Expression::SubquerySource(_)
182 | Expression::ValuesSource(_)
183 | Expression::CteReference(_)
184 | Expression::FunctionTableSource(_) => false,
185 Expression::Identifier(_)
186 | Expression::QualifiedIdentifier(_)
187 | Expression::IntegerLiteral(_)
188 | Expression::FloatLiteral(_)
189 | Expression::StringLiteral(_)
190 | Expression::BooleanLiteral(_)
191 | Expression::NullLiteral(_)
192 | Expression::IntervalLiteral(_)
193 | Expression::BoundValue(_)
194 | Expression::Parameter(_)
195 | Expression::Star(_)
196 | Expression::QualifiedStar(_)
197 | Expression::Default(_) => true,
198 }
199 }
200
201 pub fn try_storage_aggregation(
215 &self,
216 table: &dyn radixdb_storage::traits::Table,
217 stmt: &SelectStatement,
218 ctx: &ExecutionContext,
219 all_columns: &[String],
220 classification: &QueryClassification,
221 ) -> Option<Box<dyn QueryResult>> {
222 use radixdb_sql::ast::GroupByModifier;
223 use radixdb_storage::mvcc::version_store::AggregateOp;
224
225 if !classification.has_group_by {
228 return None;
229 }
230 if classification.where_has_parameters || classification.where_has_subqueries {
231 return None;
232 }
233
234 if !matches!(stmt.group_by.modifier, GroupByModifier::None) {
236 return None;
237 }
238
239 let group_by_cols = &stmt.group_by.columns;
241 if group_by_cols.is_empty() {
242 return None;
243 }
244
245 let col_map: FxHashMap<&str, usize> = all_columns
247 .iter()
248 .enumerate()
249 .map(|(i, name)| (name.as_str(), i))
250 .collect();
251
252 let mut group_by_indices: Vec<usize> = Vec::new();
254 let mut group_by_col_names: Vec<String> = Vec::new();
255 for expr in group_by_cols {
256 match expr {
257 Expression::Identifier(ident) => {
258 let col_name = ident.value.to_lowercase().to_string();
259 if let Some(&idx) = col_map.get(col_name.as_str()) {
260 group_by_indices.push(idx);
261 group_by_col_names.push(col_name);
262 } else {
263 return None; }
265 }
266 _ => return None, }
268 }
269
270 let mut select_group_count = 0;
273 let mut seen_aggregate = false;
274 let mut aggregates: Vec<(AggregateOp, usize)> = Vec::new();
275 let mut agg_aliases: Vec<(String, usize)> = Vec::new();
276 let mut result_columns: Vec<String> = Vec::new();
277
278 for col_expr in &stmt.columns {
279 match col_expr {
280 Expression::Identifier(ident) => {
281 let col_name_lower = ident.value.to_lowercase().to_string();
283 if !group_by_col_names.contains(&col_name_lower) {
284 return None; }
286 if seen_aggregate {
287 return None;
289 }
290 if group_by_col_names.get(select_group_count) != Some(&col_name_lower) {
291 return None; }
293 select_group_count += 1;
294 result_columns.push(ident.value.to_string());
295 }
296 Expression::FunctionCall(fc) => {
297 seen_aggregate = true;
298 let (expr_name, op, col_idx) = Self::storage_aggregate_from_call(fc, &col_map)?;
299 let agg_idx = aggregates.len();
300 agg_aliases.push((expr_name.clone(), group_by_indices.len() + agg_idx));
301 aggregates.push((op, col_idx));
302
303 result_columns.push(expr_name);
304 }
305 Expression::Aliased(aliased) => {
306 if let Expression::Identifier(ident) = aliased.expression.as_ref() {
308 let col_name_lower = ident.value.to_lowercase().to_string();
309 if !group_by_col_names.contains(&col_name_lower) {
310 return None; }
312 if seen_aggregate {
313 return None;
314 }
315 if group_by_col_names.get(select_group_count) != Some(&col_name_lower) {
316 return None; }
318 select_group_count += 1;
319 result_columns.push(aliased.alias.value.to_string());
320 }
321 else if let Expression::FunctionCall(fc) = aliased.expression.as_ref() {
323 seen_aggregate = true;
324 let (expr_name, op, col_idx) =
325 Self::storage_aggregate_from_call(fc, &col_map)?;
326 let agg_idx = aggregates.len();
327 agg_aliases.push((expr_name, group_by_indices.len() + agg_idx));
328 aggregates.push((op, col_idx));
329 result_columns.push(aliased.alias.value.to_string());
330 } else {
331 return None; }
333 }
334 _ => return None, }
336 }
337
338 if select_group_count != group_by_col_names.len() {
341 return None;
342 }
343
344 let public_column_count = result_columns.len();
345 let public_aggregate_count = aggregates.len();
346 for (idx, col_name) in group_by_col_names.iter().enumerate() {
347 if result_columns
348 .get(idx)
349 .is_some_and(|name| !name.eq_ignore_ascii_case(col_name))
350 {
351 agg_aliases.push((col_name.clone(), idx));
352 }
353 }
354 let mut known_agg_aliases: FxHashSet<String> = agg_aliases
355 .iter()
356 .map(|(name, _)| name.to_lowercase())
357 .collect();
358 let mut retained_order_dependencies = Vec::new();
359
360 for order_by in &stmt.order_by {
367 let mut hidden_deps = Vec::new();
368 if !Self::collect_storage_aggregate_dependencies(
369 &order_by.expression,
370 &col_map,
371 &mut hidden_deps,
372 ) {
373 return None;
374 }
375 let has_new_dependency = hidden_deps
376 .iter()
377 .any(|(name, _, _)| !known_agg_aliases.contains(&name.to_lowercase()));
378 if has_new_dependency
379 && !matches!(
380 &order_by.expression,
381 Expression::FunctionCall(function)
382 if is_aggregate_function(&function.function)
383 )
384 {
385 return None;
386 }
387 for (expr_name, op, col_idx) in hidden_deps {
388 if !known_agg_aliases.insert(expr_name.to_lowercase()) {
389 continue;
390 }
391 let agg_idx = aggregates.len();
392 agg_aliases.push((expr_name.clone(), group_by_indices.len() + agg_idx));
393 aggregates.push((op, col_idx));
394 retained_order_dependencies.push(expr_name);
395 }
396 }
397
398 if let Some(having) = stmt.having.as_ref() {
399 let mut hidden_deps = Vec::new();
400 if !Self::collect_storage_aggregate_dependencies(having, &col_map, &mut hidden_deps) {
401 return None;
402 }
403 for (expr_name, op, col_idx) in hidden_deps {
404 if !known_agg_aliases.insert(expr_name.to_lowercase()) {
405 continue;
406 }
407 let agg_idx = aggregates.len();
408 agg_aliases.push((expr_name, group_by_indices.len() + agg_idx));
409 aggregates.push((op, col_idx));
410 }
411 }
412
413 let where_storage_expr = if let Some(where_expr) = stmt.where_clause.as_ref() {
414 let (storage_expr, needs_memory_filter) =
415 crate::pushdown::try_pushdown(where_expr, table.schema(), Some(ctx));
416 if needs_memory_filter {
417 return None;
418 }
419 Some(storage_expr?)
420 } else {
421 None
422 };
423
424 let results = if let Some(where_expr) = where_storage_expr.as_ref() {
426 table.compute_filtered_grouped_aggregates(
427 &group_by_indices,
428 &aggregates,
429 where_expr.as_ref(),
430 )?
431 } else {
432 table.compute_grouped_aggregates(&group_by_indices, &aggregates)?
433 };
434
435 let mut rows = RowVec::new();
437 for (row_id, r) in results.into_iter().enumerate() {
438 let mut values = r.group_values;
439 values.extend(r.aggregate_values);
440 rows.push((
441 row_id as i64,
442 Row::from_compact_vec(CompactVec::from_vec(values)),
443 ));
444 }
445
446 if let Some(having) = stmt.having.as_ref() {
447 let having_filter =
448 RowFilter::with_aliases_and_context(having, &result_columns, &agg_aliases, ctx)
449 .ok()?;
450 let mut filtered_rows = RowVec::new();
451 for (_, row) in rows {
452 if having_filter.matches_checked(&row).ok()? {
453 let row_id = filtered_rows.len() as i64;
454 filtered_rows.push((row_id, row));
455 }
456 }
457 rows = filtered_rows;
458 }
459
460 let retained_column_count = public_column_count + retained_order_dependencies.len();
461 if aggregates.len() > public_aggregate_count + retained_order_dependencies.len() {
462 let mut projected_rows = RowVec::with_capacity(rows.len());
463 for (_, row) in rows {
464 let mut values = CompactVec::with_capacity(retained_column_count);
465 for value in row.as_slice().iter().take(retained_column_count) {
466 values.push(value.clone());
467 }
468 let row_id = projected_rows.len() as i64;
469 projected_rows.push((row_id, Row::from_compact_vec(values)));
470 }
471 rows = projected_rows;
472 }
473
474 result_columns.extend(retained_order_dependencies);
475 result_columns.truncate(retained_column_count);
476 Some(Box::new(ExecutorResult::new(result_columns, rows)))
477 }
478
479 pub(crate) fn try_fast_count_distinct_compiled(
494 &self,
495 stmt: &SelectStatement,
496 compiled: &RwLock<CompiledExecution>,
497 ) -> Option<Result<Box<dyn QueryResult>>> {
498 {
500 let active_tx = match self.host.aggregation_active_transaction().try_lock() {
501 Ok(guard) => guard,
502 Err(_) => return None,
503 };
504 if active_tx.is_some() {
505 return None;
506 }
507 }
508
509 {
511 let compiled_guard = match compiled.read() {
512 Ok(guard) => guard,
513 Err(_) => return None,
514 };
515 match &*compiled_guard {
516 CompiledExecution::NotOptimizable(epoch)
517 if self.host.aggregation_engine().schema_epoch() == *epoch =>
518 {
519 return None
520 }
521 CompiledExecution::CountDistinct(cd) => {
522 if self.host.aggregation_engine().schema_epoch() == cd.cached_epoch {
524 return Some(self.execute_compiled_count_distinct(cd));
525 }
526 }
528 CompiledExecution::NotOptimizable(_) | CompiledExecution::Unknown => {} _ => return None,
531 }
532 }
533
534 self.compile_and_execute_count_distinct(stmt, compiled)
536 }
537
538 pub(super) fn execute_compiled_count_distinct(
540 &self,
541 cd: &CompiledCountDistinct,
542 ) -> Result<Box<dyn QueryResult>> {
543 let tx = self.host.aggregation_engine().begin_transaction()?;
545 let table = tx.get_table(&cd.table_name)?;
546
547 let count = table
548 .get_partition_count(&cd.column_name)
549 .ok_or_else(|| radixdb_core::Error::internal("Index no longer available for column"))?;
550
551 let mut result_values = CompactVec::with_capacity(1);
553 result_values.push(Value::Integer(count as i64));
554 let row = Row::from_compact_vec(result_values);
555 let mut rows = RowVec::with_capacity(1);
556 rows.push((0, row));
557
558 Ok(Box::new(ExecutorResult::new(
559 vec![cd.result_column_name.clone()],
560 rows,
561 )))
562 }
563
564 pub(super) fn compile_and_execute_count_distinct(
566 &self,
567 stmt: &SelectStatement,
568 compiled: &RwLock<CompiledExecution>,
569 ) -> Option<Result<Box<dyn QueryResult>>> {
570 use radixdb_core::SmartString;
571
572 let mut compiled_guard = match compiled.write() {
574 Ok(guard) => guard,
575 Err(_) => return None,
576 };
577
578 match &*compiled_guard {
580 CompiledExecution::NotOptimizable(epoch)
581 if self.host.aggregation_engine().schema_epoch() == *epoch =>
582 {
583 return None
584 }
585 CompiledExecution::CountDistinct(cd) => {
586 if self.host.aggregation_engine().schema_epoch() == cd.cached_epoch {
587 return Some(self.execute_compiled_count_distinct(cd));
588 }
589 }
591 CompiledExecution::NotOptimizable(_) | CompiledExecution::Unknown => {} _ => return None,
593 }
594
595 if stmt.columns.len() != 1 {
603 *compiled_guard =
604 CompiledExecution::NotOptimizable(self.host.aggregation_engine().schema_epoch());
605 return None;
606 }
607
608 if stmt.where_clause.is_some()
610 || !stmt.group_by.columns.is_empty()
611 || stmt.having.is_some()
612 || !stmt.order_by.is_empty()
613 || stmt.limit.is_some()
614 || stmt.offset.is_some()
615 || stmt.with.is_some()
616 || !stmt.set_operations.is_empty()
617 {
618 *compiled_guard =
619 CompiledExecution::NotOptimizable(self.host.aggregation_engine().schema_epoch());
620 return None;
621 }
622
623 let (column_name, result_column_name) = match &stmt.columns[0] {
625 Expression::FunctionCall(func) => {
626 if func.function.to_uppercase() != "COUNT" {
628 *compiled_guard = CompiledExecution::NotOptimizable(
629 self.host.aggregation_engine().schema_epoch(),
630 );
631 return None;
632 }
633 if !func.is_distinct {
636 return None;
637 }
638 if func.arguments.len() != 1 {
639 *compiled_guard = CompiledExecution::NotOptimizable(
640 self.host.aggregation_engine().schema_epoch(),
641 );
642 return None;
643 }
644 let col = match &func.arguments[0] {
646 Expression::Identifier(ident) => ident.value.to_lowercase(),
647 _ => {
648 *compiled_guard = CompiledExecution::NotOptimizable(
649 self.host.aggregation_engine().schema_epoch(),
650 );
651 return None;
652 }
653 };
654 let result_name = format!("COUNT(DISTINCT {})", col);
655 (col, result_name)
656 }
657 Expression::Aliased(aliased) => {
658 match aliased.expression.as_ref() {
660 Expression::FunctionCall(func) => {
661 if func.function.to_uppercase() != "COUNT" {
663 *compiled_guard = CompiledExecution::NotOptimizable(
664 self.host.aggregation_engine().schema_epoch(),
665 );
666 return None;
667 }
668 if !func.is_distinct {
671 return None;
672 }
673 if func.arguments.len() != 1 {
674 *compiled_guard = CompiledExecution::NotOptimizable(
675 self.host.aggregation_engine().schema_epoch(),
676 );
677 return None;
678 }
679 let col = match &func.arguments[0] {
680 Expression::Identifier(ident) => ident.value.to_lowercase(),
681 _ => {
682 *compiled_guard = CompiledExecution::NotOptimizable(
683 self.host.aggregation_engine().schema_epoch(),
684 );
685 return None;
686 }
687 };
688 (col, aliased.alias.value.to_string())
689 }
690 _ => {
691 *compiled_guard = CompiledExecution::NotOptimizable(
692 self.host.aggregation_engine().schema_epoch(),
693 );
694 return None;
695 }
696 }
697 }
698 _ => {
699 *compiled_guard = CompiledExecution::NotOptimizable(
700 self.host.aggregation_engine().schema_epoch(),
701 );
702 return None;
703 }
704 };
705
706 let table_name = match stmt.table_expr.as_deref() {
708 Some(Expression::TableSource(ts)) => {
709 if ts.as_of.is_some() {
710 *compiled_guard = CompiledExecution::NotOptimizable(
711 self.host.aggregation_engine().schema_epoch(),
712 );
713 return None;
714 }
715 ts.name.value_lower.clone()
716 }
717 _ => {
718 *compiled_guard = CompiledExecution::NotOptimizable(
719 self.host.aggregation_engine().schema_epoch(),
720 );
721 return None;
722 }
723 };
724
725 let tx = match self.host.aggregation_engine().begin_transaction() {
727 Ok(tx) => tx,
728 Err(_) => {
729 *compiled_guard = CompiledExecution::NotOptimizable(
730 self.host.aggregation_engine().schema_epoch(),
731 );
732 return None;
733 }
734 };
735
736 let table = match tx.get_table(&table_name) {
737 Ok(t) => t,
738 Err(_) => {
739 *compiled_guard = CompiledExecution::NotOptimizable(
740 self.host.aggregation_engine().schema_epoch(),
741 );
742 return None;
743 }
744 };
745
746 if table.get_partition_count(&column_name).is_none() {
748 *compiled_guard =
749 CompiledExecution::NotOptimizable(self.host.aggregation_engine().schema_epoch());
750 return None;
751 }
752
753 let count = table.get_partition_count(&column_name).unwrap();
755
756 let compiled_cd = CompiledCountDistinct {
758 table_name: SmartString::new(&table_name),
759 column_name: SmartString::new(&column_name),
760 result_column_name: result_column_name.clone(),
761 cached_epoch: self.host.aggregation_engine().schema_epoch(),
762 };
763 *compiled_guard = CompiledExecution::CountDistinct(compiled_cd);
764 drop(compiled_guard);
765
766 let mut result_values = CompactVec::with_capacity(1);
768 result_values.push(Value::Integer(count as i64));
769 let row = Row::from_compact_vec(result_values);
770 let mut rows = RowVec::with_capacity(1);
771 rows.push((0, row));
772
773 Some(Ok(Box::new(ExecutorResult::new(
774 vec![result_column_name],
775 rows,
776 ))))
777 }
778
779 pub(crate) fn try_fast_count_star_compiled(
794 &self,
795 stmt: &SelectStatement,
796 compiled: &RwLock<CompiledExecution>,
797 ) -> Option<Result<Box<dyn QueryResult>>> {
798 {
800 let active_tx = match self.host.aggregation_active_transaction().try_lock() {
801 Ok(guard) => guard,
802 Err(_) => return None,
803 };
804 if active_tx.is_some() {
805 return None;
806 }
807 }
808
809 {
811 let compiled_guard = match compiled.read() {
812 Ok(guard) => guard,
813 Err(_) => return None,
814 };
815 match &*compiled_guard {
816 CompiledExecution::NotOptimizable(epoch)
817 if self.host.aggregation_engine().schema_epoch() == *epoch =>
818 {
819 return None
820 }
821 CompiledExecution::CountStar(cs) => {
822 if self.host.aggregation_engine().schema_epoch() == cs.cached_epoch {
824 return Some(self.execute_compiled_count_star(cs));
825 }
826 }
828 CompiledExecution::NotOptimizable(_) | CompiledExecution::Unknown => {} _ => return None,
831 }
832 }
833
834 self.compile_and_execute_count_star(stmt, compiled)
836 }
837
838 pub(super) fn execute_compiled_count_star(
840 &self,
841 cs: &crate::compiled_plan::CompiledCountStar,
842 ) -> Result<Box<dyn QueryResult>> {
843 let tx = self.host.aggregation_engine().begin_transaction()?;
845 let table = tx.get_table(&cs.table_name)?;
846
847 let count = table.row_count();
848
849 let mut result_values = CompactVec::with_capacity(1);
851 result_values.push(Value::Integer(count as i64));
852 let row = Row::from_compact_vec(result_values);
853 let mut rows = RowVec::with_capacity(1);
854 rows.push((0, row));
855
856 Ok(Box::new(ExecutorResult::new(
857 vec![cs.result_column_name.clone()],
858 rows,
859 )))
860 }
861
862 pub(super) fn compile_and_execute_count_star(
864 &self,
865 stmt: &SelectStatement,
866 compiled: &RwLock<CompiledExecution>,
867 ) -> Option<Result<Box<dyn QueryResult>>> {
868 use crate::compiled_plan::CompiledCountStar;
869 use radixdb_core::SmartString;
870
871 let mut compiled_guard = match compiled.write() {
873 Ok(guard) => guard,
874 Err(_) => return None,
875 };
876
877 match &*compiled_guard {
879 CompiledExecution::NotOptimizable(epoch)
880 if self.host.aggregation_engine().schema_epoch() == *epoch =>
881 {
882 return None
883 }
884 CompiledExecution::CountStar(cs) => {
885 if self.host.aggregation_engine().schema_epoch() == cs.cached_epoch {
886 return Some(self.execute_compiled_count_star(cs));
887 }
888 }
890 CompiledExecution::NotOptimizable(_) | CompiledExecution::Unknown => {} _ => return None,
892 }
893
894 if stmt.columns.len() != 1 {
902 *compiled_guard =
903 CompiledExecution::NotOptimizable(self.host.aggregation_engine().schema_epoch());
904 return None;
905 }
906
907 if stmt.where_clause.is_some()
909 || !stmt.group_by.columns.is_empty()
910 || stmt.having.is_some()
911 || !stmt.order_by.is_empty()
912 || stmt.limit.is_some()
913 || stmt.offset.is_some()
914 || stmt.with.is_some()
915 || !stmt.set_operations.is_empty()
916 {
917 *compiled_guard =
918 CompiledExecution::NotOptimizable(self.host.aggregation_engine().schema_epoch());
919 return None;
920 }
921
922 let result_column_name = match &stmt.columns[0] {
924 Expression::FunctionCall(func) => {
925 if func.function.to_uppercase() != "COUNT" {
927 *compiled_guard = CompiledExecution::NotOptimizable(
928 self.host.aggregation_engine().schema_epoch(),
929 );
930 return None;
931 }
932 if func.is_distinct {
935 return None;
936 }
937 if func.filter.is_some() {
939 *compiled_guard = CompiledExecution::NotOptimizable(
940 self.host.aggregation_engine().schema_epoch(),
941 );
942 return None;
943 }
944 match func.arguments.len() {
946 0 => {
947 "COUNT(*)".to_string()
949 }
950 1 => {
951 match &func.arguments[0] {
952 Expression::Star(_) => "COUNT(*)".to_string(),
953 Expression::IntegerLiteral(lit) => {
954 if lit.value == 1 {
956 "COUNT(1)".to_string()
957 } else {
958 *compiled_guard = CompiledExecution::NotOptimizable(
959 self.host.aggregation_engine().schema_epoch(),
960 );
961 return None;
962 }
963 }
964 _ => {
965 *compiled_guard = CompiledExecution::NotOptimizable(
967 self.host.aggregation_engine().schema_epoch(),
968 );
969 return None;
970 }
971 }
972 }
973 _ => {
974 *compiled_guard = CompiledExecution::NotOptimizable(
975 self.host.aggregation_engine().schema_epoch(),
976 );
977 return None;
978 }
979 }
980 }
981 Expression::Aliased(aliased) => {
982 match aliased.expression.as_ref() {
984 Expression::FunctionCall(func) => {
985 if func.function.to_uppercase() != "COUNT" {
987 *compiled_guard = CompiledExecution::NotOptimizable(
988 self.host.aggregation_engine().schema_epoch(),
989 );
990 return None;
991 }
992 if func.is_distinct {
995 return None;
996 }
997 if func.filter.is_some() {
999 *compiled_guard = CompiledExecution::NotOptimizable(
1000 self.host.aggregation_engine().schema_epoch(),
1001 );
1002 return None;
1003 }
1004 match func.arguments.len() {
1005 0 => aliased.alias.value.to_string(),
1006 1 => match &func.arguments[0] {
1007 Expression::Star(_) => aliased.alias.value.to_string(),
1008 Expression::IntegerLiteral(lit) if lit.value == 1 => {
1009 aliased.alias.value.to_string()
1010 }
1011 Expression::IntegerLiteral(_) => {
1012 *compiled_guard = CompiledExecution::NotOptimizable(
1013 self.host.aggregation_engine().schema_epoch(),
1014 );
1015 return None;
1016 }
1017 _ => {
1018 *compiled_guard = CompiledExecution::NotOptimizable(
1019 self.host.aggregation_engine().schema_epoch(),
1020 );
1021 return None;
1022 }
1023 },
1024 _ => {
1025 *compiled_guard = CompiledExecution::NotOptimizable(
1026 self.host.aggregation_engine().schema_epoch(),
1027 );
1028 return None;
1029 }
1030 }
1031 }
1032 _ => {
1033 *compiled_guard = CompiledExecution::NotOptimizable(
1034 self.host.aggregation_engine().schema_epoch(),
1035 );
1036 return None;
1037 }
1038 }
1039 }
1040 _ => {
1041 *compiled_guard = CompiledExecution::NotOptimizable(
1042 self.host.aggregation_engine().schema_epoch(),
1043 );
1044 return None;
1045 }
1046 };
1047
1048 let table_name = match stmt.table_expr.as_deref() {
1050 Some(Expression::TableSource(ts)) => {
1051 if ts.as_of.is_some() {
1052 *compiled_guard = CompiledExecution::NotOptimizable(
1053 self.host.aggregation_engine().schema_epoch(),
1054 );
1055 return None;
1056 }
1057 ts.name.value_lower.clone()
1058 }
1059 _ => {
1060 *compiled_guard = CompiledExecution::NotOptimizable(
1061 self.host.aggregation_engine().schema_epoch(),
1062 );
1063 return None;
1064 }
1065 };
1066
1067 let tx = match self.host.aggregation_engine().begin_transaction() {
1069 Ok(tx) => tx,
1070 Err(_) => {
1071 *compiled_guard = CompiledExecution::NotOptimizable(
1072 self.host.aggregation_engine().schema_epoch(),
1073 );
1074 return None;
1075 }
1076 };
1077
1078 let table = match tx.get_table(&table_name) {
1079 Ok(t) => t,
1080 Err(_) => {
1081 *compiled_guard = CompiledExecution::NotOptimizable(
1082 self.host.aggregation_engine().schema_epoch(),
1083 );
1084 return None;
1085 }
1086 };
1087
1088 let count = table.row_count();
1090
1091 let compiled_cs = CompiledCountStar {
1093 table_name: SmartString::new(&table_name),
1094 result_column_name: result_column_name.clone(),
1095 cached_epoch: self.host.aggregation_engine().schema_epoch(),
1096 };
1097 *compiled_guard = CompiledExecution::CountStar(compiled_cs);
1098 drop(compiled_guard);
1099
1100 let mut result_values = CompactVec::with_capacity(1);
1102 result_values.push(Value::Integer(count as i64));
1103 let row = Row::from_compact_vec(result_values);
1104 let mut rows = RowVec::with_capacity(1);
1105 rows.push((0, row));
1106
1107 Some(Ok(Box::new(ExecutorResult::new(
1108 vec![result_column_name],
1109 rows,
1110 ))))
1111 }
1112}