Skip to content

Commit bf26b8d

Browse files
authored
fix: reuse structured constraints for MySQL inline CHECK (#2688)
* fix: reuse structured constraints for MySQL inline checks Signed-off-by: minleejae <mmj9808@gmail.com> * test: cover PostgreSQL 18 inline check enforcement Signed-off-by: minleejae <mmj9808@gmail.com> --------- Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 82bed50 commit bf26b8d

2 files changed

Lines changed: 113 additions & 2 deletions

File tree

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14143,8 +14143,7 @@ ColumnOption ColumnDefinitionOption(): {
1414314143
LOOKAHEAD(<K_PRIMARY> <K_KEY>) <K_PRIMARY> <K_KEY>
1414414144
{ option = ColumnOption.constraint(new NamedConstraint().withType("PRIMARY KEY")); }
1414514145
|
14146-
LOOKAHEAD([ <K_CONSTRAINT> RelObjectName() ] <K_CHECK>,
14147-
{ Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect)) })
14146+
LOOKAHEAD([ <K_CONSTRAINT> RelObjectName() ] <K_CHECK>)
1414814147
[ <K_CONSTRAINT> constraintName=RelObjectName() ]
1414914148
constraint=CheckConstraintSpec(constraintName)
1415014149
{ option = ColumnOption.constraint(constraint); }
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
import net.sf.jsqlparser.JSQLParserException;
16+
import net.sf.jsqlparser.expression.LongValue;
17+
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
18+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
19+
import net.sf.jsqlparser.statement.Statement;
20+
import net.sf.jsqlparser.statement.alter.Alter;
21+
import net.sf.jsqlparser.statement.create.table.CheckConstraint;
22+
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
23+
import net.sf.jsqlparser.statement.create.table.ColumnOption;
24+
import net.sf.jsqlparser.statement.create.table.CreateTable;
25+
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
26+
import net.sf.jsqlparser.util.deparser.SelectDeParser;
27+
import net.sf.jsqlparser.util.deparser.StatementDeParser;
28+
import org.junit.jupiter.params.ParameterizedTest;
29+
import org.junit.jupiter.params.provider.ValueSource;
30+
31+
class MySqlInlineCheckTest {
32+
@ParameterizedTest
33+
@ValueSource(strings = {"", " ENFORCED", " NOT ENFORCED"})
34+
void sharesChecksAcrossCreateAndColumnAlterations(String enforcement)
35+
throws JSQLParserException {
36+
for (Dialect dialect : new Dialect[] {null, Dialect.MYSQL}) {
37+
for (String prefix : new String[] {"CREATE TABLE t (", "ALTER TABLE t ADD COLUMN ",
38+
"ALTER TABLE t MODIFY COLUMN ", "ALTER TABLE t CHANGE COLUMN old_a "}) {
39+
String sql = prefix + "a INT CONSTRAINT ck CHECK (a > 7)" + enforcement;
40+
if (prefix.startsWith("CREATE")) {
41+
sql += ")";
42+
}
43+
Statement statement = parse(sql, dialect);
44+
ColumnDefinition column = statement instanceof CreateTable
45+
? ((CreateTable) statement).getColumnDefinitions().get(0)
46+
: ((Alter) statement).getAlterExpressions().get(0).getColDataTypeList()
47+
.get(0);
48+
assertEquals(1, column.getColumnOptions().size());
49+
ColumnOption option = column.getColumnOptions().get(0);
50+
assertEquals(ColumnOption.Kind.CONSTRAINT, option.getKind());
51+
CheckConstraint check =
52+
assertInstanceOf(CheckConstraint.class, option.getConstraint());
53+
assertEquals("ck", check.getName());
54+
assertEquals(enforcement.isEmpty() ? null : !enforcement.contains("NOT"),
55+
check.getEnforced());
56+
List<Long> seen = new ArrayList<>();
57+
ExpressionDeParser expression = new ExpressionDeParser() {
58+
@Override
59+
public <S> StringBuilder visit(LongValue value, S context) {
60+
seen.add(value.getValue());
61+
return getBuilder().append(value.getValue() + 100);
62+
}
63+
};
64+
StringBuilder out = new StringBuilder();
65+
statement.accept(new StatementDeParser(expression, new SelectDeParser(), out));
66+
assertEquals(List.of(7L), seen, sql);
67+
assertTrue(out.toString().contains("a > 107"));
68+
assertTrue(statement.toString().contains("a > 7"));
69+
check.setEnforced(false);
70+
check.setExpression(CCJSqlParserUtil.parseExpression("a > 9"));
71+
roundTrip(statement, dialect);
72+
}
73+
}
74+
}
75+
76+
@ParameterizedTest
77+
@ValueSource(strings = {"CHECK (a > 0) NOT NULL", "CHECK (a > 0) CHECK (a < 100)",
78+
"CHECK (a > 0) NOT ENFORCED COMMENT 'check; note'"})
79+
void preservesFollowingColumnOptions(String tail) throws JSQLParserException {
80+
roundTrip(parse("CREATE TABLE t (a INT " + tail + ", b INT)", Dialect.MYSQL),
81+
Dialect.MYSQL);
82+
}
83+
84+
@ParameterizedTest
85+
@ValueSource(strings = {"", " NO INHERIT"})
86+
void preservesPostgreSql18InlineCheckEnforcement(String inheritance)
87+
throws JSQLParserException {
88+
CreateTable table = (CreateTable) parse(
89+
"CREATE TABLE t (a INT CHECK (a > 0)" + inheritance + " NOT ENFORCED)",
90+
Dialect.POSTGRESQL);
91+
CheckConstraint check = assertInstanceOf(CheckConstraint.class,
92+
table.getColumnDefinitions().get(0).getColumnOptions().get(0).getConstraint());
93+
assertEquals(false, check.getEnforced());
94+
assertEquals(!inheritance.isEmpty(), check.isNoInherit());
95+
roundTrip(table, Dialect.POSTGRESQL);
96+
}
97+
98+
private static Statement parse(String sql, Dialect dialect) throws JSQLParserException {
99+
return CCJSqlParserUtil.parse(sql, p -> {
100+
if (dialect != null) {
101+
p.withDialect(dialect);
102+
}
103+
});
104+
}
105+
106+
private static void roundTrip(Statement statement, Dialect dialect) throws JSQLParserException {
107+
StringBuilder out = new StringBuilder();
108+
statement.accept(new StatementDeParser(out));
109+
assertEquals(statement.toString(), out.toString());
110+
assertEquals(out.toString(), parse(out.toString(), dialect).toString());
111+
}
112+
}

0 commit comments

Comments
 (0)