From 0ce3837ab8552d33fbb4edcd84a1b4d45f1de84b Mon Sep 17 00:00:00 2001 From: PHJ2000 <105151063+PHJ2000@users.noreply.github.com> Date: Mon, 5 Oct 2026 02:04:52 +0900 Subject: [PATCH] fix: deparse casts to single-field row types --- .../jsqlparser/expression/CastExpression.java | 13 +++++------ .../util/deparser/ExpressionDeParser.java | 11 +++++----- .../expression/CastExpressionTest.java | 22 +++++++++++++++++++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/expression/CastExpression.java b/src/main/java/net/sf/jsqlparser/expression/CastExpression.java index 66af39ed50..9c77ef825f 100644 --- a/src/main/java/net/sf/jsqlparser/expression/CastExpression.java +++ b/src/main/java/net/sf/jsqlparser/expression/CastExpression.java @@ -9,18 +9,17 @@ */ package net.sf.jsqlparser.expression; -import java.util.Locale; -import net.sf.jsqlparser.parser.ASTNodeAccessImpl; -import net.sf.jsqlparser.statement.create.table.ColDataType; -import net.sf.jsqlparser.statement.create.table.ColumnDefinition; -import net.sf.jsqlparser.statement.select.Select; - import java.util.ArrayList; +import java.util.Locale; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import net.sf.jsqlparser.parser.ASTNodeAccessImpl; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; +import net.sf.jsqlparser.statement.select.Select; public class CastExpression extends ASTNodeAccessImpl implements Expression { private final static Pattern PATTERN = @@ -204,7 +203,7 @@ public String toString() { if (isImplicitCast) { return colDataType + " " + leftExpression; } else if (keyword != null && !keyword.isEmpty()) { - return columnDefinitions.size() > 1 + return !columnDefinitions.isEmpty() ? keyword + "(" + leftExpression + " AS ROW(" + Select.getStringList(columnDefinitions) + ")" + formatStr + ")" : keyword + "(" + leftExpression + " AS " + colDataType.toString() + formatStr diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index 3ef5620c5d..336e80e5d6 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -9,13 +9,12 @@ */ package net.sf.jsqlparser.util.deparser; -import net.sf.jsqlparser.expression.AliasedExpression; - import static java.util.stream.Collectors.joining; import java.util.Iterator; import java.util.List; import java.util.Map; +import net.sf.jsqlparser.expression.AliasedExpression; import net.sf.jsqlparser.expression.AllValue; import net.sf.jsqlparser.expression.AnalyticExpression; import net.sf.jsqlparser.expression.AnalyticType; @@ -42,7 +41,6 @@ import net.sf.jsqlparser.expression.HexValue; import net.sf.jsqlparser.expression.HighExpression; import net.sf.jsqlparser.expression.IntervalExpression; -import net.sf.jsqlparser.expression.RowPatternFunction; import net.sf.jsqlparser.expression.Inverse; import net.sf.jsqlparser.expression.JdbcNamedParameter; import net.sf.jsqlparser.expression.JdbcParameter; @@ -56,6 +54,7 @@ import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.LowExpression; import net.sf.jsqlparser.expression.MapExpression; +import net.sf.jsqlparser.expression.MethodCallExpression; import net.sf.jsqlparser.expression.MySQLGroupConcat; import net.sf.jsqlparser.expression.NextValExpression; import net.sf.jsqlparser.expression.NotExpression; @@ -69,7 +68,7 @@ import net.sf.jsqlparser.expression.RangeExpression; import net.sf.jsqlparser.expression.RowConstructor; import net.sf.jsqlparser.expression.RowGetExpression; -import net.sf.jsqlparser.expression.MethodCallExpression; +import net.sf.jsqlparser.expression.RowPatternFunction; import net.sf.jsqlparser.expression.SignedExpression; import net.sf.jsqlparser.expression.StringValue; import net.sf.jsqlparser.expression.StructType; @@ -119,9 +118,9 @@ import net.sf.jsqlparser.expression.operators.relational.Intersects; import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; +import net.sf.jsqlparser.expression.operators.relational.IsJsonExpression; import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; import net.sf.jsqlparser.expression.operators.relational.IsUnknownExpression; -import net.sf.jsqlparser.expression.operators.relational.IsJsonExpression; import net.sf.jsqlparser.expression.operators.relational.JsonOperator; import net.sf.jsqlparser.expression.operators.relational.LikeExpression; import net.sf.jsqlparser.expression.operators.relational.Matches; @@ -1181,7 +1180,7 @@ public StringBuilder visit(CastExpression cast, S context) { cast.getLeftExpression().accept(this, context); builder.append(" AS "); builder.append( - cast.getColumnDefinitions().size() > 1 + !cast.getColumnDefinitions().isEmpty() ? "ROW(" + Select.getStringList(cast.getColumnDefinitions()) + ")" : cast.getColDataType().toString()); builder.append(formatStr); diff --git a/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java b/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java index 6e0ff80a96..afb6778ade 100644 --- a/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java @@ -16,6 +16,7 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.test.TestUtils; +import net.sf.jsqlparser.util.deparser.StatementDeParser; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -25,6 +26,27 @@ */ public class CastExpressionTest { + @Test + void testCastToSingleFieldRow() throws JSQLParserException { + String sql = "SELECT CAST(ROW(1) AS ROW(x BIGINT))"; + PlainSelect select = (PlainSelect) CCJSqlParserUtil.parse(sql); + CastExpression cast = Assertions.assertInstanceOf(CastExpression.class, + select.getSelectItem(0).getExpression()); + Assertions.assertEquals(1, cast.getColumnDefinitions().size()); + Assertions.assertEquals("x", cast.getColumnDefinitions().get(0).getColumnName()); + Assertions.assertEquals("BIGINT", + cast.getColumnDefinitions().get(0).getColDataType().getDataType()); + + Assertions.assertAll( + () -> assertSqlCanBeParsedAndDeparsed(sql), + () -> { + StringBuilder builder = new StringBuilder(); + select.accept(new StatementDeParser(builder)); + Assertions.assertEquals(sql, builder.toString()); + }); + assertSqlCanBeParsedAndDeparsed("SELECT TRY_CAST(ROW(1) AS ROW(x BIGINT))"); + } + @Test public void testCastToRowConstructorIssue1267() throws JSQLParserException { TestUtils.assertExpressionCanBeParsedAndDeparsed(