Skip to content
Merged
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 …` |
| **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)` |
| **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` |
Expand Down
8 changes: 8 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 @@ -26,6 +26,7 @@
import net.sf.jsqlparser.statement.SetStatement;
import net.sf.jsqlparser.statement.ShowColumnsStatement;
import net.sf.jsqlparser.statement.ShowStatement;
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
import net.sf.jsqlparser.statement.UseStatement;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.alter.sequence.AlterSequence;
Expand Down Expand Up @@ -458,6 +459,13 @@ public enum Feature {
*/
analyze,

/**
* MySQL/MariaDB "CHECK TABLE", "OPTIMIZE TABLE" and "REPAIR TABLE" statements are allowed
*
* @see TableMaintenanceStatement
*/
tableMaintenance,

/**
* SQL "TRUNCATE" statement is allowed
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,20 @@ public <S> Void visit(Analyze analyze, S context) {
return null;
}

@Override
public <S> Void visit(TableMaintenanceStatement statement, S context) {
analysis.claimTopLevel();
analysis.certain(StmtFeature.READS_DATA, StmtFeature.RETURNS_RESULT_SET);
if (statement.getOperation() != TableMaintenanceStatement.Operation.CHECK) {
// OPTIMIZE rebuilds table storage; REPAIR may also rewrite or discard rows
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
if (statement.getOperation() == TableMaintenanceStatement.Operation.REPAIR) {
analysis.possible(StmtFeature.MODIFIES_DATA);
}
}
return null;
}

private Void schemaOnly() {
analysis.claimTopLevel();
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,14 @@ default void visit(DoStatement statement) {
this.visit(statement, null);
}

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

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

default void visit(Execute execute) {
this.visit(execute, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,11 @@ public <S> T visit(Analyze analyze, S context) {
return null;
}

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

@Override
public <S> T visit(SavepointStatement savepointStatement, S context) {
// @todo: do something usefully here
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*-
* #%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;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import net.sf.jsqlparser.schema.Table;

/**
* A MySQL/MariaDB {@code CHECK TABLE}, {@code OPTIMIZE TABLE} or {@code REPAIR TABLE} statement.
* {@code ANALYZE TABLE} remains {@link net.sf.jsqlparser.statement.analyze.Analyze}.
*/
public class TableMaintenanceStatement implements Statement {

public enum Operation {
CHECK, OPTIMIZE, REPAIR;

/** Whether this operation accepts the option after its table list. */
public boolean allows(Option option) {
switch (this) {
case CHECK:
return option != Option.USE_FRM;
case REPAIR:
return option == Option.QUICK || option == Option.EXTENDED
|| option == Option.USE_FRM;
default:
return false;
}
}

/** Whether this operation accepts {@code NO_WRITE_TO_BINLOG} or {@code LOCAL}. */
public boolean allowsModifier() {
return this != CHECK;
}
}

public enum Modifier {
NO_WRITE_TO_BINLOG, LOCAL
}

public enum Option {
FOR_UPGRADE, QUICK, FAST, MEDIUM, EXTENDED, CHANGED, USE_FRM;

/** Returns the SQL spelling, for example {@code FOR UPGRADE}. */
public String getKeyword() {
return this == FOR_UPGRADE ? "FOR UPGRADE" : name();
}

/** Returns the single-word option spelled by {@code word}, or null. */
public static Option fromKeyword(String word) {
for (Option option : values()) {
if (option != FOR_UPGRADE && option.name().equalsIgnoreCase(word)) {
return option;
}
}
return null;
}
}

private Operation operation;
private Modifier modifier;
private boolean tablesKeyword;
private final List<Table> tables = new ArrayList<>();
private final List<Option> options = new ArrayList<>();

public TableMaintenanceStatement() {
// empty constructor
}

public TableMaintenanceStatement(Operation operation) {
this.operation = operation;
}

@Override
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
return statementVisitor.visit(this, context);
}

public Operation getOperation() {
return operation;
}

public void setOperation(Operation operation) {
this.operation = operation;
}

public Modifier getModifier() {
return modifier;
}

public void setModifier(Modifier modifier) {
this.modifier = modifier;
}

/** Whether the statement is spelled with {@code TABLES} instead of {@code TABLE}. */
public boolean isTablesKeyword() {
return tablesKeyword;
}

public void setTablesKeyword(boolean tablesKeyword) {
this.tablesKeyword = tablesKeyword;
}

public List<Table> getTables() {
return Collections.unmodifiableList(tables);
}

public void setTables(Collection<? extends Table> tables) {
this.tables.clear();
if (tables != null) {
this.tables.addAll(tables);
}
}

/** Returns the options in source order; repeated options are retained as written. */
public List<Option> getOptions() {
return Collections.unmodifiableList(options);
}

public void setOptions(Collection<Option> options) {
this.options.clear();
if (options != null) {
this.options.addAll(options);
}
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder().append(operation);
if (modifier != null) {
builder.append(' ').append(modifier);
}
builder.append(tablesKeyword ? " TABLES " : " TABLE ")
.append(tables.stream().map(Table::toString).collect(Collectors.joining(", ")));
for (Option option : options) {
builder.append(' ').append(option.getKeyword());
}
return builder.toString();
}

public TableMaintenanceStatement withOperation(Operation operation) {
setOperation(operation);
return this;
}

public TableMaintenanceStatement withModifier(Modifier modifier) {
setModifier(modifier);
return this;
}

public TableMaintenanceStatement withTablesKeyword(boolean tablesKeyword) {
setTablesKeyword(tablesKeyword);
return this;
}

public TableMaintenanceStatement withTables(Collection<? extends Table> tables) {
setTables(tables);
return this;
}

public TableMaintenanceStatement addTables(Table... tables) {
Collections.addAll(this.tables, tables);
return this;
}

public TableMaintenanceStatement withOptions(Collection<Option> options) {
setOptions(options);
return this;
}

public TableMaintenanceStatement addOptions(Option... options) {
Collections.addAll(this.options, options);
return this;
}
}
12 changes: 12 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
import net.sf.jsqlparser.statement.StartTransaction;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
import net.sf.jsqlparser.statement.Statements;
import net.sf.jsqlparser.statement.UnsupportedStatement;
import net.sf.jsqlparser.statement.UseStatement;
Expand Down Expand Up @@ -1635,6 +1636,17 @@ public void visit(Analyze analyze) {
StatementVisitor.super.visit(analyze);
}

@Override
public <S> Void visit(TableMaintenanceStatement statement, S context) {
statement.getTables().forEach(table -> visit(table, context));
return null;
}

@Override
public void visit(TableMaintenanceStatement statement) {
StatementVisitor.super.visit(statement);
}

@Override
public <S> Void visit(Drop drop, S context) {
drop.visitTables(table -> visit(table, context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import net.sf.jsqlparser.statement.UnsupportedStatement;
import net.sf.jsqlparser.statement.UseStatement;
import net.sf.jsqlparser.statement.SetIdentityInsertStatement;
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.alter.AlterSession;
import net.sf.jsqlparser.statement.alter.AlterSystemStatement;
Expand Down Expand Up @@ -334,6 +335,12 @@ public <S> StringBuilder visit(Analyze analyzer, S context) {
return builder;
}

@Override
public <S> StringBuilder visit(TableMaintenanceStatement statement, S context) {
builder.append(statement);
return builder;
}

@Override
public <S> StringBuilder visit(Alter alter, S context) {
AlterDeParser alterDeParser = new AlterDeParser(builder, expressionDeParser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public enum MariaDbVersion implements Version {
// https://mariadb.com/kb/en/truncate-table/
Feature.truncate,

// https://mariadb.com/kb/en/table-statements/
Feature.tableMaintenance,

// https://mariadb.com/kb/en/call/
Feature.execute, Feature.executeCall,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ public enum MySqlVersion implements Version {
// https://dev.mysql.com/doc/refman/8.0/en/truncate-table.html
Feature.truncate,

// https://dev.mysql.com/doc/refman/8.0/en/table-maintenance-statements.html
Feature.tableMaintenance,

// https://dev.mysql.com/doc/refman/8.0/en/call.html
Feature.execute, Feature.executeCall,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import net.sf.jsqlparser.statement.UnsupportedStatement;
import net.sf.jsqlparser.statement.UseStatement;
import net.sf.jsqlparser.statement.SetIdentityInsertStatement;
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.alter.AlterSession;
import net.sf.jsqlparser.statement.alter.AlterSystemStatement;
Expand Down Expand Up @@ -509,6 +510,12 @@ public <S> Void visit(Analyze analyze, S context) {
return null;
}

@Override
public <S> Void visit(TableMaintenanceStatement statement, S context) {
getValidator(TableMaintenanceValidator.class).validate(statement);
return null;
}

@Override
public <S> Void visit(SavepointStatement savepointStatement, S context) {
// TODO: not yet implemented
Expand Down Expand Up @@ -823,6 +830,11 @@ public void visit(Analyze analyze) {
visit(analyze, null);
}

@Override
public void visit(TableMaintenanceStatement statement) {
visit(statement, null);
}

public void visit(SavepointStatement savepointStatement) {
visit(savepointStatement, null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*-
* #%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.util.validation.validator;

import net.sf.jsqlparser.parser.feature.Feature;
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
import net.sf.jsqlparser.util.validation.ValidationCapability;
import net.sf.jsqlparser.util.validation.metadata.NamedObject;

public class TableMaintenanceValidator extends AbstractValidator<TableMaintenanceStatement> {
@Override
public void validate(TableMaintenanceStatement statement) {
for (ValidationCapability c : getCapabilities()) {
validateFeature(c, Feature.tableMaintenance);
statement.getTables().forEach(table -> validateName(c, NamedObject.table,
table.getFullyQualifiedName(), true));
}
}
}
Loading
Loading