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 @@ -14,6 +14,7 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -50,6 +51,11 @@ public ColumnDefinition(String columnName, ColDataType colDataType, List<String>
this.columnSpecs = columnSpecs;
}

/**
* Returns raw specifications, or a token snapshot when structured options are present. Use the
* option API or {@link #addColumnSpecs(Collection)} to append without discarding structured
* references and constraints.
*/
public List<String> getColumnSpecs() {
if (columnOptions != null) {
List<String> tokens = new ArrayList<>();
Expand All @@ -76,6 +82,7 @@ public List<ColumnOption> getColumnOptions() {

public void setColumnOptions(List<ColumnOption> columnOptions) {
this.columnOptions = columnOptions;
this.columnSpecs = null;
}

public boolean isSerialDefaultValue() {
Expand All @@ -102,6 +109,9 @@ public ColumnDefinition withColumnOptions(List<ColumnOption> columnOptions) {
public ColumnDefinition addColumnOptions(ColumnOption... columnOptions) {
List<ColumnOption> collection =
Optional.ofNullable(getColumnOptions()).orElseGet(ArrayList::new);
if (this.columnOptions == null && columnSpecs != null && !columnSpecs.isEmpty()) {
collection.add(ColumnOption.raw(new ArrayList<>(columnSpecs)));
}
Collections.addAll(collection, columnOptions);
return withColumnOptions(collection);
}
Expand Down Expand Up @@ -153,12 +163,16 @@ public ColumnDefinition withColumnSpecs(List<String> columnSpecs) {
}

public ColumnDefinition addColumnSpecs(String... columnSpecs) {
List<String> collection = Optional.ofNullable(getColumnSpecs()).orElseGet(ArrayList::new);
Collections.addAll(collection, columnSpecs);
return this.withColumnSpecs(collection);
return addColumnSpecs(Arrays.asList(columnSpecs));
}

public ColumnDefinition addColumnSpecs(Collection<String> columnSpecs) {
if (columnOptions != null) {
if (!columnSpecs.isEmpty()) {
columnOptions.add(ColumnOption.raw(new ArrayList<>(columnSpecs)));
}
return this;
}
List<String> collection = Optional.ofNullable(getColumnSpecs()).orElseGet(ArrayList::new);
collection.addAll(columnSpecs);
return this.withColumnSpecs(collection);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*-
* #%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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Arrays;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.ColumnOption;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.create.table.ForeignKeyReference;
import net.sf.jsqlparser.util.TablesNamesFinder;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.api.Test;

class ColumnOptionMutationTest {
@Test
void clearingIdentityDoesNotRestoreRawSpecifications() throws JSQLParserException {
CreateTable table = parse("id INT GENERATED ALWAYS AS IDENTITY");
ColumnDefinition column = table.getColumnDefinitions().get(0);
column.getColumnOptions().clear();
assertTrue(column.getColumnSpecs().isEmpty());
assertSql(table, "CREATE TABLE t (id INT)");
}

@Test
void legacyAppendPreservesReferenceAndTraversal() throws JSQLParserException {
CreateTable table = parse("id INT REFERENCES parent(id)");
ColumnDefinition column = table.getColumnDefinitions().get(0);
ForeignKeyReference reference = column.getForeignKeyReference();
column.addColumnSpecs("NOT", "NULL");
column.addColumnSpecs(Arrays.asList("DEFAULT", "1"));
assertSame(reference, column.getForeignKeyReference());
assertTrue(new TablesNamesFinder().getTables(table).contains("parent"));
assertSql(table, "CREATE TABLE t (id INT REFERENCES parent(id) NOT NULL DEFAULT 1)");
}

@Test
void addingStructuredOptionPreservesExistingRawSpecifications() throws JSQLParserException {
CreateTable table = parse("id INT NOT NULL");
ColumnDefinition column = table.getColumnDefinitions().get(0);
assertNull(column.getColumnOptions());
ForeignKeyReference reference = parse("id INT REFERENCES parent(id)")
.getColumnDefinitions().get(0).getForeignKeyReference();
column.addColumnOptions(ColumnOption.reference(reference));
assertSql(table, "CREATE TABLE t (id INT NOT NULL REFERENCES parent(id))");
assertTrue(new TablesNamesFinder().getTables(table).contains("parent"));
}

@Test
void replacingOptionsWithNullClearsPreviousSpecifications() throws JSQLParserException {
CreateTable table = parse("id INT GENERATED ALWAYS AS IDENTITY");
ColumnDefinition column = table.getColumnDefinitions().get(0);
column.setColumnOptions(null);
assertNull(column.getColumnSpecs());
assertSql(table, "CREATE TABLE t (id INT)");
}

@Test
void legacySetterStillReplacesStructuredOptions() throws JSQLParserException {
CreateTable table = parse("id INT REFERENCES parent(id)");
ColumnDefinition column = table.getColumnDefinitions().get(0);
column.setColumnSpecs(Arrays.asList("NOT", "NULL"));
assertNull(column.getForeignKeyReference());
assertSql(table, "CREATE TABLE t (id INT NOT NULL)");
}

private static CreateTable parse(String definition) throws JSQLParserException {
return (CreateTable) CCJSqlParserUtil.parse("CREATE TABLE t (" + definition + ")");
}

private static void assertSql(CreateTable table, String expected) throws JSQLParserException {
assertEquals(expected, table.toString());
StringBuilder buffer = new StringBuilder();
table.accept(new StatementDeParser(buffer), null);
assertEquals(expected, buffer.toString());
assertEquals(expected, CCJSqlParserUtil.parse(expected).toString());
}
}
Loading