Skip to content

Commit 6c84b6b

Browse files
authored
Separate ALTER key definitions from column drop targets (#2771)
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 0a7436a commit 6c84b6b

7 files changed

Lines changed: 594 additions & 109 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java‎

Lines changed: 143 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,14 @@ public enum TableRenameKeyword {
5555
private List<ColumnSetDefault> columnSetDefaultList;
5656
private List<ColumnSetVisibility> columnSetVisibilityList;
5757

58-
private List<String> pkColumns;
59-
private List<String> ukColumns;
60-
private String ukName;
58+
private List<String> dropColumns;
6159
private Index index = null;
6260
private Index oldIndex = null;
6361
private NamedConstraint constraint;
6462
private String newConstraintName;
6563
private String constraintName;
6664
private boolean usingIfExists;
6765

68-
private boolean uk;
69-
private boolean ukTypeSpecified;
7066
private boolean useEqual;
7167

7268
private List<String> partitions;
@@ -633,75 +629,144 @@ public void setUsingIfExists(boolean usingIfExists) {
633629
}
634630

635631
/**
636-
* Returns a snapshot of the rendered key elements for a structured PRIMARY_KEY, or the legacy
637-
* list otherwise. Changing the snapshot does not change the constraint; use
638-
* {@link #setPkColumns(List)} to replace its keys, or {@link KeyConstraint#getColumns()} for
639-
* structured edits.
632+
* Returns the stored key definition, or null for another definition. The returned object is
633+
* shared with {@link #getConstraint()} and can be edited directly.
634+
*/
635+
public KeyConstraint getKeyConstraint() {
636+
return constraint instanceof KeyConstraint ? (KeyConstraint) constraint : null;
637+
}
638+
639+
/**
640+
* Returns the owned mutable column targets for parenthesized DROP, DROP UNIQUE or DROP FOREIGN
641+
* KEY syntax, or null when omitted. Stored targets are independent of any ADD definition.
642+
*/
643+
public List<String> getDropColumns() {
644+
return dropColumns;
645+
}
646+
647+
/** Copies the supplied targets without changing the operation or any stored definition. */
648+
public void setDropColumns(List<String> dropColumns) {
649+
this.dropColumns = dropColumns == null ? null : new ArrayList<>(dropColumns);
650+
}
651+
652+
public AlterExpression withDropColumns(List<String> dropColumns) {
653+
setDropColumns(dropColumns);
654+
return this;
655+
}
656+
657+
public AlterExpression addDropColumns(String... columns) {
658+
return addDropColumns(Arrays.asList(columns));
659+
}
660+
661+
public AlterExpression addDropColumns(Collection<String> columns) {
662+
if (dropColumns == null) {
663+
dropColumns = new ArrayList<>();
664+
}
665+
dropColumns.addAll(columns);
666+
return this;
667+
}
668+
669+
/**
670+
* Returns a rendered snapshot of the stored PRIMARY KEY's elements, or null when absent. For
671+
* DROP, DROP UNIQUE and DROP FOREIGN KEY operations this legacy accessor instead returns a
672+
* snapshot of {@link #getDropColumns()}. Snapshot edits never change the AST. New code should
673+
* use {@link #getKeyConstraint()} or {@link #getDropColumns()} explicitly.
640674
*/
641675
public List<String> getPkColumns() {
676+
if (usesLegacyDropColumns()) {
677+
return dropColumns == null ? null : new ArrayList<>(dropColumns);
678+
}
642679
return hasKeyConstraint(ConstraintKind.PRIMARY_KEY)
643680
? ((KeyConstraint) constraint).getColumnsNames()
644-
: pkColumns;
681+
: null;
645682
}
646683

647684
/**
648-
* Replaces a structured PRIMARY_KEY's elements using
649-
* {@link KeyConstraint#setColumnsNames(List)}. The strings are not parsed and existing element
650-
* attributes are not retained, even when the rendered SQL is unchanged. Use
651-
* {@link KeyConstraint#setColumns(List)} to preserve structured elements. Without a matching
652-
* constraint, stores the legacy list.
685+
* Replaces PRIMARY KEY elements with opaque names, without parsing or retaining element
686+
* attributes. A non-null list creates a definition when none exists; a competing definition
687+
* must be replaced explicitly using {@link #setConstraint(NamedConstraint)}. Null clears only
688+
* an existing PRIMARY KEY's elements and does not create a definition. With DROP, DROP UNIQUE
689+
* or DROP FOREIGN KEY already selected, delegates to {@link #setDropColumns(List)} instead. Set
690+
* the operation first for this legacy DROP alias; new builders should use setDropColumns.
691+
*
692+
* @throws IllegalStateException if a non-null list would replace another definition
653693
*/
654694
public void setPkColumns(List<String> pkColumns) {
655-
if (hasKeyConstraint(ConstraintKind.PRIMARY_KEY)) {
656-
((KeyConstraint) constraint).setColumnsNames(pkColumns);
657-
this.pkColumns = null;
658-
} else {
659-
this.pkColumns = pkColumns;
695+
if (usesLegacyDropColumns()) {
696+
setDropColumns(pkColumns);
697+
return;
698+
}
699+
KeyConstraint key = keyForUpdate(ConstraintKind.PRIMARY_KEY, pkColumns != null);
700+
if (key != null) {
701+
key.setColumnsNames(pkColumns);
660702
}
661703
}
662704

663705
/**
664-
* Returns a snapshot of the rendered key elements for a structured UNIQUE, or the legacy list
665-
* otherwise. Changing the snapshot does not change the constraint; use
666-
* {@link #setUkColumns(List)} to replace its keys, or {@link KeyConstraint#getColumns()} for
667-
* structured edits.
706+
* Returns a snapshot of the stored UNIQUE's elements, or null when absent. Changing the
707+
* snapshot does not change the constraint; use {@link #setUkColumns(List)} to replace its keys,
708+
* or {@link KeyConstraint#getColumns()} for structured edits.
668709
*/
669710
public List<String> getUkColumns() {
670711
return hasKeyConstraint(ConstraintKind.UNIQUE)
671712
? ((KeyConstraint) constraint).getColumnsNames()
672-
: ukColumns;
713+
: null;
673714
}
674715

675716
/**
676717
* Replaces a structured UNIQUE's elements using {@link KeyConstraint#setColumnsNames(List)}.
677718
* The strings are not parsed and existing element attributes are not retained, even when the
678719
* rendered SQL is unchanged. Use {@link KeyConstraint#setColumns(List)} to preserve structured
679-
* elements. Without a matching constraint, stores the legacy list.
720+
* elements. A non-null list creates a UNIQUE definition only when none exists; a competing
721+
* definition must be replaced explicitly. Null clears only an existing UNIQUE's elements.
722+
*
723+
* @throws IllegalStateException if a non-null list would replace another definition
680724
*/
681725
public void setUkColumns(List<String> ukColumns) {
682-
if (hasKeyConstraint(ConstraintKind.UNIQUE)) {
683-
((KeyConstraint) constraint).setColumnsNames(ukColumns);
684-
this.ukColumns = null;
685-
} else {
686-
this.ukColumns = ukColumns;
726+
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, ukColumns != null);
727+
if (key != null) {
728+
key.setColumnsNames(ukColumns);
687729
}
688730
}
689731

732+
private boolean usesLegacyDropColumns() {
733+
return operation == AlterOperation.DROP || operation == AlterOperation.DROP_UNIQUE
734+
|| operation == AlterOperation.DROP_FOREIGN_KEY;
735+
}
736+
690737
private boolean hasKeyConstraint(ConstraintKind kind) {
691738
return constraint instanceof KeyConstraint && constraint.getKind() == kind;
692739
}
693740

741+
private KeyConstraint keyForUpdate(ConstraintKind kind, boolean create) {
742+
if (hasKeyConstraint(kind)) {
743+
return (KeyConstraint) constraint;
744+
}
745+
if (!create) {
746+
return null;
747+
}
748+
if (constraint != null || index != null || getFkColumns() != null) {
749+
throw new IllegalStateException(
750+
"Key setters cannot replace an existing definition; use setConstraint");
751+
}
752+
KeyConstraint key = new KeyConstraint()
753+
.withType(kind == ConstraintKind.PRIMARY_KEY ? "PRIMARY KEY" : "UNIQUE");
754+
setConstraint(key);
755+
return key;
756+
}
757+
694758
public String getUkName() {
695759
return hasKeyConstraint(ConstraintKind.UNIQUE)
696760
? ((KeyConstraint) constraint).getIndexName()
697-
: ukName;
761+
: null;
698762
}
699763

764+
/** Uses the creation and replacement policy of {@link #setUkColumns(List)}. */
700765
public void setUkName(String ukName) {
701-
if (hasKeyConstraint(ConstraintKind.UNIQUE)) {
702-
((KeyConstraint) constraint).setIndexName(ukName);
766+
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, ukName != null);
767+
if (key != null) {
768+
key.setIndexName(ukName);
703769
}
704-
this.ukName = ukName;
705770
}
706771

707772
public Index getIndex() {
@@ -753,12 +818,6 @@ private ConstraintKind getAddedConstraintKind() {
753818
if (index != null) {
754819
return ConstraintKind.OTHER;
755820
}
756-
if (pkColumns != null) {
757-
return ConstraintKind.PRIMARY_KEY;
758-
}
759-
if (ukColumns != null) {
760-
return ConstraintKind.UNIQUE;
761-
}
762821
return ConstraintKind.OTHER;
763822
}
764823

@@ -920,20 +979,53 @@ public void setUseEqual(boolean useEqual) {
920979
}
921980

922981
public boolean getUk() {
923-
return uk;
982+
return "KEY".equalsIgnoreCase(getUniqueIndexKeyword());
924983
}
925984

985+
/** Selects KEY when true or INDEX when false on the stored UNIQUE definition. */
926986
public void setUk(boolean uk) {
927-
this.uk = uk;
928-
this.ukTypeSpecified = true;
987+
setUniqueIndexKeyword(keyForUpdate(ConstraintKind.UNIQUE, true), uk ? "KEY" : "INDEX");
929988
}
930989

931990
public boolean isUkTypeSpecified() {
932-
return ukTypeSpecified;
991+
return getUniqueIndexKeyword() != null;
933992
}
934993

994+
/**
995+
* Removes the explicit KEY/INDEX spelling when false. When true, retains an existing spelling
996+
* or selects INDEX. Clearing an absent UNIQUE definition does not create one.
997+
*/
935998
public void setUkTypeSpecified(boolean ukTypeSpecified) {
936-
this.ukTypeSpecified = ukTypeSpecified;
999+
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, ukTypeSpecified);
1000+
if (key != null) {
1001+
String keyword = getUniqueIndexKeyword();
1002+
setUniqueIndexKeyword(key,
1003+
ukTypeSpecified ? keyword == null ? "INDEX" : keyword : null);
1004+
}
1005+
}
1006+
1007+
private String getUniqueIndexKeyword() {
1008+
KeyConstraint key = keyForUpdate(ConstraintKind.UNIQUE, false);
1009+
if (key == null) {
1010+
return null;
1011+
}
1012+
if (key.getIndexKeyword() != null) {
1013+
return key.getIndexKeyword();
1014+
}
1015+
if ("UNIQUE KEY".equalsIgnoreCase(key.getType())) {
1016+
return "KEY";
1017+
}
1018+
return "UNIQUE INDEX".equalsIgnoreCase(key.getType()) ? "INDEX" : null;
1019+
}
1020+
1021+
private void setUniqueIndexKeyword(KeyConstraint key, String keyword) {
1022+
if (hasLegacyKeyConstraint() || "UNIQUE KEY".equalsIgnoreCase(key.getType())
1023+
|| "UNIQUE INDEX".equalsIgnoreCase(key.getType())) {
1024+
key.setType("UNIQUE" + (keyword == null ? "" : " " + keyword));
1025+
key.setIndexKeyword(null);
1026+
} else {
1027+
key.setIndexKeyword(keyword);
1028+
}
9371029
}
9381030

9391031
public boolean isUseIfNotExists() {
@@ -1171,7 +1263,7 @@ protected boolean isDropSpecialOperation() {
11711263
case DROP_CHECK:
11721264
return true;
11731265
case DROP:
1174-
return columnName == null && pkColumns != null && !pkColumns.isEmpty();
1266+
return columnName == null && dropColumns != null && !dropColumns.isEmpty();
11751267
default:
11761268
return false;
11771269
}
@@ -1332,22 +1424,23 @@ protected void toStringDropSpecial(StringBuilder b) {
13321424
b.append("DROP PRIMARY KEY ");
13331425
break;
13341426
case DROP_UNIQUE:
1335-
b.append("DROP UNIQUE (").append(PlainSelect.getStringList(pkColumns)).append(')');
1427+
b.append("DROP UNIQUE (").append(PlainSelect.getStringList(dropColumns))
1428+
.append(')');
13361429
break;
13371430
case DROP_FOREIGN_KEY:
13381431
b.append("DROP FOREIGN KEY ");
13391432
if (constraintName != null) {
13401433
b.append(constraintName);
13411434
} else {
1342-
b.append('(').append(PlainSelect.getStringList(pkColumns)).append(')');
1435+
b.append('(').append(PlainSelect.getStringList(dropColumns)).append(')');
13431436
}
13441437
break;
13451438
case DROP_CHECK:
13461439
b.append("DROP CHECK ").append(constraintName);
13471440
break;
13481441
default:
13491442
// Oracle Multi Column Drop
1350-
b.append("DROP (").append(PlainSelect.getStringList(pkColumns)).append(')');
1443+
b.append("DROP (").append(PlainSelect.getStringList(dropColumns)).append(')');
13511444
break;
13521445
}
13531446
}
@@ -1563,26 +1656,7 @@ private void toStringGeneral(StringBuilder b, Consumer<ColumnDataType> columnPri
15631656
} else if (constraint != null) {
15641657
b.append(constraint);
15651658
} else if (index != null) {
1566-
// The structured index is canonical. Legacy PK/UK fields may also be populated for
1567-
// source compatibility, but cannot represent names, expressions, or index options.
15681659
b.append(index);
1569-
} else if (pkColumns != null) {
1570-
b.append("PRIMARY KEY (").append(PlainSelect.getStringList(pkColumns)).append(')');
1571-
} else if (ukColumns != null) {
1572-
b.append("UNIQUE");
1573-
if (ukName != null) {
1574-
if (isUkTypeSpecified()) {
1575-
if (getUk()) {
1576-
b.append(" KEY ");
1577-
} else {
1578-
b.append(" INDEX ");
1579-
}
1580-
} else {
1581-
b.append(" ");
1582-
}
1583-
b.append(ukName);
1584-
}
1585-
b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")");
15861660
}
15871661

15881662
if (getConstraints() != null && !getConstraints().isEmpty()) {

0 commit comments

Comments
 (0)