Skip to content

Commit bc9ca85

Browse files
authored
fix(parser): distinguish MySQL constraint and index names (#2534)
1 parent c919a0c commit bc9ca85

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
@@ -11528,6 +11528,9 @@ Index CreateTableConstraint():
1152811528
Token tk2 = null;
1152911529
Token tk3 = null;
1153011530
String sk3 = null;
11531+
String indexName = null;
11532+
String using = null;
11533+
boolean useConstraintKeyword = false;
1153111534
List<Index.ColumnParams> colNames = null;
1153211535
List<String> parameter = new ArrayList<String>();
1153311536
List<String> idxSpec = new ArrayList<String>();
@@ -11555,16 +11558,25 @@ Index CreateTableConstraint():
1155511558
{
1155611559
index = new NamedConstraint();
1155711560
tk2=null;
11561+
indexName=null;
11562+
using=null;
1155811563
idxSpec.clear();
1155911564
}
11560-
[ <K_CONSTRAINT> sk3=RelObjectName() {index.setName(sk3);} ]
11565+
[ <K_CONSTRAINT> { ((NamedConstraint) index).setUseConstraintKeyword(true); }
11566+
[ LOOKAHEAD({ getToken(1).kind != K_PRIMARY && getToken(1).kind != K_UNIQUE })
11567+
sk3=RelObjectName() {index.setName(sk3);} ]
11568+
]
1156111569
(
1156211570
tk=<K_PRIMARY> tk2=<K_KEY>
1156311571
|
11564-
tk=<K_UNIQUE> [ tk2=<K_KEY> ]
11572+
tk=<K_UNIQUE> [ LOOKAHEAD(2) (tk2=<K_KEY> | tk2=<K_INDEX>) ]
11573+
[ LOOKAHEAD(2, { getToken(1).kind != K_USING }) indexName=RelObjectName() ]
11574+
[ LOOKAHEAD(2) using=UsingIndexType() ]
1156511575
)
1156611576
{
1156711577
index.setType( tk.image + ( tk2!=null ? " " + tk2.image : "" ));
11578+
((NamedConstraint) index).setIndexName(indexName);
11579+
index.setUsing(using);
1156811580
tk2=null;
1156911581
}
1157011582
colNames = ColumnNamesWithParamsList()
@@ -11595,17 +11607,21 @@ Index CreateTableConstraint():
1159511607
)
1159611608
|
1159711609
LOOKAHEAD(3) (
11598-
{ sk3=null; }
11599-
[ <K_CONSTRAINT> sk3=RelObjectName() ]
11610+
{ sk3=null; useConstraintKeyword=false; }
11611+
[ <K_CONSTRAINT> { useConstraintKeyword=true; }
11612+
[ LOOKAHEAD({ getToken(1).kind != K_FOREIGN }) sk3=RelObjectName() ]
11613+
]
1160011614
fkIndex = ForeignKeySpec(sk3)
11601-
{ index = fkIndex; }
11615+
{ fkIndex.setUseConstraintKeyword(useConstraintKeyword); index = fkIndex; }
1160211616
)
1160311617
|
1160411618
LOOKAHEAD(3) (
11605-
{ sk3 = null; }
11606-
[ <K_CONSTRAINT> sk3 = RelObjectName() ]
11619+
{ sk3 = null; useConstraintKeyword=false; }
11620+
[ <K_CONSTRAINT> { useConstraintKeyword=true; }
11621+
[ LOOKAHEAD({ getToken(1).kind != K_CHECK }) sk3 = RelObjectName() ]
11622+
]
1160711623
checkCs = CheckConstraintSpec(sk3)
11608-
{ index = checkCs; }
11624+
{ checkCs.setUseConstraintKeyword(useConstraintKeyword); index = checkCs; }
1160911625
)
1161011626
|
1161111627
LOOKAHEAD(2) (
@@ -12088,6 +12104,7 @@ CheckConstraint CheckConstraintSpec(String constraintName):
1208812104
ForeignKeyIndex ForeignKeySpec(String constraintName):
1208912105
{
1209012106
ForeignKeyIndex fkIndex = new ForeignKeyIndex();
12107+
String indexName = null;
1209112108
Token tk;
1209212109
Token tk2;
1209312110
List<String> refColNames = null;
@@ -12096,6 +12113,7 @@ ForeignKeyIndex ForeignKeySpec(String constraintName):
1209612113
}
1209712114
{
1209812115
tk=<K_FOREIGN> tk2=<K_KEY>
12116+
[ LOOKAHEAD(2) indexName=RelObjectName() { fkIndex.setIndexName(indexName); } ]
1209912117
colNames = ColumnNamesWithParamsList()
1210012118
{
1210112119
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;
@@ -37,7 +38,9 @@
3738
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
3839
import net.sf.jsqlparser.statement.create.table.CreateTable;
3940
import net.sf.jsqlparser.statement.create.table.ExcludeConstraint;
41+
import net.sf.jsqlparser.statement.create.table.ForeignKeyIndex;
4042
import net.sf.jsqlparser.statement.create.table.Index;
43+
import net.sf.jsqlparser.statement.create.table.NamedConstraint;
4144
import net.sf.jsqlparser.statement.create.table.PartitionBound;
4245
import net.sf.jsqlparser.statement.create.table.RowMovementMode;
4346
import net.sf.jsqlparser.statement.create.table.TablePartitioning;
@@ -1252,6 +1255,46 @@ void testUniqueIndexIssue1893() throws JSQLParserException {
12521255
assertSqlCanBeParsedAndDeparsed("CREATE TABLE t (a int, INDEX idx (a))", true);
12531256
}
12541257

1258+
@Test
1259+
void testMySqlConstraintSymbolAndIndexNameIssue1570() throws JSQLParserException {
1260+
String uniqueSql = "CREATE TABLE table1 (col1 INT, col2 INT UNIQUE, "
1261+
+ "CONSTRAINT my_constraint UNIQUE KEY index_name (col1))";
1262+
CreateTable uniqueTable = (CreateTable) assertSqlCanBeParsedAndDeparsed(uniqueSql, true);
1263+
NamedConstraint unique = (NamedConstraint) uniqueTable.getIndexes().get(0);
1264+
1265+
assertEquals("my_constraint", unique.getName());
1266+
assertEquals("index_name", unique.getIndexName());
1267+
assertEquals("UNIQUE KEY", unique.getType());
1268+
1269+
String foreignKeySql = "CREATE TABLE table2 (id INT, parent_id INT, "
1270+
+ "CONSTRAINT fk_parent FOREIGN KEY fk_parent_idx (parent_id) "
1271+
+ "REFERENCES parent (id))";
1272+
CreateTable foreignKeyTable =
1273+
(CreateTable) assertSqlCanBeParsedAndDeparsed(foreignKeySql, true);
1274+
ForeignKeyIndex foreignKey = (ForeignKeyIndex) foreignKeyTable.getIndexes().get(0);
1275+
1276+
assertEquals("fk_parent", foreignKey.getName());
1277+
assertEquals("fk_parent_idx", foreignKey.getIndexName());
1278+
}
1279+
1280+
@Test
1281+
void testMySqlUnnamedConstraintAndUniqueIndexOptionsIssues1570And538()
1282+
throws JSQLParserException {
1283+
assertSqlCanBeParsedAndDeparsed(
1284+
"CREATE TABLE table1 (col1 INT, CONSTRAINT UNIQUE KEY (col1))", true);
1285+
1286+
String sql = "CREATE TABLE g_platform_payway (id INT, platform_code VARCHAR (45), "
1287+
+ "pay_way VARCHAR (32), UNIQUE uniq_platform_payway USING BTREE "
1288+
+ "(platform_code, pay_way) COMMENT 'should be unique')";
1289+
CreateTable createTable = (CreateTable) assertSqlCanBeParsedAndDeparsed(sql, true);
1290+
NamedConstraint unique = (NamedConstraint) createTable.getIndexes().get(0);
1291+
1292+
assertNull(unique.getName());
1293+
assertEquals("uniq_platform_payway", unique.getIndexName());
1294+
assertEquals("BTREE", unique.getUsing());
1295+
assertEquals(List.of("COMMENT", "'should be unique'"), unique.getIndexSpec());
1296+
}
1297+
12551298
@Test
12561299
void testPostgreSqlDeclarativePartitionParentsIssue2522() throws JSQLParserException {
12571300
String sql = "CREATE TABLE pg_range_parent (tenant_id int, "

0 commit comments

Comments
 (0)