This is an automated email from the ASF dual-hosted git repository. snuyanzin pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/flink.git
commit 1c17615d9985395e14147fdeacd50c4029902942 Author: Sergey Nuyanzin <[email protected]> AuthorDate: Fri Jun 26 12:15:20 2026 +0200 [FLINK-39999][table] Add temporary DeepCopyUnparsingTesterImpl before Calcite 1.42.0 upgrade --- .../sql/parser/DeepCopyUnparsingTesterImpl.java | 155 +++++++++++++++++++++ .../flink/sql/parser/FlinkSqlUnParserTest.java | 2 +- .../MaterializedTableStatementUnParserTest.java | 3 +- 3 files changed, 157 insertions(+), 3 deletions(-) diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/DeepCopyUnparsingTesterImpl.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/DeepCopyUnparsingTesterImpl.java new file mode 100644 index 00000000000..dff1b460998 --- /dev/null +++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/DeepCopyUnparsingTesterImpl.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.flink.sql.parser; + +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlDialect; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlWriterConfig; +import org.apache.calcite.sql.dialect.AnsiSqlDialect; +import org.apache.calcite.sql.parser.SqlParser; +import org.apache.calcite.sql.parser.SqlParserTest; +import org.apache.calcite.sql.parser.StringAndPos; +import org.apache.calcite.sql.test.SqlTestFactory; +import org.apache.calcite.sql.util.SqlShuttle; +import org.apache.calcite.util.Util; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unparsing tester that, on top of the regular parse/unparse round-trips performed by {@link + * SqlParserTest.UnparsingTesterImpl}, also makes a deep copy of every parsed {@link SqlNode} and + * asserts that the copy unparses to the same SQL as the original. + * + * <p>The class should be dropped after upgrading to Calcite 1.42.0 since similar logic is there. + * More details are at CALCITE-7301. + */ +class DeepCopyUnparsingTesterImpl extends SqlParserTest.UnparsingTesterImpl { + + private static final String FLINK_PARSER_PACKAGE = "org.apache.flink.sql.parser"; + + /** Whether {@code node} or any node in its subtree is a Flink parser node. */ + private static boolean containsFlinkNode(SqlNode node) { + if (isFlinkNode(node)) { + return true; + } + final boolean[] found = {false}; + node.accept( + new SqlShuttle() { + @Override + public SqlNode visit(final SqlCall call) { + if (isFlinkNode(call)) { + found[0] = true; + } + // Default traversal copies nothing (alwaysCopy=false), so it never + // invokes createCall and is safe even for unfixed core operators. + return super.visit(call); + } + }); + return found[0]; + } + + private static boolean isFlinkNode(SqlNode node) { + return node.getClass().getName().startsWith(FLINK_PARSER_PACKAGE); + } + + private static SqlNode deepCopy(SqlNode sqlNode) { + return sqlNode.accept( + new SqlShuttle() { + @Override + public SqlNode visit(final SqlCall call) { + // Handler always creates a new copy of 'call'. + CallCopyingArgHandler argHandler = new CallCopyingArgHandler(call, true); + call.getOperator().acceptCall(this, call, false, argHandler); + return argHandler.result(); + } + }); + } + + private static UnaryOperator<SqlWriterConfig> simple() { + return c -> + c.withSelectListItemsOnSeparateLines(false) + .withUpdateSetListNewline(false) + .withIndentation(0) + .withFromFolding(SqlWriterConfig.LineFolding.TALL); + } + + private static SqlWriterConfig simpleWithParens(SqlWriterConfig c) { + return simple().apply(c).withAlwaysUseParentheses(true); + } + + private static String toSqlString( + SqlNodeList sqlNodeList, UnaryOperator<SqlWriterConfig> transform) { + return sqlNodeList.stream() + .map(node -> node.toSqlString(transform).getSql()) + .collect(Collectors.joining(";")); + } + + @Override + public void checkList( + SqlTestFactory factory, + StringAndPos sap, + SqlDialect dialect, + UnaryOperator<String> converter, + List<String> expected) { + super.checkList(factory, sap, dialect, converter, expected); + + final SqlNodeList sqlNodeList = parseStmtsAndHandleEx(factory, sap.sql); + if (!containsFlinkNode(sqlNodeList)) { + return; + } + final String sql = toSqlString(sqlNodeList, simple()); + + // Make a deep copy of the SqlNodeList, unparse it. + final SqlNodeList deepCopiedSqlNodeList = (SqlNodeList) deepCopy(sqlNodeList); + final String sqlFromDeepCopy = toSqlString(deepCopiedSqlNodeList, simple()); + // Should be the same as we started with. + assertThat(sqlFromDeepCopy).isEqualTo(sql); + } + + @Override + public void check( + SqlTestFactory factory, + StringAndPos sap, + SqlDialect dialect, + UnaryOperator<String> converter, + String expected, + Consumer<SqlParser> parserChecker) { + super.check(factory, sap, dialect, converter, expected, parserChecker); + + final SqlNode sqlNode = parseStmtAndHandleEx(factory, sap.sql, parserChecker); + if (!containsFlinkNode(sqlNode)) { + return; + } + final SqlDialect dialect2 = Util.first(dialect, AnsiSqlDialect.DEFAULT); + final UnaryOperator<SqlWriterConfig> writerTransform = + c -> simpleWithParens(c).withDialect(dialect2); + + // Make a deep copy of the original SqlNode, unparse it. + final SqlNode sqlNode5 = deepCopy(sqlNode); + final String actual5 = sqlNode5.toSqlString(writerTransform).getSql(); + assertThat(converter.apply(actual5)).isEqualTo(expected); + } +} diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlUnParserTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlUnParserTest.java index cced9d2e440..d6f34d8ed69 100644 --- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlUnParserTest.java +++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/FlinkSqlUnParserTest.java @@ -39,7 +39,7 @@ class FlinkSqlUnParserTest extends FlinkSqlParserImplTest { public SqlParserFixture fixture() { return super.fixture() - .withTester(new UnparsingTesterImpl()) + .withTester(new DeepCopyUnparsingTesterImpl()) .withConfig(c -> c.withParserFactory(FlinkSqlParserImpl.FACTORY)); } } diff --git a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/MaterializedTableStatementUnParserTest.java b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/MaterializedTableStatementUnParserTest.java index 2baa999c868..e7dd54dba16 100644 --- a/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/MaterializedTableStatementUnParserTest.java +++ b/flink-table/flink-sql-parser/src/test/java/org/apache/flink/sql/parser/MaterializedTableStatementUnParserTest.java @@ -21,7 +21,6 @@ package org.apache.flink.sql.parser; import org.apache.flink.sql.parser.impl.FlinkSqlParserImpl; import org.apache.calcite.sql.parser.SqlParserFixture; -import org.apache.calcite.sql.parser.SqlParserTest; import org.junit.jupiter.api.parallel.Execution; import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; @@ -40,7 +39,7 @@ class MaterializedTableStatementUnParserTest extends MaterializedTableStatementP public SqlParserFixture fixture() { return super.fixture() - .withTester(new SqlParserTest.UnparsingTesterImpl()) + .withTester(new DeepCopyUnparsingTesterImpl()) .withConfig(c -> c.withParserFactory(FlinkSqlParserImpl.FACTORY)); } }
