Skip to content

Commit cf51671

Browse files
committed
fix(parser): distinguish MySQL constraint and index names
1 parent 8397d3a commit cf51671

5 files changed

Lines changed: 132 additions & 12 deletions

File tree

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@ public void setEnforced(Boolean enforced) {
5050
@Override
5151
public String toString() {
5252
StringBuilder b = new StringBuilder();
53-
if (getName() != null) {
54-
b.append("CONSTRAINT ").append(getName()).append(" ");
53+
if (isUseConstraintKeyword() || getName() != null) {
54+
b.append("CONSTRAINT");
55+
if (getName() != null) {
56+
b.append(" ").append(getName());
57+
}
58+
b.append(" ");
5559
}
5660
b.append("CHECK (").append(expression).append(")");
5761
if (enforced != null) {
@@ -89,6 +93,11 @@ public CheckConstraint withUsing(String using) {
8993
return (CheckConstraint) super.withUsing(using);
9094
}
9195

96+
@Override
97+
public CheckConstraint withUseConstraintKeyword(boolean useConstraintKeyword) {
98+
return (CheckConstraint) super.withUseConstraintKeyword(useConstraintKeyword);
99+
}
100+
92101
@Override
93102
public CheckConstraint withName(List<String> name) {
94103
return (CheckConstraint) super.withName(name);

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ public ForeignKeyIndex withUsing(String using) {
169169
return (ForeignKeyIndex) super.withUsing(using);
170170
}
171171

172+
@Override
173+
public ForeignKeyIndex withIndexName(String indexName) {
174+
return (ForeignKeyIndex) super.withIndexName(indexName);
175+
}
176+
177+
@Override
178+
public ForeignKeyIndex withUseConstraintKeyword(boolean useConstraintKeyword) {
179+
return (ForeignKeyIndex) super.withUseConstraintKeyword(useConstraintKeyword);
180+
}
181+
172182
@Override
173183
public ForeignKeyIndex withName(List<String> name) {
174184
return (ForeignKeyIndex) super.withName(name);

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

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,55 @@
1616

1717
public class NamedConstraint extends Index {
1818

19+
private String indexName;
20+
private boolean useConstraintKeyword;
21+
22+
/**
23+
* Returns the optional index name declared after the constraint type. This is distinct from
24+
* {@link #getName()}, which represents the optional constraint symbol.
25+
*
26+
* @return the index name, or {@code null} when it was omitted
27+
*/
28+
public String getIndexName() {
29+
return indexName;
30+
}
31+
32+
public void setIndexName(String indexName) {
33+
this.indexName = indexName;
34+
}
35+
36+
public boolean isUseConstraintKeyword() {
37+
return useConstraintKeyword;
38+
}
39+
40+
public void setUseConstraintKeyword(boolean useConstraintKeyword) {
41+
this.useConstraintKeyword = useConstraintKeyword;
42+
}
43+
1944
@Override
2045
public String toString() {
2146
String idxSpecText = PlainSelect.getStringList(getIndexSpec(), false, false);
22-
String head = getName() != null ? "CONSTRAINT " + getName() + " " : "";
23-
String tail = getType() + " " + PlainSelect.getStringList(getColumnsNames(), true, true) +
47+
String head = useConstraintKeyword || getName() != null
48+
? "CONSTRAINT" + (getName() != null ? " " + getName() : "") + " "
49+
: "";
50+
String tail = getType()
51+
+ (indexName != null ? " " + indexName : "")
52+
+ (getUsing() != null ? " USING " + getUsing() : "")
53+
+ " " + PlainSelect.getStringList(getColumnsNames(), true, true) +
2454
(!"".equals(idxSpecText) ? " " + idxSpecText : "");
2555
return head + tail;
2656
}
2757

58+
public NamedConstraint withIndexName(String indexName) {
59+
setIndexName(indexName);
60+
return this;
61+
}
62+
63+
public NamedConstraint withUseConstraintKeyword(boolean useConstraintKeyword) {
64+
setUseConstraintKeyword(useConstraintKeyword);
65+
return this;
66+
}
67+
2868
@Override
2969
public NamedConstraint withName(List<String> name) {
3070
return (NamedConstraint) super.withName(name);

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11446,6 +11446,9 @@ Index CreateTableConstraint():
1144611446
Token tk2 = null;
1144711447
Token tk3 = null;
1144811448
String sk3 = null;
11449+
String indexName = null;
11450+
String using = null;
11451+
boolean useConstraintKeyword = false;
1144911452
List<Index.ColumnParams> colNames = null;
1145011453
List<String> parameter = new ArrayList<String>();
1145111454
List<String> idxSpec = new ArrayList<String>();
@@ -11473,16 +11476,25 @@ Index CreateTableConstraint():
1147311476
{
1147411477
index = new NamedConstraint();
1147511478
tk2=null;
11479+
indexName=null;
11480+
using=null;
1147611481
idxSpec.clear();
1147711482
}
11478-
[ <K_CONSTRAINT> sk3=RelObjectName() {index.setName(sk3);} ]
11483+
[ <K_CONSTRAINT> { ((NamedConstraint) index).setUseConstraintKeyword(true); }
11484+
[ LOOKAHEAD({ getToken(1).kind != K_PRIMARY && getToken(1).kind != K_UNIQUE })
11485+
sk3=RelObjectName() {index.setName(sk3);} ]
11486+
]
1147911487
(
1148011488
tk=<K_PRIMARY> tk2=<K_KEY>
1148111489
|
11482-
tk=<K_UNIQUE> [ tk2=<K_KEY> ]
11490+
tk=<K_UNIQUE> [ LOOKAHEAD(2) (tk2=<K_KEY> | tk2=<K_INDEX>) ]
11491+
[ LOOKAHEAD(2, { getToken(1).kind != K_USING }) indexName=RelObjectName() ]
11492+
[ LOOKAHEAD(2) using=UsingIndexType() ]
1148311493
)
1148411494
{
1148511495
index.setType( tk.image + ( tk2!=null ? " " + tk2.image : "" ));
11496+
((NamedConstraint) index).setIndexName(indexName);
11497+
index.setUsing(using);
1148611498
tk2=null;
1148711499
}
1148811500
colNames = ColumnNamesWithParamsList()
@@ -11513,17 +11525,21 @@ Index CreateTableConstraint():
1151311525
)
1151411526
|
1151511527
LOOKAHEAD(3) (
11516-
{ sk3=null; }
11517-
[ <K_CONSTRAINT> sk3=RelObjectName() ]
11528+
{ sk3=null; useConstraintKeyword=false; }
11529+
[ <K_CONSTRAINT> { useConstraintKeyword=true; }
11530+
[ LOOKAHEAD({ getToken(1).kind != K_FOREIGN }) sk3=RelObjectName() ]
11531+
]
1151811532
fkIndex = ForeignKeySpec(sk3)
11519-
{ index = fkIndex; }
11533+
{ fkIndex.setUseConstraintKeyword(useConstraintKeyword); index = fkIndex; }
1152011534
)
1152111535
|
1152211536
LOOKAHEAD(3) (
11523-
{ sk3 = null; }
11524-
[ <K_CONSTRAINT> sk3 = RelObjectName() ]
11537+
{ sk3 = null; useConstraintKeyword=false; }
11538+
[ <K_CONSTRAINT> { useConstraintKeyword=true; }
11539+
[ LOOKAHEAD({ getToken(1).kind != K_CHECK }) sk3 = RelObjectName() ]
11540+
]
1152511541
checkCs = CheckConstraintSpec(sk3)
11526-
{ index = checkCs; }
11542+
{ checkCs.setUseConstraintKeyword(useConstraintKeyword); index = checkCs; }
1152711543
)
1152811544
|
1152911545
LOOKAHEAD(2) (
@@ -11972,6 +11988,7 @@ CheckConstraint CheckConstraintSpec(String constraintName):
1197211988
ForeignKeyIndex ForeignKeySpec(String constraintName):
1197311989
{
1197411990
ForeignKeyIndex fkIndex = new ForeignKeyIndex();
11991+
String indexName = null;
1197511992
Token tk;
1197611993
Token tk2;
1197711994
List<String> refColNames = null;
@@ -11980,6 +11997,7 @@ ForeignKeyIndex ForeignKeySpec(String constraintName):
1198011997
}
1198111998
{
1198211999
tk=<K_FOREIGN> tk2=<K_KEY>
12000+
[ LOOKAHEAD(2) indexName=RelObjectName() { fkIndex.setIndexName(indexName); } ]
1198312001
colNames = ColumnNamesWithParamsList()
1198412002
{
1198512003
if (constraintName != null) { fkIndex.setName(constraintName); }

src/test/java/net/sf/jsqlparser/statement/create/CreateTableTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static org.junit.jupiter.api.Assertions.assertEquals;
1515
import static org.junit.jupiter.api.Assertions.assertFalse;
1616
import static org.junit.jupiter.api.Assertions.assertNotNull;
17+
import static org.junit.jupiter.api.Assertions.assertNull;
1718
import static org.junit.jupiter.api.Assertions.assertTrue;
1819

1920
import java.io.BufferedReader;
@@ -35,7 +36,9 @@
3536
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
3637
import net.sf.jsqlparser.statement.create.table.CreateTable;
3738
import net.sf.jsqlparser.statement.create.table.ExcludeConstraint;
39+
import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex;
3840
import net.sf.jsqlparser.statement.create.table.Index;
41+
import net.sf.jsqlparser.statement.create.table.NamedConstraint;
3942
import net.sf.jsqlparser.statement.create.table.RowMovementMode;
4043
import net.sf.jsqlparser.test.TestException;
4144
import org.assertj.core.api.Assertions;
@@ -1247,4 +1250,44 @@ void testUniqueIndexIssue1893() throws JSQLParserException {
12471250
// A plain INDEX must still parse unchanged.
12481251
assertSqlCanBeParsedAndDeparsed("CREATE TABLE t (a int, INDEX idx (a))", true);
12491252
}
1253+
1254+
@Test
1255+
void testMySqlConstraintSymbolAndIndexNameIssue1570() throws JSQLParserException {
1256+
String uniqueSql = "CREATE TABLE table1 (col1 INT, col2 INT UNIQUE, "
1257+
+ "CONSTRAINT my_constraint UNIQUE KEY index_name (col1))";
1258+
CreateTable uniqueTable = (CreateTable) assertSqlCanBeParsedAndDeparsed(uniqueSql, true);
1259+
NamedConstraint unique = (NamedConstraint) uniqueTable.getIndexes().get(0);
1260+
1261+
assertEquals("my_constraint", unique.getName());
1262+
assertEquals("index_name", unique.getIndexName());
1263+
assertEquals("UNIQUE KEY", unique.getType());
1264+
1265+
String foreignKeySql = "CREATE TABLE table2 (id INT, parent_id INT, "
1266+
+ "CONSTRAINT fk_parent FOREIGN KEY fk_parent_idx (parent_id) "
1267+
+ "REFERENCES parent (id))";
1268+
CreateTable foreignKeyTable =
1269+
(CreateTable) assertSqlCanBeParsedAndDeparsed(foreignKeySql, true);
1270+
ForeignKeyIndex foreignKey = (ForeignKeyIndex) foreignKeyTable.getIndexes().get(0);
1271+
1272+
assertEquals("fk_parent", foreignKey.getName());
1273+
assertEquals("fk_parent_idx", foreignKey.getIndexName());
1274+
}
1275+
1276+
@Test
1277+
void testMySqlUnnamedConstraintAndUniqueIndexOptionsIssues1570And538()
1278+
throws JSQLParserException {
1279+
assertSqlCanBeParsedAndDeparsed(
1280+
"CREATE TABLE table1 (col1 INT, CONSTRAINT UNIQUE KEY (col1))", true);
1281+
1282+
String sql = "CREATE TABLE g_platform_payway (id INT, platform_code VARCHAR (45), "
1283+
+ "pay_way VARCHAR (32), UNIQUE uniq_platform_payway USING BTREE "
1284+
+ "(platform_code, pay_way) COMMENT 'should be unique')";
1285+
CreateTable createTable = (CreateTable) assertSqlCanBeParsedAndDeparsed(sql, true);
1286+
NamedConstraint unique = (NamedConstraint) createTable.getIndexes().get(0);
1287+
1288+
assertNull(unique.getName());
1289+
assertEquals("uniq_platform_payway", unique.getIndexName());
1290+
assertEquals("BTREE", unique.getUsing());
1291+
assertEquals(List.of("COMMENT", "'should be unique'"), unique.getIndexSpec());
1292+
}
12501293
}

0 commit comments

Comments
 (0)