Skip to content

Commit aca82f5

Browse files
fix truncate parsing to capture multiple tables (#2048)
* fix truncate parsing to capture multiple tables * followup fixes including adding a lookahead * replacng tabs with spaces * increasing lookahead * fixing after getting maven working locally
1 parent 5dcd5bf commit aca82f5

File tree

5 files changed

+181
-19
lines changed

5 files changed

+181
-19
lines changed

src/main/java/net/sf/jsqlparser/statement/truncate/Truncate.java

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

12+
import static java.util.stream.Collectors.joining;
13+
14+
import java.util.List;
1215
import net.sf.jsqlparser.schema.Table;
1316
import net.sf.jsqlparser.statement.Statement;
1417
import net.sf.jsqlparser.statement.StatementVisitor;
@@ -19,6 +22,7 @@ public class Truncate implements Statement {
1922
boolean tableToken; // to support TRUNCATE without TABLE
2023
boolean only; // to support TRUNCATE with ONLY
2124
private Table table;
25+
private List<Table> tables;
2226

2327
@Override
2428
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
@@ -29,10 +33,18 @@ public Table getTable() {
2933
return table;
3034
}
3135

36+
public List<Table> getTables() {
37+
return tables;
38+
}
39+
3240
public void setTable(Table table) {
3341
this.table = table;
3442
}
3543

44+
public void setTables(List<Table> tables) {
45+
this.tables = tables;
46+
}
47+
3648
public boolean getCascade() {
3749
return cascade;
3850
}
@@ -52,8 +64,13 @@ public String toString() {
5264
sb.append(" ONLY");
5365
}
5466
sb.append(" ");
55-
sb.append(table);
56-
67+
if (tables != null && !tables.isEmpty()) {
68+
sb.append(tables.stream()
69+
.map(Table::toString)
70+
.collect(joining(", ")));
71+
} else {
72+
sb.append(table);
73+
}
5774
if (cascade) {
5875
sb.append(" CASCADE");
5976
}
@@ -86,6 +103,11 @@ public Truncate withTable(Table table) {
86103
return this;
87104
}
88105

106+
public Truncate withTables(List<Table> tables) {
107+
this.setTables(tables);
108+
return this;
109+
}
110+
89111
public Truncate withCascade(boolean cascade) {
90112
this.setCascade(cascade);
91113
return this;

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

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

12+
import static java.util.stream.Collectors.joining;
13+
1214
import java.lang.reflect.InvocationTargetException;
1315
import java.util.stream.Collectors;
1416

17+
import net.sf.jsqlparser.schema.Table;
1518
import net.sf.jsqlparser.statement.Block;
1619
import net.sf.jsqlparser.statement.Commit;
1720
import net.sf.jsqlparser.statement.CreateFunctionalStatement;
@@ -180,8 +183,13 @@ public <S> StringBuilder visit(Truncate truncate, S context) {
180183
buffer.append(" ONLY");
181184
}
182185
buffer.append(" ");
183-
buffer.append(truncate.getTable());
184-
186+
if (truncate.getTables() != null && !truncate.getTables().isEmpty()) {
187+
buffer.append(truncate.getTables().stream()
188+
.map(Table::toString)
189+
.collect(joining(", ")));
190+
} else {
191+
buffer.append(truncate.getTable());
192+
}
185193
if (truncate.getCascade()) {
186194
buffer.append(" CASCADE");
187195
}

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6617,6 +6617,9 @@ Truncate Truncate():
66176617
{
66186618
Truncate truncate = new Truncate();
66196619
Table table;
6620+
List<Table> tables = new ArrayList<Table>();
6621+
boolean only = false;
6622+
boolean cascade = false;
66206623
}
66216624
{
66226625
/**
@@ -6627,14 +6630,24 @@ Truncate Truncate():
66276630
* [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
66286631
*
66296632
*/
6630-
<K_TRUNCATE> [LOOKAHEAD(2) <K_TABLE> {truncate.setTableToken(true);}] [<K_ONLY> {truncate.setOnly(true);}]
6631-
table=Table() { truncate.setTable(table); truncate.setCascade(false); } [ <K_CASCADE> {truncate.setCascade(true);} ]
6632-
{
6633-
return truncate;
6633+
<K_TRUNCATE>
6634+
[LOOKAHEAD(2) <K_TABLE> {truncate.setTableToken(true);}]
6635+
[<K_ONLY> { only = true; }]
6636+
table=Table() { tables.add(table); } (LOOKAHEAD(2) "," table=Table() { tables.add(table); } )*
6637+
[<K_CASCADE> { cascade = true; }]
6638+
{
6639+
if (only && tables.size() > 1 ) {
6640+
throw new ParseException("Cannot TRUNCATE ONLY with multiple tables");
6641+
} else {
6642+
return truncate
6643+
.withTables(tables)
6644+
.withTable(table)
6645+
.withOnly(only)
6646+
.withCascade(cascade);
6647+
}
66346648
}
66356649
}
66366650

6637-
66386651
AlterExpression.ColumnDataType AlterExpressionColumnDataType():
66396652
{
66406653
String columnName = null;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2019 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.truncate;
11+
12+
import static net.sf.jsqlparser.test.TestUtils.assertDeparse;
13+
import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertNull;
16+
import static org.junit.jupiter.api.Assertions.assertThrows;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
19+
import java.io.StringReader;
20+
import java.util.List;
21+
import net.sf.jsqlparser.JSQLParserException;
22+
import net.sf.jsqlparser.parser.CCJSqlParserManager;
23+
import net.sf.jsqlparser.schema.Table;
24+
import org.junit.jupiter.api.Test;
25+
26+
public class TruncateMultipleTablesTest {
27+
28+
private CCJSqlParserManager parserManager = new CCJSqlParserManager();
29+
30+
@Test
31+
public void testTruncate2Tables() throws Exception {
32+
String statement = "TRUncATE TABLE myschema.mytab, myschema2.mytab2";
33+
Truncate truncate = (Truncate) parserManager.parse(new StringReader(statement));
34+
assertEquals("myschema2", truncate.getTable().getSchemaName());
35+
assertEquals("myschema2.mytab2", truncate.getTable().getFullyQualifiedName());
36+
assertEquals(statement.toUpperCase(), truncate.toString().toUpperCase());
37+
assertEquals("myschema.mytab", truncate.getTables().get(0).getFullyQualifiedName());
38+
assertEquals("myschema2.mytab2", truncate.getTables().get(1).getFullyQualifiedName());
39+
40+
statement = "TRUncATE TABLE mytab, my2ndtab";
41+
String toStringStatement = "TRUncATE TABLE mytab, my2ndtab";
42+
truncate = (Truncate) parserManager.parse(new StringReader(statement));
43+
assertEquals("my2ndtab", truncate.getTable().getName());
44+
assertEquals(toStringStatement.toUpperCase(), truncate.toString().toUpperCase());
45+
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
46+
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());
47+
48+
statement = "TRUNCATE TABLE mytab, my2ndtab CASCADE";
49+
truncate = (Truncate) parserManager.parse(new StringReader(statement));
50+
assertNull(truncate.getTables().get(0).getSchemaName());
51+
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
52+
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());
53+
assertTrue(truncate.getCascade());
54+
assertEquals(statement, truncate.toString());
55+
}
56+
57+
@Test
58+
public void testTruncatePostgresqlWithoutTableNames() throws Exception {
59+
String statement = "TRUncATE myschema.mytab, myschema2.mytab2";
60+
Truncate truncate = (Truncate) parserManager.parse(new StringReader(statement));
61+
assertEquals("myschema2", truncate.getTable().getSchemaName());
62+
assertEquals("myschema2.mytab2", truncate.getTable().getFullyQualifiedName());
63+
assertEquals(statement.toUpperCase(), truncate.toString().toUpperCase());
64+
assertEquals("myschema.mytab", truncate.getTables().get(0).getFullyQualifiedName());
65+
assertEquals("myschema2.mytab2", truncate.getTables().get(1).getFullyQualifiedName());
66+
67+
statement = "TRUncATE mytab, my2ndtab";
68+
String toStringStatement = "TRUncATE mytab, my2ndtab";
69+
truncate = (Truncate) parserManager.parse(new StringReader(statement));
70+
assertEquals("my2ndtab", truncate.getTable().getName());
71+
assertEquals(toStringStatement.toUpperCase(), truncate.toString().toUpperCase());
72+
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
73+
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());
74+
75+
statement = "TRUNCATE mytab, my2ndtab CASCADE";
76+
truncate = (Truncate) parserManager.parse(new StringReader(statement));
77+
assertNull(truncate.getTables().get(0).getSchemaName());
78+
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
79+
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());
80+
assertTrue(truncate.getCascade());
81+
assertEquals(statement, truncate.toString());
82+
}
83+
84+
@Test
85+
public void testTruncateDeparse() throws JSQLParserException {
86+
String statement = "TRUNCATE TABLE foo, bar";
87+
assertSqlCanBeParsedAndDeparsed(statement);
88+
assertDeparse(new Truncate()
89+
.withTables(List.of(new Table("foo"), new Table("bar")))
90+
.withTableToken(true), statement);
91+
}
92+
93+
@Test
94+
public void testTruncateCascadeDeparse() throws JSQLParserException {
95+
String statement = "TRUNCATE TABLE foo, bar CASCADE";
96+
assertSqlCanBeParsedAndDeparsed(statement);
97+
assertDeparse(new Truncate()
98+
.withTables(List.of(new Table("foo"), new Table("bar")))
99+
.withTableToken(true)
100+
.withCascade(true), statement);
101+
}
102+
103+
@Test
104+
public void testTruncateDoesNotAllowOnlyWithMultipleTables() {
105+
String statement = "TRUNCATE TABLE ONLY foo, bar";
106+
assertThrows(JSQLParserException.class,
107+
() -> parserManager.parse(new StringReader(statement)));
108+
}
109+
110+
}

src/test/java/net/sf/jsqlparser/statement/truncate/TruncateTest.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,28 +71,37 @@ public void testTruncateDeparse() throws JSQLParserException {
7171
String statement = "TRUNCATE TABLE foo";
7272
assertSqlCanBeParsedAndDeparsed(statement);
7373
assertDeparse(new Truncate()
74-
.withTable(new Table("foo"))
75-
.withTableToken(true), statement);
74+
.withTable(new Table("foo"))
75+
.withTableToken(true), statement);
7676
}
7777

7878
@Test
7979
public void testTruncateCascadeDeparse() throws JSQLParserException {
8080
String statement = "TRUNCATE TABLE foo CASCADE";
8181
assertSqlCanBeParsedAndDeparsed(statement);
8282
assertDeparse(new Truncate()
83-
.withTable(new Table("foo"))
84-
.withTableToken(true)
85-
.withCascade(true), statement);
83+
.withTable(new Table("foo"))
84+
.withTableToken(true)
85+
.withCascade(true), statement);
8686
}
8787

8888
@Test
8989
public void testTruncateOnlyDeparse() throws JSQLParserException {
90-
String statement = "TRUNCATE TABLE ONLY foo CASCADE";
90+
String statement = "TRUNCATE TABLE ONLY foo";
9191
assertSqlCanBeParsedAndDeparsed(statement);
9292
assertDeparse(new Truncate()
93-
.withTable(new Table("foo"))
94-
.withCascade(true)
95-
.withTableToken(true)
96-
.withOnly(true), statement);
93+
.withTable(new Table("foo"))
94+
.withTableToken(true)
95+
.withOnly(true), statement);
96+
}
97+
98+
@Test
99+
public void testTruncateOnlyAndCascadeDeparse() throws JSQLParserException {
100+
String statement = "TRUNCATE ONLY foo CASCADE";
101+
assertSqlCanBeParsedAndDeparsed(statement);
102+
assertDeparse(new Truncate()
103+
.withTable(new Table("foo"))
104+
.withCascade(true)
105+
.withOnly(true), statement);
97106
}
98107
}

0 commit comments

Comments
 (0)