Skip to content

Commit

Permalink
fix truncate parsing to capture multiple tables (#2048)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nick-redfearn authored Jul 26, 2024
1 parent 5dcd5bf commit aca82f5
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 19 deletions.
26 changes: 24 additions & 2 deletions src/main/java/net/sf/jsqlparser/statement/truncate/Truncate.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
*/
package net.sf.jsqlparser.statement.truncate;

import static java.util.stream.Collectors.joining;

import java.util.List;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;
Expand All @@ -19,6 +22,7 @@ public class Truncate implements Statement {
boolean tableToken; // to support TRUNCATE without TABLE
boolean only; // to support TRUNCATE with ONLY
private Table table;
private List<Table> tables;

@Override
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
Expand All @@ -29,10 +33,18 @@ public Table getTable() {
return table;
}

public List<Table> getTables() {
return tables;
}

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

public void setTables(List<Table> tables) {
this.tables = tables;
}

public boolean getCascade() {
return cascade;
}
Expand All @@ -52,8 +64,13 @@ public String toString() {
sb.append(" ONLY");
}
sb.append(" ");
sb.append(table);

if (tables != null && !tables.isEmpty()) {
sb.append(tables.stream()
.map(Table::toString)
.collect(joining(", ")));
} else {
sb.append(table);
}
if (cascade) {
sb.append(" CASCADE");
}
Expand Down Expand Up @@ -86,6 +103,11 @@ public Truncate withTable(Table table) {
return this;
}

public Truncate withTables(List<Table> tables) {
this.setTables(tables);
return this;
}

public Truncate withCascade(boolean cascade) {
this.setCascade(cascade);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
*/
package net.sf.jsqlparser.util.deparser;

import static java.util.stream.Collectors.joining;

import java.lang.reflect.InvocationTargetException;
import java.util.stream.Collectors;

import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Block;
import net.sf.jsqlparser.statement.Commit;
import net.sf.jsqlparser.statement.CreateFunctionalStatement;
Expand Down Expand Up @@ -180,8 +183,13 @@ public <S> StringBuilder visit(Truncate truncate, S context) {
buffer.append(" ONLY");
}
buffer.append(" ");
buffer.append(truncate.getTable());

if (truncate.getTables() != null && !truncate.getTables().isEmpty()) {
buffer.append(truncate.getTables().stream()
.map(Table::toString)
.collect(joining(", ")));
} else {
buffer.append(truncate.getTable());
}
if (truncate.getCascade()) {
buffer.append(" CASCADE");
}
Expand Down
23 changes: 18 additions & 5 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -6617,6 +6617,9 @@ Truncate Truncate():
{
Truncate truncate = new Truncate();
Table table;
List<Table> tables = new ArrayList<Table>();
boolean only = false;
boolean cascade = false;
}
{
/**
Expand All @@ -6627,14 +6630,24 @@ Truncate Truncate():
* [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
*
*/
<K_TRUNCATE> [LOOKAHEAD(2) <K_TABLE> {truncate.setTableToken(true);}] [<K_ONLY> {truncate.setOnly(true);}]
table=Table() { truncate.setTable(table); truncate.setCascade(false); } [ <K_CASCADE> {truncate.setCascade(true);} ]
{
return truncate;
<K_TRUNCATE>
[LOOKAHEAD(2) <K_TABLE> {truncate.setTableToken(true);}]
[<K_ONLY> { only = true; }]
table=Table() { tables.add(table); } (LOOKAHEAD(2) "," table=Table() { tables.add(table); } )*
[<K_CASCADE> { cascade = true; }]
{
if (only && tables.size() > 1 ) {
throw new ParseException("Cannot TRUNCATE ONLY with multiple tables");
} else {
return truncate
.withTables(tables)
.withTable(table)
.withOnly(only)
.withCascade(cascade);
}
}
}


AlterExpression.ColumnDataType AlterExpressionColumnDataType():
{
String columnName = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2019 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.truncate;

import static net.sf.jsqlparser.test.TestUtils.assertDeparse;
import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.StringReader;
import java.util.List;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.schema.Table;
import org.junit.jupiter.api.Test;

public class TruncateMultipleTablesTest {

private CCJSqlParserManager parserManager = new CCJSqlParserManager();

@Test
public void testTruncate2Tables() throws Exception {
String statement = "TRUncATE TABLE myschema.mytab, myschema2.mytab2";
Truncate truncate = (Truncate) parserManager.parse(new StringReader(statement));
assertEquals("myschema2", truncate.getTable().getSchemaName());
assertEquals("myschema2.mytab2", truncate.getTable().getFullyQualifiedName());
assertEquals(statement.toUpperCase(), truncate.toString().toUpperCase());
assertEquals("myschema.mytab", truncate.getTables().get(0).getFullyQualifiedName());
assertEquals("myschema2.mytab2", truncate.getTables().get(1).getFullyQualifiedName());

statement = "TRUncATE TABLE mytab, my2ndtab";
String toStringStatement = "TRUncATE TABLE mytab, my2ndtab";
truncate = (Truncate) parserManager.parse(new StringReader(statement));
assertEquals("my2ndtab", truncate.getTable().getName());
assertEquals(toStringStatement.toUpperCase(), truncate.toString().toUpperCase());
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());

statement = "TRUNCATE TABLE mytab, my2ndtab CASCADE";
truncate = (Truncate) parserManager.parse(new StringReader(statement));
assertNull(truncate.getTables().get(0).getSchemaName());
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());
assertTrue(truncate.getCascade());
assertEquals(statement, truncate.toString());
}

@Test
public void testTruncatePostgresqlWithoutTableNames() throws Exception {
String statement = "TRUncATE myschema.mytab, myschema2.mytab2";
Truncate truncate = (Truncate) parserManager.parse(new StringReader(statement));
assertEquals("myschema2", truncate.getTable().getSchemaName());
assertEquals("myschema2.mytab2", truncate.getTable().getFullyQualifiedName());
assertEquals(statement.toUpperCase(), truncate.toString().toUpperCase());
assertEquals("myschema.mytab", truncate.getTables().get(0).getFullyQualifiedName());
assertEquals("myschema2.mytab2", truncate.getTables().get(1).getFullyQualifiedName());

statement = "TRUncATE mytab, my2ndtab";
String toStringStatement = "TRUncATE mytab, my2ndtab";
truncate = (Truncate) parserManager.parse(new StringReader(statement));
assertEquals("my2ndtab", truncate.getTable().getName());
assertEquals(toStringStatement.toUpperCase(), truncate.toString().toUpperCase());
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());

statement = "TRUNCATE mytab, my2ndtab CASCADE";
truncate = (Truncate) parserManager.parse(new StringReader(statement));
assertNull(truncate.getTables().get(0).getSchemaName());
assertEquals("mytab", truncate.getTables().get(0).getFullyQualifiedName());
assertEquals("my2ndtab", truncate.getTables().get(1).getFullyQualifiedName());
assertTrue(truncate.getCascade());
assertEquals(statement, truncate.toString());
}

@Test
public void testTruncateDeparse() throws JSQLParserException {
String statement = "TRUNCATE TABLE foo, bar";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
.withTables(List.of(new Table("foo"), new Table("bar")))
.withTableToken(true), statement);
}

@Test
public void testTruncateCascadeDeparse() throws JSQLParserException {
String statement = "TRUNCATE TABLE foo, bar CASCADE";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
.withTables(List.of(new Table("foo"), new Table("bar")))
.withTableToken(true)
.withCascade(true), statement);
}

@Test
public void testTruncateDoesNotAllowOnlyWithMultipleTables() {
String statement = "TRUNCATE TABLE ONLY foo, bar";
assertThrows(JSQLParserException.class,
() -> parserManager.parse(new StringReader(statement)));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -71,28 +71,37 @@ public void testTruncateDeparse() throws JSQLParserException {
String statement = "TRUNCATE TABLE foo";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
.withTable(new Table("foo"))
.withTableToken(true), statement);
.withTable(new Table("foo"))
.withTableToken(true), statement);
}

@Test
public void testTruncateCascadeDeparse() throws JSQLParserException {
String statement = "TRUNCATE TABLE foo CASCADE";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
.withTable(new Table("foo"))
.withTableToken(true)
.withCascade(true), statement);
.withTable(new Table("foo"))
.withTableToken(true)
.withCascade(true), statement);
}

@Test
public void testTruncateOnlyDeparse() throws JSQLParserException {
String statement = "TRUNCATE TABLE ONLY foo CASCADE";
String statement = "TRUNCATE TABLE ONLY foo";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
.withTable(new Table("foo"))
.withCascade(true)
.withTableToken(true)
.withOnly(true), statement);
.withTable(new Table("foo"))
.withTableToken(true)
.withOnly(true), statement);
}

@Test
public void testTruncateOnlyAndCascadeDeparse() throws JSQLParserException {
String statement = "TRUNCATE ONLY foo CASCADE";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
.withTable(new Table("foo"))
.withCascade(true)
.withOnly(true), statement);
}
}

0 comments on commit aca82f5

Please sign in to comment.