From 466f1bd5ce5fc3f330385f7ee4ad99c74f1d9a6e Mon Sep 17 00:00:00 2001 From: Minjae Lee Date: Thu, 3 Sep 2026 15:26:46 +0900 Subject: [PATCH] fix(parser): model ALTER COLUMN nullability actions Represent SET NOT NULL with a dedicated AST node and match DROP NOT NULL before the generic column-data-type branch. Cover parsing, deparsing, validation, and reflection. --- .../statement/alter/AlterExpression.java | 33 +++++++++++++++++++ .../validation/validator/AlterValidator.java | 6 ++++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 26 ++++++++++++--- .../jsqlparser/statement/alter/AlterTest.java | 17 +++++++++- .../builder/ReflectionModelTest.java | 1 + .../validator/AlterValidatorTest.java | 6 ++++ 6 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java index dd4e07f52a..7e2fea943e 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -41,6 +41,7 @@ public class AlterExpression implements Serializable { private String columnOldName; private List colDataTypeList; private List columnDropNotNullList; + private List columnSetNotNullList; private List columnDropDefaultList; private List columnSetDefaultList; private List columnSetVisibilityList; @@ -382,6 +383,13 @@ public void addColDropNotNull(ColumnDropNotNull columnDropNotNull) { columnDropNotNullList.add(columnDropNotNull); } + public void addColSetNotNull(ColumnSetNotNull columnSetNotNull) { + if (columnSetNotNullList == null) { + columnSetNotNullList = new ArrayList<>(); + } + columnSetNotNullList.add(columnSetNotNull); + } + public List getColumnDropDefaultList() { return columnDropDefaultList; } @@ -527,6 +535,10 @@ public List getColumnDropNotNullList() { return columnDropNotNullList; } + public List getColumnSetNotNullList() { + return columnSetNotNullList; + } + public void addParameters(String... params) { if (parameters == null) { parameters = new ArrayList<>(); @@ -1134,6 +1146,9 @@ protected void toStringGeneral(StringBuilder b) { if (colDataTypeList.size() > 1) { b.append(")"); } + } else if (getColumnSetNotNullList() != null) { + b.append("COLUMN "); + b.append(PlainSelect.getStringList(columnSetNotNullList)); } else if (getColumnDropNotNullList() != null) { b.append("COLUMN "); b.append(PlainSelect.getStringList(columnDropNotNullList)); @@ -1467,6 +1482,24 @@ public String toString() { } } + public static final class ColumnSetNotNull implements Serializable { + + private final String columnName; + + public ColumnSetNotNull(String columnName) { + this.columnName = columnName; + } + + public String getColumnName() { + return columnName; + } + + @Override + public String toString() { + return columnName + " SET NOT NULL"; + } + } + public static final class ColumnDropDefault implements Serializable { private final String columnName; diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java index 4db652b960..a8aaf03ebe 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/AlterValidator.java @@ -17,6 +17,7 @@ import net.sf.jsqlparser.statement.alter.AlterExpression; import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType; import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull; +import net.sf.jsqlparser.statement.alter.AlterExpression.ColumnSetNotNull; import net.sf.jsqlparser.statement.alter.AlterOperation; import net.sf.jsqlparser.util.validation.ValidationCapability; import net.sf.jsqlparser.util.validation.ValidationUtil; @@ -47,6 +48,11 @@ public void validate(Alter alter, AlterExpression e) { ColumnDropNotNull::getColumnName)); } + if (e.getColumnSetNotNullList() != null) { + validateOptionalColumnNames(c, ValidationUtil.map(e.getColumnSetNotNullList(), + ColumnSetNotNull::getColumnName)); + } + if (e.getColDataTypeList() != null) { boolean validateForExist = !EnumSet.of(AlterOperation.ADD).contains(e.getOperation()); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index d52b7a9c2e..b89aea8bcd 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -12213,13 +12213,15 @@ Truncate Truncate(): /** * Parses common column-level changes shared between the COLUMN-prefixed and bare forms: - * DROP DEFAULT, SET DEFAULT, SET VISIBLE/INVISIBLE, and bracketed multi-column definitions. + * DROP DEFAULT, SET DEFAULT, SET NOT NULL, SET VISIBLE/INVISIBLE, and bracketed multi-column + * definitions. */ void AlterExpressionColumnChanges(AlterExpression alterExp): { AlterExpression.ColumnDataType alterExpressionColumnDataType = null; AlterExpression.ColumnDropDefault alterExpressionColumnDropDefault = null; AlterExpression.ColumnSetDefault alterExpressionColumnSetDefault = null; + AlterExpression.ColumnSetNotNull alterExpressionColumnSetNotNull = null; AlterExpression.ColumnSetVisibility alterExpressionColumnSetVisibility = null; } { @@ -12230,6 +12232,9 @@ void AlterExpressionColumnChanges(AlterExpression alterExp): LOOKAHEAD(3) alterExpressionColumnSetDefault = AlterExpressionColumnSetDefault() { alterExp.addColSetDefault(alterExpressionColumnSetDefault); } | + LOOKAHEAD(4) alterExpressionColumnSetNotNull = AlterExpressionColumnSetNotNull() + { alterExp.addColSetNotNull(alterExpressionColumnSetNotNull); } + | LOOKAHEAD(3) alterExpressionColumnSetVisibility = AlterExpressionColumnSetVisibility() { alterExp.addColSetVisibility(alterExpressionColumnSetVisibility); } | @@ -12287,6 +12292,19 @@ AlterExpression.ColumnDropNotNull AlterExpressionColumnDropNotNull(): } } +AlterExpression.ColumnSetNotNull AlterExpressionColumnSetNotNull(): +{ + String columnName = null; +} +{ + columnName = RelObjectName() + + + { + return new AlterExpression.ColumnSetNotNull(columnName); + } +} + AlterExpression.ColumnDropDefault AlterExpressionColumnDropDefault(): { String columnName = null; @@ -12926,11 +12944,11 @@ AlterExpression AlterExpressionAddAlterModify(): ( LOOKAHEAD(3) AlterExpressionColumnChanges(alterExp) | - LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType() - { alterExp.addColDataType(alterExpressionColumnDataType); } - | LOOKAHEAD(3) alterExpressionColumnDropNotNull = AlterExpressionColumnDropNotNull() { alterExp.addColDropNotNull( alterExpressionColumnDropNotNull);} + | + LOOKAHEAD(2) alterExpressionColumnDataType = AlterExpressionColumnDataType() + { alterExp.addColDataType(alterExpressionColumnDataType); } ) ) | diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index a18215b790..15dff3e923 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -644,8 +644,23 @@ public void testAlterConstraintWithoutFKSourceColumnsIssue929() throws JSQLParse @Test public void testAlterTableAlterColumnDropNotNullIssue918() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed( + Alter alter = (Alter) assertSqlCanBeParsedAndDeparsed( "ALTER TABLE \"user_table_t\" ALTER COLUMN name DROP NOT NULL"); + AlterExpression expression = alter.getAlterExpressions().get(0); + + assertNull(expression.getColDataTypeList()); + assertEquals("name", expression.getColumnDropNotNullList().get(0).getColumnName()); + assertTrue(expression.getColumnDropNotNullList().get(0).isWithNot()); + } + + @Test + public void testAlterTableAlterColumnSetNotNull() throws JSQLParserException { + Alter alter = (Alter) assertSqlCanBeParsedAndDeparsed( + "ALTER TABLE user_table ALTER COLUMN name SET NOT NULL"); + AlterExpression expression = alter.getAlterExpressions().get(0); + + assertNull(expression.getColDataTypeList()); + assertEquals("name", expression.getColumnSetNotNullList().get(0).getColumnName()); } @Test diff --git a/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java b/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java index 3fbfe8d962..64d7d83242 100644 --- a/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/builder/ReflectionModelTest.java @@ -141,6 +141,7 @@ public class ReflectionModelTest { new net.sf.jsqlparser.statement.alter.AlterExpression(), new net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDataType(false), new net.sf.jsqlparser.statement.alter.AlterExpression.ColumnDropNotNull("name"), + new net.sf.jsqlparser.statement.alter.AlterExpression.ColumnSetNotNull("name"), new net.sf.jsqlparser.statement.merge.MergeInsert(), new net.sf.jsqlparser.statement.alter.DeferrableConstraint(), new net.sf.jsqlparser.statement.alter.EnableConstraint(), diff --git a/src/test/java/net/sf/jsqlparser/util/validation/validator/AlterValidatorTest.java b/src/test/java/net/sf/jsqlparser/util/validation/validator/AlterValidatorTest.java index f2aef4e403..408e0efa5c 100644 --- a/src/test/java/net/sf/jsqlparser/util/validation/validator/AlterValidatorTest.java +++ b/src/test/java/net/sf/jsqlparser/util/validation/validator/AlterValidatorTest.java @@ -151,4 +151,10 @@ public void testAlterTableAlterColumnDropNotNullIssue918() throws JSQLParserExce DatabaseType.DATABASES); } + @Test + public void testAlterTableAlterColumnSetNotNull() throws JSQLParserException { + validateNoErrors("ALTER TABLE user_table ALTER COLUMN name SET NOT NULL", 1, + DatabaseType.DATABASES); + } + }