From 25c00d856f815942d7ab65c4693685bdb146abe1 Mon Sep 17 00:00:00 2001 From: minleejae Date: Sun, 27 Sep 2026 16:07:51 +0900 Subject: [PATCH 1/2] Preserve refresh modes and visit materialized view targets Signed-off-by: minleejae --- .../RefreshMaterializedViewStatement.java | 41 +++++------ ...reshMaterializedViewStatementDeParser.java | 36 ++++------ .../util/deparser/StatementDeParser.java | 3 +- .../RefreshMaterializedViewStatementTest.java | 72 ++++++++++++++++++- 4 files changed, 100 insertions(+), 52 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java index 6519cfe5c3..ee3d2c44c2 100644 --- a/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/refresh/RefreshMaterializedViewStatement.java @@ -9,6 +9,7 @@ */ package net.sf.jsqlparser.statement.refresh; +import java.util.function.Consumer; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; @@ -60,34 +61,24 @@ public void setConcurrently(boolean concurrently) { this.concurrently = concurrently; } - @SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault") - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); + public StringBuilder appendTo(StringBuilder builder, Consumer tablePrinter) { builder.append("REFRESH MATERIALIZED VIEW "); - if (this.refreshMode == null) { - if (concurrently) { - builder.append("CONCURRENTLY "); - } - builder.append(view); - return builder.toString(); + if (concurrently) { + builder.append("CONCURRENTLY "); } - switch (this.refreshMode) { - case WITH_DATA: - if (concurrently) { - builder.append("CONCURRENTLY "); - } - builder.append(view); - builder.append(" WITH DATA"); - break; - case WITH_NO_DATA: - builder.append(view); - if (!concurrently) { - builder.append(" WITH NO DATA"); - } - break; + tablePrinter.accept(view); + if (refreshMode == RefreshMode.WITH_DATA) { + builder.append(" WITH DATA"); + } else if (refreshMode == RefreshMode.WITH_NO_DATA) { + builder.append(" WITH NO DATA"); } - return builder.toString(); + return builder; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + return appendTo(builder, builder::append).toString(); } @Override diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java index 444279ee6a..6c20b72cdb 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/RefreshMaterializedViewStatementDeParser.java @@ -9,6 +9,8 @@ */ package net.sf.jsqlparser.util.deparser; +import java.util.function.Consumer; +import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; /** @@ -18,36 +20,22 @@ public class RefreshMaterializedViewStatementDeParser extends AbstractDeParser { + private final Consumer
tablePrinter; + public RefreshMaterializedViewStatementDeParser(StringBuilder buffer) { super(buffer); + this.tablePrinter = table -> builder.append(table); + } + + public RefreshMaterializedViewStatementDeParser(StringBuilder buffer, + Consumer
tablePrinter) { + super(buffer); + this.tablePrinter = tablePrinter; } - @SuppressWarnings("PMD.SwitchStmtsShouldHaveDefault") @Override public void deParse(RefreshMaterializedViewStatement view) { - builder.append("REFRESH MATERIALIZED VIEW "); - if (view.getRefreshMode() == null) { - if (view.isConcurrently()) { - builder.append("CONCURRENTLY "); - } - builder.append(view.getView()); - return; - } - switch (view.getRefreshMode()) { - case WITH_DATA: - if (view.isConcurrently()) { - builder.append("CONCURRENTLY "); - } - builder.append(view.getView()); - builder.append(" WITH DATA"); - break; - case WITH_NO_DATA: - builder.append(view.getView()); - if (view.isConcurrently()) { - builder.append(" WITH NO DATA"); - } - break; - } + view.appendTo(builder, tablePrinter); } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index 8516e51dc8..0111352230 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -215,7 +215,8 @@ public StringBuilder visit(CreateView createView, S context) { @Override public StringBuilder visit(RefreshMaterializedViewStatement materializedViewStatement, S context) { - new RefreshMaterializedViewStatementDeParser(builder).deParse(materializedViewStatement); + new RefreshMaterializedViewStatementDeParser(builder, + table -> table.accept(selectDeParser, context)).deParse(materializedViewStatement); return builder; } diff --git a/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java index 4ebba56c7f..c6361eb5d2 100644 --- a/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java @@ -10,9 +10,20 @@ package net.sf.jsqlparser.statement; import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static org.junit.jupiter.api.Assertions.assertEquals; import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.refresh.RefreshMode; +import net.sf.jsqlparser.util.deparser.ExpressionDeParser; +import net.sf.jsqlparser.util.deparser.RefreshMaterializedViewStatementDeParser; +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; /** * @@ -21,8 +32,65 @@ public class RefreshMaterializedViewStatementTest { + @ParameterizedTest + @ValueSource(strings = {"REFRESH MATERIALIZED VIEW my_view", + "REFRESH MATERIALIZED VIEW my_view WITH DATA", + "REFRESH MATERIALIZED VIEW my_view WITH NO DATA", + "REFRESH MATERIALIZED VIEW CONCURRENTLY my_view", + "REFRESH MATERIALIZED VIEW CONCURRENTLY my_view WITH DATA"}) + public void testRefreshModes(String sql) throws JSQLParserException { + RefreshMaterializedViewStatement statement = + (RefreshMaterializedViewStatement) assertSqlCanBeParsedAndDeparsed(sql); + StringBuilder output = new StringBuilder(); + new RefreshMaterializedViewStatementDeParser(output).deParse(statement); + assertEquals(sql, output.toString()); + assertEquals(2, CCJSqlParserUtil.parseStatements(sql + "; SELECT 1").size()); + } + + @Test + void preservesModeWhenVisitingViewName() throws JSQLParserException { + RefreshMaterializedViewStatement statement = + (RefreshMaterializedViewStatement) CCJSqlParserUtil + .parse("REFRESH MATERIALIZED VIEW old_schema.mv WITH NO DATA"); + StringBuilder output = new StringBuilder(); + ExpressionDeParser expressions = new ExpressionDeParser(); + SelectDeParser selects = new SelectDeParser(expressions, output) { + @Override + public StringBuilder visit(Table table, S context) { + assertEquals("context", context); + getBuilder().append("new_schema.\"renamed view\""); + return getBuilder(); + } + }; + statement.accept(new StatementDeParser(expressions, selects, output), "context"); + assertEquals("REFRESH MATERIALIZED VIEW new_schema.\"renamed view\" WITH NO DATA", + output.toString()); + RefreshMaterializedViewStatement reparsed = + (RefreshMaterializedViewStatement) CCJSqlParserUtil.parse(output.toString()); + assertEquals(RefreshMode.WITH_NO_DATA, reparsed.getRefreshMode()); + assertEquals("new_schema", reparsed.getView().getSchemaName()); + } + + @Test + void respectsReplacementOutputBuffer() throws JSQLParserException { + StringBuilder original = new StringBuilder(); + StringBuilder replacement = new StringBuilder(); + RefreshMaterializedViewStatementDeParser deparser = + new RefreshMaterializedViewStatementDeParser(original); + deparser.setBuilder(replacement); + deparser.deParse((RefreshMaterializedViewStatement) CCJSqlParserUtil + .parse("REFRESH MATERIALIZED VIEW mv WITH NO DATA")); + assertEquals("", original.toString()); + assertEquals("REFRESH MATERIALIZED VIEW mv WITH NO DATA", replacement.toString()); + } + @Test - public void testSimpleUse() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("REFRESH MATERIALIZED VIEW my_view"); + void rendersDefaultModeLikeOmittedMode() { + RefreshMaterializedViewStatement statement = new RefreshMaterializedViewStatement( + new Table("mv"), true, RefreshMode.DEFAULT); + StringBuilder output = new StringBuilder(); + new RefreshMaterializedViewStatementDeParser(output).deParse(statement); + assertEquals("REFRESH MATERIALIZED VIEW CONCURRENTLY mv", output.toString()); + assertEquals(statement.toString(), output.toString()); } } From 3b763b83c9d4d35ad1a449e40d985ddb4c616b82 Mon Sep 17 00:00:00 2001 From: minleejae Date: Sun, 27 Sep 2026 16:37:46 +0900 Subject: [PATCH 2/2] Keep refresh table visitors on the replacement output buffer Signed-off-by: minleejae --- .../util/deparser/StatementDeParser.java | 1 + .../RefreshMaterializedViewStatementTest.java | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index 0111352230..de9fc62d0e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -215,6 +215,7 @@ public StringBuilder visit(CreateView createView, S context) { @Override public StringBuilder visit(RefreshMaterializedViewStatement materializedViewStatement, S context) { + selectDeParser.setBuilder(builder); new RefreshMaterializedViewStatementDeParser(builder, table -> table.accept(selectDeParser, context)).deParse(materializedViewStatement); return builder; diff --git a/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java index c6361eb5d2..ad0561352b 100644 --- a/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/RefreshMaterializedViewStatementTest.java @@ -11,6 +11,7 @@ import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil; @@ -62,7 +63,11 @@ public StringBuilder visit(Table table, S context) { return getBuilder(); } }; - statement.accept(new StatementDeParser(expressions, selects, output), "context"); + StringBuilder original = new StringBuilder(); + StatementDeParser deparser = new StatementDeParser(expressions, selects, original); + deparser.setBuilder(output); + assertSame(output, statement.accept(deparser, "context")); + assertEquals("", original.toString()); assertEquals("REFRESH MATERIALIZED VIEW new_schema.\"renamed view\" WITH NO DATA", output.toString()); RefreshMaterializedViewStatement reparsed = @@ -71,6 +76,20 @@ public StringBuilder visit(Table table, S context) { assertEquals("new_schema", reparsed.getView().getSchemaName()); } + @Test + void statementDeparserUsesReplacementOutputBuffer() throws JSQLParserException { + StringBuilder original = new StringBuilder("old output"); + StringBuilder replacement = new StringBuilder(); + StatementDeParser deparser = new StatementDeParser(original); + deparser.setBuilder(replacement); + RefreshMaterializedViewStatement statement = + (RefreshMaterializedViewStatement) CCJSqlParserUtil + .parse("REFRESH MATERIALIZED VIEW schema_name.mv WITH NO DATA"); + assertSame(replacement, statement.accept(deparser, null)); + assertEquals("old output", original.toString()); + assertEquals(statement.toString(), replacement.toString()); + } + @Test void respectsReplacementOutputBuffer() throws JSQLParserException { StringBuilder original = new StringBuilder();