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 @@ -18,6 +18,7 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.ReferentialAction;
Expand Down Expand Up @@ -883,14 +884,29 @@ protected void toStringConstraintAlter(StringBuilder b) {
}

protected void toStringAlterColumn(StringBuilder b) {
appendAlterColumn(b, b::append);
}

/** Renders ALTER COLUMN default/visibility actions with their common tail. */
public void appendColumnActionTo(StringBuilder b, Consumer<Expression> expressionPrinter) {
appendAlterColumn(b, expressionPrinter);
appendCommonTail(b);
}

private void appendAlterColumn(StringBuilder b, Consumer<Expression> expressionPrinter) {
b.append("ALTER ");
if (hasColumn) {
b.append("COLUMN ");
}
if (columnDropDefaultList != null && !columnDropDefaultList.isEmpty()) {
b.append(PlainSelect.getStringList(columnDropDefaultList));
} else if (columnSetDefaultList != null && !columnSetDefaultList.isEmpty()) {
b.append(PlainSelect.getStringList(columnSetDefaultList));
for (int i = 0; i < columnSetDefaultList.size(); i++) {
if (i > 0) {
b.append(", ");
}
columnSetDefaultList.get(i).appendTo(b, expressionPrinter);
}
} else {
b.append(PlainSelect.getStringList(columnSetVisibilityList));
}
Expand Down Expand Up @@ -1575,7 +1591,8 @@ public String toString() {

public static final class ColumnSetDefault implements Serializable {
private final String columnName;
private final String defaultValue;
private String defaultValue;
private Expression defaultExpression;

public ColumnSetDefault(String columnName, String defaultValue) {
this.columnName = columnName;
Expand All @@ -1586,13 +1603,40 @@ public String getColumnName() {
return columnName;
}

/** Constructs a structured default without overloading the legacy nullable String API. */
public static ColumnSetDefault fromExpression(String columnName, Expression expression) {
ColumnSetDefault result = new ColumnSetDefault(columnName, null);
result.setDefaultExpression(expression);
return result;
}

public Expression getDefaultExpression() {
return defaultExpression;
}

public void setDefaultExpression(Expression defaultExpression) {
this.defaultExpression = defaultExpression;
this.defaultValue = null;
}

public String getDefaultValue() {
return defaultValue;
return defaultExpression == null ? defaultValue : defaultExpression.toString();
}

public void appendTo(StringBuilder sql, Consumer<Expression> expressionPrinter) {
sql.append(columnName).append(" SET DEFAULT ");
if (defaultExpression == null) {
sql.append(defaultValue);
} else {
expressionPrinter.accept(defaultExpression);
}
}

@Override
public String toString() {
return columnName + " SET DEFAULT " + defaultValue;
StringBuilder sql = new StringBuilder();
appendTo(sql, sql::append);
return sql.toString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public static void visit(CreateIndex createIndex, Consumer<Expression> expressio
/** Visits the structured definitions and expressions belonging to a single ALTER action. */
public static void visit(AlterExpression action, Consumer<Expression> expressions,
Consumer<Table> tables) {
if (action.getColumnSetDefaultList() != null) {
action.getColumnSetDefaultList()
.forEach(column -> accept(column.getDefaultExpression(), expressions));
}
if (action.getColDataTypeList() != null) {
action.getColDataTypeList().forEach(column -> visit(column, expressions, tables));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ private void deParseAction(AlterExpression action) {
deParseTail(action);
return;
}
if (action.getOperation() == net.sf.jsqlparser.statement.alter.AlterOperation.ALTER
&& action.getColumnSetDefaultList() != null
&& !action.getColumnSetDefaultList().isEmpty()) {
action.appendColumnActionTo(builder,
expression -> expression.accept(expressionVisitor, null));
return;
}
if (action.getColDataTypeList() == null || action.getColDataTypeList().size() != 1
|| action.getColDataTypeList().get(0).getUsingExpression() == null) {
builder.append(action);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,8 @@ public void validate(Alter alter) {
}

public void validate(Alter alter, AlterExpression e) {
if (e.getColDataTypeList() != null) {
e.getColDataTypeList().forEach(column -> TableDefinitionTraversal.visit(column,
this::validateOptionalExpression, this::validateOptionalFromItem));
}
if (e.getIndex() != null) {
TableDefinitionTraversal.visit(e.getIndex(), this::validateOptionalExpression,
this::validateOptionalFromItem);
}
TableDefinitionTraversal.visit(e, this::validateOptionalExpression,
this::validateOptionalFromItem);
for (ValidationCapability c : getCapabilities()) {

validateOptionalColumnName(c, e.getColumnOldName());
Expand Down
2 changes: 1 addition & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -14552,7 +14552,7 @@ AlterExpression.ColumnSetDefault AlterExpressionColumnSetDefault():
{
columnName = RelObjectName() <K_SET> <K_DEFAULT> defaultValue = Expression()
{
return new AlterExpression.ColumnSetDefault(columnName, defaultValue.toString());
return AlterExpression.ColumnSetDefault.fromExpression(columnName, defaultValue);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*-
* #%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.alter;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.util.ArrayList;
import java.util.List;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.StatementVisitorAdapter;
import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnSetDefault;
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
import net.sf.jsqlparser.util.deparser.SelectDeParser;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class AlterColumnDefaultExpressionTest {
@ParameterizedTest
@ValueSource(strings = {"(1 + 2)", "NULL", "CURRENT_TIMESTAMP",
"nextval('app.counter'::regclass)", "'value'::text", "-1"})
void parsedDefaultRetainsExpressionAndLegacyText(String value) throws JSQLParserException {
Alter alter = parse(value);
ColumnSetDefault column =
alter.getAlterExpressions().get(0).getColumnSetDefaultList().get(0);
assertNotNull(column.getDefaultExpression());
assertEquals(column.getDefaultExpression().toString(), column.getDefaultValue());
StringBuilder buffer = new StringBuilder();
alter.accept(new StatementDeParser(buffer), null);
assertEquals(alter.toString(), buffer.toString());
assertEquals(alter.toString(), CCJSqlParserUtil.parse(buffer.toString()).toString());
}

@Test
void visitorReachesDefaultsInEachAlterActionWithContext() throws JSQLParserException {
List<Long> values = new ArrayList<>();
ExpressionVisitorAdapter<Void> expressions = new ExpressionVisitorAdapter<Void>() {
@Override
public <S> Void visit(LongValue value, S context) {
assertEquals("context", context);
values.add(value.getValue());
return null;
}
};
CCJSqlParserUtil.parse("ALTER TABLE t ALTER COLUMN a SET DEFAULT (1 + 2), "
+ "ALTER COLUMN b SET DEFAULT 3")
.accept(new StatementVisitorAdapter<>(new SelectVisitorAdapter<>(expressions)),
"context");
assertThat(values).containsExactly(1L, 2L, 3L);
}

@Test
void customDeparserAndAstEditsUseTheStructuredDefault() throws JSQLParserException {
Alter alter = parse("(1 + 2)");
StringBuilder output = new StringBuilder();
ExpressionDeParser expressions = new ExpressionDeParser() {
@Override
public <S> StringBuilder visit(LongValue value, S context) {
return getBuilder().append(value.getValue() + 100);
}
};
alter.accept(new StatementDeParser(expressions, new SelectDeParser(), output), null);
assertEquals("ALTER TABLE t ALTER COLUMN a SET DEFAULT (101 + 102)", output.toString());
ColumnSetDefault column =
alter.getAlterExpressions().get(0).getColumnSetDefaultList().get(0);
column.setDefaultExpression(new LongValue(42));
assertEquals("42", column.getDefaultValue());
assertEquals("ALTER TABLE t ALTER COLUMN a SET DEFAULT 42", alter.toString());
}

@Test
void legacyStringConstructorRemainsOpaqueAndAcceptsNull() {
ColumnSetDefault column = new ColumnSetDefault("a", "vendor_default()");
assertNull(column.getDefaultExpression());
assertEquals("a SET DEFAULT vendor_default()", column.toString());
assertEquals("a SET DEFAULT null", new ColumnSetDefault("a", null).toString());
column.setDefaultExpression(new LongValue(1));
assertEquals("1", column.getDefaultValue());
column.setDefaultExpression(null);
assertNull(column.getDefaultValue());
}

private static Alter parse(String value) throws JSQLParserException {
return (Alter) CCJSqlParserUtil.parse("ALTER TABLE t ALTER COLUMN a SET DEFAULT " + value);
}
}
Loading