Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,21 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
return Dialect.MYSQL.name().equals(dialect) || Dialect.MARIADB.name().equals(dialect);
}

/**
* Whether ALTER TABLE DROP names a MySQL FOREIGN KEY or CHECK constraint. The default
* configuration accepts both like MySQL; MariaDB has no DROP CHECK. A parenthesized FOREIGN KEY
* column list keeps the legacy form, and other dialects use DROP CONSTRAINT.
*/
private boolean isNamedConstraintDropAhead() {
String dialect = getAsString(Feature.dialect);
boolean namedForeignKey = getToken(1).kind == K_FOREIGN && getToken(2).kind == K_KEY
&& getToken(3).kind != OPENING_BRACKET;
if (dialect == null || Dialect.MYSQL.name().equals(dialect)) {
return namedForeignKey || getToken(1).kind == K_CHECK;
}
return Dialect.MARIADB.name().equals(dialect) && namedForeignKey;
}

private boolean isBeginTransactionAhead() {
String dialect = getAsString(Feature.dialect);
if (getToken(1).kind != K_BEGIN || !(isMySqlDialect()
Expand Down Expand Up @@ -18154,10 +18169,7 @@ AlterExpression AlterExpressionDrop():
{
<K_DROP> { alterExp.setOperation(AlterOperation.DROP); }
(
LOOKAHEAD({ Dialect.MYSQL.name().equals(getAsString(Feature.dialect))
&& (getToken(1).kind == K_CHECK
|| getToken(1).kind == K_FOREIGN && getToken(2).kind == K_KEY
&& getToken(3).kind != OPENING_BRACKET) })
LOOKAHEAD({ isNamedConstraintDropAhead() })
MySqlDropNamedConstraint(alterExp)
|
(
Expand Down
7 changes: 5 additions & 2 deletions src/site/sphinx/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,11 @@ MySQL ALGORITHM/LOCK tokens retain their order and optional equals signs.
MySQL named constraint drops
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

With ``Dialect.MYSQL``, ``DROP FOREIGN KEY name`` and ``DROP CHECK name``
expose the target through ``AlterExpression.getConstraintName()``. Inspect
Without a dialect and with ``Dialect.MYSQL``, ``DROP FOREIGN KEY name`` and
``DROP CHECK name`` expose the target through ``AlterExpression.getConstraintName()``.
``Dialect.MARIADB`` accepts the named FOREIGN KEY form; MariaDB has no ``DROP CHECK``.
Other dialects drop constraints with ``DROP CONSTRAINT``. A parenthesized
``DROP FOREIGN KEY (...)`` keeps the legacy column-list form. Inspect
``getOperation()`` for ``DROP_FOREIGN_KEY`` or ``DROP_CHECK``. Identifier
quotes are preserved, and changing the name changes both SQL renderers.
These named targets do not populate the legacy ``getPkColumns()`` list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,53 @@ void preservesDefaultDropForms(String action) throws JSQLParserException {
assertEquals(sql, alter.toString().trim());
}

@ParameterizedTest
@ValueSource(strings = {"DROP FOREIGN KEY fk1", "DROP FOREIGN KEY `fk name`",
"DROP CHECK c1", "DROP FOREIGN KEY fk1, DROP CHECK c1"})
void acceptsNamedDropsWithoutDialect(String actions) throws JSQLParserException {
String sql = "ALTER TABLE t " + actions;
Alter alter = (Alter) CCJSqlParserUtil.parse(sql);

assertEquals(sql, alter.toString());
for (AlterExpression drop : alter.getAlterExpressions()) {
assertNotNull(drop.getConstraintName());
assertNull(drop.getDropColumns());
}
assertEquals(sql, CCJSqlParserUtil.parse(alter.toString()).toString());
}

@Test
void mariaDbAcceptsNamedForeignKeyButNotCheckDrops() throws JSQLParserException {
Alter alter = (Alter) CCJSqlParserUtil.parse("ALTER TABLE t DROP FOREIGN KEY fk1",
p -> p.withDialect(Dialect.MARIADB));
assertEquals(AlterOperation.DROP_FOREIGN_KEY,
alter.getAlterExpressions().get(0).getOperation());
assertEquals("fk1", alter.getAlterExpressions().get(0).getConstraintName());

assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil
.parse("ALTER TABLE t DROP CHECK c1", p -> p.withDialect(Dialect.MARIADB)));
}

@ParameterizedTest
@ValueSource(strings = {"DROP FOREIGN KEY fk1", "DROP CHECK c1"})
void otherDialectsKeepRejectingMySqlNamedDrops(String action) {
for (Dialect dialect : List.of(Dialect.POSTGRESQL, Dialect.ORACLE, Dialect.SQLSERVER)) {
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil
.parse("ALTER TABLE t " + action, p -> p.withDialect(dialect)),
dialect.name());
}
}

@Test
void parenthesizedForeignKeyDropKeepsColumnsWithoutDialect() throws JSQLParserException {
Alter alter = (Alter) CCJSqlParserUtil.parse("ALTER TABLE t DROP FOREIGN KEY (a, b)");
AlterExpression drop = alter.getAlterExpressions().get(0);

assertEquals(AlterOperation.DROP_FOREIGN_KEY, drop.getOperation());
assertEquals(List.of("a", "b"), drop.getDropColumns());
assertNull(drop.getConstraintName());
}

private static Alter parse(String sql) throws JSQLParserException {
return (Alter) CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.MYSQL));
}
Expand Down
Loading