From 23f40cdc409708c1d71cd72d9a2c57d8c0d9ad1b Mon Sep 17 00:00:00 2001 From: Yoav Cohen <59807311+yoavcloud@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:41:50 +0300 Subject: [PATCH] MySQL: Support `EXPLAIN ANALYZE` format variants (#1945) --- src/ast/mod.rs | 27 +++++++++++++++++++++++++-- src/parser/mod.rs | 10 +++++++++- tests/sqlparser_common.rs | 17 +++++++++++++---- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c2ec8c68..1f5311af 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4126,7 +4126,7 @@ pub enum Statement { /// A SQL query that specifies what to explain statement: Box, /// Optional output format of explain - format: Option, + format: Option, /// Postgres style utility options, `(analyze, verbose true)` options: Option>, }, @@ -4494,7 +4494,7 @@ impl fmt::Display for Statement { } if let Some(format) = format { - write!(f, "FORMAT {format} ")?; + write!(f, "{format} ")?; } if let Some(options) = options { @@ -7641,6 +7641,25 @@ impl fmt::Display for DuplicateTreatment { } } +#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum AnalyzeFormatKind { + /// e.g. `EXPLAIN ANALYZE FORMAT JSON SELECT * FROM tbl` + Keyword(AnalyzeFormat), + /// e.g. `EXPLAIN ANALYZE FORMAT=JSON SELECT * FROM tbl` + Assignment(AnalyzeFormat), +} + +impl fmt::Display for AnalyzeFormatKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + AnalyzeFormatKind::Keyword(format) => write!(f, "FORMAT {format}"), + AnalyzeFormatKind::Assignment(format) => write!(f, "FORMAT={format}"), + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] @@ -7648,6 +7667,8 @@ pub enum AnalyzeFormat { TEXT, GRAPHVIZ, JSON, + TRADITIONAL, + TREE, } impl fmt::Display for AnalyzeFormat { @@ -7656,6 +7677,8 @@ impl fmt::Display for AnalyzeFormat { AnalyzeFormat::TEXT => "TEXT", AnalyzeFormat::GRAPHVIZ => "GRAPHVIZ", AnalyzeFormat::JSON => "JSON", + AnalyzeFormat::TRADITIONAL => "TRADITIONAL", + AnalyzeFormat::TREE => "TREE", }) } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d96a3002..3bb91311 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5674,6 +5674,14 @@ impl<'a> Parser<'a> { } } + fn parse_analyze_format_kind(&mut self) -> Result { + if self.consume_token(&Token::Eq) { + Ok(AnalyzeFormatKind::Assignment(self.parse_analyze_format()?)) + } else { + Ok(AnalyzeFormatKind::Keyword(self.parse_analyze_format()?)) + } + } + pub fn parse_analyze_format(&mut self) -> Result { let next_token = self.next_token(); match &next_token.token { @@ -11074,7 +11082,7 @@ impl<'a> Parser<'a> { analyze = self.parse_keyword(Keyword::ANALYZE); verbose = self.parse_keyword(Keyword::VERBOSE); if self.parse_keyword(Keyword::FORMAT) { - format = Some(self.parse_analyze_format()?); + format = Some(self.parse_analyze_format_kind()?); } } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 32bc70e6..4183c553 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -5161,7 +5161,7 @@ fn run_explain_analyze( query: &str, expected_verbose: bool, expected_analyze: bool, - expected_format: Option, + expected_format: Option, expected_options: Option>, ) { match dialect.verified_stmt(query) { @@ -5272,7 +5272,7 @@ fn parse_explain_analyze_with_simple_select() { "EXPLAIN ANALYZE FORMAT GRAPHVIZ SELECT sqrt(id) FROM foo", false, true, - Some(AnalyzeFormat::GRAPHVIZ), + Some(AnalyzeFormatKind::Keyword(AnalyzeFormat::GRAPHVIZ)), None, ); @@ -5281,7 +5281,16 @@ fn parse_explain_analyze_with_simple_select() { "EXPLAIN ANALYZE VERBOSE FORMAT JSON SELECT sqrt(id) FROM foo", true, true, - Some(AnalyzeFormat::JSON), + Some(AnalyzeFormatKind::Keyword(AnalyzeFormat::JSON)), + None, + ); + + run_explain_analyze( + all_dialects(), + "EXPLAIN ANALYZE VERBOSE FORMAT=JSON SELECT sqrt(id) FROM foo", + true, + true, + Some(AnalyzeFormatKind::Assignment(AnalyzeFormat::JSON)), None, ); @@ -5290,7 +5299,7 @@ fn parse_explain_analyze_with_simple_select() { "EXPLAIN VERBOSE FORMAT TEXT SELECT sqrt(id) FROM foo", true, false, - Some(AnalyzeFormat::TEXT), + Some(AnalyzeFormatKind::Keyword(AnalyzeFormat::TEXT)), None, ); }