Skip to content

Commit b0a36ed

Browse files
committed
Add support of EXPLAIN QUERY PLAN syntax for SQLite dialect
This closes apache#1455. For the syntax documentation, please refer to: https://sqlite.org/lang_explain.html
1 parent 8ccb87a commit b0a36ed

File tree

4 files changed

+55
-8
lines changed

4 files changed

+55
-8
lines changed

src/ast/mod.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3111,6 +3111,11 @@ pub enum Statement {
31113111
analyze: bool,
31123112
// Display additional information regarding the plan.
31133113
verbose: bool,
3114+
/// `EXPLAIN QUERY PLAN`
3115+
/// Display the query plan without running the query.
3116+
///
3117+
/// [SQLite](https://sqlite.org/lang_explain.html)
3118+
query_plan: bool,
31143119
/// A SQL query that specifies what to explain
31153120
statement: Box<Statement>,
31163121
/// Optional output format of explain
@@ -3302,22 +3307,27 @@ impl fmt::Display for Statement {
33023307
describe_alias,
33033308
verbose,
33043309
analyze,
3310+
query_plan,
33053311
statement,
33063312
format,
33073313
options,
33083314
} => {
33093315
write!(f, "{describe_alias} ")?;
33103316

3311-
if *analyze {
3312-
write!(f, "ANALYZE ")?;
3313-
}
3317+
if *query_plan {
3318+
write!(f, "QUERY PLAN ")?;
3319+
} else {
3320+
if *analyze {
3321+
write!(f, "ANALYZE ")?;
3322+
}
33143323

3315-
if *verbose {
3316-
write!(f, "VERBOSE ")?;
3317-
}
3324+
if *verbose {
3325+
write!(f, "VERBOSE ")?;
3326+
}
33183327

3319-
if let Some(format) = format {
3320-
write!(f, "FORMAT {format} ")?;
3328+
if let Some(format) = format {
3329+
write!(f, "FORMAT {format} ")?;
3330+
}
33213331
}
33223332

33233333
if let Some(options) = options {

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ define_keywords!(
572572
PERSISTENT,
573573
PIVOT,
574574
PLACING,
575+
PLAN,
575576
PLANS,
576577
POLICY,
577578
PORTION,

src/parser/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8632,6 +8632,7 @@ impl<'a> Parser<'a> {
86328632
) -> Result<Statement, ParserError> {
86338633
let mut analyze = false;
86348634
let mut verbose = false;
8635+
let mut query_plan = false;
86358636
let mut format = None;
86368637
let mut options = None;
86378638

@@ -8642,6 +8643,8 @@ impl<'a> Parser<'a> {
86428643
&& self.peek_token().token == Token::LParen
86438644
{
86448645
options = Some(self.parse_utility_options()?)
8646+
} else if self.parse_keywords(&[Keyword::QUERY, Keyword::PLAN]) {
8647+
query_plan = true;
86458648
} else {
86468649
analyze = self.parse_keyword(Keyword::ANALYZE);
86478650
verbose = self.parse_keyword(Keyword::VERBOSE);
@@ -8658,6 +8661,7 @@ impl<'a> Parser<'a> {
86588661
describe_alias,
86598662
analyze,
86608663
verbose,
8664+
query_plan,
86618665
statement: Box::new(statement),
86628666
format,
86638667
options,

tests/sqlparser_common.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4295,6 +4295,7 @@ fn run_explain_analyze(
42954295
describe_alias: _,
42964296
analyze,
42974297
verbose,
4298+
query_plan,
42984299
statement,
42994300
format,
43004301
options,
@@ -4303,6 +4304,7 @@ fn run_explain_analyze(
43034304
assert_eq!(analyze, expected_analyze);
43044305
assert_eq!(format, expected_format);
43054306
assert_eq!(options, exepcted_options);
4307+
assert!(!query_plan);
43064308
assert_eq!("SELECT sqrt(id) FROM foo", statement.to_string());
43074309
}
43084310
_ => panic!("Unexpected Statement, must be Explain"),
@@ -4417,6 +4419,36 @@ fn parse_explain_analyze_with_simple_select() {
44174419
);
44184420
}
44194421

4422+
#[test]
4423+
fn parse_explain_query_plan() {
4424+
match all_dialects().verified_stmt("EXPLAIN QUERY PLAN SELECT sqrt(id) FROM foo") {
4425+
Statement::Explain {
4426+
query_plan,
4427+
analyze,
4428+
verbose,
4429+
statement,
4430+
..
4431+
} => {
4432+
assert!(query_plan);
4433+
assert!(!analyze);
4434+
assert!(!verbose);
4435+
assert_eq!("SELECT sqrt(id) FROM foo", statement.to_string());
4436+
}
4437+
_ => unreachable!(),
4438+
}
4439+
4440+
// omit QUERY PLAN should be good
4441+
all_dialects().verified_stmt("EXPLAIN SELECT sqrt(id) FROM foo");
4442+
4443+
// missing PLAN keyword should return error
4444+
assert_eq!(
4445+
ParserError::ParserError("Expected: end of statement, found: SELECT".to_string()),
4446+
all_dialects()
4447+
.parse_sql_statements("EXPLAIN QUERY SELECT sqrt(id) FROM foo")
4448+
.unwrap_err()
4449+
);
4450+
}
4451+
44204452
#[test]
44214453
fn parse_named_argument_function() {
44224454
let sql = "SELECT FUN(a => '1', b => '2') FROM foo";

0 commit comments

Comments
 (0)