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
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public enum TableRenameKeyword {
private List<String> parameters;

private ConvertType convertType;
private boolean useCharsetKeyword;
private boolean hasEqualForCharacterSet;
private boolean hasEqualForCollate;

Expand Down Expand Up @@ -956,6 +957,15 @@ public void setConvertType(ConvertType convertType) {
this.convertType = convertType;
}

/** Whether the character set clause uses MySQL's CHARSET abbreviation. */
public boolean isUseCharsetKeyword() {
return useCharsetKeyword;
}

public void setUseCharsetKeyword(boolean useCharsetKeyword) {
this.useCharsetKeyword = useCharsetKeyword;
}

public String getCharacterSet() {
return characterSet;
}
Expand Down Expand Up @@ -1485,15 +1495,16 @@ protected void toStringDropSpecial(StringBuilder b) {

protected void toStringConvert(StringBuilder b) {
if (operation == AlterOperation.CONVERT) {
String keyword = useCharsetKeyword ? "CHARSET " : "CHARACTER SET ";
if (convertType == ConvertType.CONVERT_TO) {
b.append("CONVERT TO CHARACTER SET ");
b.append("CONVERT TO ").append(keyword);
} else if (convertType == ConvertType.DEFAULT_CHARACTER_SET) {
b.append("DEFAULT CHARACTER SET ");
b.append("DEFAULT ").append(keyword);
if (hasEqualForCharacterSet) {
b.append("= ");
}
} else if (convertType == ConvertType.CHARACTER_SET) {
b.append("CHARACTER SET ");
b.append(keyword);
if (hasEqualForCharacterSet) {
b.append("= ");
}
Expand Down
18 changes: 15 additions & 3 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -19131,6 +19131,7 @@ AlterExpression AlterSharedTableOption():
charset.setConvertType(useDefault ? AlterExpression.ConvertType.DEFAULT_CHARACTER_SET
: AlterExpression.ConvertType.CHARACTER_SET);
charset.setCharacterSet(option.getValue());
charset.setUseCharsetKeyword(option.getName().toUpperCase(Locale.ROOT).endsWith("CHARSET"));
charset.setHasEqualForCharacterSet(option.isUseEquals());
if (collation != null) {
charset.setCollation(collation.getValue());
Expand Down Expand Up @@ -19519,10 +19520,18 @@ void TypeCharacterSet(ColDataType type):
{
( <K_CHARACTER> <K_SET>
| LOOKAHEAD({ isKeywordAhead("CHARSET") }) <S_IDENTIFIER> { abbreviated = true; } )
( name=<S_IDENTIFIER> | name=<K_BINARY> | name=<S_CHAR_LITERAL> | name=<S_QUOTED_IDENTIFIER> )
name=CharacterSetName()
{ type.setCharacterSet(name.image); type.setUseCharsetKeyword(abbreviated); }
}

/** A MySQL character set or collation name: an identifier, BINARY or a quoted name. */
Token CharacterSetName():
{ Token name; }
{
( name=<S_IDENTIFIER> | name=<K_BINARY> | name=<S_CHAR_LITERAL> | name=<S_QUOTED_IDENTIFIER> )
{ return name; }
}

XmlTypeModifier XmlTypeModifier():
{
XmlTypeModifier.Kind kind = null;
Expand Down Expand Up @@ -22081,8 +22090,11 @@ AlterExpression AlterExpression():
alterExp.setOperation(AlterOperation.CONVERT);
alterExp.setConvertType(AlterExpression.ConvertType.CONVERT_TO);
}
<K_TO> <K_CHARACTER> <K_SET> tk=<S_IDENTIFIER> { alterExp.setCharacterSet(tk.image); }
[ LOOKAHEAD(2) <K_COLLATE> tk2=<S_IDENTIFIER> { alterExp.setCollation(tk2.image); }]
<K_TO>
( <K_CHARACTER> <K_SET>
| LOOKAHEAD({ isKeywordAhead("CHARSET") }) <S_IDENTIFIER> { alterExp.setUseCharsetKeyword(true); } )
( tk=<K_DEFAULT> | tk=CharacterSetName() ) { alterExp.setCharacterSet(tk.image); }
[ LOOKAHEAD(2) <K_COLLATE> tk2=CharacterSetName() { alterExp.setCollation(tk2.image); }]
)
|
alterExp = AlterExpressionDiscardOrImport()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.alter;

import static org.junit.jupiter.api.Assertions.*;

import java.util.function.Consumer;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParser;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class MySqlConvertToCharacterSetTest {
private static final Consumer<CCJSqlParser> MYSQL = p -> p.withDialect(Dialect.MYSQL);

@ParameterizedTest
@ValueSource(strings = {"ALTER TABLE t CONVERT TO CHARSET utf8mb4",
"ALTER TABLE t CONVERT TO CHARSET utf8mb4 COLLATE utf8mb4_bin",
"ALTER TABLE t CONVERT TO CHARACTER SET DEFAULT",
"ALTER TABLE t CONVERT TO CHARACTER SET DEFAULT COLLATE utf8mb4_bin",
"ALTER TABLE t CONVERT TO CHARACTER SET binary",
"ALTER TABLE t CONVERT TO CHARACTER SET binary COLLATE binary",
"ALTER TABLE t CONVERT TO CHARACTER SET 'utf8mb4'",
"ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE 'utf8mb4_bin'",
"ALTER TABLE t CONVERT TO CHARACTER SET `utf8mb4` COLLATE `utf8mb4_bin`",
"ALTER TABLE t CONVERT TO CHARSET utf8mb4, ADD COLUMN c INT",
"ALTER TABLE t DEFAULT CHARSET utf8mb4",
"ALTER TABLE t CHARSET = utf8mb4 COLLATE = utf8mb4_bin"})
void keepsEveryCharacterSetForm(String sql) throws JSQLParserException {
for (Statement statement : new Statement[] {CCJSqlParserUtil.parse(sql),
CCJSqlParserUtil.parse(sql, MYSQL)}) {
assertEquals(sql, statement.toString());
StringBuilder deparsed = new StringBuilder();
statement.accept(new StatementDeParser(deparsed));
assertEquals(sql, deparsed.toString());
}
assertEquals(2, CCJSqlParserUtil.parseStatements(sql + "; SELECT 1", MYSQL).size());
}

@Test
void exposesCharsetSpellingNameAndCollation() throws JSQLParserException {
AlterExpression action =
parseAction("ALTER TABLE t CONVERT TO CHARSET utf8mb4 COLLATE 'utf8mb4_bin'");
assertEquals(AlterOperation.CONVERT, action.getOperation());
assertEquals(AlterExpression.ConvertType.CONVERT_TO, action.getConvertType());
assertTrue(action.isUseCharsetKeyword());
assertEquals("utf8mb4", action.getCharacterSet());
assertEquals("'utf8mb4_bin'", action.getCollation());

action.setUseCharsetKeyword(false);
action.setCollation("utf8mb4_0900_ai_ci");
assertEquals("CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci",
action.toString());

AlterExpression standard = parseAction("ALTER TABLE t CONVERT TO CHARACTER SET DEFAULT");
assertFalse(standard.isUseCharsetKeyword());
assertEquals("DEFAULT", standard.getCharacterSet());
assertNull(standard.getCollation());
}

@Test
void keepsCharsetSpellingOfTableOptions() throws JSQLParserException {
AlterExpression action = parseAction("ALTER TABLE t DEFAULT CHARSET = utf8mb4");
assertEquals(AlterExpression.ConvertType.DEFAULT_CHARACTER_SET, action.getConvertType());
assertTrue(action.isUseCharsetKeyword());
assertFalse(
parseAction("ALTER TABLE t DEFAULT CHARACTER SET = utf8mb4").isUseCharsetKeyword());
}

@ParameterizedTest
@ValueSource(strings = {"ALTER TABLE t CONVERT TO CHARSET",
"ALTER TABLE t CONVERT TO utf8mb4",
"ALTER TABLE t CONVERT TO CHARSET = utf8mb4",
"ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE"})
void rejectsIncompleteForms(String sql) {
assertThrows(JSQLParserException.class, () -> CCJSqlParserUtil.parse(sql, MYSQL));
}

private static AlterExpression parseAction(String sql) throws JSQLParserException {
Alter alter = (Alter) CCJSqlParserUtil.parse(sql, MYSQL);
assertEquals(1, alter.getAlterExpressions().size());
return alter.getAlterExpressions().get(0);
}
}
Loading