1pub mod dialect;
25pub mod parsers;
26#[cfg(feature = "perf-baseline")]
27pub mod perf_baseline;
28#[cfg(feature = "explain-analyzer")]
29pub mod regression;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ExplainDialect {
34 MySql,
36 Postgres,
38 Sqlite,
40 Oracle,
42 Mssql,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48#[cfg_attr(
49 feature = "explain-analyzer",
50 derive(serde::Serialize, serde::Deserialize)
51)]
52pub enum ScanType {
53 FullTable,
55 IndexRange,
57 IndexLookup,
59 UniqueLookup,
61 Other,
63}
64
65impl ScanType {
66 pub fn is_full_table_scan(&self) -> bool {
68 matches!(self, ScanType::FullTable)
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
74#[cfg_attr(
75 feature = "explain-analyzer",
76 derive(serde::Serialize, serde::Deserialize)
77)]
78pub struct ExplainPlan {
79 pub scan_type: ScanType,
81 pub table: String,
83 pub index: Option<String>,
85 pub rows: u64,
87 pub extra: Vec<String>,
89}
90
91impl ExplainPlan {
92 pub fn missing_index(&self, row_threshold: u64) -> bool {
94 self.index.is_none() && (self.scan_type.is_full_table_scan() || self.rows >= row_threshold)
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub enum ExplainError {
101 Unparseable { reason: String },
103 UnsupportedDialect,
105}
106
107impl std::fmt::Display for ExplainError {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 ExplainError::Unparseable { reason } => {
111 write!(f, "unparseable explain output: {reason}")
112 }
113 ExplainError::UnsupportedDialect => {
114 write!(f, "unsupported dialect for explain parsing")
115 }
116 }
117 }
118}
119
120impl std::error::Error for ExplainError {}
121
122pub trait ExplainParser: Send + Sync + std::fmt::Debug {
124 fn parse(&self, raw: &str) -> Result<ExplainPlan, ExplainError>;
126}
127
128pub fn parser_for(dialect: ExplainDialect) -> Result<Box<dyn ExplainParser>, ExplainError> {
130 match dialect {
131 ExplainDialect::MySql => Ok(Box::new(parsers::mysql::MySqlParser)),
132 ExplainDialect::Postgres => Ok(Box::new(parsers::postgres::PostgresParser)),
133 ExplainDialect::Sqlite => Ok(Box::new(parsers::sqlite::SqliteParser)),
134 ExplainDialect::Oracle => Ok(Box::new(parsers::oracle::OracleParser)),
135 ExplainDialect::Mssql => Ok(Box::new(parsers::mssql::MssqlParser)),
136 }
137}