Skip to content

Commit e9ec7ba

Browse files
authored
feat: model PostgreSQL ON COMMIT, access method and DROP behavior (#2814)
CREATE TABLE ON COMMIT and USING clauses were split into one raw TableOption per word. Parse them as Kind.ON_COMMIT with a typed OnCommitAction and as Kind.ACCESS_METHOD; the legacy token lists and SQL output are unchanged. ALTER TABLE DROP [COLUMN] c RESTRICT was rejected. AlterExpressionDrop now keeps CASCADE/RESTRICT as a CascadeBehavior for column and constraint drops, and getParameters() still lists the keyword. Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 9fff92e commit e9ec7ba

6 files changed

Lines changed: 529 additions & 13 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/alter/AlterExpressionDrop.java‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
*/
1010
package net.sf.jsqlparser.statement.alter;
1111

12+
import java.util.ArrayList;
13+
import java.util.Collections;
1214
import java.util.List;
1315

16+
import net.sf.jsqlparser.statement.CascadeBehavior;
1417
import net.sf.jsqlparser.statement.select.PlainSelect;
1518
import net.sf.jsqlparser.statement.create.table.ConstraintKind;
1619

@@ -19,6 +22,47 @@
1922
* DROP INDEX/KEY, DROP PRIMARY KEY, DROP UNIQUE, DROP FOREIGN KEY, and DROP PARTITION.
2023
*/
2124
public class AlterExpressionDrop extends AlterExpression {
25+
private CascadeBehavior behavior;
26+
27+
/** The explicit CASCADE or RESTRICT clause; null when omitted. */
28+
public CascadeBehavior getBehavior() {
29+
return behavior;
30+
}
31+
32+
public void setBehavior(CascadeBehavior behavior) {
33+
this.behavior = behavior;
34+
}
35+
36+
public AlterExpressionDrop withBehavior(CascadeBehavior behavior) {
37+
setBehavior(behavior);
38+
return this;
39+
}
40+
41+
/**
42+
* Returns the raw parameters, such as Oracle's {@code INVALIDATE} or
43+
* {@code CASCADE CONSTRAINTS}. With a {@link #getBehavior() behavior}, this is an unmodifiable
44+
* snapshot that ends with its keyword; use {@link #addParameters(String...)} to append raw
45+
* parameters.
46+
*/
47+
@Override
48+
public List<String> getParameters() {
49+
List<String> parameters = super.getParameters();
50+
if (behavior == null) {
51+
return parameters;
52+
}
53+
List<String> tokens = parameters == null ? new ArrayList<>() : new ArrayList<>(parameters);
54+
tokens.add(behavior.name());
55+
return Collections.unmodifiableList(tokens);
56+
}
57+
58+
@Override
59+
protected void appendCommonTail(StringBuilder b) {
60+
super.appendCommonTail(b);
61+
if (behavior != null) {
62+
b.append(' ').append(behavior);
63+
}
64+
}
65+
2266
@Override
2367
public List<ColumnIdentityAction> getColumnIdentityActions() {
2468
return getColumnIdentityActionsFromDefinitions();

‎src/main/java/net/sf/jsqlparser/statement/create/table/TableOption.java‎

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,24 @@ public class TableOption implements Serializable {
3333
private List<Table> unionTables;
3434
private List<IndexOption> storageParameters;
3535
private ColumnOption.Storage tablespaceStorage;
36+
private OnCommitAction onCommitAction;
3637

3738
public enum Kind {
38-
ENGINE, CHARACTER_SET, COLLATE, COMMENT, AUTO_INCREMENT, STATS_AUTO_RECALC, STATS_PERSISTENT, STATS_SAMPLE_PAGES, UNION, ENCRYPTION, PASSWORD, DATA_DIRECTORY, INDEX_DIRECTORY, SECONDARY_ENGINE, AUTOEXTEND_SIZE, INSERT_METHOD, PACK_KEYS, DELAY_KEY_WRITE, CHECKSUM, CONNECTION, COMPRESSION, STORAGE_PARAMETERS, WITHOUT_OIDS, ENGINE_ATTRIBUTE, SECONDARY_ENGINE_ATTRIBUTE, ROW_FORMAT, AVG_ROW_LENGTH, MAX_ROWS, MIN_ROWS, KEY_BLOCK_SIZE, TABLESPACE, FOREIGN_SERVER, OTHER
39+
ENGINE, CHARACTER_SET, COLLATE, COMMENT, AUTO_INCREMENT, STATS_AUTO_RECALC, STATS_PERSISTENT, STATS_SAMPLE_PAGES, UNION, ENCRYPTION, PASSWORD, DATA_DIRECTORY, INDEX_DIRECTORY, SECONDARY_ENGINE, AUTOEXTEND_SIZE, INSERT_METHOD, PACK_KEYS, DELAY_KEY_WRITE, CHECKSUM, CONNECTION, COMPRESSION, STORAGE_PARAMETERS, WITHOUT_OIDS, ENGINE_ATTRIBUTE, SECONDARY_ENGINE_ATTRIBUTE, ROW_FORMAT, AVG_ROW_LENGTH, MAX_ROWS, MIN_ROWS, KEY_BLOCK_SIZE, TABLESPACE, FOREIGN_SERVER, ACCESS_METHOD, ON_COMMIT, OTHER
40+
}
41+
42+
/**
43+
* The action of a temporary table's {@code ON COMMIT} clause. The DEFINITION actions belong to
44+
* Oracle private temporary tables.
45+
*/
46+
public enum OnCommitAction {
47+
PRESERVE_ROWS, DELETE_ROWS, DROP, PRESERVE_DEFINITION, DROP_DEFINITION;
48+
49+
/** Returns the SQL keywords, for example {@code DELETE ROWS}. */
50+
@Override
51+
public String toString() {
52+
return name().replace('_', ' ');
53+
}
3954
}
4055

4156
public ForeignTableOptions getForeignTableOptions() {
@@ -78,6 +93,31 @@ public void setTablespaceStorage(ColumnOption.Storage storage) {
7893
tablespaceStorage = storage;
7994
}
8095

96+
/** Creates a table access method option such as PostgreSQL's {@code USING heap}. */
97+
public static TableOption accessMethod(String method) {
98+
return new TableOption(Kind.ACCESS_METHOD, "USING", method, false);
99+
}
100+
101+
/** Returns the {@code ON COMMIT} action, or null if this option has none. */
102+
public OnCommitAction getOnCommitAction() {
103+
return onCommitAction;
104+
}
105+
106+
/** Replaces the current contents with an {@code ON COMMIT} clause. */
107+
public void setOnCommitAction(OnCommitAction action) {
108+
clearContents();
109+
kind = Kind.ON_COMMIT;
110+
name = "ON COMMIT";
111+
onCommitAction = action;
112+
}
113+
114+
/** Creates an {@code ON COMMIT} option, for example {@code ON COMMIT DROP}. */
115+
public static TableOption onCommit(OnCommitAction action) {
116+
TableOption option = new TableOption();
117+
option.setOnCommitAction(action);
118+
return option;
119+
}
120+
81121
public List<IndexOption> getStorageParameters() {
82122
return storageParameters;
83123
}
@@ -140,6 +180,9 @@ public void setKind(Kind kind) {
140180
if (kind != Kind.UNION) {
141181
unionTables = null;
142182
}
183+
if (kind != Kind.ON_COMMIT) {
184+
onCommitAction = null;
185+
}
143186
}
144187

145188
public String getName() {
@@ -158,6 +201,9 @@ public String getValue() {
158201
if (storageParameters != null) {
159202
return PlainSelect.getStringList(storageParameters, true, true);
160203
}
204+
if (onCommitAction != null) {
205+
return onCommitAction.toString();
206+
}
161207
return unionTables == null ? value : PlainSelect.getStringList(unionTables, true, true);
162208
}
163209

@@ -167,6 +213,7 @@ public void setValue(String value) {
167213
foreignTableOptions = null;
168214
unionTables = null;
169215
storageParameters = null;
216+
onCommitAction = null;
170217
}
171218

172219
/** Returns the mutable MERGE table sources, including an empty UNION list. */
@@ -214,7 +261,10 @@ public List<String> getTokens() {
214261
result.add("=");
215262
}
216263
String renderedValue = getValue();
217-
if (renderedValue != null) {
264+
if (onCommitAction != null) {
265+
// one token per keyword, as in the legacy raw options
266+
Collections.addAll(result, renderedValue.split(" "));
267+
} else if (renderedValue != null) {
218268
result.add(renderedValue);
219269
}
220270
if (kind == Kind.TABLESPACE && tablespaceStorage != null) {
@@ -245,6 +295,7 @@ private void clearContents() {
245295
foreignTableOptions = null;
246296
unionTables = null;
247297
storageParameters = null;
298+
onCommitAction = null;
248299
}
249300

250301
public TableOption withKind(Kind kind) {

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

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16884,7 +16884,7 @@ void CreateTableOptions(List<TableOption> options):
1688416884
}
1688516885

1688616886
TableOption CreateTableOption():
16887-
{ TableOption option; List<String> tokens; List<IndexOption> storageParameters; }
16887+
{ TableOption option; List<String> tokens; List<IndexOption> storageParameters; String name; }
1688816888
{
1688916889
(
1689016890
LOOKAHEAD(<K_WITH> "(") <K_WITH> storageParameters=PostgreSqlIndexOptions() {
@@ -16895,6 +16895,12 @@ TableOption CreateTableOption():
1689516895
option = new TableOption(TableOption.Kind.WITHOUT_OIDS, "WITHOUT OIDS", null, false);
1689616896
}
1689716897
|
16898+
LOOKAHEAD(<K_ON> <K_COMMIT>) option=OnCommitTableOption()
16899+
|
16900+
// USING INDEX TABLESPACE stays a raw parameter
16901+
LOOKAHEAD(2, { getToken(2).kind != K_INDEX }) <K_USING> name=RelObjectName()
16902+
{ option = TableOption.accessMethod(name); }
16903+
|
1689816904
LOOKAHEAD({ isMySqlTableOptionAhead() }) option=MySqlTableOption()
1689916905
|
1690016906
// Snowflake: COPY is reserved because it starts the COPY statement
@@ -16904,6 +16910,28 @@ TableOption CreateTableOption():
1690416910
{ return option; }
1690516911
}
1690616912

16913+
/** Temporary table ON COMMIT action, including Oracle's private temporary table forms. */
16914+
TableOption OnCommitTableOption():
16915+
{ TableOption.OnCommitAction action; }
16916+
{
16917+
<K_ON> <K_COMMIT>
16918+
(
16919+
<K_DELETE> <K_ROWS> { action = TableOption.OnCommitAction.DELETE_ROWS; }
16920+
|
16921+
<K_DROP> { action = TableOption.OnCommitAction.DROP; }
16922+
[ LOOKAHEAD({ isKeywordAhead("DEFINITION") }) ContextualKeyword("DEFINITION")
16923+
{ action = TableOption.OnCommitAction.DROP_DEFINITION; } ]
16924+
|
16925+
LOOKAHEAD({ isKeywordAhead("PRESERVE") }) ContextualKeyword("PRESERVE")
16926+
(
16927+
<K_ROWS> { action = TableOption.OnCommitAction.PRESERVE_ROWS; }
16928+
|
16929+
ContextualKeyword("DEFINITION") { action = TableOption.OnCommitAction.PRESERVE_DEFINITION; }
16930+
)
16931+
)
16932+
{ return TableOption.onCommit(action); }
16933+
}
16934+
1690716935
TableOption MySqlTableOption(): {
1690816936
Token tk;
1690916937
Token tk2 = null;
@@ -19262,7 +19290,9 @@ void AlterExpressionAddConstraint(AlterExpression alterExp):
1926219290
*/
1926319291
AlterExpression AlterExpressionDrop():
1926419292
{
19265-
AlterExpression alterExp = new AlterExpressionDrop();
19293+
AlterExpressionDrop drop = new AlterExpressionDrop();
19294+
AlterExpression alterExp = drop;
19295+
CascadeBehavior behavior;
1926619296
Token tk;
1926719297
Token tk2;
1926819298
List<String> columnNames = null;
@@ -19301,8 +19331,9 @@ AlterExpression AlterExpressionDrop():
1930119331
(tk=KeywordOrIdentifier() ) { alterExp.setColumnName(tk.image); }
1930219332
[ "INVALIDATE" { alterExp.addParameters("INVALIDATE"); } ]
1930319333
[
19304-
"CASCADE" { alterExp.addParameters("CASCADE"); }
19305-
[ "CONSTRAINTS" { alterExp.addParameters("CONSTRAINTS"); } ]
19334+
// Oracle CASCADE CONSTRAINTS remains a raw parameter
19335+
LOOKAHEAD(2) "CASCADE" "CONSTRAINTS" { alterExp.addParameters("CASCADE", "CONSTRAINTS"); }
19336+
| behavior=TypeAlterBehavior() { drop.setBehavior(behavior); }
1930619337
]
1930719338
)
1930819339
|
@@ -19317,24 +19348,24 @@ AlterExpression AlterExpressionDrop():
1931719348
(
1931819349
tk=<K_UNIQUE> { alterExp.setOperation(AlterOperation.DROP_UNIQUE); }
1931919350
columnNames=ColumnsNamesList() { alterExp.setDropColumns(columnNames); columnNames = null; }
19320-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19351+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1932119352
)
1932219353
|
1932319354
(
1932419355
tk=<K_PRIMARY> tk2=<K_KEY> { alterExp.setOperation(AlterOperation.DROP_PRIMARY_KEY); }
19325-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19356+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1932619357
)
1932719358
|
1932819359
(
1932919360
tk=<K_FOREIGN> tk2=<K_KEY> { alterExp.setOperation(AlterOperation.DROP_FOREIGN_KEY); }
1933019361
columnNames=ColumnsNamesList() { alterExp.setDropColumns(columnNames); columnNames = null; }
19331-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19362+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1933219363
)
1933319364
|
1933419365
(
1933519366
<K_CONSTRAINT> [<K_IF> <K_EXISTS> { alterExp.setUsingIfExists(true); } ]
1933619367
( tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER>) { alterExp.setConstraintName(tk.image); }
19337-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19368+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1933819369
)
1933919370
)
1934019371
{ return alterExp; }

‎src/site/sphinx/usage.rst‎

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,14 @@ The existing getter and setter names remain, but their return and parameter type
405405
have changed, so clients must recompile. ``Truncate.setCascade(false)`` still
406406
clears the clause. Foreign-key referential actions retain their separate enum.
407407

408+
``ALTER TABLE`` drop actions expose the same enum through
409+
``AlterExpressionDrop.getBehavior()``, for example in
410+
``ALTER TABLE t DROP COLUMN IF EXISTS c RESTRICT`` and
411+
``ALTER TABLE t DROP CONSTRAINT c CASCADE``. Oracle's ``CASCADE CONSTRAINTS`` and
412+
``INVALIDATE`` remain raw parameters. ``getParameters()`` still ends with the
413+
behavior keyword; when a behavior is set, it returns a read-only snapshot, so use
414+
``setBehavior`` to change the clause.
415+
408416
Edit shared table options
409417
-------------------------
410418

@@ -432,14 +440,34 @@ returns an unmodifiable snapshot.
432440
Any structured setter leaves the opaque representation; it never recovers
433441
previous values from those tokens. ``setTokens(null)`` clears opaque tokens
434442
without restoring discarded properties, and leaves structured options alone.
435-
``setUnionTables``, ``setStorageParameters`` and ``setForeignTableOptions``
436-
replace the current contents with their respective structured payloads. Their
437-
lists and child nodes remain editable. An empty UNION list means ``UNION ()``;
443+
``setUnionTables``, ``setStorageParameters``, ``setForeignTableOptions`` and
444+
``setOnCommitAction`` replace the current contents with their respective
445+
structured payloads. Their lists and child nodes remain editable. An empty
446+
UNION list means ``UNION ()``;
438447
a null payload omits its value. A non-null ``setTablespaceStorage`` also selects
439448
TABLESPACE; supply its tablespace name with ``setValue``. Choose the new kind and spelling explicitly
440449
when using the scalar setters. These methods edit the AST rather than checking
441450
database-specific DDL validity.
442451

452+
A temporary table's ``ON COMMIT`` clause and a table access method are single
453+
options. ``Kind.ON_COMMIT`` exposes ``getOnCommitAction()``: ``PRESERVE_ROWS``,
454+
``DELETE_ROWS`` or ``DROP``, plus ``PRESERVE_DEFINITION`` and ``DROP_DEFINITION``
455+
for Oracle private temporary tables. ``Kind.ACCESS_METHOD`` stores the method of
456+
``USING method`` as its value, including ``CREATE TABLE … USING method AS query``.
457+
Use ``TableOption.onCommit`` and ``TableOption.accessMethod`` to build them.
458+
``getTableOptionsStrings()`` keeps the legacy word tokens, such as ``ON``,
459+
``COMMIT``, ``DELETE`` and ``ROWS``.
460+
461+
.. code-block:: java
462+
463+
CreateTable table = (CreateTable) CCJSqlParserUtil.parse(
464+
"CREATE TEMP TABLE t (id INT) USING heap ON COMMIT DROP",
465+
parser -> parser.withDialect(Dialect.POSTGRESQL));
466+
table.getTableOption(TableOption.Kind.ACCESS_METHOD).orElseThrow().getValue(); // heap
467+
table.getTableOption(TableOption.Kind.ON_COMMIT).orElseThrow()
468+
.setOnCommitAction(TableOption.OnCommitAction.PRESERVE_ROWS);
469+
// CREATE TEMP TABLE t (id INT) USING heap ON COMMIT PRESERVE ROWS
470+
443471
Inspect PostgreSQL schema statements
444472
------------------------------------
445473

0 commit comments

Comments
 (0)