diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java index 1f26a70c7..4fd1f4209 100644 --- a/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterExpression.java @@ -53,6 +53,11 @@ public class AlterExpression implements Serializable { private List partitionDefinitions; private List constraints; private List parameters; + + private ConvertType convertType; + private boolean hasEqualForCharacterSet; + private boolean hasEqualForCollate; + private String characterSet; private String collation; private String lockOption; @@ -401,6 +406,14 @@ public List getParameters() { return parameters; } + public ConvertType getConvertType() { + return convertType; + } + + public void setConvertType(ConvertType convertType) { + this.convertType = convertType; + } + public String getCharacterSet() { return characterSet; } @@ -485,6 +498,32 @@ public String toString() { } else if (operation == AlterOperation.DROP_PRIMARY_KEY) { b.append("DROP PRIMARY KEY "); + } else if (operation == AlterOperation.CONVERT) { + if (convertType == ConvertType.CONVERT_TO) { + b.append("CONVERT TO CHARACTER SET "); + } else if (convertType == ConvertType.DEFAULT_CHARACTER_SET) { + b.append("DEFAULT CHARACTER SET "); + if (hasEqualForCharacterSet) { + b.append("= "); + } + } else if (convertType == ConvertType.CHARACTER_SET) { + b.append("CHARACTER SET "); + if (hasEqualForCharacterSet) { + b.append("= "); + } + } + + if (getCharacterSet() != null) { + b.append(getCharacterSet()); + } + + if (getCollation() != null) { + b.append(" COLLATE "); + if (hasEqualForCollate) { + b.append("= "); + } + b.append(getCollation()); + } } else if (operation == AlterOperation.DROP_UNIQUE) { b.append("DROP UNIQUE (").append(PlainSelect.getStringList(pkColumns)).append(')'); @@ -797,6 +836,14 @@ public void setPartitionDefinitions(List partitionDefinitio this.partitionDefinitions = partitionDefinition; } + public void setHasEqualForCharacterSet(boolean hasEqualForCharacterSet) { + this.hasEqualForCharacterSet = hasEqualForCharacterSet; + } + + public void setHasEqualForCollate(boolean hasEqualForCollate) { + this.hasEqualForCollate = hasEqualForCollate; + } + public static final class ColumnDataType extends ColumnDefinition { private final boolean withType; @@ -890,4 +937,8 @@ public String toString() { return columnName + " DROP DEFAULT"; } } + + public enum ConvertType { + CONVERT_TO, DEFAULT_CHARACTER_SET, CHARACTER_SET + } } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index f124a2ea9..4d26413b5 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -7430,11 +7430,31 @@ AlterExpression AlterExpression(): {alterExp.setOperation(AlterOperation.RENAME_TABLE);} (tk2= | tk2=) { alterExp.setNewTableName(tk2.image);} ) - | - ( { alterExp.setOperation(AlterOperation.CONVERT); } + | ( { + alterExp.setOperation(AlterOperation.CONVERT); + alterExp.setConvertType(AlterExpression.ConvertType.CONVERT_TO); + } tk= { alterExp.setCharacterSet(tk.image); } [ tk2= { alterExp.setCollation(tk2.image); }] - ) + ) + | ( { + alterExp.setOperation(AlterOperation.CONVERT); + alterExp.setConvertType(AlterExpression.ConvertType.DEFAULT_CHARACTER_SET); + } + [ "=" { alterExp.setHasEqualForCharacterSet(true); } ] + tk= { alterExp.setCharacterSet(tk.image); } + [ [ "=" { alterExp.setHasEqualForCollate(true); } ] + tk2= { alterExp.setCollation(tk2.image); }] + ) + | ( [ "=" { alterExp.setHasEqualForCharacterSet(true); } ] + tk= { + alterExp.setOperation(AlterOperation.CONVERT); + alterExp.setConvertType(AlterExpression.ConvertType.CHARACTER_SET); + alterExp.setCharacterSet(tk.image); + } + [ [ "=" { alterExp.setHasEqualForCollate(true); } ] + tk2= { alterExp.setCollation(tk2.image); }] + ) | ( {alterExp.setOperation(AlterOperation.COMMENT);} ["=" {alterExp.setOperation(AlterOperation.COMMENT_WITH_EQUAL_SIGN);} ] diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index 005961a3d..03ec3e85e 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -24,13 +24,16 @@ import net.sf.jsqlparser.statement.create.table.*; import net.sf.jsqlparser.statement.create.table.Index.ColumnParams; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Stream; import static net.sf.jsqlparser.test.TestUtils.*; -import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; import static org.junit.jupiter.api.Assertions.*; public class AlterTest { @@ -1093,41 +1096,54 @@ public void testIssue2090LockExclusive() throws JSQLParserException { assertEquals("EXCLUSIVE", lockExp.getLockOption()); } - @Test - public void testIssue2089() throws JSQLParserException { - String sql = "ALTER TABLE test_table CONVERT TO CHARACTER SET utf8mb4"; + @ParameterizedTest + @MethodSource("provideMySQLConvertTestCases") + public void testIssue2089(String sql, String expectedCharacterSet, String expectedCollation) + throws JSQLParserException { Statement stmt = CCJSqlParserUtil.parse(sql); - assertTrue(stmt instanceof Alter); + assertTrue(stmt instanceof Alter, + "Expected instance of Alter but got: " + stmt.getClass().getSimpleName()); + Alter alter = (Alter) stmt; assertEquals("test_table", alter.getTable().getFullyQualifiedName()); List alterExpressions = alter.getAlterExpressions(); - assertNotNull(alterExpressions); - assertEquals(1, alterExpressions.size()); + assertNotNull(alterExpressions, "Alter expressions should not be null for SQL: " + sql); + assertEquals(1, alterExpressions.size(), "Expected 1 alter expression for SQL: " + sql); AlterExpression convertExp = alterExpressions.get(0); assertEquals(AlterOperation.CONVERT, convertExp.getOperation()); - assertEquals("utf8mb4", convertExp.getCharacterSet()); - assertNull(convertExp.getCollation()); - } - - @Test - public void testIssue2089WithCollation() throws JSQLParserException { - String sql = - "ALTER TABLE test_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci"; - Statement stmt = CCJSqlParserUtil.parse(sql); - assertTrue(stmt instanceof Alter); - Alter alter = (Alter) stmt; - assertEquals("test_table", alter.getTable().getFullyQualifiedName()); - List alterExpressions = alter.getAlterExpressions(); - assertNotNull(alterExpressions); - assertEquals(1, alterExpressions.size()); + assertEquals(expectedCharacterSet, convertExp.getCharacterSet(), + "CHARACTER SET mismatch for SQL: " + sql); + assertEquals(expectedCollation, convertExp.getCollation(), + "COLLATE mismatch for SQL: " + sql); + assertSqlCanBeParsedAndDeparsed(sql); + } - AlterExpression convertExp = alterExpressions.get(0); - assertEquals(AlterOperation.CONVERT, convertExp.getOperation()); - assertEquals("utf8mb4", convertExp.getCharacterSet()); - assertEquals("utf8mb4_general_ci", convertExp.getCollation()); + private static Stream provideMySQLConvertTestCases() { + return Stream.of( + Arguments.of("ALTER TABLE test_table CONVERT TO CHARACTER SET utf8mb4", "utf8mb4", + null), + Arguments.of( + "ALTER TABLE test_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci", + "utf8mb4", "utf8mb4_general_ci"), + Arguments.of( + "ALTER TABLE test_table DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci", + "utf8mb4", "utf8mb4_general_ci"), + Arguments.of( + "ALTER TABLE test_table DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci", + "utf8mb4", "utf8mb4_general_ci"), + Arguments.of( + "ALTER TABLE test_table CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci", + "utf8mb4", "utf8mb4_general_ci"), + Arguments.of( + "ALTER TABLE test_table CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci", + "utf8mb4", "utf8mb4_general_ci"), + Arguments.of("ALTER TABLE test_table DEFAULT CHARACTER SET utf8mb4", "utf8mb4", + null), + Arguments.of("ALTER TABLE test_table DEFAULT CHARACTER SET = utf8mb4", "utf8mb4", + null)); } @Test