1use std::sync::Arc;
2
3use crate::compiler::ir::{QueryIR, SelectExpr, FilterNode};
4
5#[derive(Debug, Clone)]
10pub struct QueryStats {
11 pub cube: String,
12 pub table: String,
13 pub limit: u32,
14 pub offset: u32,
15 pub select_count: usize,
16 pub aggregate_count: usize,
17 pub has_group_by: bool,
18 pub has_having: bool,
19 pub filter_depth: usize,
20 pub row_count: usize,
21 pub sql_generated: String,
22}
23
24impl QueryStats {
25 pub fn from_ir(ir: &QueryIR, row_count: usize, sql: &str) -> Self {
27 let aggregate_count = ir.selects.iter()
28 .filter(|s| matches!(s, SelectExpr::Aggregate { .. }))
29 .count();
30
31 Self {
32 cube: ir.cube.clone(),
33 table: ir.table.clone(),
34 limit: ir.limit,
35 offset: ir.offset,
36 select_count: ir.selects.len(),
37 aggregate_count,
38 has_group_by: !ir.group_by.is_empty(),
39 has_having: !ir.having.is_empty(),
40 filter_depth: filter_depth(&ir.filters),
41 row_count,
42 sql_generated: sql.to_string(),
43 }
44 }
45}
46
47fn filter_depth(node: &FilterNode) -> usize {
48 match node {
49 FilterNode::Empty | FilterNode::Condition { .. } => 1,
50 FilterNode::And(children) | FilterNode::Or(children) => {
51 1 + children.iter().map(filter_depth).max().unwrap_or(0)
52 }
53 FilterNode::ArrayIncludes { element_conditions, .. } => {
54 1 + element_conditions.iter()
55 .flat_map(|conds| conds.iter().map(filter_depth))
56 .max()
57 .unwrap_or(0)
58 }
59 }
60}
61
62pub type StatsCallback = Arc<dyn Fn(QueryStats) + Send + Sync>;