Skip to content

Commit 49c1d52

Browse files
authored
Use one foreign-key state in ALTER TABLE (#2764)
* Use one foreign-key state in ALTER TABLE Delegate legacy FK accessors to the structured constraint, remove copied grammar state and the fallback renderer, and validate the current reference. Preserve quoted name parts and protect existing non-FK definitions. This follow-up is based on PR #2747 at 9be3636. Signed-off-by: minleejae <mmj9808@gmail.com> * Use key accessors when checking competing foreign key definitions Signed-off-by: minleejae <mmj9808@gmail.com> --------- Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent cdbaf1a commit 49c1d52

6 files changed

Lines changed: 483 additions & 92 deletions

File tree

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

Lines changed: 112 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414
import java.util.Arrays;
1515
import java.util.Collection;
1616
import java.util.Collections;
17-
import java.util.LinkedHashSet;
1817
import java.util.List;
1918
import java.util.Optional;
20-
import java.util.Set;
2119
import java.util.function.Consumer;
2220
import java.util.stream.Collectors;
2321
import net.sf.jsqlparser.expression.Expression;
@@ -27,6 +25,7 @@
2725
import net.sf.jsqlparser.statement.ReferentialAction.Type;
2826
import net.sf.jsqlparser.statement.create.table.ColDataType;
2927
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
28+
import net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint;
3029
import net.sf.jsqlparser.statement.create.table.Index;
3130
import net.sf.jsqlparser.statement.create.table.NamedConstraint;
3231
import net.sf.jsqlparser.statement.create.table.KeyConstraint;
@@ -42,7 +41,6 @@ public enum TableRenameKeyword {
4241
NONE, TO, AS
4342
}
4443

45-
private final Set<ReferentialAction> referentialActions = new LinkedHashSet<>(2);
4644
private AlterOperation operation;
4745
private String optionalSpecifier;
4846
private Table newTable;
@@ -67,33 +65,6 @@ public enum TableRenameKeyword {
6765
private String constraintName;
6866
private boolean usingIfExists;
6967

70-
/**
71-
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
72-
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
73-
*/
74-
@Deprecated
75-
private List<String> fkColumns;
76-
77-
/**
78-
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
79-
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
80-
*/
81-
@Deprecated
82-
private String fkSourceSchema;
83-
84-
/**
85-
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
86-
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
87-
*/
88-
@Deprecated
89-
private String fkSourceTable;
90-
91-
/**
92-
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
93-
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
94-
*/
95-
@Deprecated
96-
private List<String> fkSourceColumns;
9768
private boolean uk;
9869
private boolean ukTypeSpecified;
9970
private boolean useEqual;
@@ -183,16 +154,20 @@ public void hasColumns(boolean hasColumns) {
183154
*/
184155
@Deprecated
185156
public String getFkSourceSchema() {
186-
return fkSourceSchema;
157+
Table table = getForeignKeyTable();
158+
return table == null ? null : table.getSchemaName();
187159
}
188160

189161
/**
162+
* Updates only the referenced schema identifier, preserving its quoting and the table's other
163+
* name parts. Uses the creation and replacement policy of {@link #setFkColumns(List)}.
164+
*
190165
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
191166
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
192167
*/
193168
@Deprecated
194169
public void setFkSourceSchema(String fkSourceSchema) {
195-
this.fkSourceSchema = fkSourceSchema;
170+
setForeignKeyTablePart(fkSourceSchema, 1);
196171
}
197172

198173
public String getCommentText() {
@@ -228,8 +203,13 @@ public void setOptionalSpecifier(String optionalSpecifier) {
228203
}
229204

230205
/**
206+
* Updates the stored foreign key's referential action without replacing its object or column
207+
* list. A null action removes it. Uses the creation and replacement policy of
208+
* {@link #setFkColumns(List)}.
209+
*
231210
* @param type
232211
* @param action
212+
* @throws IllegalStateException if a non-null action would replace a different definition
233213
* @deprecated Standalone FK fields are deprecated. Use a
234214
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} via
235215
* {@link #setConstraint(NamedConstraint)} instead.
@@ -258,28 +238,27 @@ public void removeReferentialAction(Type type) {
258238
}
259239

260240
/**
241+
* Returns the action owned by the stored foreign key, or null when absent. Changes to the
242+
* returned object are reflected in the constraint.
243+
*
261244
* @param type
262245
* @return
263246
* @deprecated Standalone FK fields are deprecated.
264247
*/
265248
@Deprecated
266249
public ReferentialAction getReferentialAction(Type type) {
267-
return referentialActions.stream()
268-
.filter(ra -> type.equals(ra.getType()))
269-
.findFirst()
270-
.orElse(null);
250+
ForeignKeyConstraint foreignKey = getForeignKeyConstraint();
251+
return foreignKey == null ? null : foreignKey.getReferentialAction(type);
271252
}
272253

273254
private void setReferentialAction(Type type, Action action, boolean set) {
274-
ReferentialAction found = getReferentialAction(type);
275-
if (set) {
276-
if (found == null) {
277-
referentialActions.add(new ReferentialAction(type, action));
255+
ForeignKeyConstraint foreignKey = foreignKeyForUpdate(set && action != null);
256+
if (foreignKey != null) {
257+
if (set && action != null) {
258+
foreignKey.setReferentialAction(type, action);
278259
} else {
279-
found.setAction(action);
260+
foreignKey.removeReferentialAction(type);
280261
}
281-
} else if (found != null) {
282-
referentialActions.remove(found);
283262
}
284263
}
285264

@@ -344,21 +323,41 @@ public void setOnDeleteSetNull(boolean onDeleteSetNull) {
344323
}
345324

346325
/**
347-
* @deprecated Use {@link #getConstraint()} with
348-
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
326+
* Returns a rendered snapshot of the stored foreign key's elements, or null when absent.
327+
* Changing the snapshot does not change the constraint; use {@link #setFkColumns(List)} to
328+
* replace its keys, or {@link ForeignKeyConstraint#getColumns()} for structured edits.
329+
*
330+
* @deprecated Use {@link #getConstraint()} with a {@link ForeignKeyConstraint} instead.
349331
*/
350332
@Deprecated
351333
public List<String> getFkColumns() {
352-
return fkColumns;
334+
ForeignKeyConstraint foreignKey = getForeignKeyConstraint();
335+
return foreignKey == null || foreignKey.getColumns() == null ? null
336+
: foreignKey.getColumnsNames();
353337
}
354338

355339
/**
340+
* Replaces the stored foreign key's elements with opaque names without parsing or retaining
341+
* element attributes. A non-null value creates a foreign key when no definition exists, but
342+
* never replaces a different constraint or an ordinary index; use
343+
* {@link #setConstraint(NamedConstraint)} to replace a definition explicitly. A null value
344+
* clears existing foreign-key elements without creating a constraint. Other actions and their
345+
* rendering precedence are unchanged.
346+
*
347+
* @throws IllegalStateException if a non-null value would replace a different definition
356348
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
357349
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
358350
*/
359351
@Deprecated
360352
public void setFkColumns(List<String> fkColumns) {
361-
this.fkColumns = fkColumns;
353+
ForeignKeyConstraint foreignKey = foreignKeyForUpdate(fkColumns != null);
354+
if (foreignKey != null) {
355+
if (fkColumns == null) {
356+
foreignKey.setColumns(null);
357+
} else {
358+
foreignKey.setColumnsNames(fkColumns);
359+
}
360+
}
362361
}
363362

364363
/**
@@ -367,16 +366,63 @@ public void setFkColumns(List<String> fkColumns) {
367366
*/
368367
@Deprecated
369368
public String getFkSourceTable() {
370-
return fkSourceTable;
369+
Table table = getForeignKeyTable();
370+
return table == null ? null : table.getName();
371371
}
372372

373373
/**
374+
* Updates only the referenced table identifier, preserving its quoting and the table's other
375+
* name parts. Uses the creation and replacement policy of {@link #setFkColumns(List)}.
376+
*
374377
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
375378
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
376379
*/
377380
@Deprecated
378381
public void setFkSourceTable(String fkSourceTable) {
379-
this.fkSourceTable = fkSourceTable;
382+
setForeignKeyTablePart(fkSourceTable, 0);
383+
}
384+
385+
private ForeignKeyConstraint getForeignKeyConstraint() {
386+
return constraint instanceof ForeignKeyConstraint ? (ForeignKeyConstraint) constraint
387+
: null;
388+
}
389+
390+
private ForeignKeyConstraint foreignKeyForUpdate(boolean create) {
391+
ForeignKeyConstraint foreignKey = getForeignKeyConstraint();
392+
if (foreignKey == null && create) {
393+
if (constraint != null || index != null || getPkColumns() != null
394+
|| getUkColumns() != null) {
395+
throw new IllegalStateException(
396+
"Foreign-key setters cannot replace an existing definition; use setConstraint");
397+
}
398+
foreignKey = new ForeignKeyConstraint();
399+
setConstraint(foreignKey);
400+
}
401+
return foreignKey;
402+
}
403+
404+
private Table getForeignKeyTable() {
405+
ForeignKeyConstraint foreignKey = getForeignKeyConstraint();
406+
return foreignKey == null ? null : foreignKey.getTable();
407+
}
408+
409+
private void setForeignKeyTablePart(String value, int partIndex) {
410+
ForeignKeyConstraint foreignKey = foreignKeyForUpdate(value != null);
411+
if (foreignKey == null || foreignKey.getTable() == null && value == null) {
412+
return;
413+
}
414+
if (foreignKey.getTable() == null) {
415+
foreignKey.setTable(new Table());
416+
}
417+
Table table = foreignKey.getTable();
418+
// Table stores identifier parts in name-first order. Updating raw parts avoids splitting
419+
// quoted identifiers containing dots and leaves the existing Table object attached.
420+
List<String> parts = new ArrayList<>(table.getNameParts());
421+
while (parts.size() <= partIndex) {
422+
parts.add(null);
423+
}
424+
parts.set(partIndex, value);
425+
table.setNameParts(parts);
380426
}
381427

382428
public List<ColumnDataType> getColDataTypeList() {
@@ -456,21 +502,29 @@ public List<ColumnSetVisibility> getColumnSetVisibilityList() {
456502
}
457503

458504
/**
459-
* @deprecated Use {@link #getConstraint()} with
460-
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
505+
* Returns the referenced-column list owned by the stored foreign key, or null when absent.
506+
* Changes to the returned list are reflected in the constraint.
507+
*
508+
* @deprecated Use {@link #getConstraint()} with a {@link ForeignKeyConstraint} instead.
461509
*/
462510
@Deprecated
463511
public List<String> getFkSourceColumns() {
464-
return fkSourceColumns;
512+
ForeignKeyConstraint foreignKey = getForeignKeyConstraint();
513+
return foreignKey == null ? null : foreignKey.getReferencedColumnNames();
465514
}
466515

467516
/**
517+
* Uses the creation and replacement policy of {@link #setFkColumns(List)}.
518+
*
468519
* @deprecated Use {@link #setConstraint(NamedConstraint)} with a
469520
* {@link net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint} instead.
470521
*/
471522
@Deprecated
472523
public void setFkSourceColumns(List<String> fkSourceColumns) {
473-
this.fkSourceColumns = fkSourceColumns;
524+
ForeignKeyConstraint foreignKey = foreignKeyForUpdate(fkSourceColumns != null);
525+
if (foreignKey != null) {
526+
foreignKey.setReferencedColumnNames(fkSourceColumns);
527+
}
474528
}
475529

476530
public String getNewTableName() {
@@ -677,7 +731,7 @@ private ConstraintKind getAddedConstraintKind() {
677731
if (ukColumns != null) {
678732
return ConstraintKind.UNIQUE;
679733
}
680-
return fkColumns != null ? ConstraintKind.FOREIGN_KEY : ConstraintKind.OTHER;
734+
return ConstraintKind.OTHER;
681735
}
682736

683737
private boolean hasColumnOrNamedTarget() {
@@ -1451,7 +1505,7 @@ private void toStringGeneral(StringBuilder b, Consumer<ColumnDataType> columnPri
14511505
} else if (constraint != null) {
14521506
b.append(constraint);
14531507
} else if (index != null) {
1454-
// The structured index is canonical. Legacy PK/UK/FK fields may also be populated for
1508+
// The structured index is canonical. Legacy PK/UK fields may also be populated for
14551509
// source compatibility, but cannot represent names, expressions, or index options.
14561510
b.append(index);
14571511
} else if (pkColumns != null) {
@@ -1471,21 +1525,6 @@ private void toStringGeneral(StringBuilder b, Consumer<ColumnDataType> columnPri
14711525
b.append(ukName);
14721526
}
14731527
b.append(" (").append(PlainSelect.getStringList(ukColumns)).append(")");
1474-
} else if (fkColumns != null) {
1475-
// @deprecated path - kept for backward compatibility when ForeignKeyConstraint is not
1476-
// set
1477-
b.append("FOREIGN KEY (")
1478-
.append(PlainSelect.getStringList(fkColumns))
1479-
.append(") REFERENCES ")
1480-
.append(
1481-
fkSourceSchema != null && fkSourceSchema.trim().length() > 0
1482-
? fkSourceSchema + "."
1483-
: "")
1484-
.append(fkSourceTable)
1485-
.append(" (")
1486-
.append(PlainSelect.getStringList(fkSourceColumns))
1487-
.append(")");
1488-
referentialActions.forEach(b::append);
14891528
}
14901529

14911530
if (getConstraints() != null && !getConstraints().isEmpty()) {
@@ -1574,7 +1613,7 @@ public AlterExpression withFkColumns(List<String> fkColumns) {
15741613

15751614
@Deprecated
15761615
public AlterExpression withFkSourceSchema(String fkSourceSchema) {
1577-
this.setFkSourceTable(fkSourceSchema);
1616+
this.setFkSourceSchema(fkSourceSchema);
15781617
return this;
15791618
}
15801619

@@ -1643,13 +1682,15 @@ public AlterExpression addUkColumns(Collection<String> ukColumns) {
16431682
return this.withUkColumns(collection);
16441683
}
16451684

1685+
/** Adds strings using the replacement semantics of {@link #setFkColumns(List)}. */
16461686
@Deprecated
16471687
public AlterExpression addFkColumns(String... fkColumns) {
16481688
List<String> collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new);
16491689
Collections.addAll(collection, fkColumns);
16501690
return this.withFkColumns(collection);
16511691
}
16521692

1693+
/** Adds strings using the replacement semantics of {@link #setFkColumns(List)}. */
16531694
@Deprecated
16541695
public AlterExpression addFkColumns(Collection<String> fkColumns) {
16551696
List<String> collection = Optional.ofNullable(getFkColumns()).orElseGet(ArrayList::new);

‎src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import net.sf.jsqlparser.statement.create.table.KeyColumnSource;
2424
import net.sf.jsqlparser.statement.create.table.KeyElement;
2525
import net.sf.jsqlparser.statement.create.table.ConstraintUsingIndex;
26+
import net.sf.jsqlparser.statement.create.table.ForeignKeyConstraint;
2627
import net.sf.jsqlparser.util.TableDefinitionTraversal;
2728
import net.sf.jsqlparser.util.validation.ValidationCapability;
2829
import net.sf.jsqlparser.util.validation.ValidationUtil;
@@ -76,12 +77,6 @@ public void validate(Alter alter, AlterExpression e) {
7677
validateOptionalColumnNames(c, e.getPkColumns());
7778
}
7879

79-
if (e.getFkColumns() != null) {
80-
validateName(c, NamedObject.table, e.getFkSourceTable());
81-
validateOptionalColumnNames(c, e.getFkColumns());
82-
validateOptionalColumnNames(c, e.getFkSourceColumns());
83-
}
84-
8580
if (e.getUk()) {
8681
validateName(c, NamedObject.uniqueConstraint, e.getUkName());
8782
validateOptionalColumnNames(c, e.getUkColumns(), NamedObject.uniqueConstraint);
@@ -91,6 +86,10 @@ public void validate(Alter alter, AlterExpression e) {
9186
if (constraint != null) {
9287
validateOptionalName(c, NamedObject.constraint, constraint.getName(), null, false,
9388
NamedObject.table);
89+
if (constraint instanceof ForeignKeyConstraint) {
90+
validateOptionalColumnNames(c,
91+
((ForeignKeyConstraint) constraint).getReferencedColumnNames());
92+
}
9493
if (constraint instanceof ConstraintUsingIndex) {
9594
validateName(c, NamedObject.index,
9695
((ConstraintUsingIndex) constraint).getExistingIndexName());

0 commit comments

Comments
 (0)