Skip to content

Commit 78d2456

Browse files
committed
feat: model PostgreSQL ON COMMIT, access method and DROP behavior
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 bdf8ac3 commit 78d2456

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
@@ -16593,7 +16593,7 @@ void CreateTableOptions(List<TableOption> options):
1659316593
}
1659416594

1659516595
TableOption CreateTableOption():
16596-
{ TableOption option; List<String> tokens; List<IndexOption> storageParameters; }
16596+
{ TableOption option; List<String> tokens; List<IndexOption> storageParameters; String name; }
1659716597
{
1659816598
(
1659916599
LOOKAHEAD(<K_WITH> "(") <K_WITH> storageParameters=PostgreSqlIndexOptions() {
@@ -16604,6 +16604,12 @@ TableOption CreateTableOption():
1660416604
option = new TableOption(TableOption.Kind.WITHOUT_OIDS, "WITHOUT OIDS", null, false);
1660516605
}
1660616606
|
16607+
LOOKAHEAD(<K_ON> <K_COMMIT>) option=OnCommitTableOption()
16608+
|
16609+
// USING INDEX TABLESPACE stays a raw parameter
16610+
LOOKAHEAD(2, { getToken(2).kind != K_INDEX }) <K_USING> name=RelObjectName()
16611+
{ option = TableOption.accessMethod(name); }
16612+
|
1660716613
LOOKAHEAD({ isMySqlTableOptionAhead() }) option=MySqlTableOption()
1660816614
|
1660916615
// Snowflake: COPY is reserved because it starts the COPY statement
@@ -16613,6 +16619,28 @@ TableOption CreateTableOption():
1661316619
{ return option; }
1661416620
}
1661516621

16622+
/** Temporary table ON COMMIT action, including Oracle's private temporary table forms. */
16623+
TableOption OnCommitTableOption():
16624+
{ TableOption.OnCommitAction action; }
16625+
{
16626+
<K_ON> <K_COMMIT>
16627+
(
16628+
<K_DELETE> <K_ROWS> { action = TableOption.OnCommitAction.DELETE_ROWS; }
16629+
|
16630+
<K_DROP> { action = TableOption.OnCommitAction.DROP; }
16631+
[ LOOKAHEAD({ isKeywordAhead("DEFINITION") }) ContextualKeyword("DEFINITION")
16632+
{ action = TableOption.OnCommitAction.DROP_DEFINITION; } ]
16633+
|
16634+
LOOKAHEAD({ isKeywordAhead("PRESERVE") }) ContextualKeyword("PRESERVE")
16635+
(
16636+
<K_ROWS> { action = TableOption.OnCommitAction.PRESERVE_ROWS; }
16637+
|
16638+
ContextualKeyword("DEFINITION") { action = TableOption.OnCommitAction.PRESERVE_DEFINITION; }
16639+
)
16640+
)
16641+
{ return TableOption.onCommit(action); }
16642+
}
16643+
1661616644
TableOption MySqlTableOption(): {
1661716645
Token tk;
1661816646
Token tk2 = null;
@@ -18920,7 +18948,9 @@ void AlterExpressionAddConstraint(AlterExpression alterExp):
1892018948
*/
1892118949
AlterExpression AlterExpressionDrop():
1892218950
{
18923-
AlterExpression alterExp = new AlterExpressionDrop();
18951+
AlterExpressionDrop drop = new AlterExpressionDrop();
18952+
AlterExpression alterExp = drop;
18953+
CascadeBehavior behavior;
1892418954
Token tk;
1892518955
Token tk2;
1892618956
List<String> columnNames = null;
@@ -18959,8 +18989,9 @@ AlterExpression AlterExpressionDrop():
1895918989
(tk=KeywordOrIdentifier() ) { alterExp.setColumnName(tk.image); }
1896018990
[ "INVALIDATE" { alterExp.addParameters("INVALIDATE"); } ]
1896118991
[
18962-
"CASCADE" { alterExp.addParameters("CASCADE"); }
18963-
[ "CONSTRAINTS" { alterExp.addParameters("CONSTRAINTS"); } ]
18992+
// Oracle CASCADE CONSTRAINTS remains a raw parameter
18993+
LOOKAHEAD(2) "CASCADE" "CONSTRAINTS" { alterExp.addParameters("CASCADE", "CONSTRAINTS"); }
18994+
| behavior=TypeAlterBehavior() { drop.setBehavior(behavior); }
1896418995
]
1896518996
)
1896618997
|
@@ -18975,24 +19006,24 @@ AlterExpression AlterExpressionDrop():
1897519006
(
1897619007
tk=<K_UNIQUE> { alterExp.setOperation(AlterOperation.DROP_UNIQUE); }
1897719008
columnNames=ColumnsNamesList() { alterExp.setDropColumns(columnNames); columnNames = null; }
18978-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19009+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1897919010
)
1898019011
|
1898119012
(
1898219013
tk=<K_PRIMARY> tk2=<K_KEY> { alterExp.setOperation(AlterOperation.DROP_PRIMARY_KEY); }
18983-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19014+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1898419015
)
1898519016
|
1898619017
(
1898719018
tk=<K_FOREIGN> tk2=<K_KEY> { alterExp.setOperation(AlterOperation.DROP_FOREIGN_KEY); }
1898819019
columnNames=ColumnsNamesList() { alterExp.setDropColumns(columnNames); columnNames = null; }
18989-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19020+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1899019021
)
1899119022
|
1899219023
(
1899319024
<K_CONSTRAINT> [<K_IF> <K_EXISTS> { alterExp.setUsingIfExists(true); } ]
1899419025
( tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER>) { alterExp.setConstraintName(tk.image); }
18995-
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
19026+
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
1899619027
)
1899719028
)
1899819029
{ return alterExp; }

‎src/site/sphinx/usage.rst‎

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

344+
``ALTER TABLE`` drop actions expose the same enum through
345+
``AlterExpressionDrop.getBehavior()``, for example in
346+
``ALTER TABLE t DROP COLUMN IF EXISTS c RESTRICT`` and
347+
``ALTER TABLE t DROP CONSTRAINT c CASCADE``. Oracle's ``CASCADE CONSTRAINTS`` and
348+
``INVALIDATE`` remain raw parameters. ``getParameters()`` still ends with the
349+
behavior keyword; when a behavior is set, it returns a read-only snapshot, so use
350+
``setBehavior`` to change the clause.
351+
344352
Edit shared table options
345353
-------------------------
346354

@@ -368,14 +376,34 @@ returns an unmodifiable snapshot.
368376
Any structured setter leaves the opaque representation; it never recovers
369377
previous values from those tokens. ``setTokens(null)`` clears opaque tokens
370378
without restoring discarded properties, and leaves structured options alone.
371-
``setUnionTables``, ``setStorageParameters`` and ``setForeignTableOptions``
372-
replace the current contents with their respective structured payloads. Their
373-
lists and child nodes remain editable. An empty UNION list means ``UNION ()``;
379+
``setUnionTables``, ``setStorageParameters``, ``setForeignTableOptions`` and
380+
``setOnCommitAction`` replace the current contents with their respective
381+
structured payloads. Their lists and child nodes remain editable. An empty
382+
UNION list means ``UNION ()``;
374383
a null payload omits its value. A non-null ``setTablespaceStorage`` also selects
375384
TABLESPACE; supply its tablespace name with ``setValue``. Choose the new kind and spelling explicitly
376385
when using the scalar setters. These methods edit the AST rather than checking
377386
database-specific DDL validity.
378387

388+
A temporary table's ``ON COMMIT`` clause and a table access method are single
389+
options. ``Kind.ON_COMMIT`` exposes ``getOnCommitAction()``: ``PRESERVE_ROWS``,
390+
``DELETE_ROWS`` or ``DROP``, plus ``PRESERVE_DEFINITION`` and ``DROP_DEFINITION``
391+
for Oracle private temporary tables. ``Kind.ACCESS_METHOD`` stores the method of
392+
``USING method`` as its value, including ``CREATE TABLE … USING method AS query``.
393+
Use ``TableOption.onCommit`` and ``TableOption.accessMethod`` to build them.
394+
``getTableOptionsStrings()`` keeps the legacy word tokens, such as ``ON``,
395+
``COMMIT``, ``DELETE`` and ``ROWS``.
396+
397+
.. code-block:: java
398+
399+
CreateTable table = (CreateTable) CCJSqlParserUtil.parse(
400+
"CREATE TEMP TABLE t (id INT) USING heap ON COMMIT DROP",
401+
parser -> parser.withDialect(Dialect.POSTGRESQL));
402+
table.getTableOption(TableOption.Kind.ACCESS_METHOD).orElseThrow().getValue(); // heap
403+
table.getTableOption(TableOption.Kind.ON_COMMIT).orElseThrow()
404+
.setOnCommitAction(TableOption.OnCommitAction.PRESERVE_ROWS);
405+
// CREATE TEMP TABLE t (id INT) USING heap ON COMMIT PRESERVE ROWS
406+
379407
Inspect PostgreSQL schema statements
380408
------------------------------------
381409

0 commit comments

Comments
 (0)