From a0556bdc13538afde14d1a5a5295e3bbfee2aba0 Mon Sep 17 00:00:00 2001 From: Minjae Lee Date: Thu, 3 Sep 2026 15:26:46 +0900 Subject: [PATCH] feat(parser): add column data type fragment parser Expose strict CCJSqlParserUtil helpers for ColDataType fragments, including parser configuration and full-input validation. --- .../jsqlparser/parser/CCJSqlParserUtil.java | 56 +++++++++++++++++++ .../parser/CCJSqlParserUtilTest.java | 30 ++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java index 31e070975..eea0c7c63 100644 --- a/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java +++ b/src/main/java/net/sf/jsqlparser/parser/CCJSqlParserUtil.java @@ -30,6 +30,7 @@ import net.sf.jsqlparser.parser.feature.Feature; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.Statements; +import net.sf.jsqlparser.statement.create.table.ColDataType; /** * Toolfunctions to start and use JSqlParser. @@ -199,6 +200,61 @@ public static Expression parseExpression(String expression, boolean allowPartial }); } + /** + * Parses a column data type fragment. The complete input must represent the data type; trailing + * tokens are rejected. + * + * @param columnDataType the column data type fragment to parse + * @return the parsed column data type, or {@code null} for a null or empty input + * @throws JSQLParserException when the input cannot be parsed completely + * @see #parseColDataType(String, Consumer) + */ + public static ColDataType parseColDataType(String columnDataType) throws JSQLParserException { + return parseColDataType(columnDataType, null); + } + + /** + * Parses a column data type fragment while allowing the parser to be configured. The complete + * input must represent the data type; trailing tokens are rejected. + * + * @param columnDataType the column data type fragment to parse + * @param consumer parser configuration callback, or {@code null} + * @return the parsed column data type, or {@code null} for a null or empty input + * @throws JSQLParserException when the input cannot be parsed completely + */ + public static ColDataType parseColDataType(String columnDataType, + Consumer consumer) throws JSQLParserException { + if (columnDataType == null || columnDataType.isEmpty()) { + return null; + } + + try { + return parseColDataType(columnDataType, false, consumer); + } catch (JSQLParserException ex) { + return parseColDataType(columnDataType, true, consumer); + } + } + + private static ColDataType parseColDataType(String columnDataType, boolean allowComplexParsing, + Consumer consumer) throws JSQLParserException { + CCJSqlParser parser = newParser(columnDataType) + .withAllowComplexParsing(allowComplexParsing); + if (consumer != null) { + consumer.accept(parser); + } + + try { + ColDataType result = parser.ColDataType(); + if (parser.getNextToken().kind != CCJSqlParserTokenManager.EOF) { + throw new JSQLParserException( + "could only parse partial column data type " + result); + } + return result; + } catch (ParseException ex) { + throw new JSQLParserException(columnDataType, ex); + } + } + @SuppressWarnings("PMD.CyclomaticComplexity") public static Expression parseExpression(String expressionStr, boolean allowPartialParse, Consumer consumer) throws JSQLParserException { diff --git a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java index 40e61e708..5aa92e1c7 100644 --- a/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java +++ b/src/test/java/net/sf/jsqlparser/parser/CCJSqlParserUtilTest.java @@ -20,6 +20,7 @@ import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.Statements; import net.sf.jsqlparser.statement.UnsupportedStatement; +import net.sf.jsqlparser.statement.create.table.ColDataType; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.TableStatement; @@ -39,6 +40,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -107,6 +109,34 @@ public void testParseExpression2() throws Exception { assertInstanceOf(ParenthesedExpressionList.class, mult.getRightExpression()); } + @Test + public void testParseColDataType() throws Exception { + assertNull(CCJSqlParserUtil.parseColDataType(null)); + assertNull(CCJSqlParserUtil.parseColDataType("")); + + ColDataType arrayType = CCJSqlParserUtil.parseColDataType("integer[][]"); + assertEquals("integer", arrayType.getDataType()); + assertEquals(2, arrayType.getArrayData().size()); + assertNull(arrayType.getArrayData().get(0)); + assertNull(arrayType.getArrayData().get(1)); + + AtomicBoolean configured = new AtomicBoolean(); + ColDataType enumType = + CCJSqlParserUtil.parseColDataType("enum('small','medium')", parser -> { + configured.set(true); + parser.withDialect(AbstractJSqlParser.Dialect.MYSQL); + }); + assertTrue(configured.get()); + assertEquals("enum", enumType.getDataType()); + assertEquals(List.of("'small'", "'medium'"), enumType.getArgumentsStringList()); + } + + @Test + public void testParseColDataTypeRejectsTrailingTokens() { + assertThrows(JSQLParserException.class, + () -> CCJSqlParserUtil.parseColDataType("varchar(255) NOT NULL")); + } + @Test public void testParseExpressionNonPartial() throws Exception { assertThrows(JSQLParserException.class,