This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit 78b3d979d1a7ae37f88c0b04484723fb03ec7f18 Author: Julian Hyde <[email protected]> AuthorDate: Tue Jan 28 14:44:34 2020 -0800 In JDBC adapter tests that check generated SQL, extract SQL from string literals in generated Java Previously we would compare the actual generated Java with the Java which would be generated for the expected SQL. This was correct, but produced large diffs that were difficult to understand. --- .../org/apache/calcite/test/CalciteAssert.java | 118 +++++++++++++++++---- 1 file changed, 98 insertions(+), 20 deletions(-) diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java index 8684ed0..64da551 100644 --- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java +++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java @@ -111,6 +111,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.sql.DataSource; @@ -1713,33 +1714,37 @@ public class CalciteAssert { } public AssertQuery planContains(String expected) { - ensurePlan(null); - assertTrue(toLinux(plan) - .replaceAll("\\\\r\\\\n", "\\\\n") - .contains(expected), "Plan [" + plan + "] contains [" + expected + "]"); - return this; + return planContains(null, JavaSql.fromJava(expected)); } public AssertQuery planUpdateHasSql(String expected, int count) { - ensurePlan(checkUpdateCount(count)); - expected = ".unwrap(javax.sql.DataSource.class), \"" - + expected.replace("\\", "\\\\") - .replace("\"", "\\\"") - .replaceAll("\n", "\\\\n") - + "\""; - assertTrue(toLinux(plan) - .replaceAll("\\\\r\\\\n", "\\\\n") - .contains(expected), "Plan [" + plan + "] contains [" + expected + "]"); + return planContains(checkUpdateCount(count), JavaSql.fromSql(expected)); + } + + @Nonnull private AssertQuery planContains(Consumer<Integer> checkUpdate, + JavaSql expected) { + ensurePlan(checkUpdate); + if (expected.sql != null) { + final List<String> planSqls = JavaSql.fromJava(plan).extractSql(); + final String planSql; + if (planSqls.size() == 1) { + planSql = planSqls.get(0); + assertThat(planSql, is(expected.sql)); + } else { + assertThat("contains " + planSqls + " expected " + expected, + planSqls.contains(expected.sql), is(true)); + } + } else { + final String message = + "Plan [" + plan + "] contains [" + expected.java + "]"; + final String actualJava = toLinux(plan).replaceAll("\\\\r\\\\n", "\\\\n"); + assertTrue(actualJava.contains(expected.java), message); + } return this; } public AssertQuery planHasSql(String expected) { - return planContains( - ".unwrap(javax.sql.DataSource.class), \"" - + expected.replace("\\", "\\\\") - .replace("\"", "\\\"") - .replaceAll("\n", "\\\\n") - + "\""); + return planContains(null, JavaSql.fromSql(expected)); } private void ensurePlan(Consumer<Integer> checkUpdate) { @@ -2139,4 +2144,77 @@ public class CalciteAssert { public interface PreparedStatementConsumer { void accept(PreparedStatement statement) throws SQLException; } + + /** An expected string that may contain either Java or a SQL string embedded + * in the Java. */ + private static class JavaSql { + private static final String START = + ".unwrap(javax.sql.DataSource.class), \""; + private static final String END = "\""; + + private final String java; + private final String sql; + + JavaSql(String java, String sql) { + this.java = Objects.requireNonNull(java); + this.sql = sql; + } + + static JavaSql fromJava(String java) { + return new JavaSql(java, null); + } + + static JavaSql fromSql(String sql) { + return new JavaSql(wrap(sql), sql); + } + + private static String wrap(String sql) { + return START + + sql.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replaceAll("\n", "\\\\n") + + END; + } + + /** Extracts the SQL statement(s) from within a Java plan. */ + public List<String> extractSql() { + return unwrap(java); + } + + static @Nonnull List<String> unwrap(String java) { + final List<String> sqlList = new ArrayList<>(); + final StringBuilder b = new StringBuilder(); + hLoop: + for (int h = 0;;) { + final int i = java.indexOf(START, h); + if (i < 0) { + return sqlList; + } + for (int j = i + START.length(); j < java.length();) { + char c = java.charAt(j++); + switch (c) { + case '"': + sqlList.add(b.toString()); + b.setLength(0); + h = j; + continue hLoop; + case '\\': + c = java.charAt(j++); + if (c == 'n') { + b.append('\n'); + break; + } + if (c == 'r') { + // Ignore CR, thus converting Windows strings to Unix. + break; + } + // fall through for '\\' and '\"' + default: + b.append(c); + } + } + return sqlList; // last SQL literal was incomplete + } + } + } }
