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
212 changes: 143 additions & 69 deletions src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,14 @@ public enum TableRenameKeyword {
private List<ColumnSetDefault> columnSetDefaultList;
private List<ColumnSetVisibility> columnSetVisibilityList;

private List<String> pkColumns;
private List<String> ukColumns;
private String ukName;
private List<String> dropColumns;
private Index index = null;
private Index oldIndex = null;
private NamedConstraint constraint;
private String newConstraintName;
private String constraintName;
private boolean usingIfExists;

private boolean uk;
private boolean ukTypeSpecified;
private boolean useEqual;

private List<String> partitions;
Expand Down Expand Up @@ -634,75 +630,144 @@ public void setUsingIfExists(boolean usingIfExists) {
}

/**
* Returns a snapshot of the rendered key elements for a structured PRIMARY_KEY, or the legacy
* list otherwise. Changing the snapshot does not change the constraint; use
* {@link #setPkColumns(List)} to replace its keys, or {@link KeyConstraint#getColumns()} for
* structured edits.
* Returns the stored key definition, or null for another definition. The returned object is
* shared with {@link #getConstraint()} and can be edited directly.
*/
public KeyConstraint getKeyConstraint() {
return constraint instanceof KeyConstraint ? (KeyConstraint) constraint : null;
}

/**
* Returns the owned mutable column targets for parenthesized DROP, DROP UNIQUE or DROP FOREIGN
* KEY syntax, or null when omitted. Stored targets are independent of any ADD definition.
*/
public List<String> getDropColumns() {
return dropColumns;
}

/** Copies the supplied targets without changing the operation or any stored definition. */
public void setDropColumns(List<String> dropColumns) {
this.dropColumns = dropColumns == null ? null : new ArrayList<>(dropColumns);
}

public AlterExpression withDropColumns(List<String> dropColumns) {
setDropColumns(dropColumns);
return this;
}

public AlterExpression addDropColumns(String... columns) {
return addDropColumns(Arrays.asList(columns));
}

public AlterExpression addDropColumns(Collection<String> columns) {
if (dropColumns == null) {
dropColumns = new ArrayList<>();
}
dropColumns.addAll(columns);
return this;
}

/**
* Returns a rendered snapshot of the stored PRIMARY KEY's elements, or null when absent. For
* DROP, DROP UNIQUE and DROP FOREIGN KEY operations this legacy accessor instead returns a
* snapshot of {@link #getDropColumns()}. Snapshot edits never change the AST. New code should
* use {@link #getKeyConstraint()} or {@link #getDropColumns()} explicitly.
*/
public List<String> getPkColumns() {
if (usesLegacyDropColumns()) {
return dropColumns == null ? null : new ArrayList<>(dropColumns);
}
return hasKeyConstraint(ConstraintKind.PRIMARY_KEY)
? ((KeyConstraint) constraint).getColumnsNames()
: pkColumns;
: null;
}

/**
* Replaces a structured PRIMARY_KEY's elements using
* {@link KeyConstraint#setColumnsNames(List)}. The strings are not parsed and existing element
* attributes are not retained, even when the rendered SQL is unchanged. Use
* {@link KeyConstraint#setColumns(List)} to preserve structured elements. Without a matching
* constraint, stores the legacy list.
* Replaces PRIMARY KEY elements with opaque names, without parsing or retaining element
* attributes. A non-null list creates a definition when none exists; a competing definition
* must be replaced explicitly using {@link #setConstraint(NamedConstraint)}. Null clears only
* an existing PRIMARY KEY's elements and does not create a definition. With DROP, DROP UNIQUE
* or DROP FOREIGN KEY already selected, delegates to {@link #setDropColumns(List)} instead. Set
* the operation first for this legacy DROP alias; new builders should use setDropColumns.
*
* @throws IllegalStateException if a non-null list would replace another definition
*/
public void setPkColumns(List<String> pkColumns) {
if (hasKeyConstraint(ConstraintKind.PRIMARY_KEY)) {
((KeyConstraint) constraint).setColumnsNames(pkColumns);
this.pkColumns = null;
} else {
this.pkColumns = pkColumns;
if (usesLegacyDropColumns()) {
setDropColumns(pkColumns);
return;
}
KeyConstraint key = keyForUpdate(ConstraintKind.PRIMARY_KEY, pkColumns != null);
if (key != null) {
key.setColumnsNames(pkColumns);
}
}

/**
* Returns a snapshot of the rendered key elements for a structured UNIQUE, or the legacy list
* otherwise. Changing the snapshot does not change the constraint; use
* {@link #setUkColumns(List)} to replace its keys, or {@link KeyConstraint#getColumns()} for
* structured edits.
* Returns a snapshot of the stored UNIQUE's elements, or null when absent. Changing the
* snapshot does not change the constraint; use {@link #setUkColumns(List)} to replace its keys,
* or {@link KeyConstraint#getColumns()} for structured edits.
*/
public List<String> getUkColumns() {
return hasKeyConstraint(ConstraintKind.UNIQUE)
? ((KeyConstraint) constraint).getColumnsNames()
: ukColumns;
: null;
}

/**
* Replaces a structured UNIQUE's elements using {@link KeyConstraint#setColumnsNames(List)}.
* The strings are not parsed and existing element attributes are not retained, even when the
* rendered SQL is unchanged. Use {@link KeyConstraint#setColumns(List)} to preserve structured
* elements. Without a matching constraint, stores the legacy list.
* elements. A non-null list creates a UNIQUE definition only when none exists; a competing
* definition must be replaced explicitly. Null clears only an existing UNIQUE's elements.
*
* @throws IllegalStateException if a non-null list would replace another definition
*/
public void setUkColumns(List<String> ukColumns) {
if (hasKeyConstraint(ConstraintKind.UNIQUE)) {
((KeyConstraint) constraint).setColumnsNames(ukColumns);
this.ukColumns = null;
} else {
this.ukColumns = ukColumns;
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, ukColumns != null);
if (key != null) {
key.setColumnsNames(ukColumns);
}
}

private boolean usesLegacyDropColumns() {
return operation == AlterOperation.DROP || operation == AlterOperation.DROP_UNIQUE
|| operation == AlterOperation.DROP_FOREIGN_KEY;
}

private boolean hasKeyConstraint(ConstraintKind kind) {
return constraint instanceof KeyConstraint && constraint.getKind() == kind;
}

private KeyConstraint keyForUpdate(ConstraintKind kind, boolean create) {
if (hasKeyConstraint(kind)) {
return (KeyConstraint) constraint;
}
if (!create) {
return null;
}
if (constraint != null || index != null || getFkColumns() != null) {
throw new IllegalStateException(
"Key setters cannot replace an existing definition; use setConstraint");
}
KeyConstraint key = new KeyConstraint()
.withType(kind == ConstraintKind.PRIMARY_KEY ? "PRIMARY KEY" : "UNIQUE");
setConstraint(key);
return key;
}

public String getUkName() {
return hasKeyConstraint(ConstraintKind.UNIQUE)
? ((KeyConstraint) constraint).getIndexName()
: ukName;
: null;
}

/** Uses the creation and replacement policy of {@link #setUkColumns(List)}. */
public void setUkName(String ukName) {
if (hasKeyConstraint(ConstraintKind.UNIQUE)) {
((KeyConstraint) constraint).setIndexName(ukName);
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, ukName != null);
if (key != null) {
key.setIndexName(ukName);
}
this.ukName = ukName;
}

public Index getIndex() {
Expand Down Expand Up @@ -754,12 +819,6 @@ private ConstraintKind getAddedConstraintKind() {
if (index != null) {
return ConstraintKind.OTHER;
}
if (pkColumns != null) {
return ConstraintKind.PRIMARY_KEY;
}
if (ukColumns != null) {
return ConstraintKind.UNIQUE;
}
return ConstraintKind.OTHER;
}

Expand Down Expand Up @@ -921,20 +980,53 @@ public void setUseEqual(boolean useEqual) {
}

public boolean getUk() {
return uk;
return "KEY".equalsIgnoreCase(getUniqueIndexKeyword());
}

/** Selects KEY when true or INDEX when false on the stored UNIQUE definition. */
public void setUk(boolean uk) {
this.uk = uk;
this.ukTypeSpecified = true;
setUniqueIndexKeyword(keyForUpdate(ConstraintKind.UNIQUE, true), uk ? "KEY" : "INDEX");
}

public boolean isUkTypeSpecified() {
return ukTypeSpecified;
return getUniqueIndexKeyword() != null;
}

/**
* Removes the explicit KEY/INDEX spelling when false. When true, retains an existing spelling
* or selects INDEX. Clearing an absent UNIQUE definition does not create one.
*/
public void setUkTypeSpecified(boolean ukTypeSpecified) {
this.ukTypeSpecified = ukTypeSpecified;
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, ukTypeSpecified);
if (key != null) {
String keyword = getUniqueIndexKeyword();
setUniqueIndexKeyword(key,
ukTypeSpecified ? keyword == null ? "INDEX" : keyword : null);
}
}

private String getUniqueIndexKeyword() {
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, false);
if (key == null) {
return null;
}
if (key.getIndexKeyword() != null) {
return key.getIndexKeyword();
}
if ("UNIQUE KEY".equalsIgnoreCase(key.getType())) {
return "KEY";
}
return "UNIQUE INDEX".equalsIgnoreCase(key.getType()) ? "INDEX" : null;
}

private void setUniqueIndexKeyword(KeyConstraint key, String keyword) {
if (hasLegacyKeyConstraint() || "UNIQUE KEY".equalsIgnoreCase(key.getType())
|| "UNIQUE INDEX".equalsIgnoreCase(key.getType())) {
key.setType("UNIQUE" + (keyword == null ? "" : " " + keyword));
key.setIndexKeyword(null);
} else {
key.setIndexKeyword(keyword);
}
}

public boolean isUseIfNotExists() {
Expand Down Expand Up @@ -1146,7 +1238,7 @@ protected boolean isDropSpecialOperation() {
case DROP_CHECK:
return true;
case DROP:
return columnName == null && pkColumns != null && !pkColumns.isEmpty();
return columnName == null && dropColumns != null && !dropColumns.isEmpty();
default:
return false;
}
Expand Down Expand Up @@ -1307,22 +1399,23 @@ protected void toStringDropSpecial(StringBuilder b) {
b.append("DROP PRIMARY KEY ");
break;
case DROP_UNIQUE:
b.append("DROP UNIQUE (").append(PlainSelect.getStringList(pkColumns)).append(')');
b.append("DROP UNIQUE (").append(PlainSelect.getStringList(dropColumns))
.append(')');
break;
case DROP_FOREIGN_KEY:
b.append("DROP FOREIGN KEY ");
if (constraintName != null) {
b.append(constraintName);
} else {
b.append('(').append(PlainSelect.getStringList(pkColumns)).append(')');
b.append('(').append(PlainSelect.getStringList(dropColumns)).append(')');
}
break;
case DROP_CHECK:
b.append("DROP CHECK ").append(constraintName);
break;
default:
// Oracle Multi Column Drop
b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')');
b.append("DROP (").append(PlainSelect.getStringList(dropColumns)).append(')');
break;
}
}
Expand Down Expand Up @@ -1538,26 +1631,7 @@ private void toStringGeneral(StringBuilder b, Consumer<ColumnDataType> columnPri
} else if (constraint != null) {
b.append(constraint);
} else if (index != null) {
// The structured index is canonical. Legacy PK/UK fields may also be populated for
// source compatibility, but cannot represent names, expressions, or index options.
b.append(index);
} else if (pkColumns != null) {
b.append("PRIMARY KEY (").append(PlainSelect.getStringList(pkColumns)).append(')');
} else if (ukColumns != null) {
b.append("UNIQUE");
if (ukName != null) {
if (isUkTypeSpecified()) {
if (getUk()) {
b.append(" KEY ");
} else {
b.append(" INDEX ");
}
} else {
b.append(" ");
}
b.append(ukName);
}
b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")");
}

if (getConstraints() != null && !getConstraints().isEmpty()) {
Expand Down
Loading
Loading