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 @@ -1115,9 +1115,19 @@ protected void toStringPartition(StringBuilder b) {
* Handles the general case for ADD, MODIFY, CHANGE, DROP (column), COMMENT, row-level security,
* and all field-based dispatch (columns, constraints, FK, UK, PK, index).
*/
protected void toStringGeneral(StringBuilder b) {
toStringGeneral(b, b::append);
}

/** Appends a column-definition action, including its common tail. */
public void appendColumnDefinitionsTo(StringBuilder b, Consumer<Expression> expressionPrinter) {
toStringGeneral(b, column -> column.appendTo(b, expressionPrinter));
appendCommonTail(b);
}

@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity",
"PMD.ExcessiveMethodLength"})
protected void toStringGeneral(StringBuilder b) {
private void toStringGeneral(StringBuilder b, Consumer<ColumnDataType> columnPrinter) {
if (operation == AlterOperation.COMMENT_WITH_EQUAL_SIGN) {
b.append("COMMENT =").append(" ");
} else if (operation == AlterOperation.ENABLE_ROW_LEVEL_SECURITY) {
Expand Down Expand Up @@ -1170,7 +1180,12 @@ protected void toStringGeneral(StringBuilder b) {
if (useBrackets && colDataTypeList.size() == 1) {
b.append(" ( ");
}
b.append(PlainSelect.getStringList(colDataTypeList));
for (int i = 0; i < colDataTypeList.size(); i++) {
if (i > 0) {
b.append(", ");
}
columnPrinter.accept(colDataTypeList.get(i));
}
if (useBrackets && colDataTypeList.size() == 1) {
b.append(" ) ");
}
Expand Down Expand Up @@ -1490,13 +1505,25 @@ public ColumnDataType(

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
appendTo(builder, builder::append);
return builder.toString();
}

@Override
public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
builder.append(getColumnName());
if (identityAlterations != null) {
return getColumnName() + " "
+ PlainSelect.getStringList(identityAlterations, false, false);
builder.append(' ')
.append(PlainSelect.getStringList(identityAlterations, false, false));
return;
}
builder.append(withType ? " TYPE " : getColDataType() == null ? "" : " ");
appendDataTypeAndSpecTo(builder, expressionPrinter);
if (usingExpression != null) {
builder.append(" USING ");
expressionPrinter.accept(usingExpression);
}
return getColumnName() + (withType ? " TYPE " : getColDataType() == null ? "" : " ")
+ toStringDataTypeAndSpec()
+ (usingExpression == null ? "" : " USING " + usingExpression);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import net.sf.jsqlparser.statement.imprt.ImportColumn;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.expression.Expression;

import java.io.Serializable;
import java.util.ArrayList;
Expand All @@ -19,6 +20,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

/**
* Globally used definition class for columns.
Expand Down Expand Up @@ -54,7 +56,7 @@ public ColumnDefinition(String columnName, ColDataType colDataType, List<String>
/**
* 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.
* expressions, references and constraints. DEFAULT values use the expression's SQL rendering.
*/
public List<String> getColumnSpecs() {
if (columnOptions != null) {
Expand All @@ -73,7 +75,7 @@ public void setColumnSpecs(List<String> list) {
}

/**
* Returns column options in source order, including structured references and MySQL
* Returns column options in source order, including structured defaults, references and MySQL
* {@code SERIAL DEFAULT VALUE}.
*/
public List<ColumnOption> getColumnOptions() {
Expand Down Expand Up @@ -134,17 +136,42 @@ public void setColumnName(String string) {

@Override
public String toString() {
return (columnName + " " + toStringDataTypeAndSpec()).trim();
StringBuilder builder = new StringBuilder();
appendTo(builder, builder::append);
return builder.toString().trim();
}

/** Appends a column definition using the supplied printer for structured expressions. */
public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
builder.append(columnName);
if (colDataType != null || withOptions) {
builder.append(' ');
}
appendDataTypeAndSpecTo(builder, expressionPrinter);
}

public String toStringDataTypeAndSpec() {
return (colDataType == null ? "" : colDataType)
+ (withOptions ? "WITH OPTIONS" : "")
+ (columnOptions != null && !columnOptions.isEmpty()
? " " + PlainSelect.getStringList(columnOptions, false, false)
: columnSpecs != null && !columnSpecs.isEmpty()
? " " + PlainSelect.getStringList(columnSpecs, false, false)
: "");
StringBuilder builder = new StringBuilder();
appendDataTypeAndSpecTo(builder, builder::append);
return builder.toString();
}

protected void appendDataTypeAndSpecTo(StringBuilder builder,
Consumer<Expression> expressionPrinter) {
if (colDataType != null) {
builder.append(colDataType);
}
if (withOptions) {
builder.append("WITH OPTIONS");
}
if (columnOptions != null) {
for (ColumnOption option : columnOptions) {
builder.append(' ');
option.appendTo(builder, expressionPrinter);
}
} else if (columnSpecs != null && !columnSpecs.isEmpty()) {
builder.append(' ').append(PlainSelect.getStringList(columnSpecs, false, false));
}
}

public ColumnDefinition withColumnName(String columnName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,41 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.select.PlainSelect;

/** A structured option following a column data type. */
public class ColumnOption implements Serializable {

public enum Kind {
SERIAL_DEFAULT_VALUE, REFERENCE, IDENTITY, CONSTRAINT, OTHER
SERIAL_DEFAULT_VALUE, REFERENCE, IDENTITY, CONSTRAINT, DEFAULT, OTHER
}

private Kind kind = Kind.OTHER;
private List<String> tokens;
private ForeignKeyReference foreignKeyReference;
private IdentityDefinition identityDefinition;
private Index constraint;
private Expression defaultExpression;

/** Creates a DEFAULT option. Use a NullValue expression for SQL NULL. */
public static ColumnOption defaultValue(Expression expression) {
ColumnOption option = new ColumnOption();
option.kind = Kind.DEFAULT;
option.setDefaultExpression(expression);
return option;
}

public Expression getDefaultExpression() {
return defaultExpression;
}

/** Replaces the expression of a DEFAULT option created by {@link #defaultValue(Expression)}. */
public void setDefaultExpression(Expression expression) {
defaultExpression = Objects.requireNonNull(expression, "defaultExpression");
}

public static ColumnOption identity(IdentityDefinition definition) {
ColumnOption option = new ColumnOption();
Expand Down Expand Up @@ -78,6 +99,9 @@ public Kind getKind() {
}

public List<String> getTokens() {
if (kind == Kind.DEFAULT) {
return Arrays.asList("DEFAULT", String.valueOf(defaultExpression));
}
return kind == Kind.OTHER || kind == Kind.SERIAL_DEFAULT_VALUE ? tokens
: Collections.singletonList(toString());
}
Expand All @@ -88,15 +112,30 @@ public ForeignKeyReference getForeignKeyReference() {

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
appendTo(builder, builder::append);
return builder.toString();
}

/** Appends the option using the supplied printer for structured expressions. */
public void appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
switch (kind) {
case DEFAULT:
builder.append("DEFAULT ");
expressionPrinter.accept(defaultExpression);
break;
case REFERENCE:
return foreignKeyReference.toString();
builder.append(foreignKeyReference);
break;
case IDENTITY:
return identityDefinition.toString();
builder.append(identityDefinition);
break;
case CONSTRAINT:
return constraint.toString();
constraint.appendTo(builder, expressionPrinter);
break;
default:
return PlainSelect.getStringList(tokens, false, false);
builder.append(PlainSelect.getStringList(tokens, false, false));
break;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public static void visit(TableElement element, Consumer<Expression> expressions,
ColumnDefinition column = (ColumnDefinition) element;
if (column.getColumnOptions() != null) {
for (ColumnOption option : column.getColumnOptions()) {
accept(option.getDefaultExpression(), expressions);
if (option.getForeignKeyReference() != null) {
accept(option.getForeignKeyReference().getTable(), tables);
}
Expand Down
19 changes: 4 additions & 15 deletions src/main/java/net/sf/jsqlparser/util/deparser/AlterDeParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,12 @@ private void deParseAction(AlterExpression action) {
expression -> expression.accept(expressionVisitor, null));
return;
}
if (action.getColDataTypeList() == null || action.getColDataTypeList().size() != 1
|| action.getColDataTypeList().get(0).getUsingExpression() == null) {
if (action.getColDataTypeList() != null) {
action.appendColumnDefinitionsTo(builder,
expression -> expression.accept(expressionVisitor, null));
} else {
builder.append(action);
return;
}
AlterExpression.ColumnDataType column = action.getColDataTypeList().get(0);
builder.append(action.getOperation()).append(' ');
if (action.hasColumn()) {
builder.append("COLUMN ");
}
if (action.isUsingIfExists()) {
builder.append("IF EXISTS ");
}
builder.append(column.getColumnName()).append(column.isWithType() ? " TYPE " : " ")
.append(column.toStringDataTypeAndSpec()).append(" USING ");
column.getUsingExpression().accept(expressionVisitor, null);
deParseTail(action);
}

private void deParseTail(AlterExpression action) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.ColumnOption;
import net.sf.jsqlparser.statement.create.table.Index;
import net.sf.jsqlparser.statement.create.table.TableElement;

Expand All @@ -30,30 +29,12 @@ public void deParse(TableElement element) {
if (element instanceof Index) {
((Index) element).appendTo(builder,
expression -> expression.accept(expressionVisitor, null));
} else if (element instanceof ColumnDefinition
&& ((ColumnDefinition) element).getColumnOptions() != null) {
deParseColumn((ColumnDefinition) element);
} else if (element instanceof ColumnDefinition) {
((ColumnDefinition) element).appendTo(builder,
expression -> expression.accept(expressionVisitor, null));
} else {
builder.append(element);
}
}

private void deParseColumn(ColumnDefinition column) {
builder.append(column.getColumnName());
if (column.getColDataType() != null) {
builder.append(' ').append(column.getColDataType());
}
if (column.isWithOptions()) {
builder.append(" WITH OPTIONS");
}
for (ColumnOption option : column.getColumnOptions()) {
builder.append(' ');
if (option.getConstraint() != null) {
deParse(option.getConstraint());
} else {
builder.append(option);
}
}
}

}
16 changes: 15 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1702,8 +1702,10 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
case K_MATCH_PHRASE: // MATCH_PHRASE
case K_MATCH_PHRASE_PREFIX: // MATCH_PHRASE_PREFIX
case K_MATCH_REGEXP: // MATCH_REGEXP
case K_NOT: // NOT IN / NOT BETWEEN / NOT LIKE / NOT ISNULL / NOT SIMILAR
return true;
case K_NOT: // NOT IN / NOT BETWEEN / NOT LIKE / NOT ISNULL / NOT SIMILAR
// A column's NOT NULL constraint starts after its DEFAULT expression.
return getToken(2).kind != K_NULL;
// Oracle (+) before IN: col(+) IN (...)
case OPENING_BRACKET:
return getToken(2).image.equals("+");
Expand Down Expand Up @@ -12566,6 +12568,7 @@ ColumnOption ColumnDefinitionOption(): {
ColumnOption option;
IdentityDefinition identity;
NamedConstraint constraint;
Expression defaultExpression;
} {
(
LOOKAHEAD({ isKeywordAhead("GENERATED")
Expand All @@ -12583,6 +12586,17 @@ ColumnOption ColumnDefinitionOption(): {
LOOKAHEAD(<K_REFERENCES>) reference=ForeignKeyReferenceSpec()
{ option = ColumnOption.reference(reference); }
|
LOOKAHEAD(<K_DROP> <K_DEFAULT>) <K_DROP> <K_DEFAULT>
{ option = ColumnOption.raw("DROP", "DEFAULT"); }
|
LOOKAHEAD(<K_DEFAULT> <K_ON> <K_NULL>) <K_DEFAULT> <K_ON> <K_NULL>
{ option = ColumnOption.raw("DEFAULT", "ON", "NULL"); }
|
LOOKAHEAD(<K_DEFAULT>) <K_DEFAULT> { option = ColumnOption.raw("DEFAULT"); }
// Db2 permits an omitted default value. Keep these implicit defaults raw.
[ LOOKAHEAD(2, { !(getToken(1).kind == K_NOT && getToken(2).kind == K_NULL) })
defaultExpression=Expression() { option = ColumnOption.defaultValue(defaultExpression); } ]
|
parameter=ColumnDefinitionParameter()
{ option = ColumnOption.raw(parameter); }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ public void testAlterTableDefaultValueTrueIssue926() throws JSQLParserException

// There shall be no COLUMN where there is no COLUMN
assertStatementCanBeDeparsedAs(parsed,
"ALTER TABLE my_table ADD some_column BOOLEAN DEFAULT FALSE");
"ALTER TABLE my_table ADD some_column BOOLEAN DEFAULT false");
}

private void assertReferentialActionOnConstraint(Alter parsed, Action onUpdate,
Expand Down
Loading
Loading