Skip to content

Commit 30b9684

Browse files
authored
fix(splitter): do not split EXPLAIN from the explained statement (#759)
1 parent 51cb69d commit 30b9684

4 files changed

Lines changed: 77 additions & 1 deletion

File tree

crates/pgls_statement_splitter/src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,39 @@ mod tests {
133133
}
134134
}
135135

136+
#[test]
137+
fn explain_statements() {
138+
let single_statements = vec![
139+
"EXPLAIN SELECT 1;",
140+
"explain select 1;",
141+
"EXPLAIN ANALYZE VERBOSE SELECT * FROM contact;",
142+
"EXPLAIN ANALYSE SELECT * FROM contact;",
143+
"EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) UPDATE contact SET name = 'x';",
144+
"EXPLAIN INSERT INTO contact (id) VALUES (1);",
145+
"EXPLAIN DELETE FROM contact WHERE id = 1;",
146+
"EXPLAIN WITH x AS (SELECT 1) SELECT * FROM x;",
147+
"EXPLAIN CREATE TABLE contact_copy AS SELECT * FROM contact;",
148+
"EXPLAIN VALUES (1);",
149+
];
150+
151+
for stmt in single_statements {
152+
Tester::from(stmt).expect_statements(vec![stmt]);
153+
}
154+
155+
Tester::from("EXPLAIN SELECT 1; SELECT 2;")
156+
.expect_statements(vec!["EXPLAIN SELECT 1;", "SELECT 2;"]);
157+
158+
Tester::from("explain select 1\nselect 2")
159+
.expect_statements(vec!["explain select 1", "select 2"]);
160+
161+
Tester::from("explain select 1\n\nselect 2")
162+
.expect_statements(vec!["explain select 1", "select 2"]);
163+
164+
// explain is an unreserved keyword and remains usable as an identifier
165+
Tester::from("SELECT explain FROM contact;")
166+
.expect_statements(vec!["SELECT explain FROM contact;"]);
167+
}
168+
136169
#[test]
137170
fn begin_commit() {
138171
Tester::from(

crates/pgls_statement_splitter/src/splitter/common.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use super::{
77
Splitter,
88
data::at_statement_start,
99
ddl::{alter, create},
10-
dml::{cte, delete, insert, select, update},
10+
dml::{cte, delete, explain, insert, select, update},
1111
};
1212

1313
#[derive(Debug)]
@@ -58,6 +58,7 @@ pub(crate) fn statement(p: &mut Splitter) -> SplitterResult {
5858
SyntaxKind::DELETE_KW => delete(p),
5959
SyntaxKind::CREATE_KW => create(p),
6060
SyntaxKind::ALTER_KW => alter(p),
61+
SyntaxKind::EXPLAIN_KW => explain(p),
6162
_ => unknown(p, &[]),
6263
};
6364

crates/pgls_statement_splitter/src/splitter/dml.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::splitter::common::SplitterResult;
55
use super::{
66
Splitter,
77
common::{parenthesis, unknown},
8+
ddl::create,
89
};
910

1011
pub(crate) fn cte(p: &mut Splitter) -> SplitterResult {
@@ -39,6 +40,38 @@ pub(crate) fn cte(p: &mut Splitter) -> SplitterResult {
3940
Ok(())
4041
}
4142

43+
/// `EXPLAIN [ ANALYZE ] [ VERBOSE ] <statement>` and
44+
/// `EXPLAIN ( option [, ...] ) <statement>`
45+
///
46+
/// EXPLAIN is a prefix to the statement it explains, so after consuming the
47+
/// prefix we delegate to the splitter function of the inner statement instead
48+
/// of treating its leading keyword as a new statement start.
49+
pub(crate) fn explain(p: &mut Splitter) -> SplitterResult {
50+
p.expect(SyntaxKind::EXPLAIN_KW)?;
51+
52+
// EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON, ...) <statement>
53+
if p.current() == SyntaxKind::L_PAREN {
54+
parenthesis(p)?;
55+
} else {
56+
// legacy EXPLAIN [ANALYZE | ANALYSE] [VERBOSE] <statement>
57+
p.eat(SyntaxKind::ANALYZE_KW)?;
58+
p.eat(SyntaxKind::ANALYSE_KW)?;
59+
p.eat(SyntaxKind::VERBOSE_KW)?;
60+
}
61+
62+
match p.current() {
63+
SyntaxKind::WITH_KW => cte(p),
64+
SyntaxKind::SELECT_KW => select(p),
65+
SyntaxKind::INSERT_KW => insert(p),
66+
SyntaxKind::UPDATE_KW => update(p),
67+
SyntaxKind::DELETE_KW => delete(p),
68+
// EXPLAIN CREATE TABLE AS / CREATE MATERIALIZED VIEW AS
69+
SyntaxKind::CREATE_KW => create(p),
70+
// MERGE, VALUES, EXECUTE, DECLARE
71+
_ => unknown(p, &[]),
72+
}
73+
}
74+
4275
pub(crate) fn select(p: &mut Splitter) -> SplitterResult {
4376
p.expect(SyntaxKind::SELECT_KW)?;
4477

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
explain select 1 from contact;
2+
3+
explain analyze select * from contact where id = 1;
4+
5+
explain (analyze, buffers, format json) update contact set name = 'x' where id = 1;
6+
7+
explain with x as (select 1) select * from x;
8+
9+
select 1;

0 commit comments

Comments
 (0)