diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6c279518e..8aeb380de 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1290,6 +1290,8 @@ pub enum Statement { verbose: bool, /// A SQL query that specifies what to explain statement: Box, + /// Optional output format of explain + format: Option, }, /// SAVEPOINT -- define a new savepoint within the current transaction Savepoint { name: Ident }, @@ -1340,6 +1342,7 @@ impl fmt::Display for Statement { verbose, analyze, statement, + format, } => { if *describe_alias { write!(f, "DESCRIBE ")?; @@ -1355,6 +1358,10 @@ impl fmt::Display for Statement { write!(f, "VERBOSE ")?; } + if let Some(format) = format { + write!(f, "FORMAT {} ", format)?; + } + write!(f, "{}", statement) } Statement::Query(s) => write!(f, "{}", s), @@ -2478,6 +2485,24 @@ pub struct Function { pub special: bool, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum AnalyzeFormat { + TEXT, + GRAPHVIZ, + JSON, +} + +impl fmt::Display for AnalyzeFormat { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(match self { + AnalyzeFormat::TEXT => "TEXT", + AnalyzeFormat::GRAPHVIZ => "GRAPHVIZ", + AnalyzeFormat::JSON => "JSON", + }) + } +} + impl fmt::Display for Function { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.special { diff --git a/src/keywords.rs b/src/keywords.rs index 30ec735f7..c1568eb89 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -255,6 +255,7 @@ define_keywords!( GLOBAL, GRANT, GRANTED, + GRAPHVIZ, GROUP, GROUPING, GROUPS, @@ -287,6 +288,7 @@ define_keywords!( ISOYEAR, JAR, JOIN, + JSON, JSONFILE, JULIAN, KEY, diff --git a/src/parser.rs b/src/parser.rs index 968b1a5dd..db7d69c56 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2023,6 +2023,18 @@ impl<'a> Parser<'a> { } } + pub fn parse_analyze_format(&mut self) -> Result { + match self.next_token() { + Token::Word(w) => match w.keyword { + Keyword::TEXT => Ok(AnalyzeFormat::TEXT), + Keyword::GRAPHVIZ => Ok(AnalyzeFormat::GRAPHVIZ), + Keyword::JSON => Ok(AnalyzeFormat::JSON), + _ => self.expected("fileformat", Token::Word(w)), + }, + unexpected => self.expected("fileformat", unexpected), + } + } + pub fn parse_create_view(&mut self, or_replace: bool) -> Result { let materialized = self.parse_keyword(Keyword::MATERIALIZED); self.expect_keyword(Keyword::VIEW)?; @@ -3432,6 +3444,10 @@ impl<'a> Parser<'a> { pub fn parse_explain(&mut self, describe_alias: bool) -> Result { let analyze = self.parse_keyword(Keyword::ANALYZE); let verbose = self.parse_keyword(Keyword::VERBOSE); + let mut format = None; + if self.parse_keyword(Keyword::FORMAT) { + format = Some(self.parse_analyze_format()?); + } if let Some(statement) = self.maybe_parse(|parser| parser.parse_statement()) { Ok(Statement::Explain { @@ -3439,6 +3455,7 @@ impl<'a> Parser<'a> { analyze, verbose, statement: Box::new(statement), + format, }) } else { let table_name = self.parse_object_name()?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index d2d2646cd..abb227dcd 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2657,16 +2657,23 @@ fn parse_scalar_function_in_projection() { } } -fn run_explain_analyze(query: &str, expected_verbose: bool, expected_analyze: bool) { +fn run_explain_analyze( + query: &str, + expected_verbose: bool, + expected_analyze: bool, + expected_format: Option, +) { match verified_stmt(query) { Statement::Explain { describe_alias: _, analyze, verbose, statement, + format, } => { assert_eq!(verbose, expected_verbose); assert_eq!(analyze, expected_analyze); + assert_eq!(format, expected_format); assert_eq!("SELECT sqrt(id) FROM foo", statement.to_string()); } _ => panic!("Unexpected Statement, must be Explain"), @@ -2693,15 +2700,47 @@ fn parse_explain_table() { #[test] fn parse_explain_analyze_with_simple_select() { // Describe is an alias for EXPLAIN - run_explain_analyze("DESCRIBE SELECT sqrt(id) FROM foo", false, false); + run_explain_analyze("DESCRIBE SELECT sqrt(id) FROM foo", false, false, None); - run_explain_analyze("EXPLAIN SELECT sqrt(id) FROM foo", false, false); - run_explain_analyze("EXPLAIN VERBOSE SELECT sqrt(id) FROM foo", true, false); - run_explain_analyze("EXPLAIN ANALYZE SELECT sqrt(id) FROM foo", false, true); + run_explain_analyze("EXPLAIN SELECT sqrt(id) FROM foo", false, false, None); + run_explain_analyze( + "EXPLAIN VERBOSE SELECT sqrt(id) FROM foo", + true, + false, + None, + ); + run_explain_analyze( + "EXPLAIN ANALYZE SELECT sqrt(id) FROM foo", + false, + true, + None, + ); run_explain_analyze( "EXPLAIN ANALYZE VERBOSE SELECT sqrt(id) FROM foo", true, true, + None, + ); + + run_explain_analyze( + "EXPLAIN ANALYZE FORMAT GRAPHVIZ SELECT sqrt(id) FROM foo", + false, + true, + Some(AnalyzeFormat::GRAPHVIZ), + ); + + run_explain_analyze( + "EXPLAIN ANALYZE VERBOSE FORMAT JSON SELECT sqrt(id) FROM foo", + true, + true, + Some(AnalyzeFormat::JSON), + ); + + run_explain_analyze( + "EXPLAIN VERBOSE FORMAT TEXT SELECT sqrt(id) FROM foo", + true, + false, + Some(AnalyzeFormat::TEXT), ); }