From 6c091e1dfd0e34d154c830ffe98f71a5b1ce686e Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Tue, 15 Jul 2025 16:52:33 +0300 Subject: [PATCH 1/3] MySQL: EXPLAIN ANALYZE format type --- src/ast/mod.rs | 25 +++++++++++++++++++++++-- src/parser/mod.rs | 10 +++++++++- tests/sqlparser_common.rs | 9 +++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c2ec8c686..4a12205c2 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,23 @@ 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 { + Keyword(AnalyzeFormat), + 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 +7665,8 @@ pub enum AnalyzeFormat { TEXT, GRAPHVIZ, JSON, + TRADITIONAL, + TREE } impl fmt::Display for AnalyzeFormat { @@ -7656,6 +7675,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 d96a3002e..3bb913118 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 32bc70e65..aa7dc823e 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -5285,6 +5285,15 @@ fn parse_explain_analyze_with_simple_select() { None, ); + run_explain_analyze( + all_dialects(), + "EXPLAIN ANALYZE VERBOSE FORMAT=JSON SELECT sqrt(id) FROM foo", + true, + true, + Some(AnalyzeFormat::JSON), + None, + ); + run_explain_analyze( all_dialects(), "EXPLAIN VERBOSE FORMAT TEXT SELECT sqrt(id) FROM foo", From 4a17153d2ff61210c017f980c1318343eea90880 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Tue, 15 Jul 2025 17:14:28 +0300 Subject: [PATCH 2/3] Fixes --- src/ast/mod.rs | 6 +++--- tests/sqlparser_common.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4a12205c2..64e7b0901 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4494,7 +4494,7 @@ impl fmt::Display for Statement { } if let Some(format) = format { - write!(f, " {format} ")?; + write!(f, "{format} ")?; } if let Some(options) = options { @@ -7646,7 +7646,7 @@ impl fmt::Display for DuplicateTreatment { #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum AnalyzeFormatKind { Keyword(AnalyzeFormat), - Assignment(AnalyzeFormat) + Assignment(AnalyzeFormat), } impl fmt::Display for AnalyzeFormatKind { @@ -7666,7 +7666,7 @@ pub enum AnalyzeFormat { GRAPHVIZ, JSON, TRADITIONAL, - TREE + TREE, } impl fmt::Display for AnalyzeFormat { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index aa7dc823e..4183c5539 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,7 @@ 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, ); @@ -5290,7 +5290,7 @@ fn parse_explain_analyze_with_simple_select() { "EXPLAIN ANALYZE VERBOSE FORMAT=JSON SELECT sqrt(id) FROM foo", true, true, - Some(AnalyzeFormat::JSON), + Some(AnalyzeFormatKind::Assignment(AnalyzeFormat::JSON)), None, ); @@ -5299,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, ); } From 8701bc44a25bc3b746e2dd8e83416df98a8da3ae Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Thu, 17 Jul 2025 15:22:43 +0300 Subject: [PATCH 3/3] Add docs for AnalyzeFormatKind options --- src/ast/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 64e7b0901..1f5311aff 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -7645,7 +7645,9 @@ impl fmt::Display for DuplicateTreatment { #[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), }