From 88251434c0fbd9252330ef3e31c85d2cc3bc9c49 Mon Sep 17 00:00:00 2001 From: minleejae Date: Sat, 12 Sep 2026 19:50:51 +0900 Subject: [PATCH] fix: visit pipe expressions through shared deparser paths --- .../util/deparser/SelectDeParser.java | 18 +++-- .../piped/PipeExpressionDeParserTest.java | 73 +++++++++++++++++++ 2 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/statement/piped/PipeExpressionDeParserTest.java diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index 3ba7483125..10a35d68f7 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -79,7 +79,6 @@ import net.sf.jsqlparser.statement.select.UnPivot; import net.sf.jsqlparser.statement.select.Values; import net.sf.jsqlparser.statement.select.WithItem; -import net.sf.jsqlparser.statement.update.UpdateSet; @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) public class SelectDeParser extends AbstractDeParser @@ -1159,9 +1158,11 @@ public StringBuilder visit(JoinPipeOperator join, Void context) { @Override public StringBuilder visit(LimitPipeOperator limit, Void context) { - builder.append("|> ").append("LIMIT ").append(limit.getLimitExpression()); + builder.append("|> LIMIT "); + limit.getLimitExpression().accept(expressionVisitor, context); if (limit.getOffsetExpression() != null) { - builder.append(" OFFSET ").append(limit.getOffsetExpression()); + builder.append(" OFFSET "); + limit.getOffsetExpression().accept(expressionVisitor, context); } return builder; } @@ -1206,7 +1207,8 @@ public StringBuilder visit(SelectPipeOperator select, Void context) { int i = 0; for (SelectItem selectItem : select.getSelectItems()) { - builder.append(i++ > 0 ? ", " : " ").append(selectItem); + builder.append(i++ > 0 ? ", " : " "); + selectItem.accept(this, context); } builder.append("\n"); return builder; @@ -1214,10 +1216,10 @@ public StringBuilder visit(SelectPipeOperator select, Void context) { @Override public StringBuilder visit(SetPipeOperator set, Void context) { - builder.append("|> ").append("SET"); - int i = 0; - for (UpdateSet updateSet : set.getUpdateSets()) { - builder.append(i++ > 0 ? ", " : " ").append(updateSet); + builder.append("|> SET"); + if (!set.getUpdateSets().isEmpty()) { + builder.append(' '); + deparseUpdateSets(set.getUpdateSets(), builder, expressionVisitor); } builder.append("\n"); return builder; diff --git a/src/test/java/net/sf/jsqlparser/statement/piped/PipeExpressionDeParserTest.java b/src/test/java/net/sf/jsqlparser/statement/piped/PipeExpressionDeParserTest.java new file mode 100644 index 0000000000..717a303595 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/piped/PipeExpressionDeParserTest.java @@ -0,0 +1,73 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2025 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.piped; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.util.deparser.ExpressionDeParser; +import net.sf.jsqlparser.util.deparser.SelectDeParser; +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 PipeExpressionDeParserTest { + @Test + void customPrinterVisitsEveryValueOnceAcrossThePipeline() throws Exception { + String sql = "FROM t |> SELECT 1 AS a, 'secret' AS b |> EXTEND a + 2 AS c " + + "|> SET a = 3, b = 'hidden' |> LIMIT 4 OFFSET 5"; + var statement = CCJSqlParserUtil.parse(sql); + String original = statement.toString(); + List visited = new ArrayList<>(); + List strings = new ArrayList<>(); + StringBuilder output = new StringBuilder(); + ExpressionDeParser expressions = new ExpressionDeParser() { + @Override + public StringBuilder visit(LongValue value, S context) { + visited.add(value.getValue()); + return getBuilder().append(value.getValue() + 100); + } + + @Override + public StringBuilder visit(StringValue value, S context) { + strings.add(value.getValue()); + return getBuilder().append("'masked'"); + } + }; + statement.accept(new StatementDeParser(expressions, new SelectDeParser(), output), null); + assertEquals(Arrays.asList(1L, 2L, 3L, 4L, 5L), visited); + assertEquals(Arrays.asList("secret", "hidden"), strings); + assertEquals("FROM t\n|> SELECT 101 AS a, 'masked' AS b\n" + + "|> EXTEND a + 102 AS c\n|> SET a = 103, b = 'masked'\n" + + "|> LIMIT 104 OFFSET 105", output.toString()); + assertEquals(original, statement.toString()); + assertEquals(output.toString(), CCJSqlParserUtil.parse(output.toString()).toString()); + } + + @ParameterizedTest + @ValueSource(strings = {"FROM t |> SELECT DISTINCT a + 1 AS b, a * 2 AS c", + "FROM t |> EXTEND COALESCE(a, 1) AS b", + "FROM t |> SET a = 1, b = (SELECT 2)", + "FROM t |> SET (a, b) = (1, 2)", + "FROM t |> LIMIT 1", "FROM t |> LIMIT 1 OFFSET 2"}) + void defaultPrinterPreservesAliasesSubqueriesAndAssignmentBrackets(String sql) + throws Exception { + var statement = CCJSqlParserUtil.parse(sql); + StringBuilder output = new StringBuilder(); + statement.accept(new StatementDeParser(output), null); + assertEquals(statement.toString(), output.toString()); + assertEquals(statement.toString(), CCJSqlParserUtil.parse(output.toString()).toString()); + } +}