From 430dbe6573b7db78db487e92df4138e919fd454e Mon Sep 17 00:00:00 2001 From: dengliming Date: Sat, 22 Aug 2026 22:39:44 +0800 Subject: [PATCH] fix(parser): support CHARACTER SET in CAST CHAR types --- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 14 ++++++++------ .../jsqlparser/expression/CastExpressionTest.java | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index f56523b4f..7db26305c 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -11410,12 +11410,14 @@ ColDataType DataType(): ( tk= | tk= | tk = | tk = | tk = | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= ) { type = tk.image; } - [ - // MySQL seems to allow: INT UNSIGNED - LOOKAHEAD(2) ( LOOKAHEAD(2) ( tk = | tk = | tk = - | tk= | tk= | tk= | tk= | tk= - | tk= | tk= | tk= ) { type += " " + tk.image; } )+ - ] + ( + // MySQL seems to allow: INT UNSIGNED. Do not consume CHARACTER when it starts + // the trailing CHARACTER SET clause of a character type. + LOOKAHEAD(2, { getToken(1).kind != K_CHARACTER || getToken(2).kind != K_SET }) + ( tk = | tk = | tk = + | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= ) { type += " " + tk.image; } + )* [ LOOKAHEAD(2) "(" ( tk= { precision = Integer.valueOf(tk.image); } | tk= { precision = Integer.MAX_VALUE; } ) [ | ] diff --git a/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java b/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java index 81c2ae58a..d0365054e 100644 --- a/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java @@ -68,6 +68,21 @@ void testImplicitCastDoublePrecisionIssue1344() throws JSQLParserException { Assertions.assertTrue(select.getSelectItem(0).getExpression() instanceof CastExpression); } + @Test + void testCastCharWithCharacterSet() throws JSQLParserException { + String sqlStr = "SELECT CAST('abc' AS CHAR CHARACTER SET utf8mb4)"; + PlainSelect select = (PlainSelect) assertSqlCanBeParsedAndDeparsed(sqlStr, true); + CastExpression castExpression = Assertions.assertInstanceOf(CastExpression.class, + select.getSelectItem(0).getExpression()); + + Assertions.assertEquals("CHAR", castExpression.getColDataType().getDataType()); + Assertions.assertEquals("utf8mb4", castExpression.getColDataType().getCharacterSet()); + + assertSqlCanBeParsedAndDeparsed( + "SELECT CAST('abc' AS CHAR(10) CHARACTER SET utf8mb4)", true); + assertSqlCanBeParsedAndDeparsed("SELECT CAST('abc' AS CHARACTER VARYING)", true); + } + @Test public void testCastToSigned() throws JSQLParserException {