use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AnnotationKind {
Vectorized,
AggregatePushdown,
MaterializedViewMatch,
StarSchemaOpt,
ColumnarScan,
}
impl AnnotationKind {
pub fn as_str(&self) -> &'static str {
match self {
AnnotationKind::Vectorized => "vectorized",
AnnotationKind::AggregatePushdown => "aggregate-pushdown",
AnnotationKind::MaterializedViewMatch => "mv-match",
AnnotationKind::StarSchemaOpt => "star-schema-opt",
AnnotationKind::ColumnarScan => "columnar-scan",
}
}
}
#[derive(Debug, Clone)]
pub struct PlanAnnotation {
pub kind: AnnotationKind,
pub detail: String,
pub node_id: Option<usize>,
}
#[derive(Debug)]
pub struct OlapPlanAnnotator {
annotations: Vec<PlanAnnotation>,
}
impl OlapPlanAnnotator {
pub fn new() -> Self {
Self {
annotations: Vec::new(),
}
}
pub fn add(&mut self, kind: AnnotationKind, detail: impl Into<String>) {
self.annotations.push(PlanAnnotation {
kind,
detail: detail.into(),
node_id: None,
});
}
pub fn add_with_node(
&mut self,
kind: AnnotationKind,
detail: impl Into<String>,
node_id: usize,
) {
self.annotations.push(PlanAnnotation {
kind,
detail: detail.into(),
node_id: Some(node_id),
});
}
pub fn annotate(&self, explain_output: &str) -> String {
if self.annotations.is_empty() {
return explain_output.to_string();
}
let mut result = explain_output.to_string();
result.push_str("\n-- OLAP Annotations:");
for ann in &self.annotations {
let node_str = ann
.node_id
.map(|id| format!("[node={}]", id))
.unwrap_or_default();
result.push_str(&format!(
"\n-- {}{}: {}",
ann.kind.as_str(),
node_str,
ann.detail
));
}
result
}
pub fn annotations(&self) -> &[PlanAnnotation] {
&self.annotations
}
pub fn summary(&self) -> HashMap<AnnotationKind, usize> {
let mut counts = HashMap::new();
for ann in &self.annotations {
*counts.entry(ann.kind).or_default() += 1;
}
counts
}
}
impl Default for OlapPlanAnnotator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_and_annotate() {
let mut a = OlapPlanAnnotator::new();
a.add(AnnotationKind::Vectorized, "batch size 1024");
a.add(
AnnotationKind::AggregatePushdown,
"SUM(amount) pushed to storage",
);
let result = a.annotate("EXPLAIN SELECT SUM(amount) FROM sales");
assert!(result.contains("vectorized"));
assert!(result.contains("aggregate-pushdown"));
}
#[test]
fn annotate_with_node_id() {
let mut a = OlapPlanAnnotator::new();
a.add_with_node(AnnotationKind::ColumnarScan, "columnar access", 3);
let result = a.annotate("EXPLAIN");
assert!(result.contains("[node=3]"));
}
#[test]
fn no_annotations_returns_original() {
let a = OlapPlanAnnotator::new();
let result = a.annotate("EXPLAIN SELECT * FROM t");
assert_eq!(result, "EXPLAIN SELECT * FROM t");
}
#[test]
fn summary_counts() {
let mut a = OlapPlanAnnotator::new();
a.add(AnnotationKind::Vectorized, "a");
a.add(AnnotationKind::Vectorized, "b");
a.add(AnnotationKind::ColumnarScan, "c");
let s = a.summary();
assert_eq!(s.get(&AnnotationKind::Vectorized), Some(&2));
assert_eq!(s.get(&AnnotationKind::ColumnarScan), Some(&1));
}
}