diff --git a/src/main/java/net/sf/jsqlparser/schema/TableReference.java b/src/main/java/net/sf/jsqlparser/schema/TableReference.java
new file mode 100644
index 0000000000..1584d1dc21
--- /dev/null
+++ b/src/main/java/net/sf/jsqlparser/schema/TableReference.java
@@ -0,0 +1,91 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.schema;
+
+import java.io.Serializable;
+
+/** A relation target with optional PostgreSQL inheritance scope, shared by LOCK and TRUNCATE. */
+public class TableReference implements Serializable {
+ private Table table;
+ private boolean only;
+ private boolean includeDescendants;
+ private boolean parenthesized;
+
+ public TableReference(Table table) {
+ this.table = table;
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public void setTable(Table table) {
+ this.table = table;
+ }
+
+ public boolean isOnly() {
+ return only;
+ }
+
+ public void setOnly(boolean only) {
+ this.only = only;
+ if (only) {
+ includeDescendants = false;
+ } else {
+ parenthesized = false;
+ }
+ }
+
+ public boolean isIncludeDescendants() {
+ return includeDescendants;
+ }
+
+ public void setIncludeDescendants(boolean includeDescendants) {
+ this.includeDescendants = includeDescendants;
+ if (includeDescendants) {
+ only = false;
+ parenthesized = false;
+ }
+ }
+
+ public boolean isParenthesized() {
+ return parenthesized;
+ }
+
+ public void setParenthesized(boolean parenthesized) {
+ this.parenthesized = parenthesized;
+ if (parenthesized) {
+ only = true;
+ includeDescendants = false;
+ }
+ }
+
+ public StringBuilder appendTo(StringBuilder builder) {
+ if (only) {
+ builder.append("ONLY ");
+ }
+ if (parenthesized) {
+ builder.append('(');
+ }
+ builder.append(table.getFullyQualifiedName());
+ if (parenthesized) {
+ builder.append(')');
+ }
+ if (includeDescendants) {
+ builder.append(" *");
+ }
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return appendTo(new StringBuilder()).toString();
+ }
+}
diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java
index ffdcbed52d..e7a592c3db 100644
--- a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java
+++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java
@@ -34,34 +34,20 @@ public enum Scope {
DEFAULT, ONLY, INCLUDING_DESCENDANTS
}
- public static class Target {
- private Table table;
- private Scope scope = Scope.DEFAULT;
-
+ public static class Target extends net.sf.jsqlparser.schema.TableReference {
public Target(Table table) {
- this.table = table;
- }
-
- public Table getTable() {
- return table;
- }
-
- public void setTable(Table table) {
- this.table = table;
+ super(table);
}
public Scope getScope() {
- return scope;
+ return isOnly() ? Scope.ONLY
+ : isIncludeDescendants() ? Scope.INCLUDING_DESCENDANTS : Scope.DEFAULT;
}
public void setScope(Scope scope) {
- this.scope = Objects.requireNonNull(scope);
- }
-
- @Override
- public String toString() {
- return (scope == Scope.ONLY ? "ONLY " : "") + table.getFullyQualifiedName()
- + (scope == Scope.INCLUDING_DESCENDANTS ? " *" : "");
+ Objects.requireNonNull(scope);
+ setOnly(scope == Scope.ONLY);
+ setIncludeDescendants(scope == Scope.INCLUDING_DESCENDANTS);
}
}
diff --git a/src/main/java/net/sf/jsqlparser/statement/truncate/Truncate.java b/src/main/java/net/sf/jsqlparser/statement/truncate/Truncate.java
index 58442d38ee..4ffc75b2fe 100644
--- a/src/main/java/net/sf/jsqlparser/statement/truncate/Truncate.java
+++ b/src/main/java/net/sf/jsqlparser/statement/truncate/Truncate.java
@@ -9,113 +9,186 @@
*/
package net.sf.jsqlparser.statement.truncate;
-import static java.util.stream.Collectors.joining;
-
+import java.util.AbstractList;
+import java.util.ArrayList;
import java.util.List;
import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.schema.TableReference;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;
public class Truncate implements Statement {
+ public enum IdentityOption {
+ RESTART, CONTINUE
+ }
+ public enum DropBehavior {
+ CASCADE, RESTRICT
+ }
- boolean cascade; // to support TRUNCATE TABLE ... CASCADE
- boolean tableToken; // to support TRUNCATE without TABLE
- boolean only; // to support TRUNCATE with ONLY
- private Table table;
- private List
tables;
+ private boolean tableToken;
+ private boolean only;
+ private final List targets = new ArrayList<>();
+ private IdentityOption identityOption;
+ private DropBehavior dropBehavior;
@Override
public T accept(StatementVisitor statementVisitor, S context) {
return statementVisitor.visit(this, context);
}
- public Table getTable() {
- return table;
+ public List getTargets() {
+ return targets;
}
- public List getTables() {
- return tables;
+ public void setTargets(List references) {
+ List replacement = new ArrayList<>(references);
+ targets.clear();
+ targets.addAll(replacement);
+ }
+
+ /** Preserves the legacy parser's singular accessor for the last target. */
+ public Table getTable() {
+ return targets.isEmpty() ? null : targets.get(targets.size() - 1).getTable();
}
public void setTable(Table table) {
- this.table = table;
+ if (targets.isEmpty()) {
+ TableReference target = new TableReference(table);
+ target.setOnly(only);
+ targets.add(target);
+ } else {
+ targets.get(targets.size() - 1).setTable(table);
+ }
+ }
+
+ /** Mutable table projection; replacing a table preserves its target's inheritance scope. */
+ public List getTables() {
+ return new AbstractList() {
+ @Override
+ public Table get(int index) {
+ return targets.get(index).getTable();
+ }
+
+ @Override
+ public int size() {
+ return targets.size();
+ }
+
+ @Override
+ public Table set(int index, Table table) {
+ Table previous = get(index);
+ targets.get(index).setTable(table);
+ return previous;
+ }
+
+ @Override
+ public void add(int index, Table table) {
+ targets.add(index, new TableReference(table));
+ }
+
+ @Override
+ public Table remove(int index) {
+ return targets.remove(index).getTable();
+ }
+ };
}
public void setTables(List tables) {
- this.tables = tables;
+ List replacement = tables == null ? new ArrayList<>() : new ArrayList<>(tables);
+ boolean firstOnly = isOnly();
+ targets.clear();
+ replacement.forEach(table -> targets.add(new TableReference(table)));
+ setOnly(firstOnly);
}
- public boolean getCascade() {
- return cascade;
+ public IdentityOption getIdentityOption() {
+ return identityOption;
}
- public void setCascade(boolean c) {
- cascade = c;
+ public void setIdentityOption(IdentityOption option) {
+ identityOption = option;
}
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("TRUNCATE");
- if (tableToken) {
- sb.append(" TABLE");
- }
- if (only) {
- sb.append(" ONLY");
- }
- sb.append(" ");
- if (tables != null && !tables.isEmpty()) {
- sb.append(tables.stream()
- .map(Table::toString)
- .collect(joining(", ")));
- } else {
- sb.append(table);
- }
- if (cascade) {
- sb.append(" CASCADE");
- }
- return sb.toString();
+ public DropBehavior getDropBehavior() {
+ return dropBehavior;
+ }
+
+ public void setDropBehavior(DropBehavior behavior) {
+ dropBehavior = behavior;
+ }
+
+ public boolean getCascade() {
+ return dropBehavior == DropBehavior.CASCADE;
+ }
+
+ public void setCascade(boolean cascade) {
+ dropBehavior = cascade ? DropBehavior.CASCADE : null;
}
public boolean isTableToken() {
return tableToken;
}
- public void setTableToken(boolean hasTable) {
- this.tableToken = hasTable;
+ public void setTableToken(boolean tableToken) {
+ this.tableToken = tableToken;
}
+ /** Legacy ONLY accessor describes the first target. */
public boolean isOnly() {
- return only;
+ return targets.isEmpty() ? only : targets.get(0).isOnly();
}
public void setOnly(boolean only) {
this.only = only;
+ if (!targets.isEmpty()) {
+ targets.get(0).setOnly(only);
+ }
}
- public Truncate withTableToken(boolean hasTableToken) {
- this.setTableToken(hasTableToken);
+ public StringBuilder appendTo(StringBuilder builder) {
+ builder.append(tableToken ? "TRUNCATE TABLE " : "TRUNCATE ");
+ for (int i = 0; i < targets.size(); i++) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ targets.get(i).appendTo(builder);
+ }
+ if (identityOption != null) {
+ builder.append(' ').append(identityOption).append(" IDENTITY");
+ }
+ if (dropBehavior != null) {
+ builder.append(' ').append(dropBehavior);
+ }
+ return builder;
+ }
+
+ @Override
+ public String toString() {
+ return appendTo(new StringBuilder()).toString();
+ }
+
+ public Truncate withTableToken(boolean value) {
+ setTableToken(value);
return this;
}
- public Truncate withTable(Table table) {
- this.setTable(table);
+ public Truncate withTable(Table value) {
+ setTable(value);
return this;
}
- public Truncate withTables(List tables) {
- this.setTables(tables);
+ public Truncate withTables(List value) {
+ setTables(value);
return this;
}
- public Truncate withCascade(boolean cascade) {
- this.setCascade(cascade);
+ public Truncate withCascade(boolean value) {
+ setCascade(value);
return this;
}
- public Truncate withOnly(boolean only) {
- this.setOnly(only);
+ public Truncate withOnly(boolean value) {
+ setOnly(value);
return this;
}
}
-
diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
index 9d21d83770..703a4abe34 100644
--- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
+++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
@@ -1612,7 +1612,7 @@ public void visit(Drop drop) {
@Override
public Void visit(Truncate truncate, S context) {
- visit(truncate.getTable(), context);
+ truncate.getTables().forEach(table -> visit(table, context));
return null;
}
diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
index d949c3e623..306ac059d1 100644
--- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
+++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java
@@ -38,9 +38,7 @@
import java.lang.reflect.InvocationTargetException;
import java.util.List;
-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;
@@ -301,26 +299,7 @@ public StringBuilder visit(Select select, S context) {
@Override
public StringBuilder visit(Truncate truncate, S context) {
- builder.append("TRUNCATE");
- if (truncate.isTableToken()) {
- builder.append(" TABLE");
- }
- if (truncate.isOnly()) {
- builder.append(" ONLY");
- }
- builder.append(" ");
- if (truncate.getTables() != null && !truncate.getTables().isEmpty()) {
- builder.append(truncate.getTables().stream()
- .map(Table::toString)
- .collect(Collectors.joining(", ")));
- } else {
- builder.append(truncate.getTable());
- }
- if (truncate.getCascade()) {
- builder.append(" CASCADE");
- }
-
- return builder;
+ return truncate.appendTo(builder);
}
@Override
diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java
index 0b8d2765d5..839664dbfd 100644
--- a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java
+++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java
@@ -208,7 +208,7 @@ public Void visit(Select select, S context) {
@Override
public Void visit(Truncate truncate, S context) {
validateFeature(Feature.truncate);
- validateOptionalFromItem(truncate.getTable());
+ truncate.getTables().forEach(this::validateOptionalFromItem);
return null;
}
diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
index 4b505dab0d..c617489c38 100644
--- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
+++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
@@ -3653,18 +3653,39 @@ LockStatement LockStatement(): {
{ return statement; }
}
-LockStatement.Target LockTarget(): {
+LockStatement.Target LockTarget():
+{ TableReference reference; LockStatement.Target target; }
+{
+ reference=RelationTarget()
+ {
+ target = new LockStatement.Target(reference.getTable());
+ target.setOnly(reference.isOnly());
+ target.setIncludeDescendants(reference.isIncludeDescendants());
+ target.setParenthesized(reference.isParenthesized());
+ return target;
+ }
+}
+
+/** Shared PostgreSQL relation_expr target, including ONLY (qualified_name). */
+TableReference RelationTarget():
+{
Table table;
boolean only = false;
boolean descendants = false;
- LockStatement.Target target;
-} {
- [ { only = true; } ] table=Table() [ "*" { descendants = true; } ]
+ boolean parenthesized = false;
+ TableReference target;
+}
+{
+ [ { only = true; } ]
+ ( "(" table=Table() ")" { parenthesized = true; } | table=Table() )
+ [ "*" { descendants = true; } ]
{
- requireDdlSyntax(!only || !descendants, "ONLY and * cannot describe the same lock target");
- target = new LockStatement.Target(table);
- target.setScope(only ? LockStatement.Scope.ONLY
- : descendants ? LockStatement.Scope.INCLUDING_DESCENDANTS : LockStatement.Scope.DEFAULT);
+ requireDdlSyntax(!only || !descendants, "ONLY and * cannot describe the same relation target");
+ requireDdlSyntax(!parenthesized || only, "Parenthesized relation targets require ONLY");
+ target = new TableReference(table);
+ target.setOnly(only);
+ target.setIncludeDescendants(descendants);
+ target.setParenthesized(parenthesized);
return target;
}
}
@@ -16826,38 +16847,18 @@ Drop Drop():
}
Truncate Truncate():
+{ Truncate truncate = new Truncate(); TableReference target; }
{
- Truncate truncate = new Truncate();
- Table table;
- List tables = new ArrayList();
- boolean only = false;
- boolean cascade = false;
-}
-{
-/**
-* TRUNCATE can be followed directly by the table name in Postgresql
-* See: https://www.postgresql.org/docs/current/sql-truncate.html
-*
-* TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
-* [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
-*
-*/
- [LOOKAHEAD(2) {truncate.setTableToken(true);}]
- [ { only = true; }]
- table=Table() { tables.add(table); } (LOOKAHEAD(2) "," table=Table() { tables.add(table); } )*
- [ { 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);
- }
- }
+ [ LOOKAHEAD(2) { truncate.setTableToken(true); } ]
+ target=RelationTarget() { truncate.getTargets().add(target); }
+ ( LOOKAHEAD(2) "," target=RelationTarget() { truncate.getTargets().add(target); } )*
+ [ ( { truncate.setIdentityOption(Truncate.IdentityOption.RESTART); }
+ | ContextualKeyword("CONTINUE") { truncate.setIdentityOption(Truncate.IdentityOption.CONTINUE); } )
+ ]
+ [ { truncate.setDropBehavior(Truncate.DropBehavior.CASCADE); }
+ | { truncate.setDropBehavior(Truncate.DropBehavior.RESTRICT); } ]
+ { return truncate; }
}
/**
diff --git a/src/test/java/net/sf/jsqlparser/statement/truncate/PostgreSqlTruncateTest.java b/src/test/java/net/sf/jsqlparser/statement/truncate/PostgreSqlTruncateTest.java
new file mode 100644
index 0000000000..a0ef7a0dbf
--- /dev/null
+++ b/src/test/java/net/sf/jsqlparser/statement/truncate/PostgreSqlTruncateTest.java
@@ -0,0 +1,104 @@
+/*-
+ * #%L
+ * JSQLParser library
+ * %%
+ * Copyright (C) 2004 - 2026 JSQLParser
+ * %%
+ * Dual licensed under GNU LGPL 2.1 or Apache License 2.0
+ * #L%
+ */
+package net.sf.jsqlparser.statement.truncate;
+
+import static org.junit.jupiter.api.Assertions.*;
+import java.util.List;
+import java.util.Set;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.Statement;
+import net.sf.jsqlparser.statement.lock.LockStatement;
+import net.sf.jsqlparser.util.TablesNamesFinder;
+import net.sf.jsqlparser.util.deparser.StatementDeParser;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+class PostgreSqlTruncateTest {
+ @ParameterizedTest
+ @ValueSource(strings = {"t", "ONLY t", "t *", "ONLY (public.t)",
+ "ONLY t, public.u *", "t, ONLY u", "ONLY t, ONLY u"})
+ void targetsAndOptionalClausesRoundTrip(String targets) throws JSQLParserException {
+ for (String identity : new String[] {"", " RESTART IDENTITY", " CONTINUE IDENTITY"}) {
+ for (String behavior : new String[] {"", " CASCADE", " RESTRICT"}) {
+ for (String tableKeyword : new String[] {"", " TABLE"}) {
+ String sql = "TRUNCATE" + tableKeyword + " " + targets + identity + behavior;
+ Truncate statement = (Truncate) CCJSqlParserUtil.parse(sql);
+ assertEquals(sql, statement.toString());
+ assertEquals(targets.contains(",") ? 2 : 1, statement.getTargets().size());
+ assertEquals(identity.contains("RESTART") ? Truncate.IdentityOption.RESTART
+ : identity.contains("CONTINUE") ? Truncate.IdentityOption.CONTINUE
+ : null,
+ statement.getIdentityOption());
+ assertEquals(behavior.contains("CASCADE") ? Truncate.DropBehavior.CASCADE
+ : behavior.contains("RESTRICT") ? Truncate.DropBehavior.RESTRICT : null,
+ statement.getDropBehavior());
+ roundTrip(statement);
+ assertEquals(2, CCJSqlParserUtil.parseStatements(sql + "; SELECT 1").size());
+ }
+ }
+ }
+ }
+
+ @Test
+ void visitsEveryTableAndKeepsLegacyMutationsLive() throws JSQLParserException {
+ Truncate statement = (Truncate) CCJSqlParserUtil.parse(
+ "TRUNCATE ONLY a, b *, ONLY c RESTART IDENTITY CASCADE");
+ assertEquals(Set.of("a", "b", "c"), new TablesNamesFinder().getTables(statement));
+ assertEquals("c", statement.getTable().getName());
+ statement.setTable(new Table("x"));
+ statement.getTables().set(1, new Table("y"));
+ assertTrue(statement.getTargets().get(1).isIncludeDescendants());
+ statement.getTargets().get(2).setOnly(false);
+ statement.getTables().add(new Table("z"));
+ statement.setCascade(false);
+ statement.setDropBehavior(Truncate.DropBehavior.RESTRICT);
+ assertFalse(statement.getCascade());
+ assertEquals("TRUNCATE ONLY a, y *, x, z RESTART IDENTITY RESTRICT", statement.toString());
+ assertEquals(Set.of("a", "y", "x", "z"), new TablesNamesFinder().getTables(statement));
+ statement.getTables().remove(2);
+ roundTrip(statement);
+ statement.setTables(statement.getTables());
+ assertEquals(3, statement.getTargets().size());
+ statement.setTables(List.of(new Table("replacement")));
+ assertTrue(statement.isOnly());
+ assertEquals("replacement", statement.getTable().getName());
+ }
+
+ @Test
+ void lockAndTruncateShareRelationTargets() throws JSQLParserException {
+ LockStatement lock = (LockStatement) CCJSqlParserUtil.parse(
+ "LOCK TABLE ONLY (public.t), u * IN ACCESS SHARE MODE");
+ assertEquals(LockStatement.Scope.ONLY, lock.getTargets().get(0).getScope());
+ assertTrue(lock.getTargets().get(0).isParenthesized());
+ assertEquals(LockStatement.Scope.INCLUDING_DESCENDANTS,
+ lock.getTargets().get(1).getScope());
+ lock.getTargets().get(0).setScope(LockStatement.Scope.DEFAULT);
+ assertFalse(lock.getTargets().get(0).isParenthesized());
+ roundTrip(lock);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"ONLY t *", "(t)", "t RESTART", "t CONTINUE",
+ "t RESTART IDENTITY CONTINUE IDENTITY", "t CASCADE RESTRICT", "t,",
+ "t RESTRICT RESTART IDENTITY"})
+ void rejectsMalformedTargetsAndClauses(String sql) {
+ assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse("TRUNCATE " + sql));
+ }
+
+ private static void roundTrip(Statement statement) throws JSQLParserException {
+ StringBuilder output = new StringBuilder();
+ statement.accept(new StatementDeParser(output));
+ assertEquals(statement.toString(), output.toString());
+ assertEquals(output.toString(), CCJSqlParserUtil.parse(output.toString()).toString());
+ }
+}
diff --git a/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateMultipleTablesTest.java b/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateMultipleTablesTest.java
index 18894c8ca0..494a0cdec4 100644
--- a/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateMultipleTablesTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateMultipleTablesTest.java
@@ -13,7 +13,6 @@
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;
@@ -86,8 +85,8 @@ 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);
+ .withTables(List.of(new Table("foo"), new Table("bar")))
+ .withTableToken(true), statement);
}
@Test
@@ -95,16 +94,18 @@ 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);
+ .withTables(List.of(new Table("foo"), new Table("bar")))
+ .withTableToken(true)
+ .withCascade(true), statement);
}
@Test
- public void testTruncateDoesNotAllowOnlyWithMultipleTables() {
+ public void testTruncateAllowsOnlyForAnIndividualTarget() throws JSQLParserException {
String statement = "TRUNCATE TABLE ONLY foo, bar";
- assertThrows(JSQLParserException.class,
- () -> parserManager.parse(new StringReader(statement)));
+ Truncate truncate = (Truncate) parserManager.parse(new StringReader(statement));
+ assertEquals(statement, truncate.toString());
+ assertTrue(truncate.getTargets().get(0).isOnly());
+ assertEquals(2, truncate.getTargets().size());
}
}
diff --git a/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateTest.java b/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateTest.java
index 698d2a5b84..129b3f43f0 100644
--- a/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateTest.java
+++ b/src/test/java/net/sf/jsqlparser/statement/truncate/TruncateTest.java
@@ -18,7 +18,6 @@
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.assertThrows;
import org.junit.jupiter.api.Test;
@@ -72,8 +71,8 @@ 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
@@ -81,9 +80,9 @@ 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
@@ -91,9 +90,9 @@ public void testTruncateOnlyDeparse() throws JSQLParserException {
String statement = "TRUNCATE TABLE ONLY foo";
assertSqlCanBeParsedAndDeparsed(statement);
assertDeparse(new Truncate()
- .withTable(new Table("foo"))
- .withTableToken(true)
- .withOnly(true), statement);
+ .withTable(new Table("foo"))
+ .withTableToken(true)
+ .withOnly(true), statement);
}
@Test
@@ -101,16 +100,15 @@ 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);
+ .withTable(new Table("foo"))
+ .withCascade(true)
+ .withOnly(true), statement);
}
@Test
- public void throwsParseWhenOnlyUsedWithMultipleTables() {
+ public void onlyAppliesToItsOwnTarget() throws JSQLParserException {
String statement = "TRUNCATE TABLE ONLY foo, bar";
- assertThrows(JSQLParserException.class,
- () -> parserManager.parse(new StringReader(statement)));
+ assertSqlCanBeParsedAndDeparsed(statement);
}
}