Skip to content

Commit d45f796

Browse files
authored
Support SQL Server ALTER TABLE ADD DEFAULT constraints (#2561)
1 parent 8ecff15 commit d45f796

8 files changed

Lines changed: 316 additions & 3 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.create.table;
11+
12+
import java.util.function.Consumer;
13+
import net.sf.jsqlparser.expression.Expression;
14+
import net.sf.jsqlparser.schema.Column;
15+
16+
/** A SQL Server table-level DEFAULT expression FOR a column, with an optional constraint name. */
17+
public class DefaultConstraint extends NamedConstraint {
18+
private Expression expression;
19+
private Column column;
20+
private boolean withValues;
21+
22+
public DefaultConstraint() {
23+
setType("DEFAULT");
24+
setKind(Kind.DEFAULT);
25+
}
26+
27+
public Expression getExpression() {
28+
return expression;
29+
}
30+
31+
public void setExpression(Expression expression) {
32+
this.expression = expression;
33+
}
34+
35+
public Column getColumn() {
36+
return column;
37+
}
38+
39+
public void setColumn(Column column) {
40+
this.column = column;
41+
}
42+
43+
public boolean isWithValues() {
44+
return withValues;
45+
}
46+
47+
public void setWithValues(boolean withValues) {
48+
this.withValues = withValues;
49+
}
50+
51+
public DefaultConstraint withExpression(Expression expression) {
52+
setExpression(expression);
53+
return this;
54+
}
55+
56+
public DefaultConstraint withColumn(Column column) {
57+
setColumn(column);
58+
return this;
59+
}
60+
61+
public DefaultConstraint withWithValues(boolean withValues) {
62+
setWithValues(withValues);
63+
return this;
64+
}
65+
66+
@Override
67+
public DefaultConstraint withName(String name) {
68+
setName(name);
69+
return this;
70+
}
71+
72+
/** Shares rendering with deparsers while allowing both the value and column to be visited. */
73+
public void appendTo(StringBuilder builder, Consumer<Expression> expressionWriter) {
74+
if (expression == null || column == null) {
75+
throw new IllegalStateException("DEFAULT requires an expression and a target column");
76+
}
77+
if (getName() != null) {
78+
builder.append("CONSTRAINT ").append(getName()).append(' ');
79+
}
80+
builder.append("DEFAULT ");
81+
expressionWriter.accept(expression);
82+
builder.append(" FOR ");
83+
expressionWriter.accept(column);
84+
if (withValues) {
85+
builder.append(" WITH VALUES");
86+
}
87+
}
88+
89+
@Override
90+
public String toString() {
91+
StringBuilder builder = new StringBuilder();
92+
appendTo(builder, builder::append);
93+
return builder.toString();
94+
}
95+
}

src/main/java/net/sf/jsqlparser/statement/create/table/Index.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
public class Index implements TableElement, Serializable {
2525

2626
public enum Kind {
27-
PRIMARY_KEY, UNIQUE, INDEX, FULLTEXT, SPATIAL, FOREIGN_KEY, CHECK, EXCLUDE, OTHER
27+
PRIMARY_KEY, UNIQUE, INDEX, FULLTEXT, SPATIAL, FOREIGN_KEY, CHECK, EXCLUDE, DEFAULT, OTHER
2828
}
2929

3030
private final List<String> name = new ArrayList<>();
@@ -196,6 +196,8 @@ public void setType(String string) {
196196
kind = Kind.CHECK;
197197
} else if (normalized.startsWith("EXCLUDE")) {
198198
kind = Kind.EXCLUDE;
199+
} else if (normalized.equals("DEFAULT")) {
200+
kind = Kind.DEFAULT;
199201
} else if (normalized.contains("INDEX") || normalized.contains("KEY")) {
200202
kind = Kind.INDEX;
201203
}

src/main/java/net/sf/jsqlparser/util/TableDefinitionTraversal.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
2020
import net.sf.jsqlparser.statement.create.table.ColumnOption;
2121
import net.sf.jsqlparser.statement.create.table.CreateTable;
22+
import net.sf.jsqlparser.statement.create.table.DefaultConstraint;
2223
import net.sf.jsqlparser.statement.create.table.ExcludeConstraint;
2324
import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex;
2425
import net.sf.jsqlparser.statement.create.table.Index;
@@ -75,6 +76,11 @@ public static void visit(TableElement element, Consumer<Expression> expressions,
7576
if (index instanceof CheckConstraint) {
7677
accept(((CheckConstraint) index).getExpression(), expressions);
7778
}
79+
if (index instanceof DefaultConstraint) {
80+
DefaultConstraint constraint = (DefaultConstraint) index;
81+
accept(constraint.getExpression(), expressions);
82+
accept(constraint.getColumn(), expressions);
83+
}
7884
if (index instanceof ExcludeConstraint) {
7985
accept(((ExcludeConstraint) index).getExpression(), expressions);
8086
}

src/main/java/net/sf/jsqlparser/util/deparser/AlterDeParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import net.sf.jsqlparser.statement.alter.Alter;
1313
import net.sf.jsqlparser.expression.ExpressionVisitor;
1414
import net.sf.jsqlparser.statement.alter.AlterExpression;
15+
import net.sf.jsqlparser.statement.create.table.DefaultConstraint;
1516
import net.sf.jsqlparser.statement.select.PlainSelect;
1617
import java.util.Iterator;
1718

@@ -47,6 +48,12 @@ public void deParse(Alter alter) {
4748
}
4849

4950
private void deParseAction(AlterExpression action) {
51+
if (action.getIndex() instanceof DefaultConstraint) {
52+
builder.append(action.getOperation()).append(' ');
53+
new TableElementDeParser(builder, expressionVisitor).deParse(action.getIndex());
54+
deParseTail(action);
55+
return;
56+
}
5057
if (action.getColDataTypeList() == null || action.getColDataTypeList().size() != 1
5158
|| action.getColDataTypeList().get(0).getUsingExpression() == null) {
5259
builder.append(action);
@@ -63,10 +70,17 @@ private void deParseAction(AlterExpression action) {
6370
builder.append(column.getColumnName()).append(column.isWithType() ? " TYPE " : " ")
6471
.append(column.toStringDataTypeAndSpec()).append(" USING ");
6572
column.getUsingExpression().accept(expressionVisitor, null);
73+
deParseTail(action);
74+
}
75+
76+
private void deParseTail(AlterExpression action) {
6677
if (action.getParameters() != null && !action.getParameters().isEmpty()) {
6778
builder.append(' ')
6879
.append(PlainSelect.getStringList(action.getParameters(), false, false));
6980
}
81+
if (action.getIndex() != null && action.getIndex().getCommentText() != null) {
82+
builder.append(" COMMENT ").append(action.getIndex().getCommentText());
83+
}
7084
}
7185

7286
}

src/main/java/net/sf/jsqlparser/util/deparser/TableElementDeParser.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.sf.jsqlparser.statement.create.table.CheckConstraint;
1515
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
1616
import net.sf.jsqlparser.statement.create.table.ColumnOption;
17+
import net.sf.jsqlparser.statement.create.table.DefaultConstraint;
1718
import net.sf.jsqlparser.statement.create.table.ExcludeConstraint;
1819
import net.sf.jsqlparser.statement.create.table.Index;
1920
import net.sf.jsqlparser.statement.create.table.TableElement;
@@ -30,7 +31,10 @@ public TableElementDeParser(StringBuilder builder,
3031

3132
@Override
3233
public void deParse(TableElement element) {
33-
if (element instanceof ExcludeConstraint) {
34+
if (element instanceof DefaultConstraint) {
35+
((DefaultConstraint) element).appendTo(builder,
36+
expression -> expression.accept(expressionVisitor, null));
37+
} else if (element instanceof ExcludeConstraint) {
3438
deParseExclude((ExcludeConstraint) element);
3539
} else if (element instanceof CheckConstraint) {
3640
deParseCheck((CheckConstraint) element);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull;
2020
import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnSetNotNull;
2121
import net.sf.jsqlparser.statement.alter.AlterOperation;
22+
import net.sf.jsqlparser.statement.create.table.DefaultConstraint;
2223
import net.sf.jsqlparser.util.TableDefinitionTraversal;
2324
import net.sf.jsqlparser.util.validation.ValidationCapability;
2425
import net.sf.jsqlparser.util.validation.ValidationUtil;
@@ -87,7 +88,10 @@ public void validate(Alter alter, AlterExpression e) {
8788
validateOptionalColumnNames(c, e.getUkColumns(), NamedObject.uniqueConstraint);
8889
}
8990

90-
if (e.getIndex() != null) {
91+
if (e.getIndex() instanceof DefaultConstraint) {
92+
validateOptionalName(c, NamedObject.constraint, e.getIndex().getName(), null, false,
93+
NamedObject.table);
94+
} else if (e.getIndex() != null) {
9195
validateName(c, NamedObject.index, e.getIndex().getName());
9296
if (e.getIndex().getColumns() != null) {
9397
validateOptionalColumnNames(c,

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14288,6 +14288,23 @@ AlterExpression AlterExpressionDiscardOrImport():
1428814288
}
1428914289

1429014290

14291+
/**
14292+
* Parses SQL Server's [CONSTRAINT name] DEFAULT expression FOR column [WITH VALUES].
14293+
*/
14294+
DefaultConstraint DefaultConstraintSpec():
14295+
{
14296+
DefaultConstraint constraint = new DefaultConstraint();
14297+
String name;
14298+
Expression expression;
14299+
}
14300+
{
14301+
[ <K_CONSTRAINT> name=RelObjectName() { constraint.setName(name); } ]
14302+
<K_DEFAULT> expression=Expression() { constraint.setExpression(expression); }
14303+
<K_FOR> name=RelObjectName() { constraint.setColumn(new Column(name)); }
14304+
[ <K_WITH> <K_VALUES> { constraint.setWithValues(true); } ]
14305+
{ return constraint; }
14306+
}
14307+
1429114308
/**
1429214309
* Parses ADD/ALTER CONSTRAINT clause within AlterExpression.
1429314310
* Handles: CONSTRAINT [UNIQUE [KEY|INDEX]] name columns
@@ -14573,6 +14590,13 @@ AlterExpression AlterExpressionAddAlterModify():
1457314590
)
1457414591

1457514592
(
14593+
LOOKAHEAD({ getToken(1).kind == K_DEFAULT || getToken(1).kind == K_CONSTRAINT
14594+
&& getToken(3).kind == K_DEFAULT }) index=DefaultConstraintSpec() {
14595+
requireDdlSyntax(alterExp.getOperation() == AlterOperation.ADD,
14596+
"Table-level DEFAULT constraints require ADD");
14597+
alterExp.setIndex(index);
14598+
}
14599+
|
1457614600
LOOKAHEAD({ alterExp.getOperation() == AlterOperation.ALTER
1457714601
&& getToken(1).kind == K_INDEX })
1457814602
tk=<K_INDEX> sk3=RelObjectName() IndexOptionList(indexSpec) {

0 commit comments

Comments
 (0)