Skip to content

Commit 26a1fba

Browse files
hayssamsclaude
andcommitted
Support DuckDB DESCRIBE <query>
DESCRIBE now accepts a query as well as a table, so DESCRIBE SELECT ..., DESCRIBE FROM ... and DESCRIBE WITH ... parse. The described query is reachable from TablesNamesFinder and rendered by the deparser. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
1 parent 34c97e4 commit 26a1fba

5 files changed

Lines changed: 66 additions & 4 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/DescribeStatement.java‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
package net.sf.jsqlparser.statement;
1111

1212
import net.sf.jsqlparser.schema.Table;
13+
import net.sf.jsqlparser.statement.select.Select;
1314

1415
public class DescribeStatement implements Statement {
1516

1617
private Table table;
18+
private Select select;
1719
private String describeType;
1820

1921
public DescribeStatement() {
@@ -32,9 +34,24 @@ public void setTable(Table table) {
3234
this.table = table;
3335
}
3436

37+
/** DuckDB also describes a query: {@code DESCRIBE SELECT ...}. */
38+
public Select getSelect() {
39+
return select;
40+
}
41+
42+
public void setSelect(Select select) {
43+
this.select = select;
44+
}
45+
46+
public DescribeStatement withSelect(Select select) {
47+
setSelect(select);
48+
return this;
49+
}
50+
3551
@Override
3652
public String toString() {
37-
return this.describeType + " " + table.getFullyQualifiedName();
53+
return this.describeType + " "
54+
+ (select != null ? select.toString() : table.getFullyQualifiedName());
3855
}
3956

4057
@Override

‎src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2044,7 +2044,11 @@ public void visit(Values values) {
20442044

20452045
@Override
20462046
public <S> Void visit(DescribeStatement describe, S context) {
2047-
describe.getTable().accept(this, context);
2047+
if (describe.getSelect() != null) {
2048+
describe.getSelect().accept((SelectVisitor<?>) this, context);
2049+
} else {
2050+
describe.getTable().accept(this, context);
2051+
}
20482052
return null;
20492053
}
20502054

‎src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,11 @@ public <S> StringBuilder visit(Comment comment, S context) {
444444
public <S> StringBuilder visit(DescribeStatement describe, S context) {
445445
builder.append(describe.getDescribeType());
446446
builder.append(" ");
447-
builder.append(describe.getTable());
447+
if (describe.getSelect() != null) {
448+
describe.getSelect().accept(this, context);
449+
} else {
450+
builder.append(describe.getTable());
451+
}
448452
return builder;
449453
}
450454

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4373,11 +4373,19 @@ PurgeStatement PurgeStatement(): {
43734373

43744374
DescribeStatement Describe(): {
43754375
Table table;
4376+
Select select;
43764377
DescribeStatement stmt = new DescribeStatement();
43774378
Token tk = null;
43784379
} {
43794380
(tk=<K_DESCRIBE> | tk=<K_DESC>)
4380-
table = Table() { stmt.setDescribeType(tk.image).setTable(table); }
4381+
(
4382+
// DuckDB describes the result of a query, not only a table
4383+
LOOKAHEAD({ getToken(1).kind == K_SELECT || getToken(1).kind == K_WITH
4384+
|| getToken(1).kind == K_FROM || getToken(1).kind == OPENING_BRACKET })
4385+
select = Select() { stmt.setDescribeType(tk.image).setSelect(select); }
4386+
|
4387+
table = Table() { stmt.setDescribeType(tk.image).setTable(table); }
4388+
)
43814389
{
43824390
return stmt;
43834391
}

‎src/test/java/net/sf/jsqlparser/statement/select/DuckDBTest.java‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import net.sf.jsqlparser.schema.Table;
1414
import net.sf.jsqlparser.statement.create.table.CreateTable;
1515
import net.sf.jsqlparser.statement.insert.Insert;
16+
import net.sf.jsqlparser.statement.DescribeStatement;
1617
import net.sf.jsqlparser.test.TestUtils;
1718
import org.junit.jupiter.api.Assertions;
1819
import org.junit.jupiter.api.Test;
@@ -176,4 +177,32 @@ void testUsingSampleReservoir() throws JSQLParserException {
176177
Assertions.assertEquals(SampleClause.SampleMethod.RESERVOIR,
177178
select.getFromItem().getSampleClause().getMethod());
178179
}
180+
181+
@Test
182+
void testDescribeQuery() throws JSQLParserException {
183+
String sqlStr = "DESCRIBE SELECT * FROM t";
184+
DescribeStatement describe =
185+
(DescribeStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
186+
187+
Assertions.assertNotNull(describe.getSelect());
188+
Assertions.assertNull(describe.getTable());
189+
}
190+
191+
@Test
192+
void testDescribeQueryTablesNamesFinder() throws JSQLParserException {
193+
String sqlStr = "DESCRIBE SELECT * FROM ds.t";
194+
195+
Assertions.assertEquals(java.util.Collections.singletonList("ds.t"),
196+
new net.sf.jsqlparser.util.TablesNamesFinder<Void>()
197+
.getTableList(net.sf.jsqlparser.parser.CCJSqlParserUtil.parse(sqlStr)));
198+
}
199+
200+
@Test
201+
void testDescribeTableStillParses() throws JSQLParserException {
202+
String sqlStr = "DESCRIBE t";
203+
DescribeStatement describe =
204+
(DescribeStatement) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
205+
206+
Assertions.assertEquals("t", describe.getTable().getName());
207+
}
179208
}

0 commit comments

Comments
 (0)