diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/AbstractDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/AbstractDeParser.java index 6458f79c9..aa56a5076 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/AbstractDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/AbstractDeParser.java @@ -9,6 +9,7 @@ */ package net.sf.jsqlparser.util.deparser; +import net.sf.jsqlparser.statement.ReturningClause; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.statement.update.UpdateSet; @@ -26,6 +27,14 @@ protected AbstractDeParser(StringBuilder builder) { this.builder = builder; } + protected void deparseReturningClause(ReturningClause clause, + ExpressionVisitor visitor) { + if (clause != null) { + SelectDeParser selectItems = new SelectDeParser(visitor, builder); + clause.appendTo(builder, item -> item.accept(selectItems, null)); + } + } + public static void deparseUpdateSets(List updateSets, StringBuilder buffer, ExpressionVisitor visitor) { ExpressionListDeParser expressionListDeParser = diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java index 7472c968f..21842c094 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/DeleteDeParser.java @@ -109,9 +109,7 @@ public void deParse(Delete delete) { builder.append(delete.getOption()); } - if (delete.getReturningClause() != null) { - delete.getReturningClause().appendTo(builder); - } + deparseReturningClause(delete.getReturningClause(), expressionVisitor); } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java index 492c3c5ef..596af0456 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java @@ -153,9 +153,7 @@ public void deParse(Insert insert) { insert.getConflictAction().appendTo(builder); } - if (insert.getReturningClause() != null) { - insert.getReturningClause().appendTo(builder); - } + deparseReturningClause(insert.getReturningClause(), expressionVisitor); } public ExpressionVisitor getExpressionVisitor() { diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java index a6d9c2670..e2aaf1e89 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UpdateDeParser.java @@ -101,9 +101,7 @@ public void deParse(Update update) { builder.append(update.getOption()); } - if (update.getReturningClause() != null) { - update.getReturningClause().appendTo(builder); - } + deparseReturningClause(update.getReturningClause(), expressionVisitor); } protected void deparseWhereClause(Update update) { diff --git a/src/test/java/net/sf/jsqlparser/util/deparser/DmlReturningExpressionTest.java b/src/test/java/net/sf/jsqlparser/util/deparser/DmlReturningExpressionTest.java new file mode 100644 index 000000000..a5f247f06 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/util/deparser/DmlReturningExpressionTest.java @@ -0,0 +1,57 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2019 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.util.deparser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.ArrayList; +import java.util.List; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class DmlReturningExpressionTest { + @ParameterizedTest + @ValueSource(strings = {"UPDATE t SET a = 7 RETURNING a + 8 AS result", + "DELETE FROM t WHERE a = 7 RETURNING a + 8 AS result", + "INSERT INTO t(a) VALUES (7) RETURNING a + 8 AS result", + "UPDATE t SET a = 7 RETURNING (SELECT b + 8 FROM source) AS result"}) + void visitsReturnedExpressionsExactlyOnce(String sql) throws Exception { + var statement = CCJSqlParserUtil.parse(sql); + String original = statement.toString(); + List seen = new ArrayList<>(); + ExpressionDeParser expressions = new ExpressionDeParser() { + @Override + public StringBuilder visit(LongValue value, S context) { + seen.add(value.getValue()); + return getBuilder().append(value.getValue() + 100); + } + }; + StringBuilder output = new StringBuilder(); + statement.accept(new StatementDeParser(expressions, new SelectDeParser(), output), null); + assertEquals(List.of(7L, 8L), seen); + assertEquals(original.replace("7", "107").replace("8", "108"), output.toString()); + assertEquals(original, statement.toString()); + assertEquals(output.toString(), CCJSqlParserUtil.parse(output.toString()).toString()); + } + + @ParameterizedTest + @ValueSource(strings = {"INSERT INTO t(a) VALUES (1) RETURNING *", + "DELETE FROM t RETURNING t.*, a AS result", + "UPDATE t SET a = 1 RETURNING a, b INTO x, y", + "DELETE FROM t RETURNING WITH (OLD AS o, NEW AS n) o.*, n.id"}) + void preservesWildcardsAliasesAndOutputTargets(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()); + } +}