From 787f33e97ed917c737c0683a5dfb8fd844b1adef Mon Sep 17 00:00:00 2001 From: minleejae Date: Sun, 11 Oct 2026 23:44:59 +0900 Subject: [PATCH] fix: keep cleared index and constraint keys editable Signed-off-by: minleejae --- .../create/table/KeyColumnSource.java | 8 +- .../create/KeyColumnSourceMutationTest.java | 88 +++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/statement/create/KeyColumnSourceMutationTest.java diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/KeyColumnSource.java b/src/main/java/net/sf/jsqlparser/statement/create/table/KeyColumnSource.java index 0c35298ba..9fec2adea 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/KeyColumnSource.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/KeyColumnSource.java @@ -10,7 +10,6 @@ package net.sf.jsqlparser.statement.create.table; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -29,9 +28,12 @@ default List getColumnsNames() { .map(KeyElement::toString).collect(Collectors.toList()); } - /** Replaces keys with opaque names, without parsing or retaining expression options. */ + /** + * Replaces keys with opaque names, without parsing or retaining expression options. A null or + * empty list clears the keys and leaves the structured list editable. + */ default void setColumnsNames(List names) { - setColumns(names == null ? Collections.emptyList() + setColumns(names == null ? new ArrayList<>() : names.stream() .map(KeyElement::new).collect(Collectors.toList())); } diff --git a/src/test/java/net/sf/jsqlparser/statement/create/KeyColumnSourceMutationTest.java b/src/test/java/net/sf/jsqlparser/statement/create/KeyColumnSourceMutationTest.java new file mode 100644 index 000000000..e2dbd4005 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/create/KeyColumnSourceMutationTest.java @@ -0,0 +1,88 @@ +/*- + * #%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.create; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.create.table.*; +import net.sf.jsqlparser.util.deparser.StatementDeParser; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class KeyColumnSourceMutationTest { + @ParameterizedTest + @ValueSource(strings = {"INDEX ix (id DESC)", "PRIMARY KEY (id)", "UNIQUE (id DESC)", + "FOREIGN KEY (id) REFERENCES parent (id)", "EXCLUDE USING btree (id WITH =)"}) + void clearedNamesCanBeRepopulatedThroughBothAppendApisAndTheLiveList(String declaration) + throws Exception { + for (boolean nullClear : new boolean[] {true, false}) { + for (boolean collectionAppend : new boolean[] {false, true}) { + CreateTable table = (CreateTable) CCJSqlParserUtil.parse( + "CREATE TABLE t (id INT, " + declaration + ")"); + KeyColumnSource key = (KeyColumnSource) table.getTableElements().get(1); + KeyElement element = key.getColumns().get(0); + String expected = table.toString(); + + key.setColumnsNames(nullClear ? null : List.of()); + assertNotNull(key.getColumns()); + assertTrue(key.getColumns().isEmpty()); + assertTrue(key.getColumnsNames().isEmpty()); + append(key, element, collectionAppend); + assertSame(element, key.getColumns().get(0)); + assertSql(table, expected); + + // Clearing and editing through the shared structured API must also work. + key.setColumnsNames(null); + key.getColumns().add(element); + assertSql(table, expected); + } + } + } + + private static void append(KeyColumnSource key, KeyElement element, boolean collection) { + if (key instanceof Index) { + if (collection) { + ((Index) key).addColumns(List.of(element)); + } else { + ((Index) key).addColumns(element); + } + } else if (key instanceof KeyConstraint) { + if (collection) { + ((KeyConstraint) key).addColumns(List.of(element)); + } else { + ((KeyConstraint) key).addColumns(element); + } + } else if (key instanceof ForeignKeyConstraint) { + if (collection) { + ((ForeignKeyConstraint) key).addColumns(List.of(element)); + } else { + ((ForeignKeyConstraint) key).addColumns(element); + } + } else if (key instanceof ExcludeConstraint) { + if (collection) { + ((ExcludeConstraint) key).addColumns(List.of(element)); + } else { + ((ExcludeConstraint) key).addColumns(element); + } + } else { + fail("Unexpected key owner: " + key.getClass()); + } + } + + private static void assertSql(CreateTable table, String expected) throws Exception { + assertEquals(expected, table.toString()); + StringBuilder output = new StringBuilder(); + table.accept(new StatementDeParser(output), null); + assertEquals(expected, output.toString()); + assertEquals(expected, CCJSqlParserUtil.parse(output.toString()).toString()); + } +}