From 1c8c29cab8f5b99f4e442e441a60a745b5fa244a Mon Sep 17 00:00:00 2001 From: Minjae Lee Date: Thu, 10 Sep 2026 21:05:43 +0900 Subject: [PATCH 1/2] Support Informix trailing constraint names and share constraint rendering --- README.md | 1 + .../create/table/CheckConstraint.java | 9 +- .../create/table/ForeignKeyIndex.java | 1 + .../create/table/NamedConstraint.java | 47 +++++- .../util/deparser/TableElementDeParser.java | 9 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 38 +++++ .../alter/InformixConstraintTest.java | 147 ++++++++++++++++++ 7 files changed, 234 insertions(+), 18 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java diff --git a/README.md b/README.md index 8ad991b09..5ff97a386 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J | **DML** | `INSERT` · `UPDATE` · `UPSERT` · `MERGE` · `DELETE` · `TRUNCATE TABLE` | | **DDL** | `CREATE …` · `ALTER …` · `DROP …` | | **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` | +| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints | | **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` | Beyond statement shapes, the grammar handles nested sub-selects, bind parameters (`?`, diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java index 1972d43a1..faf95a641 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/CheckConstraint.java @@ -54,17 +54,12 @@ public void setEnforced(Boolean enforced) { @Override public String toString() { StringBuilder b = new StringBuilder(); - if (isUseConstraintKeyword() || getName() != null) { - b.append("CONSTRAINT"); - if (getName() != null) { - b.append(" ").append(getName()); - } - b.append(" "); - } + appendConstraintPrefixTo(b); b.append("CHECK (").append(expression).append(")"); if (enforced != null) { b.append(enforced ? " ENFORCED" : " NOT ENFORCED"); } + appendConstraintSuffixTo(b); appendConstraintAttributesTo(b); return b.toString(); } diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java b/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java index 344039e82..8ed95f456 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/ForeignKeyIndex.java @@ -174,6 +174,7 @@ public String toString() { .append(PlainSelect.getStringList(getReferencedColumnNames(), true, true)); referentialActions.forEach(b::append); } + appendConstraintSuffixTo(b); appendConstraintAttributesTo(b); return b.toString(); } diff --git a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java index e6b8ed2f1..d35eaee03 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/table/NamedConstraint.java @@ -18,6 +18,45 @@ public class NamedConstraint extends Index { private String indexName; private boolean useConstraintKeyword; + private ConstraintNamePosition constraintNamePosition = ConstraintNamePosition.BEFORE; + + /** Position of the constraint symbol relative to its definition. */ + public enum ConstraintNamePosition { + BEFORE, AFTER + } + + public ConstraintNamePosition getConstraintNamePosition() { + return constraintNamePosition; + } + + public void setConstraintNamePosition(ConstraintNamePosition position) { + constraintNamePosition = java.util.Objects.requireNonNull(position, "position"); + } + + public NamedConstraint withConstraintNamePosition(ConstraintNamePosition position) { + setConstraintNamePosition(position); + return this; + } + + /** Appends the leading keyword and, for the usual syntax, the constraint name. */ + public void appendConstraintPrefixTo(StringBuilder builder) { + boolean leadingName = getName() != null + && constraintNamePosition == ConstraintNamePosition.BEFORE; + if (useConstraintKeyword || leadingName) { + builder.append("CONSTRAINT"); + if (leadingName) { + builder.append(' ').append(getName()); + } + builder.append(' '); + } + } + + /** Appends an Informix constraint name after the complete constraint definition. */ + public void appendConstraintSuffixTo(StringBuilder builder) { + if (constraintNamePosition == ConstraintNamePosition.AFTER && getName() != null) { + builder.append(" CONSTRAINT ").append(getName()); + } + } /** * Returns the optional index name declared after the constraint type. This is distinct from @@ -44,9 +83,6 @@ public void setUseConstraintKeyword(boolean useConstraintKeyword) { @Override public String toString() { String idxSpecText = PlainSelect.getStringList(getIndexSpec(), false, false); - String head = useConstraintKeyword || getName() != null - ? "CONSTRAINT" + (getName() != null ? " " + getName() : "") + " " - : ""; String keyword = getIndexKeyword() != null && !getType().toUpperCase(java.util.Locale.ROOT) .endsWith(getIndexKeyword().toUpperCase(java.util.Locale.ROOT)) @@ -61,9 +97,12 @@ public String toString() { : " " + PlainSelect.getStringList(getColumnsNames(), true, true)) + (!"".equals(idxSpecText) ? " " + idxSpecText : ""); - StringBuilder sql = new StringBuilder(head).append(tail); + StringBuilder sql = new StringBuilder(); + appendConstraintPrefixTo(sql); + sql.append(tail); appendConstraintOptionsTo(sql); if (getKind() != Kind.FOREIGN_KEY) { + appendConstraintSuffixTo(sql); appendConstraintAttributesTo(sql); } return sql.toString(); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/TableElementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/TableElementDeParser.java index 709997ed3..b96f14f15 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/TableElementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/TableElementDeParser.java @@ -94,13 +94,7 @@ private void deParseExclude(ExcludeConstraint constraint) { } private void deParseCheck(CheckConstraint constraint) { - if (constraint.getName() != null || constraint.isUseConstraintKeyword()) { - builder.append("CONSTRAINT"); - if (constraint.getName() != null) { - builder.append(' ').append(constraint.getName()); - } - builder.append(' '); - } + constraint.appendConstraintPrefixTo(builder); builder.append("CHECK ("); if (constraint.getExpression() != null) { constraint.getExpression().accept(expressionVisitor, null); @@ -111,6 +105,7 @@ private void deParseCheck(CheckConstraint constraint) { if (constraint.getEnforced() != null) { builder.append(constraint.getEnforced() ? " ENFORCED" : " NOT ENFORCED"); } + constraint.appendConstraintSuffixTo(builder); constraint.appendConstraintAttributesTo(builder); } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 860ab3821..dfe65ef7f 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -14403,6 +14403,36 @@ DefaultConstraint DefaultConstraintSpec(): { return constraint; } } +/** Parses Informix's ADD CONSTRAINT definition [CONSTRAINT name]. */ +NamedConstraint InformixConstraint(): +{ + NamedConstraint constraint; + Token type; + List columns; + String name; +} +{ + + ( + ( type= | type= ) + columns=ColumnsNamesList() { + constraint = new NamedConstraint() + .withType(type.kind == K_PRIMARY ? "PRIMARY KEY" : type.image) + .withColumnsNames(columns); + } + | + constraint=ForeignKeySpec(null) + | + constraint=CheckConstraintSpec(null) + ) + [ name=RelObjectName() { constraint.setName(name); } ] + { + constraint.setUseConstraintKeyword(true); + constraint.setConstraintNamePosition(NamedConstraint.ConstraintNamePosition.AFTER); + return constraint; + } +} + /** * Parses ADD/ALTER CONSTRAINT clause within AlterExpression. * Handles: CONSTRAINT [UNIQUE [KEY|INDEX]] name columns @@ -14703,6 +14733,14 @@ AlterExpression AlterExpressionAddAlterModify(): alterExp.setIndex(index); } | + LOOKAHEAD( ( | + | | ) "(") + index=InformixConstraint() { + requireDdlSyntax(alterExp.getOperation() == AlterOperation.ADD, + "Informix constraint definitions require ADD"); + alterExp.setIndex(index); + } + | LOOKAHEAD({ isTableIndexAhead() }) index=TableIndexSpec(false) { alterExp.setIndex(index); if (index.getKind() == Index.Kind.PRIMARY_KEY) { diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java new file mode 100644 index 000000000..b2db514bf --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java @@ -0,0 +1,147 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.alter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import net.sf.jsqlparser.statement.create.table.CheckConstraint; +import net.sf.jsqlparser.util.deparser.ExpressionDeParser; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.util.deparser.TableElementDeParser; +import net.sf.jsqlparser.util.TablesNamesFinder; +import net.sf.jsqlparser.test.TestUtils; +import java.util.Set; +import java.util.List; +import java.util.stream.Stream; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex; +import net.sf.jsqlparser.statement.create.table.NamedConstraint; +import net.sf.jsqlparser.statement.create.table.NamedConstraint.ConstraintNamePosition; +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.MethodSource; + +class InformixConstraintTest { + static Stream definitions() { + return Stream.of("PRIMARY KEY (column)", "UNIQUE (column)", + "FOREIGN KEY (column) REFERENCES referenced_table(referenced_column)", + "CHECK (id > 0)", "PRIMARY KEY (id, tenant_id)", + "FOREIGN KEY (id, tenant_id) REFERENCES parent(id, tenant_id) ON DELETE CASCADE"); + } + + @ParameterizedTest + @MethodSource("definitions") + void preservesNamePlacement(String definition) throws Exception { + for (String suffix : List.of("", " CONSTRAINT constraint_name", + " CONSTRAINT \"constraint name\"")) { + String sql = "ALTER TABLE table_name ADD CONSTRAINT " + definition + suffix; + TestUtils.assertSqlCanBeParsedAndDeparsed(sql); + Alter statement = (Alter) CCJSqlParserUtil.parse(sql); + NamedConstraint constraint = + (NamedConstraint) statement.getAlterExpressions().get(0).getIndex(); + assertEquals(ConstraintNamePosition.AFTER, constraint.getConstraintNamePosition()); + assertTrue(constraint.isUseConstraintKeyword()); + assertEquals(suffix.isEmpty() ? null : suffix.substring(" CONSTRAINT ".length()), + constraint.getName()); + StringBuilder deparsed = new StringBuilder(); + statement.accept(new StatementDeParser(deparsed), null); + assertEquals(statement.toString(), deparsed.toString()); + assertEquals(statement.toString(), + CCJSqlParserUtil.parse(deparsed.toString()).toString()); + assertTrue(statement.toString().contains("ADD CONSTRAINT " + definition)); + if (!suffix.isEmpty()) { + assertTrue(statement.toString().endsWith(suffix)); + } + } + } + + @Test + void exposesForeignKeyAndMutableName() throws Exception { + Alter statement = (Alter) CCJSqlParserUtil.parse( + "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) REFERENCES parent(id) CONSTRAINT fk_child"); + ForeignKeyIndex key = (ForeignKeyIndex) statement.getAlterExpressions().get(0).getIndex(); + assertEquals(List.of("id"), key.getColumnsNames()); + assertEquals("parent", key.getTable().getName()); + assertEquals(List.of("id"), key.getReferencedColumnNames()); + key.setName("renamed_fk"); + assertTrue(statement.toString().endsWith("REFERENCES parent(id) CONSTRAINT renamed_fk")); + key.setName((String) null); + assertFalse(statement.toString().contains("fk_child")); + assertEquals(statement.toString(), CCJSqlParserUtil.parse(statement.toString()).toString()); + } + + @Test + void keepsFollowingAlterActionsAndStatements() throws Exception { + String sql = + "ALTER TABLE t ADD CONSTRAINT PRIMARY KEY (id) CONSTRAINT pk_t, ADD COLUMN note INT; SELECT 1;"; + Statements statements = CCJSqlParserUtil.parseStatements(sql); + assertEquals(2, statements.size()); + assertEquals(2, ((Alter) statements.get(0)).getAlterExpressions().size()); + } + + @Test + void retainsLeadingNamesAndBuilders() throws Exception { + NamedConstraint built = new NamedConstraint().withType("PRIMARY KEY").withName("pk_t") + .withColumnsNames(List.of("id")); + assertEquals(ConstraintNamePosition.BEFORE, built.getConstraintNamePosition()); + assertEquals("CONSTRAINT pk_t PRIMARY KEY (id)", built.toString()); + for (String definition : List.of("PRIMARY KEY (id)", "UNIQUE (id)", + "FOREIGN KEY (id) REFERENCES parent(id)", "CHECK (id > 0)")) { + Alter statement = + (Alter) CCJSqlParserUtil.parse("ALTER TABLE t ADD CONSTRAINT c " + definition); + assertEquals(ConstraintNamePosition.BEFORE, + ((NamedConstraint) statement.getAlterExpressions().get(0).getIndex()) + .getConstraintNamePosition()); + assertTrue(statement.toString().contains("CONSTRAINT c " + definition)); + } + } + + @Test + void keepsCheckExpressionVisitorAndForeignTableTraversal() throws Exception { + Alter statement = (Alter) CCJSqlParserUtil + .parse("ALTER TABLE child ADD CONSTRAINT CHECK (id > 0) CONSTRAINT positive_id"); + CheckConstraint check = + (CheckConstraint) statement + .getAlterExpressions().get(0).getIndex(); + StringBuilder builder = new StringBuilder(); + ExpressionDeParser visitor = + new ExpressionDeParser() { + @Override + public StringBuilder visit(Column column, + S context) { + getBuilder().append("renamed_id"); + return getBuilder(); + } + }; + visitor.setBuilder(builder); + new TableElementDeParser(builder, visitor).deParse(check); + assertEquals("CONSTRAINT CHECK (renamed_id > 0) CONSTRAINT positive_id", + builder.toString()); + assertEquals(Set.of("child", "parent"), + TablesNamesFinder.findTables( + "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) REFERENCES parent(id) CONSTRAINT fk")); + } + + @Test + void rejectsMissingOrDuplicateNames() { + for (String sql : List.of( + "ALTER TABLE t ADD CONSTRAINT PRIMARY KEY (id) CONSTRAINT", + "ALTER TABLE t ADD CONSTRAINT UNIQUE (id) CONSTRAINT a CONSTRAINT b", + "ALTER TABLE t ADD CONSTRAINT FOREIGN KEY (id) CONSTRAINT fk", + "ALTER TABLE t MODIFY CONSTRAINT UNIQUE (id) CONSTRAINT uk")) { + assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql)); + } + } +} From 5c7d7d913f18aab84e4551df61c6215f314bf732 Mon Sep 17 00:00:00 2001 From: minleejae Date: Fri, 11 Sep 2026 12:21:07 +0900 Subject: [PATCH 2/2] fix(parser): gate Informix constraints on dialect selection --- README.md | 2 +- .../jsqlparser/parser/AbstractJSqlParser.java | 2 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 3 +- src/site/sphinx/usage.rst | 11 +++ .../alter/InformixConstraintTest.java | 74 +++++++++++++++---- 5 files changed, 73 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5ff97a386..0a851bfd8 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J | **DML** | `INSERT` · `UPDATE` · `UPSERT` · `MERGE` · `DELETE` · `TRUNCATE TABLE` | | **DDL** | `CREATE …` · `ALTER …` · `DROP …` | | **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` | -| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints | +| **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` | | **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` | Beyond statement shapes, the grammar handles nested sub-selects, bind parameters (`?`, diff --git a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java index cd4bfba7e..e40fb2373 100644 --- a/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java +++ b/src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java @@ -45,7 +45,7 @@ public enum Dialect { AdjacentStringLiterals.WHITESPACE, Feature.allowDoubleQuotedStrings, Feature.allowBackslashEscapeCharacter), SNOWFLAKE( - Feature.allowBackslashEscapeCharacter); + Feature.allowBackslashEscapeCharacter), INFORMIX; private final Set lexerFeatures; private final AdjacentStringLiterals adjacentStringLiterals; diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index dfe65ef7f..5ed73351c 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -14734,7 +14734,8 @@ AlterExpression AlterExpressionAddAlterModify(): } | LOOKAHEAD( ( | - | | ) "(") + | | ) "(", + { Dialect.INFORMIX.name().equals(getAsString(Feature.dialect)) }) index=InformixConstraint() { requireDdlSyntax(alterExp.getOperation() == AlterOperation.ADD, "Informix constraint definitions require ADD"); diff --git a/src/site/sphinx/usage.rst b/src/site/sphinx/usage.rst index 593c10b60..ba3848122 100644 --- a/src/site/sphinx/usage.rst +++ b/src/site/sphinx/usage.rst @@ -711,9 +711,20 @@ One grammar covers every supported RDBMS, but a few pieces of syntax mean differ - ``withDoubleQuotedStrings``, ``withBackslashEscapeCharacter``, any-whitespace rule for adjacent string literals * - ``SNOWFLAKE`` - ``withBackslashEscapeCharacter`` only, double quotes stay quoted identifiers + * - ``INFORMIX`` + - Informix ``ALTER TABLE ... ADD CONSTRAINT`` definitions with optional trailing constraint names Features set explicitly *after* the preset win over it. +Informix's constraint form requires an explicit dialect selection: + +.. code-block:: java + + Statement stmt = CCJSqlParserUtil.parse( + "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) " + + "REFERENCES parent(id) CONSTRAINT fk_child", + parser -> parser.withDialect(Dialect.INFORMIX)); + The individual features ------------------------------ diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java index b2db514bf..cc27254d4 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/InformixConstraintTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.stream.Stream; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.Statements; import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex; @@ -47,8 +48,8 @@ void preservesNamePlacement(String definition) throws Exception { for (String suffix : List.of("", " CONSTRAINT constraint_name", " CONSTRAINT \"constraint name\"")) { String sql = "ALTER TABLE table_name ADD CONSTRAINT " + definition + suffix; - TestUtils.assertSqlCanBeParsedAndDeparsed(sql); - Alter statement = (Alter) CCJSqlParserUtil.parse(sql); + Alter statement = (Alter) TestUtils.assertSqlCanBeParsedAndDeparsed(sql, true, + parser -> parser.withDialect(Dialect.INFORMIX)); NamedConstraint constraint = (NamedConstraint) statement.getAlterExpressions().get(0).getIndex(); assertEquals(ConstraintNamePosition.AFTER, constraint.getConstraintNamePosition()); @@ -59,7 +60,8 @@ void preservesNamePlacement(String definition) throws Exception { statement.accept(new StatementDeParser(deparsed), null); assertEquals(statement.toString(), deparsed.toString()); assertEquals(statement.toString(), - CCJSqlParserUtil.parse(deparsed.toString()).toString()); + CCJSqlParserUtil.parse(deparsed.toString(), + parser -> parser.withDialect(Dialect.INFORMIX)).toString()); assertTrue(statement.toString().contains("ADD CONSTRAINT " + definition)); if (!suffix.isEmpty()) { assertTrue(statement.toString().endsWith(suffix)); @@ -67,10 +69,41 @@ void preservesNamePlacement(String definition) throws Exception { } } + @ParameterizedTest + @MethodSource("definitions") + void requiresInformixDialectForTrailingNames(String definition) { + for (String suffix : List.of(" CONSTRAINT constraint_name", + " CONSTRAINT \"constraint name\"")) { + String sql = "ALTER TABLE table_name ADD CONSTRAINT " + definition + suffix; + assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql)); + for (Dialect dialect : Dialect.values()) { + if (dialect != Dialect.INFORMIX) { + assertThrows(JSQLParserException.class, + () -> CCJSqlParserUtil.parse(sql, + parser -> parser.withDialect(dialect)), + dialect.name()); + } + } + } + } + + @Test + void retainsDefaultInterpretationOfPrimaryAsConstraintName() throws Exception { + String sql = "ALTER TABLE t ADD CONSTRAINT PRIMARY KEY (id)"; + Alter statement = (Alter) CCJSqlParserUtil.parse(sql); + NamedConstraint constraint = + (NamedConstraint) statement.getAlterExpressions().get(0).getIndex(); + assertEquals("PRIMARY", constraint.getName()); + assertEquals("KEY", constraint.getType()); + assertEquals(ConstraintNamePosition.BEFORE, constraint.getConstraintNamePosition()); + assertEquals(sql, statement.toString()); + } + @Test void exposesForeignKeyAndMutableName() throws Exception { Alter statement = (Alter) CCJSqlParserUtil.parse( - "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) REFERENCES parent(id) CONSTRAINT fk_child"); + "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) REFERENCES parent(id) CONSTRAINT fk_child", + parser -> parser.withDialect(Dialect.INFORMIX)); ForeignKeyIndex key = (ForeignKeyIndex) statement.getAlterExpressions().get(0).getIndex(); assertEquals(List.of("id"), key.getColumnsNames()); assertEquals("parent", key.getTable().getName()); @@ -79,14 +112,16 @@ void exposesForeignKeyAndMutableName() throws Exception { assertTrue(statement.toString().endsWith("REFERENCES parent(id) CONSTRAINT renamed_fk")); key.setName((String) null); assertFalse(statement.toString().contains("fk_child")); - assertEquals(statement.toString(), CCJSqlParserUtil.parse(statement.toString()).toString()); + assertEquals(statement.toString(), CCJSqlParserUtil.parse(statement.toString(), + parser -> parser.withDialect(Dialect.INFORMIX)).toString()); } @Test void keepsFollowingAlterActionsAndStatements() throws Exception { String sql = "ALTER TABLE t ADD CONSTRAINT PRIMARY KEY (id) CONSTRAINT pk_t, ADD COLUMN note INT; SELECT 1;"; - Statements statements = CCJSqlParserUtil.parseStatements(sql); + Statements statements = CCJSqlParserUtil.parseStatements(sql, + parser -> parser.withDialect(Dialect.INFORMIX)); assertEquals(2, statements.size()); assertEquals(2, ((Alter) statements.get(0)).getAlterExpressions().size()); } @@ -99,19 +134,23 @@ void retainsLeadingNamesAndBuilders() throws Exception { assertEquals("CONSTRAINT pk_t PRIMARY KEY (id)", built.toString()); for (String definition : List.of("PRIMARY KEY (id)", "UNIQUE (id)", "FOREIGN KEY (id) REFERENCES parent(id)", "CHECK (id > 0)")) { - Alter statement = - (Alter) CCJSqlParserUtil.parse("ALTER TABLE t ADD CONSTRAINT c " + definition); - assertEquals(ConstraintNamePosition.BEFORE, - ((NamedConstraint) statement.getAlterExpressions().get(0).getIndex()) - .getConstraintNamePosition()); - assertTrue(statement.toString().contains("CONSTRAINT c " + definition)); + String sql = "ALTER TABLE t ADD CONSTRAINT c " + definition; + for (Alter statement : List.of((Alter) CCJSqlParserUtil.parse(sql), + (Alter) CCJSqlParserUtil.parse(sql, + parser -> parser.withDialect(Dialect.INFORMIX)))) { + assertEquals(ConstraintNamePosition.BEFORE, + ((NamedConstraint) statement.getAlterExpressions().get(0).getIndex()) + .getConstraintNamePosition()); + assertTrue(statement.toString().contains("CONSTRAINT c " + definition)); + } } } @Test void keepsCheckExpressionVisitorAndForeignTableTraversal() throws Exception { Alter statement = (Alter) CCJSqlParserUtil - .parse("ALTER TABLE child ADD CONSTRAINT CHECK (id > 0) CONSTRAINT positive_id"); + .parse("ALTER TABLE child ADD CONSTRAINT CHECK (id > 0) CONSTRAINT positive_id", + parser -> parser.withDialect(Dialect.INFORMIX)); CheckConstraint check = (CheckConstraint) statement .getAlterExpressions().get(0).getIndex(); @@ -130,8 +169,9 @@ public StringBuilder visit(Column column, assertEquals("CONSTRAINT CHECK (renamed_id > 0) CONSTRAINT positive_id", builder.toString()); assertEquals(Set.of("child", "parent"), - TablesNamesFinder.findTables( - "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) REFERENCES parent(id) CONSTRAINT fk")); + new TablesNamesFinder<>().getTables(CCJSqlParserUtil.parse( + "ALTER TABLE child ADD CONSTRAINT FOREIGN KEY (id) REFERENCES parent(id) CONSTRAINT fk", + parser -> parser.withDialect(Dialect.INFORMIX)))); } @Test @@ -141,7 +181,9 @@ void rejectsMissingOrDuplicateNames() { "ALTER TABLE t ADD CONSTRAINT UNIQUE (id) CONSTRAINT a CONSTRAINT b", "ALTER TABLE t ADD CONSTRAINT FOREIGN KEY (id) CONSTRAINT fk", "ALTER TABLE t MODIFY CONSTRAINT UNIQUE (id) CONSTRAINT uk")) { - assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql)); + assertThrows(JSQLParserException.class, + () -> CCJSqlParserUtil.parse(sql, + parser -> parser.withDialect(Dialect.INFORMIX))); } } }