diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a3d52d77..cb066538 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -764,8 +764,18 @@ pub enum Statement { data_types: Vec, statement: Box, }, - /// EXPLAIN + /// EXPLAIN TABLE + /// Note: this is a MySQL-specific statement. See + ExplainTable { + // If true, query used the MySQL `DESCRIBE` alias for explain + describe_alias: bool, + // Table name + table_name: ObjectName, + }, + /// EXPLAIN / DESCRIBE for select_statement Explain { + // If true, query used the MySQL `DESCRIBE` alias for explain + describe_alias: bool, /// Carry out the command and show actual run times and other statistics. analyze: bool, // Display additional information regarding the plan. @@ -781,12 +791,29 @@ impl fmt::Display for Statement { #[allow(clippy::cognitive_complexity)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + Statement::ExplainTable { + describe_alias, + table_name, + } => { + if *describe_alias { + write!(f, "DESCRIBE ")?; + } else { + write!(f, "EXPLAIN ")?; + } + + write!(f, "{}", table_name) + } Statement::Explain { + describe_alias, verbose, analyze, statement, } => { - write!(f, "EXPLAIN ")?; + if *describe_alias { + write!(f, "DESCRIBE ")?; + } else { + write!(f, "EXPLAIN ")?; + } if *analyze { write!(f, "ANALYZE ")?; diff --git a/src/parser.rs b/src/parser.rs index 659f3d1e..ec116e6d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -141,7 +141,8 @@ impl<'a> Parser<'a> { pub fn parse_statement(&mut self) -> Result { match self.next_token() { Token::Word(w) => match w.keyword { - Keyword::EXPLAIN => Ok(self.parse_explain()?), + Keyword::DESCRIBE => Ok(self.parse_explain(true)?), + Keyword::EXPLAIN => Ok(self.parse_explain(false)?), Keyword::ANALYZE => Ok(self.parse_analyze()?), Keyword::SELECT | Keyword::WITH | Keyword::VALUES => { self.prev_token(); @@ -2218,17 +2219,25 @@ impl<'a> Parser<'a> { }) } - pub fn parse_explain(&mut self) -> Result { + 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 statement = Box::new(self.parse_statement()?); + if let Some(statement) = self.maybe_parse(|parser| parser.parse_statement()) { + Ok(Statement::Explain { + describe_alias, + analyze, + verbose, + statement: Box::new(statement), + }) + } else { + let table_name = self.parse_object_name()?; - Ok(Statement::Explain { - analyze, - verbose, - statement, - }) + Ok(Statement::ExplainTable { + describe_alias, + table_name, + }) + } } /// Parse a query expression, i.e. a `SELECT` statement optionally diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5545eaaf..5bc052a3 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1803,6 +1803,7 @@ fn parse_scalar_function_in_projection() { fn run_explain_analyze(query: &str, expected_verbose: bool, expected_analyze: bool) { match verified_stmt(query) { Statement::Explain { + describe_alias: _, analyze, verbose, statement, @@ -1815,8 +1816,28 @@ fn run_explain_analyze(query: &str, expected_verbose: bool, expected_analyze: bo } } +#[test] +fn parse_explain_table() { + let validate_explain = |query: &str, expected_describe_alias: bool| match verified_stmt(query) { + Statement::ExplainTable { + describe_alias, + table_name, + } => { + assert_eq!(describe_alias, expected_describe_alias); + assert_eq!("test_identifier", table_name.to_string()); + } + _ => panic!("Unexpected Statement, must be ExplainTable"), + }; + + validate_explain("EXPLAIN test_identifier", false); + validate_explain("DESCRIBE test_identifier", true); +} + #[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("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);