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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
| **ClickHouse column selection** | `COLUMNS('regexp')` select items with chained `APPLY`, `EXCEPT`, and `REPLACE` transformers |
| **DML** | `INSERT` · `UPDATE` · `UPSERT` · `MERGE` · `DELETE` · `TRUNCATE TABLE` |
| **DDL** | `CREATE …` · `ALTER …` · `DROP …` |
| **PostgreSQL ownership** | `DROP OWNED` · `REASSIGN OWNED` |
| **MySQL table maintenance** | `ANALYZE` · `CHECK` · `OPTIMIZE` · `REPAIR TABLE` |
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |
| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` |
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import net.sf.jsqlparser.statement.grant.Grant;
import net.sf.jsqlparser.statement.merge.Merge;
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
import net.sf.jsqlparser.statement.role.ReassignOwned;
import net.sf.jsqlparser.statement.select.Fetch;
import net.sf.jsqlparser.statement.select.First;
import net.sf.jsqlparser.statement.select.KSQLWindow;
Expand Down Expand Up @@ -502,6 +503,12 @@ public enum Feature {
* @see Drop
*/
drop, dropTable, dropIndex, dropView, dropSchema, dropSequence, dropTableIfExists, dropIndexIfExists, dropViewIfExists, dropSchemaIfExists, dropSequenceIfExists,
/**
* PostgreSQL "REASSIGN OWNED BY ... TO ..." statement is allowed
*
* @see ReassignOwned
*/
reassignOwned,

/**
* SQL "CREATE DATABASE" statement is allowed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
import net.sf.jsqlparser.statement.role.AlterRole;
import net.sf.jsqlparser.statement.role.CreateRole;
import net.sf.jsqlparser.statement.role.ReassignOwned;
import net.sf.jsqlparser.statement.select.FromItemVisitor;
import net.sf.jsqlparser.statement.select.FromItemVisitorAdapter;
import net.sf.jsqlparser.statement.select.MySqlSelectIntoClause;
Expand Down Expand Up @@ -546,6 +547,11 @@ public <S> Void visit(Drop drop, S context) {
return null;
}

@Override
public <S> Void visit(ReassignOwned statement, S context) {
return schemaOnly();
}

@Override
public <S> Void visit(PragmaStatement pragmaStatement, S context) {
analysis.claimTopLevel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

import net.sf.jsqlparser.statement.role.CreateRole;
import net.sf.jsqlparser.statement.role.AlterRole;
import net.sf.jsqlparser.statement.role.ReassignOwned;
import net.sf.jsqlparser.statement.grant.Revoke;
import net.sf.jsqlparser.statement.grant.AlterDefaultPrivileges;
import net.sf.jsqlparser.statement.create.type.CreateType;
Expand Down Expand Up @@ -175,6 +176,14 @@ default void visit(Drop drop) {
this.visit(drop, null);
}

default <S> T visit(ReassignOwned statement, S context) {
return null;
}

default void visit(ReassignOwned statement) {
this.visit(statement, null);
}

<S> T visit(Truncate truncate, S context);

default void visit(Truncate truncate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import net.sf.jsqlparser.statement.role.CreateRole;
import net.sf.jsqlparser.statement.role.AlterRole;
import net.sf.jsqlparser.statement.role.ReassignOwned;
import net.sf.jsqlparser.statement.role.RoleOption;
import net.sf.jsqlparser.statement.grant.Revoke;
import net.sf.jsqlparser.statement.grant.AlterDefaultPrivileges;
Expand Down Expand Up @@ -368,6 +369,11 @@ public <S> T visit(Drop drop, S context) {
return null;
}

@Override
public <S> T visit(ReassignOwned statement, S context) {
return null;
}

@Override
public <S> T visit(Truncate truncate, S context) {
return truncate.getTable().accept(fromItemVisitor, context);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*-
* #%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.create.index;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* MySQL's {@code ALGORITHM [=] value} or {@code LOCK [=] value} clause of CREATE INDEX and DROP
* INDEX. The value keeps its SQL spelling, such as INPLACE, COPY, NONE or SHARED.
*/
public class AlgorithmOrLockOption implements Serializable {

public enum Kind {
ALGORITHM, LOCK
}

private Kind kind;
private String value;
private boolean useEquals;

public AlgorithmOrLockOption(Kind kind, String value, boolean useEquals) {
this.kind = Objects.requireNonNull(kind, "kind");
this.value = Objects.requireNonNull(value, "value");
this.useEquals = useEquals;
}

public static AlgorithmOrLockOption algorithm(String value) {
return new AlgorithmOrLockOption(Kind.ALGORITHM, value, true);
}

public static AlgorithmOrLockOption lock(String value) {
return new AlgorithmOrLockOption(Kind.LOCK, value, true);
}

public Kind getKind() {
return kind;
}

public void setKind(Kind kind) {
this.kind = Objects.requireNonNull(kind, "kind");
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = Objects.requireNonNull(value, "value");
}

public boolean isUseEquals() {
return useEquals;
}

public void setUseEquals(boolean useEquals) {
this.useEquals = useEquals;
}

/** Returns an unmodifiable token snapshot, such as {@code [ALGORITHM, =, INPLACE]}. */
public List<String> getTokens() {
List<String> tokens = new ArrayList<>();
tokens.add(kind.name());
if (useEquals) {
tokens.add("=");
}
tokens.add(value);
return Collections.unmodifiableList(tokens);
}

@Override
public String toString() {
return String.join(" ", getTokens());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class CreateIndex implements Statement {
private Table table;
private Index index;
private IndexOptions detachedOptions;
private List<String> tailParameters;
private final List<AlgorithmOrLockOption> algorithmOrLockOptions = new ArrayList<>();
private boolean indexTypeBeforeOn = false;
private boolean usingIfNotExists = false;
private boolean concurrently;
Expand Down Expand Up @@ -170,6 +170,7 @@ public void setIndex(Index index) {
detachedOptions.setNullsDistinct(previousOptions.getNullsDistinct());
detachedOptions.setStorageParameters(previousOptions.getStorageParameters());
detachedOptions.setTableSpace(previousOptions.getTableSpace());
detachedOptions.setSpecOptions(previousOptions.getSpecOptions());
}
} else {
if (previousOptions != null && previousOptions != index.getIndexOptions()) {
Expand All @@ -185,6 +186,9 @@ public void setIndex(Index index) {
if (index.getTableSpace() == null) {
index.setTableSpace(previousOptions.getTableSpace());
}
if (index.getSpecOptions() == null) {
index.setSpecOptions(previousOptions.getSpecOptions());
}
}
detachedOptions = null;
}
Expand All @@ -199,12 +203,56 @@ public void setTable(Table table) {
this.table = table;
}

/**
* Returns the mutable MySQL ALGORITHM and LOCK clauses in source order. They are rendered after
* the index options, as MySQL requires.
*/
public List<AlgorithmOrLockOption> getAlgorithmOrLockOptions() {
return algorithmOrLockOptions;
}

/** Copies the list membership and retains the option nodes; null clears the clauses. */
public void setAlgorithmOrLockOptions(List<AlgorithmOrLockOption> options) {
algorithmOrLockOptions.clear();
if (options != null) {
algorithmOrLockOptions.addAll(options);
}
}

public CreateIndex withAlgorithmOrLockOptions(List<AlgorithmOrLockOption> options) {
setAlgorithmOrLockOptions(options);
return this;
}

public CreateIndex addAlgorithmOrLockOptions(AlgorithmOrLockOption... options) {
Collections.addAll(algorithmOrLockOptions, options);
return this;
}

/**
* Returns an unmodifiable token snapshot of the trailing clauses: the index options after the
* key list ({@link Index#getSpecOptions()}), followed by {@link #getAlgorithmOrLockOptions()}.
* Edit those nodes instead of the snapshot.
*/
public List<String> getTailParameters() {
return tailParameters;
List<String> tokens = new ArrayList<>();
IndexOptions options = getOptions();
if (options != null && options.getIndexSpec() != null) {
tokens.addAll(options.getIndexSpec());
}
for (AlgorithmOrLockOption option : algorithmOrLockOptions) {
tokens.addAll(option.getTokens());
}
return Collections.unmodifiableList(tokens);
}

/**
* Replaces the index options after the key list and the ALGORITHM and LOCK clauses with one
* opaque option holding a copy of the tokens, without parsing them. Null clears them.
*/
public void setTailParameters(List<String> tailParameters) {
this.tailParameters = tailParameters;
algorithmOrLockOptions.clear();
getOrCreateOptions().setIndexSpec(tailParameters);
}

@Override
Expand All @@ -223,11 +271,14 @@ public StringBuilder appendTo(StringBuilder buffer, Consumer<Expression> express
appendIndexTarget(buffer);
appendIndexColumns(buffer, expressionPrinter);
appendPostgreSqlTail(buffer, expressionPrinter);
if (tailParameters != null) {
for (String param : tailParameters) {
buffer.append(" ").append(param);
if (index.getSpecOptions() != null) {
for (IndexSpecOption option : index.getSpecOptions()) {
buffer.append(" ").append(option);
}
}
for (AlgorithmOrLockOption option : algorithmOrLockOptions) {
buffer.append(" ").append(option);
}
return buffer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ public Index withIndexSpec(List<String> value) {
return this;
}

public Index withSpecOptions(List<IndexSpecOption> value) {
setSpecOptions(value);
return this;
}

public Index withClustering(IndexOptions.Clustering value) {
setClustering(value);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ default void setIndexSpec(List<String> value) {
getIndexOptions().setIndexSpec(value);
}

default List<IndexSpecOption> getSpecOptions() {
return getIndexOptions().getSpecOptions();
}

default void setSpecOptions(List<IndexSpecOption> value) {
getIndexOptions().setSpecOptions(value);
}

default String getCommentText() {
return getIndexOptions().getCommentText();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import net.sf.jsqlparser.expression.Expression;
Expand All @@ -23,7 +26,7 @@ public enum Clustering {
}

private String using;
private List<String> indexSpec;
private List<IndexSpecOption> specOptions;
private String commentText;
private Clustering clustering;
private Boolean nullsDistinct;
Expand All @@ -39,12 +42,60 @@ public void setUsing(String using) {
this.using = using;
}

/**
* Returns the mutable, ordered options written after the key list, or null when unset. An index
* type written before the key list is {@link #getUsing()} instead.
*/
public List<IndexSpecOption> getSpecOptions() {
return specOptions;
}

/** Copies the list membership and retains the option nodes; null clears the options. */
public void setSpecOptions(List<IndexSpecOption> specOptions) {
this.specOptions = specOptions == null ? null : new ArrayList<>(specOptions);
}

public IndexOptions addSpecOptions(IndexSpecOption... options) {
return addSpecOptions(Arrays.asList(options));
}

public IndexOptions addSpecOptions(Collection<? extends IndexSpecOption> options) {
if (specOptions == null) {
specOptions = new ArrayList<>();
}
specOptions.addAll(options);
return this;
}

/**
* Returns an unmodifiable token snapshot of {@link #getSpecOptions()}, such as
* {@code [KEY_BLOCK_SIZE, =, 8, COMMENT, 'text']}, or null when unset. Edit the option nodes
* instead of the snapshot.
*/
public List<String> getIndexSpec() {
return indexSpec;
if (specOptions == null) {
return null;
}
List<String> tokens = new ArrayList<>();
for (IndexSpecOption option : specOptions) {
tokens.addAll(option.getTokens());
}
return Collections.unmodifiableList(tokens);
}

/**
* Replaces all options after the key list with one opaque option holding a copy of the tokens,
* without parsing them. Null clears the options; an empty list stores no options.
*/
public void setIndexSpec(List<String> indexSpec) {
this.indexSpec = indexSpec;
if (indexSpec == null) {
specOptions = null;
return;
}
specOptions = new ArrayList<>();
if (!indexSpec.isEmpty()) {
specOptions.add(IndexSpecOption.raw(indexSpec));
}
}

public String getCommentText() {
Expand Down
Loading
Loading