Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
*/
package net.sf.jsqlparser.statement.alter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.sf.jsqlparser.statement.CascadeBehavior;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.create.table.ConstraintKind;

Expand All @@ -19,6 +22,47 @@
* DROP INDEX/KEY, DROP PRIMARY KEY, DROP UNIQUE, DROP FOREIGN KEY, and DROP PARTITION.
*/
public class AlterExpressionDrop extends AlterExpression {
private CascadeBehavior behavior;

/** The explicit CASCADE or RESTRICT clause; null when omitted. */
public CascadeBehavior getBehavior() {
return behavior;
}

public void setBehavior(CascadeBehavior behavior) {
this.behavior = behavior;
}

public AlterExpressionDrop withBehavior(CascadeBehavior behavior) {
setBehavior(behavior);
return this;
}

/**
* Returns the raw parameters, such as Oracle's {@code INVALIDATE} or
* {@code CASCADE CONSTRAINTS}. With a {@link #getBehavior() behavior}, this is an unmodifiable
* snapshot that ends with its keyword; use {@link #addParameters(String...)} to append raw
* parameters.
*/
@Override
public List<String> getParameters() {
List<String> parameters = super.getParameters();
if (behavior == null) {
return parameters;
}
List<String> tokens = parameters == null ? new ArrayList<>() : new ArrayList<>(parameters);
tokens.add(behavior.name());
return Collections.unmodifiableList(tokens);
}

@Override
protected void appendCommonTail(StringBuilder b) {
super.appendCommonTail(b);
if (behavior != null) {
b.append(' ').append(behavior);
}
}

@Override
public List<ColumnIdentityAction> getColumnIdentityActions() {
return getColumnIdentityActionsFromDefinitions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,24 @@ public class TableOption implements Serializable {
private List<Table> unionTables;
private List<IndexOption> storageParameters;
private ColumnOption.Storage tablespaceStorage;
private OnCommitAction onCommitAction;

public enum Kind {
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
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
}

/**
* The action of a temporary table's {@code ON COMMIT} clause. The DEFINITION actions belong to
* Oracle private temporary tables.
*/
public enum OnCommitAction {
PRESERVE_ROWS, DELETE_ROWS, DROP, PRESERVE_DEFINITION, DROP_DEFINITION;

/** Returns the SQL keywords, for example {@code DELETE ROWS}. */
@Override
public String toString() {
return name().replace('_', ' ');
}
}

public ForeignTableOptions getForeignTableOptions() {
Expand Down Expand Up @@ -78,6 +93,31 @@ public void setTablespaceStorage(ColumnOption.Storage storage) {
tablespaceStorage = storage;
}

/** Creates a table access method option such as PostgreSQL's {@code USING heap}. */
public static TableOption accessMethod(String method) {
return new TableOption(Kind.ACCESS_METHOD, "USING", method, false);
}

/** Returns the {@code ON COMMIT} action, or null if this option has none. */
public OnCommitAction getOnCommitAction() {
return onCommitAction;
}

/** Replaces the current contents with an {@code ON COMMIT} clause. */
public void setOnCommitAction(OnCommitAction action) {
clearContents();
kind = Kind.ON_COMMIT;
name = "ON COMMIT";
onCommitAction = action;
}

/** Creates an {@code ON COMMIT} option, for example {@code ON COMMIT DROP}. */
public static TableOption onCommit(OnCommitAction action) {
TableOption option = new TableOption();
option.setOnCommitAction(action);
return option;
}

public List<IndexOption> getStorageParameters() {
return storageParameters;
}
Expand Down Expand Up @@ -140,6 +180,9 @@ public void setKind(Kind kind) {
if (kind != Kind.UNION) {
unionTables = null;
}
if (kind != Kind.ON_COMMIT) {
onCommitAction = null;
}
}

public String getName() {
Expand All @@ -158,6 +201,9 @@ public String getValue() {
if (storageParameters != null) {
return PlainSelect.getStringList(storageParameters, true, true);
}
if (onCommitAction != null) {
return onCommitAction.toString();
}
return unionTables == null ? value : PlainSelect.getStringList(unionTables, true, true);
}

Expand All @@ -167,6 +213,7 @@ public void setValue(String value) {
foreignTableOptions = null;
unionTables = null;
storageParameters = null;
onCommitAction = null;
}

/** Returns the mutable MERGE table sources, including an empty UNION list. */
Expand Down Expand Up @@ -214,7 +261,10 @@ public List<String> getTokens() {
result.add("=");
}
String renderedValue = getValue();
if (renderedValue != null) {
if (onCommitAction != null) {
// one token per keyword, as in the legacy raw options
Collections.addAll(result, renderedValue.split(" "));
} else if (renderedValue != null) {
result.add(renderedValue);
}
if (kind == Kind.TABLESPACE && tablespaceStorage != null) {
Expand Down Expand Up @@ -245,6 +295,7 @@ private void clearContents() {
foreignTableOptions = null;
unionTables = null;
storageParameters = null;
onCommitAction = null;
}

public TableOption withKind(Kind kind) {
Expand Down
47 changes: 39 additions & 8 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -16593,7 +16593,7 @@ void CreateTableOptions(List<TableOption> options):
}

TableOption CreateTableOption():
{ TableOption option; List<String> tokens; List<IndexOption> storageParameters; }
{ TableOption option; List<String> tokens; List<IndexOption> storageParameters; String name; }
{
(
LOOKAHEAD(<K_WITH> "(") <K_WITH> storageParameters=PostgreSqlIndexOptions() {
Expand All @@ -16604,6 +16604,12 @@ TableOption CreateTableOption():
option = new TableOption(TableOption.Kind.WITHOUT_OIDS, "WITHOUT OIDS", null, false);
}
|
LOOKAHEAD(<K_ON> <K_COMMIT>) option=OnCommitTableOption()
|
// USING INDEX TABLESPACE stays a raw parameter
LOOKAHEAD(2, { getToken(2).kind != K_INDEX }) <K_USING> name=RelObjectName()
{ option = TableOption.accessMethod(name); }
|
LOOKAHEAD({ isMySqlTableOptionAhead() }) option=MySqlTableOption()
|
// Snowflake: COPY is reserved because it starts the COPY statement
Expand All @@ -16613,6 +16619,28 @@ TableOption CreateTableOption():
{ return option; }
}

/** Temporary table ON COMMIT action, including Oracle's private temporary table forms. */
TableOption OnCommitTableOption():
{ TableOption.OnCommitAction action; }
{
<K_ON> <K_COMMIT>
(
<K_DELETE> <K_ROWS> { action = TableOption.OnCommitAction.DELETE_ROWS; }
|
<K_DROP> { action = TableOption.OnCommitAction.DROP; }
[ LOOKAHEAD({ isKeywordAhead("DEFINITION") }) ContextualKeyword("DEFINITION")
{ action = TableOption.OnCommitAction.DROP_DEFINITION; } ]
|
LOOKAHEAD({ isKeywordAhead("PRESERVE") }) ContextualKeyword("PRESERVE")
(
<K_ROWS> { action = TableOption.OnCommitAction.PRESERVE_ROWS; }
|
ContextualKeyword("DEFINITION") { action = TableOption.OnCommitAction.PRESERVE_DEFINITION; }
)
)
{ return TableOption.onCommit(action); }
}

TableOption MySqlTableOption(): {
Token tk;
Token tk2 = null;
Expand Down Expand Up @@ -18920,7 +18948,9 @@ void AlterExpressionAddConstraint(AlterExpression alterExp):
*/
AlterExpression AlterExpressionDrop():
{
AlterExpression alterExp = new AlterExpressionDrop();
AlterExpressionDrop drop = new AlterExpressionDrop();
AlterExpression alterExp = drop;
CascadeBehavior behavior;
Token tk;
Token tk2;
List<String> columnNames = null;
Expand Down Expand Up @@ -18959,8 +18989,9 @@ AlterExpression AlterExpressionDrop():
(tk=KeywordOrIdentifier() ) { alterExp.setColumnName(tk.image); }
[ "INVALIDATE" { alterExp.addParameters("INVALIDATE"); } ]
[
"CASCADE" { alterExp.addParameters("CASCADE"); }
[ "CONSTRAINTS" { alterExp.addParameters("CONSTRAINTS"); } ]
// Oracle CASCADE CONSTRAINTS remains a raw parameter
LOOKAHEAD(2) "CASCADE" "CONSTRAINTS" { alterExp.addParameters("CASCADE", "CONSTRAINTS"); }
| behavior=TypeAlterBehavior() { drop.setBehavior(behavior); }
]
)
|
Expand All @@ -18975,24 +19006,24 @@ AlterExpression AlterExpressionDrop():
(
tk=<K_UNIQUE> { alterExp.setOperation(AlterOperation.DROP_UNIQUE); }
columnNames=ColumnsNamesList() { alterExp.setDropColumns(columnNames); columnNames = null; }
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
)
|
(
tk=<K_PRIMARY> tk2=<K_KEY> { alterExp.setOperation(AlterOperation.DROP_PRIMARY_KEY); }
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
)
|
(
tk=<K_FOREIGN> tk2=<K_KEY> { alterExp.setOperation(AlterOperation.DROP_FOREIGN_KEY); }
columnNames=ColumnsNamesList() { alterExp.setDropColumns(columnNames); columnNames = null; }
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
)
|
(
<K_CONSTRAINT> [<K_IF> <K_EXISTS> { alterExp.setUsingIfExists(true); } ]
( tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER>) { alterExp.setConstraintName(tk.image); }
[ ( tk=<K_CASCADE> | tk=<K_RESTRICT> ) { alterExp.addParameters(tk.image); } ]
[ behavior=TypeAlterBehavior() { drop.setBehavior(behavior); } ]
)
)
{ return alterExp; }
Expand Down
34 changes: 31 additions & 3 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ The existing getter and setter names remain, but their return and parameter type
have changed, so clients must recompile. ``Truncate.setCascade(false)`` still
clears the clause. Foreign-key referential actions retain their separate enum.

``ALTER TABLE`` drop actions expose the same enum through
``AlterExpressionDrop.getBehavior()``, for example in
``ALTER TABLE t DROP COLUMN IF EXISTS c RESTRICT`` and
``ALTER TABLE t DROP CONSTRAINT c CASCADE``. Oracle's ``CASCADE CONSTRAINTS`` and
``INVALIDATE`` remain raw parameters. ``getParameters()`` still ends with the
behavior keyword; when a behavior is set, it returns a read-only snapshot, so use
``setBehavior`` to change the clause.

Edit shared table options
-------------------------

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

A temporary table's ``ON COMMIT`` clause and a table access method are single
options. ``Kind.ON_COMMIT`` exposes ``getOnCommitAction()``: ``PRESERVE_ROWS``,
``DELETE_ROWS`` or ``DROP``, plus ``PRESERVE_DEFINITION`` and ``DROP_DEFINITION``
for Oracle private temporary tables. ``Kind.ACCESS_METHOD`` stores the method of
``USING method`` as its value, including ``CREATE TABLE … USING method AS query``.
Use ``TableOption.onCommit`` and ``TableOption.accessMethod`` to build them.
``getTableOptionsStrings()`` keeps the legacy word tokens, such as ``ON``,
``COMMIT``, ``DELETE`` and ``ROWS``.

.. code-block:: java

CreateTable table = (CreateTable) CCJSqlParserUtil.parse(
"CREATE TEMP TABLE t (id INT) USING heap ON COMMIT DROP",
parser -> parser.withDialect(Dialect.POSTGRESQL));
table.getTableOption(TableOption.Kind.ACCESS_METHOD).orElseThrow().getValue(); // heap
table.getTableOption(TableOption.Kind.ON_COMMIT).orElseThrow()
.setOnCommitAction(TableOption.OnCommitAction.PRESERVE_ROWS);
// CREATE TEMP TABLE t (id INT) USING heap ON COMMIT PRESERVE ROWS

Inspect PostgreSQL schema statements
------------------------------------

Expand Down
Loading
Loading