Skip to content

Fix: DESC and EXPLAIN #1933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,14 @@ public enum Feature {
* @see DescribeStatement
*/
describe,

/**
* SQL "DESC" statement is allowed
*
* @see DescribeStatement
*/
desc,

/**
* SQL "EXPLAIN" statement is allowed
*
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
public class DescribeStatement implements Statement {

private Table table;
private String describeType;

public DescribeStatement() {
// empty constructor
Expand All @@ -33,7 +34,7 @@ public void setTable(Table table) {

@Override
public String toString() {
return "DESCRIBE " + table.getFullyQualifiedName();
return this.describeType + " " + table.getFullyQualifiedName();
}

@Override
Expand All @@ -45,4 +46,13 @@ public DescribeStatement withTable(Table table) {
this.setTable(table);
return this;
}

public String getDescribeType() {
return describeType;
}

public DescribeStatement setDescribeType(String describeType) {
this.describeType = describeType;
return this;
}
}
31 changes: 24 additions & 7 deletions src/main/java/net/sf/jsqlparser/statement/ExplainStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
*/
package net.sf.jsqlparser.statement;

import net.sf.jsqlparser.statement.select.Select;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.Select;

/**
* An {@code EXPLAIN} statement
Expand All @@ -22,11 +22,21 @@ public class ExplainStatement implements Statement {

private Select select;
private LinkedHashMap<OptionType, Option> options;
private Table table;

public ExplainStatement() {
// empty constructor
}

public Table getTable() {
return table;
}

public ExplainStatement setTable(Table table) {
this.table = table;
return this;
}

public ExplainStatement(Select select) {
this.select = select;
}
Expand Down Expand Up @@ -68,14 +78,21 @@ public Option getOption(OptionType optionType) {
@Override
public String toString() {
StringBuilder statementBuilder = new StringBuilder("EXPLAIN");
if (options != null) {
if (table != null) {
statementBuilder.append(" ").append(table);
} else {
if (options != null) {
statementBuilder.append(" ");
statementBuilder.append(options.values().stream().map(Option::formatOption)
.collect(Collectors.joining(" ")));
}

statementBuilder.append(" ");
statementBuilder.append(options.values().stream().map(Option::formatOption)
.collect(Collectors.joining(" ")));
if (select != null) {
statementBuilder.append(select.toString());
}
}

statementBuilder.append(" ");
statementBuilder.append(select.toString());
return statementBuilder.toString();
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,9 @@ public void visit(DescribeStatement describe) {

@Override
public void visit(ExplainStatement explain) {
explain.getStatement().accept((StatementVisitor) this);
if (explain.getStatement() != null) {
explain.getStatement().accept((StatementVisitor) this);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
*/
package net.sf.jsqlparser.util.deparser;

import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import net.sf.jsqlparser.statement.Block;
import net.sf.jsqlparser.statement.Commit;
import net.sf.jsqlparser.statement.CreateFunctionalStatement;
Expand Down Expand Up @@ -59,10 +62,6 @@
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.upsert.Upsert;

import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

public class StatementDeParser extends AbstractDeParser<Statement> implements StatementVisitor {

private final ExpressionDeParser expressionDeParser;
Expand Down Expand Up @@ -347,19 +346,25 @@ public void visit(Comment comment) {

@Override
public void visit(DescribeStatement describe) {
buffer.append("DESCRIBE ");
buffer.append(describe.getDescribeType());
buffer.append(" ");
buffer.append(describe.getTable());
}

@Override
public void visit(ExplainStatement explain) {
buffer.append("EXPLAIN ");
if (explain.getOptions() != null) {
if (explain.getTable() != null) {
buffer.append(explain.getTable());
} else if (explain.getOptions() != null) {
buffer.append(explain.getOptions().values().stream()
.map(ExplainStatement.Option::formatOption).collect(Collectors.joining(" ")));
buffer.append(" ");
}
explain.getStatement().accept(this);
if (explain.getStatement() != null) {
explain.getStatement().accept(this);

}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public enum MySqlVersion implements Version {

// https://dev.mysql.com/doc/refman/8.0/en/describe.html
Feature.describe,
Feature.desc,
// https://dev.mysql.com/doc/refman/8.0/en/explain.html
Feature.explain,
// https://dev.mysql.com/doc/refman/8.0/en/show.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,16 @@ public void visit(Comment comment) {
@Override
public void visit(DescribeStatement describe) {
validateFeature(Feature.describe);
validateFeature(Feature.desc);
validateOptionalFromItem(describe.getTable());
}

@Override
public void visit(ExplainStatement explain) {
validateFeature(Feature.explain);
explain.getStatement().accept(this);
if (explain.getStatement() != null) {
explain.getStatement().accept(this);
}
}


Expand Down
40 changes: 27 additions & 13 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1056,29 +1056,43 @@ PurgeStatement PurgeStatement(): {

DescribeStatement Describe(): {
Table table;
DescribeStatement stmt = new DescribeStatement();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next time, create the statement at the end only after all tokens have parsed and passed, like:

return new Statement().withX(token.image).withY(token.image);

Its not a hard requirement, but in general Objects should be initialized only when needed.

Token tk = null;
} {
<K_DESCRIBE> table = Table()
(tk=<K_DESCRIBE> | tk=<K_DESC>)
table = Table() { stmt.setDescribeType(tk.image).setTable(table); }
{
return new DescribeStatement(table);
return stmt;
}
}

ExplainStatement Explain(): {
Select select;
Table table = null;
List<ExplainStatement.Option> options = null;
ExplainStatement es = new ExplainStatement();
} {
<K_EXPLAIN>
options=ExplainStatementOptions()
select = Select( )
{
ExplainStatement es = new ExplainStatement(select);
if(options != null && !options.isEmpty()) {
for(ExplainStatement.Option o : options) {
es.addOption(o);
}
}
return es;
}
(
LOOKAHEAD(3)(
options=ExplainStatementOptions()
select = Select( )
{
es.setStatement(select);
if (options != null && !options.isEmpty()) {
for(ExplainStatement.Option o : options) {
es.addOption(o);
}
}
}
)
|
(
table=Table( ) { es.setTable(table); }
)

)
{ return es; }
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/test/java/net/sf/jsqlparser/statement/DescribeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
*/
package net.sf.jsqlparser.statement;

import net.sf.jsqlparser.JSQLParserException;
import static net.sf.jsqlparser.test.TestUtils.*;

import net.sf.jsqlparser.JSQLParserException;
import org.junit.jupiter.api.Test;

public class DescribeTest {
Expand All @@ -20,6 +21,12 @@ public void testDescribe() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("DESCRIBE foo.products");
}

@Test
public void testDescribeIssue1931() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("DESC table_name");
assertSqlCanBeParsedAndDeparsed("EXPLAIN table_name");
}

@Test
public void testDescribeIssue1212() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("DESCRIBE file_azbs.productcategory.json");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,19 @@ public void testValidateCreateSchema() throws JSQLParserException {

@Test
public void testValidateCreateSchemaNotAllowed() throws JSQLParserException {
for (String sql : Arrays.asList("CREATE SCHEMA my_schema", "CREATE SCHEMA myschema AUTHORIZATION myauth")) {
for (String sql : Arrays.asList("CREATE SCHEMA my_schema",
"CREATE SCHEMA myschema AUTHORIZATION myauth")) {
validateNotAllowed(sql, 1, 1, FeaturesAllowed.DML, Feature.createSchema);
}
}

@Test
public void testValidateDescNoErrors() throws JSQLParserException {
for (String sql : Arrays.asList("DESC table_name", "EXPLAIN table_name")) {
validateNoErrors(sql, 1, DatabaseType.MYSQL);
}
}

@Test
public void testValidateTruncate() throws JSQLParserException {
validateNoErrors("TRUNCATE TABLE my_table", 1, DatabaseType.DATABASES);
Expand All @@ -53,7 +61,8 @@ public void testValidateBlock() throws JSQLParserException {
@Test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Next time please use a Parametrised Test for this. Loops in Tests are an Anti-Pattern because you won't know which one failed. You can lookup the KeywordTests for getting a template.

public void testValidateComment() throws JSQLParserException {
for (String sql : Arrays.asList("COMMENT ON VIEW myschema.myView IS 'myComment'",
"COMMENT ON COLUMN myTable.myColumn is 'Some comment'", "COMMENT ON TABLE table1 IS 'comment1'")) {
"COMMENT ON COLUMN myTable.myColumn is 'Some comment'",
"COMMENT ON TABLE table1 IS 'comment1'")) {
validateNoErrors(sql, 1, DatabaseType.H2, DatabaseType.ORACLE, DatabaseType.POSTGRESQL);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ explain plan
(select department_id from departments
where location_id = 1700)

--@FAILURE: Encountered unexpected token: "plan" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered unexpected token: "plan" <S_IDENTIFIER> recorded first on Aug 3, 2021, 7:20:08 AM
--@FAILURE: Encountered unexpected token: "set" "SET" recorded first on 2023年12月23日 下午1:38:33