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
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public enum Dialect {
AdjacentStringLiterals.WHITESPACE,
Feature.allowDoubleQuotedStrings,
Feature.allowBackslashEscapeCharacter), SNOWFLAKE(
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS;
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS, COCKROACHDB;

private final Set<Feature> lexerFeatures;
private final AdjacentStringLiterals adjacentStringLiterals;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,16 +397,9 @@ public <S> T visit(CreateView createView, S context) {
public <S> T visit(Alter alter, S context) {
alter.getTable().accept(fromItemVisitor, context);
for (AlterExpression action : alter.getAlterExpressions()) {
if (action.getColDataTypeList() != null) {
action.getColDataTypeList().forEach(column -> TableDefinitionTraversal.visit(column,
expression -> expression.accept(expressionVisitor, context),
table -> table.accept(fromItemVisitor, context)));
}
if (action.getIndex() != null) {
TableDefinitionTraversal.visit(action.getIndex(),
expression -> expression.accept(expressionVisitor, context),
table -> table.accept(fromItemVisitor, context));
}
TableDefinitionTraversal.visit(action,
expression -> expression.accept(expressionVisitor, context),
table -> table.accept(fromItemVisitor, context));
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*-
* #%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.alter;

import java.util.Iterator;
import java.util.function.Consumer;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.create.table.Index;

/**
* CockroachDB's ALTER PRIMARY KEY USING COLUMNS operation. Key elements and storage options are
* available through {@link #getIndex()}; hash sharding and the legacy WITH BUCKET_COUNT expression
* are represented separately.
*/
public class AlterExpressionPrimaryKey extends AlterExpression {
private boolean usingHash;
private Expression bucketCount;

public AlterExpressionPrimaryKey() {
setOperation(AlterOperation.ALTER_PRIMARY_KEY);
setIndex(new Index().withType("PRIMARY KEY"));
}

public boolean isUsingHash() {
return usingHash;
}

public void setUsingHash(boolean usingHash) {
this.usingHash = usingHash;
}

public Expression getBucketCount() {
return bucketCount;
}

public void setBucketCount(Expression bucketCount) {
this.bucketCount = bucketCount;
}

@Override
protected void appendBody(StringBuilder builder) {
appendDefinition(builder, expression -> builder.append(expression));
}

/** Shares statement rendering while preserving expression visitor customization. */
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
appendDefinition(builder, expressionPrinter);
appendCommonTail(builder);
return builder;
}

private void appendDefinition(StringBuilder builder, Consumer<Expression> expressionPrinter) {
builder.append("ALTER PRIMARY KEY USING COLUMNS (");
if (getIndex().getColumns() != null) {
for (Iterator<Index.ColumnParams> columns = getIndex().getColumns().iterator(); columns
.hasNext();) {
columns.next().appendTo(builder, expressionPrinter);
if (columns.hasNext()) {
builder.append(", ");
}
}
}
builder.append(')');
appendSharding(builder, expressionPrinter);
appendStorageOptions(builder, expressionPrinter);
}

private void appendSharding(StringBuilder builder, Consumer<Expression> expressionPrinter) {
if (usingHash) {
builder.append(" USING HASH");
if (bucketCount != null) {
builder.append(" WITH BUCKET_COUNT = ");
expressionPrinter.accept(bucketCount);
}
}
}

private void appendStorageOptions(StringBuilder builder,
Consumer<Expression> expressionPrinter) {
if (getIndex().getStorageParameters() != null) {
builder.append(" WITH (");
for (Iterator<Index.Option> options =
getIndex().getStorageParameters().iterator(); options.hasNext();) {
options.next().appendTo(builder, expressionPrinter);
if (options.hasNext()) {
builder.append(", ");
}
}
builder.append(')');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import java.util.Locale;

public enum AlterOperation {
ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, CONVERT, COLLATE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC, ADD_PARTITION, DROP_PARTITION, ATTACH_PARTITION, DETACH_PARTITION, DISCARD_PARTITION, IMPORT_PARTITION, TRUNCATE_PARTITION, COALESCE_PARTITION, REORGANIZE_PARTITION, EXCHANGE_PARTITION, ANALYZE_PARTITION, CHECK_PARTITION, OPTIMIZE_PARTITION, REBUILD_PARTITION, REPAIR_PARTITION, REMOVE_PARTITIONING, PARTITION_BY, SET_TABLE_OPTION, ENGINE, FORCE, KEY_BLOCK_SIZE, LOCK, DISCARD_TABLESPACE, IMPORT_TABLESPACE, DISABLE_KEYS, ENABLE_KEYS, ENABLE_ROW_LEVEL_SECURITY, DISABLE_ROW_LEVEL_SECURITY, FORCE_ROW_LEVEL_SECURITY, NO_FORCE_ROW_LEVEL_SECURITY;
ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, CONVERT, COLLATE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC, ADD_PARTITION, DROP_PARTITION, ATTACH_PARTITION, DETACH_PARTITION, DISCARD_PARTITION, IMPORT_PARTITION, TRUNCATE_PARTITION, COALESCE_PARTITION, REORGANIZE_PARTITION, EXCHANGE_PARTITION, ANALYZE_PARTITION, CHECK_PARTITION, OPTIMIZE_PARTITION, REBUILD_PARTITION, REPAIR_PARTITION, REMOVE_PARTITIONING, PARTITION_BY, SET_TABLE_OPTION, ENGINE, FORCE, KEY_BLOCK_SIZE, LOCK, DISCARD_TABLESPACE, IMPORT_TABLESPACE, DISABLE_KEYS, ENABLE_KEYS, ENABLE_ROW_LEVEL_SECURITY, DISABLE_ROW_LEVEL_SECURITY, FORCE_ROW_LEVEL_SECURITY, NO_FORCE_ROW_LEVEL_SECURITY, ALTER_PRIMARY_KEY;

public static AlterOperation from(String operation) {
return Enum.valueOf(AlterOperation.class, operation.toUpperCase(Locale.ROOT));
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TableDefinitionTraversal.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.LikeClause;
import net.sf.jsqlparser.statement.alter.AlterExpression;
import net.sf.jsqlparser.statement.alter.AlterExpressionPrimaryKey;
import net.sf.jsqlparser.statement.create.index.CreateIndex;
import net.sf.jsqlparser.statement.create.table.CheckConstraint;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
Expand All @@ -40,6 +41,23 @@ public static void visit(CreateIndex createIndex, Consumer<Expression> expressio
accept(createIndex.getWhere(), expressions);
}

/** Visits the structured definitions and expressions belonging to a single ALTER action. */
public static void visit(AlterExpression action, Consumer<Expression> expressions,
Consumer<Table> tables) {
if (action.getColDataTypeList() != null) {
action.getColDataTypeList().forEach(column -> visit(column, expressions, tables));
}
if (action.getIndex() != null) {
visit(action.getIndex(), expressions, tables);
}
if (action instanceof AlterExpressionPrimaryKey) {
AlterExpressionPrimaryKey primaryKey = (AlterExpressionPrimaryKey) action;
if (primaryKey.isUsingHash()) {
accept(primaryKey.getBucketCount(), expressions);
}
}
}

public static void visit(CreateTable table, Consumer<Expression> expressions,
Consumer<Table> tables) {
if (table.getTableElements() != null) {
Expand Down
13 changes: 3 additions & 10 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1660,16 +1660,9 @@ public void visit(CreateView createView) {
public <S> Void visit(Alter alter, S context) {
for (net.sf.jsqlparser.statement.alter.AlterExpression action : alter
.getAlterExpressions()) {
if (action.getColDataTypeList() != null) {
action.getColDataTypeList().forEach(column -> TableDefinitionTraversal.visit(column,
expression -> expression.accept(this, context),
table -> visit(table, context)));
}
if (action.getIndex() != null) {
TableDefinitionTraversal.visit(action.getIndex(),
expression -> expression.accept(this, context),
table -> visit(table, context));
}
TableDefinitionTraversal.visit(action,
expression -> expression.accept(this, context),
table -> visit(table, context));
}
return alter.getTable().accept(this, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.statement.alter.AlterExpression;
import net.sf.jsqlparser.statement.alter.AlterExpressionPrimaryKey;
import net.sf.jsqlparser.statement.create.table.DefaultConstraint;
import net.sf.jsqlparser.statement.select.PlainSelect;
import java.util.Iterator;
Expand Down Expand Up @@ -48,6 +49,11 @@ public void deParse(Alter alter) {
}

private void deParseAction(AlterExpression action) {
if (action instanceof AlterExpressionPrimaryKey) {
((AlterExpressionPrimaryKey) action).appendTo(builder,
expression -> expression.accept(expressionVisitor, null));
return;
}
if (action.getIndex() instanceof DefaultConstraint) {
builder.append(action.getOperation()).append(' ');
new TableElementDeParser(builder, expressionVisitor).deParse(action.getIndex());
Expand Down
55 changes: 55 additions & 0 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -15305,6 +15305,57 @@ AlterExpression AlterExpressionAddAlterModify():
{ return alterExp; }
}

/** Parses CockroachDB primary-key elements without legacy raw index parameters. */
Index.ColumnParams CockroachPrimaryKeyColumn():
{
Index.ColumnParams column;
String name;
Expression expression;
Token token;
}
{
(
LOOKAHEAD({ isFunctionAhead() }) expression=Function()
{ column = new Index.ColumnParams(expression).withExpressionParenthesized(false); }
|
name=RelObjectName() { column = new Index.ColumnParams(name); }
|
"(" expression=Expression() ")" { column = new Index.ColumnParams(expression); }
)
[ (token=<S_IDENTIFIER> | token=<S_QUOTED_IDENTIFIER>) { column.setOperatorClass(token.image); } ]
[ (token=<K_ASC> | token=<K_DESC>) {
column.setSortOrder(Index.ColumnParams.SortOrder.valueOf(token.image.toUpperCase(Locale.ROOT)));
} ]
[ <K_NULLS> (token=<K_FIRST> | token=<K_LAST>) {
column.setNullOrdering(Index.ColumnParams.NullOrdering.valueOf(token.image.toUpperCase(Locale.ROOT)));
} ]
{ return column; }
}

AlterExpressionPrimaryKey CockroachAlterPrimaryKey():
{
AlterExpressionPrimaryKey action = new AlterExpressionPrimaryKey();
List<Index.ColumnParams> columns = new ArrayList<Index.ColumnParams>();
Index.ColumnParams column;
List<Index.Option> options;
Expression bucketCount;
Token token;
}
{
<K_ALTER> <K_PRIMARY> <K_KEY> <K_USING> <K_COLUMNS>
"(" column=CockroachPrimaryKeyColumn() { columns.add(column); }
( "," column=CockroachPrimaryKeyColumn() { columns.add(column); } )* ")"
{ action.getIndex().setColumns(columns); }
[ <K_USING> <K_HASH> { action.setUsingHash(true); }
[ LOOKAHEAD(<K_WITH> <S_IDENTIFIER>) <K_WITH> token=<S_IDENTIFIER> {
requireDdlSyntax("BUCKET_COUNT".equalsIgnoreCase(token.image), "Expected BUCKET_COUNT");
}
"=" bucketCount=Expression() { action.setBucketCount(bucketCount); } ]
]
[ <K_WITH> options=PostgreSqlIndexOptions() { action.getIndex().setStorageParameters(options); } ]
{ return action; }
}

/**
* Parses all RENAME variants within ALTER TABLE.
* Handles: RENAME [COLUMN] old TO new, RENAME [TO|AS] tablename, RENAME tablename,
Expand Down Expand Up @@ -15417,6 +15468,10 @@ AlterExpression AlterExpression():
{

(
LOOKAHEAD(<K_ALTER> <K_PRIMARY> <K_KEY>,
{ Dialect.COCKROACHDB.name().equals(getAsString(Feature.dialect)) })
alterExp = CockroachAlterPrimaryKey()
|
alterExp = AlterExpressionAddAlterModify()
|
(
Expand Down
8 changes: 8 additions & 0 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,8 @@ One grammar covers every supported RDBMS, but a few pieces of syntax mean differ
- GoogleSQL ``CREATE [UNIQUE] NULL_FILTERED INDEX`` with a separate null-filtering flag
* - ``DORIS``
- ``JOIN [shuffle]`` and ``JOIN [broadcast]`` distribution hints
* - ``COCKROACHDB``
- ``ALTER TABLE ... ALTER PRIMARY KEY USING COLUMNS (...)`` with optional hash sharding and storage parameters

Features set explicitly *after* the preset win over it.

Expand All @@ -750,6 +752,12 @@ Doris distribution hints require ``parser.withDialect(Dialect.DORIS)``.
the existing SQL Server hints use ``Position.BEFORE_JOIN``. Rendering preserves
both the position and the brackets around a Doris hint.

CockroachDB primary-key changes require ``parser.withDialect(Dialect.COCKROACHDB)``.
Their action is an ``AlterExpressionPrimaryKey`` with key elements and storage
parameters in ``getIndex()``. ``isUsingHash()`` preserves ``USING HASH``, while
``getBucketCount()`` holds the legacy ``WITH BUCKET_COUNT = expression`` value.
The newer ``WITH (bucket_count = expression)`` form uses the index storage parameters.

With ``Dialect.SQLSERVER``, ``PRIMARY KEY NONCLUSTERED (id)`` and
``UNIQUE CLUSTERED (id)`` store their clustering option in ``Index.getClustering()``
for both ``CREATE TABLE`` and ``ALTER TABLE``. Without that dialect, these words
Expand Down
Loading
Loading