From a0657affdb5ee896f29779cc8bf9bad1b0a77494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BB=98=E5=85=B8?= Date: Fri, 7 Aug 2026 12:24:07 +0800 Subject: [PATCH] fix: support nested parametric data types as CAST target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClickHouse allows parametric (constructor-style) data types as a CAST target, including nested ones such as Nullable(Decimal(10, 2)). Parsing failed because the argument production of ColDataType only accepted atomic tokens (numbers, string literals, bare identifiers/keywords), so the inner opening bracket of a nested parametric type was unexpected. Allow an argument that is itself a parametric data type (a DATA_TYPE or identifier immediately followed by an argument list) to be parsed as a nested ColDataType, recursing through the existing production. The nested type is rendered back via its toString() so the statement round-trips. Fixes #2441 Signed-off-by: 付典 --- .../jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt | 12 +++++++++++- .../jsqlparser/statement/select/ClickHouseTest.java | 12 ++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 2a28d21ba..a291445f6 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -10707,6 +10707,7 @@ ColDataType ColDataType(): List array = new ArrayList(); List name; ColDataType arrayType; + ColDataType nestedType = null; int precision = -1; int scale = -1; @@ -10755,6 +10756,9 @@ ColDataType ColDataType(): [ LOOKAHEAD(2) "(" {tk2 =null;} ( + ( + LOOKAHEAD(( | ) "(") nestedType=ColDataType() + | ( ( ( tk= | tk= ) [ LOOKAHEAD(2) (tk2= | tk2=) ] @@ -10765,9 +10769,15 @@ ColDataType ColDataType(): tk= | tk= + ) ) { - argumentsStringList.add(tk.image + (tk2!=null?" " + tk2.image:"")); + if (nestedType != null) { + argumentsStringList.add(nestedType.toString()); + nestedType = null; + } else { + argumentsStringList.add(tk.image + (tk2!=null?" " + tk2.image:"")); + } } [ "," ] diff --git a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java index a7046ada0..6d5dae30a 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/ClickHouseTest.java @@ -140,4 +140,16 @@ public void testMultipleSettingsClauseIssue2362() throws JSQLParserException { Assertions.assertNotNull(select.getSettings()); Assertions.assertEquals(2, select.getSettings().size()); } + + @Test + public void testCastToNestedParametricTypeIssue2441() throws JSQLParserException { + // ClickHouse allows parametric (constructor-style) data types as a CAST target, + // including nested ones such as Nullable(Decimal(p, s)). + String sql = "SELECT CAST(x AS Nullable(Decimal(10, 2))) FROM cast_demo"; + assertSqlCanBeParsedAndDeparsed(sql, true); + + // The inner parametric type may itself be wrapped by another parametric type. + sql = "SELECT CAST(x AS LowCardinality(Decimal(10, 2))) FROM cast_demo"; + assertSqlCanBeParsedAndDeparsed(sql, true); + } }