Skip to content

Commit 22f8d8d

Browse files
committed
feat: support EXPLAIN for DML and simplify ExplainStatement
- parse EXPLAIN with SELECT/INSERT/UPDATE/DELETE/MERGE (including WITH) - unify ExplainStatement target to Statement (remove Select-only API) - add regression tests for EXPLAIN DELETE/UPDATE/INSERT
1 parent 4f982e7 commit 22f8d8d

File tree

3 files changed

+73
-26
lines changed

3 files changed

+73
-26
lines changed

src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@
1313
import java.util.LinkedHashMap;
1414
import java.util.List;
1515
import java.util.stream.Collectors;
16-
1716
import net.sf.jsqlparser.schema.Table;
18-
import net.sf.jsqlparser.statement.select.Select;
1917

2018
/**
2119
* An {@code EXPLAIN} statement
2220
*/
2321
public class ExplainStatement implements Statement {
2422
private String keyword;
25-
private Select select;
23+
private Statement statement;
2624
private LinkedHashMap<OptionType, Option> options;
2725
private Table table;
2826

@@ -37,24 +35,17 @@ public ExplainStatement() {
3735
public ExplainStatement(String keyword, Table table) {
3836
this.keyword = keyword;
3937
this.table = table;
40-
this.select = null;
4138
}
4239

43-
public ExplainStatement(String keyword, Select select, List<Option> optionList) {
40+
public ExplainStatement(String keyword, Statement statement, List<Option> optionList) {
4441
this.keyword = keyword;
45-
this.select = select;
46-
this.table = null;
42+
setStatement(statement);
4743

48-
if (optionList != null && !optionList.isEmpty()) {
49-
options = new LinkedHashMap<>();
50-
for (Option o : optionList) {
51-
options.put(o.getType(), o);
52-
}
53-
}
44+
initializeOptions(optionList);
5445
}
5546

56-
public ExplainStatement(Select select) {
57-
this("EXPLAIN", select, null);
47+
public ExplainStatement(Statement statement) {
48+
this("EXPLAIN", statement, null);
5849
}
5950

6051
public Table getTable() {
@@ -63,15 +54,20 @@ public Table getTable() {
6354

6455
public ExplainStatement setTable(Table table) {
6556
this.table = table;
57+
if (table != null) {
58+
this.statement = null;
59+
}
6660
return this;
6761
}
6862

69-
public Select getStatement() {
70-
return select;
63+
public Statement getStatement() {
64+
return statement;
7165
}
7266

73-
public void setStatement(Select select) {
74-
this.select = select;
67+
public ExplainStatement setStatement(Statement statement) {
68+
this.table = null;
69+
this.statement = statement;
70+
return this;
7571
}
7672

7773
public LinkedHashMap<OptionType, Option> getOptions() {
@@ -122,8 +118,8 @@ public String toString() {
122118
}
123119

124120
builder.append(" ");
125-
if (select != null) {
126-
select.appendTo(builder);
121+
if (statement != null) {
122+
builder.append(statement);
127123
}
128124
}
129125

@@ -135,6 +131,15 @@ public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
135131
return statementVisitor.visit(this, context);
136132
}
137133

134+
private void initializeOptions(List<Option> optionList) {
135+
if (optionList != null && !optionList.isEmpty()) {
136+
options = new LinkedHashMap<>();
137+
for (Option o : optionList) {
138+
options.put(o.getType(), o);
139+
}
140+
}
141+
}
142+
138143
public enum OptionType {
139144
ANALYZE, VERBOSE, COSTS, BUFFERS, FORMAT, PLAN, PLAN_FOR;
140145

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2359,7 +2359,8 @@ DescribeStatement Describe(): {
23592359
ExplainStatement Explain():
23602360
{
23612361
Token tk;
2362-
Select select;
2362+
Statement statement;
2363+
List<WithItem<?>> with = null;
23632364
Table table;
23642365
List<ExplainStatement.Option> options;
23652366
ExplainStatement es;
@@ -2369,9 +2370,18 @@ ExplainStatement Explain():
23692370
(
23702371
LOOKAHEAD(3)(
23712372
options= ExplainStatementOptions()
2372-
select = Select( )
2373+
(
2374+
[ LOOKAHEAD(2) with=WithList() ]
2375+
(
2376+
statement = SelectWithWithItems( with )
2377+
| statement = InsertWithWithItems( with )
2378+
| statement = UpdateWithWithItems( with )
2379+
| statement = DeleteWithWithItems( with )
2380+
| statement = Merge( with )
2381+
)
2382+
)
23732383
{
2374-
es = new ExplainStatement(tk.image, select, options);
2384+
es = new ExplainStatement(tk.image, statement, options);
23752385
}
23762386
)
23772387
|

src/test/java/net/sf/jsqlparser/statement/ExplainTest.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
*/
1010
package net.sf.jsqlparser.statement;
1111

12-
import net.sf.jsqlparser.JSQLParserException;
13-
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
1412
import static net.sf.jsqlparser.test.TestUtils.*;
1513
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
import net.sf.jsqlparser.JSQLParserException;
16+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
17+
import net.sf.jsqlparser.statement.delete.Delete;
1618
import org.junit.jupiter.api.Test;
1719

1820
public class ExplainTest {
@@ -72,4 +74,34 @@ public void getOption_returnsValues() throws JSQLParserException {
7274
explain = (ExplainStatement) CCJSqlParserUtil.parse("EXPLAIN SELECT * FROM mytable");
7375
assertThat(explain.getOption(ExplainStatement.OptionType.ANALYZE)).isNull();
7476
}
77+
78+
@Test
79+
public void testDelete() throws JSQLParserException {
80+
assertSqlCanBeParsedAndDeparsed("EXPLAIN DELETE FROM mytable");
81+
}
82+
83+
@Test
84+
public void testUpdate() throws JSQLParserException {
85+
assertSqlCanBeParsedAndDeparsed("EXPLAIN UPDATE mytable SET col = 1");
86+
}
87+
88+
@Test
89+
public void testInsert() throws JSQLParserException {
90+
assertSqlCanBeParsedAndDeparsed("EXPLAIN INSERT INTO mytable (col) VALUES (1)");
91+
}
92+
93+
@Test
94+
public void explainDelete_usesGenericStatementSlot() throws JSQLParserException {
95+
ExplainStatement explain =
96+
(ExplainStatement) CCJSqlParserUtil.parse("EXPLAIN DELETE FROM mytable");
97+
assertThat(explain.getStatement()).isInstanceOf(Delete.class);
98+
}
99+
100+
@Test
101+
public void testDeleteInStatementsList() throws JSQLParserException {
102+
Statements statements = CCJSqlParserUtil.parseStatements("EXPLAIN DELETE FROM mytable;");
103+
assertThat(statements).isNotNull();
104+
assertThat(statements).hasSize(1);
105+
assertThat(statements.get(0)).isInstanceOf(ExplainStatement.class);
106+
}
75107
}

0 commit comments

Comments
 (0)