You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2022/04/12 00:57:51 UTC
[calcite] 01/02: Refactor RelWriterTest
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 3e877d11ae4bf3ff6dbfc990b6a13914e7c30944
Author: Julian Hyde <jh...@apache.org>
AuthorDate: Sun Apr 10 15:10:46 2022 +0200
Refactor RelWriterTest
(No changes in functionality.)
---
.../java/org/apache/calcite/util/JsonBuilder.java | 2 +
.../org/apache/calcite/plan/RelWriterTest.java | 673 ++++++++++-----------
2 files changed, 336 insertions(+), 339 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/util/JsonBuilder.java b/core/src/main/java/org/apache/calcite/util/JsonBuilder.java
index 6e20834af..b9fdec835 100644
--- a/core/src/main/java/org/apache/calcite/util/JsonBuilder.java
+++ b/core/src/main/java/org/apache/calcite/util/JsonBuilder.java
@@ -18,6 +18,8 @@ package org.apache.calcite.util;
import org.apache.calcite.avatica.util.Spaces;
+import com.google.common.collect.ImmutableList;
+
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
diff --git a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
index 6b5c55347..b75d09f70 100644
--- a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
@@ -47,7 +47,6 @@ import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCorrelVariable;
import org.apache.calcite.rex.RexFieldCollation;
import org.apache.calcite.rex.RexInputRef;
-import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.rex.RexWindowBounds;
@@ -92,6 +91,7 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Stream;
import static org.apache.calcite.test.Matchers.isLinux;
@@ -101,6 +101,8 @@ import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static java.util.Objects.requireNonNull;
+
/**
* Unit test for {@link org.apache.calcite.rel.externalize.RelJson}.
*/
@@ -437,6 +439,11 @@ class RelWriterTest {
return Stream.of(SqlExplainFormat.TEXT, SqlExplainFormat.DOT);
}
+ /** Creates a fixture. */
+ private static Fixture relFn(Function<RelBuilder, RelNode> relFn) {
+ return new Fixture(relFn, false, SqlExplainFormat.TEXT);
+ }
+
/** Unit test for {@link RelJson#toJson(Object)} for an object of type
* {@link RelDataType}. */
@Test void testTypeJson() {
@@ -594,40 +601,28 @@ class RelWriterTest {
}
@Test void testExchange() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .exchange(RelDistributions.hash(ImmutableList.of(0, 1)))
- .build();
- final String relJson = RelOptUtil.dumpPlan("", rel,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .exchange(RelDistributions.hash(ImmutableList.of(0, 1)))
+ .build();
final String expected = ""
+ "LogicalExchange(distribution=[hash[0, 1]])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test public void testExchangeWithDistributionTraitDef() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .exchange(RelDistributions.hash(ImmutableList.of(0, 1)))
- .build();
- final String relJson = RelOptUtil.dumpPlan("", rel,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
-
- VolcanoPlanner planner = new VolcanoPlanner();
- planner.addRelTraitDef(RelDistributionTraitDef.INSTANCE);
- RelOptCluster cluster = RelOptCluster.create(planner, builder.getRexBuilder());
-
- String plan = deserializeAndDump(cluster, getSchema(rel), relJson, SqlExplainFormat.TEXT);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .exchange(RelDistributions.hash(ImmutableList.of(0, 1)))
+ .build();
final String expected = ""
+ "LogicalExchange(distribution=[hash[0, 1]])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(plan, isLinux(expected));
+ relFn(relFn)
+ .withDistribution(true)
+ .assertThatPlan(isLinux(expected));
}
/**
@@ -714,7 +709,7 @@ class RelWriterTest {
+ " LogicalTableScan(table=[[hr, emps]])\n"));
}
- @Test void testJsonToRex() throws JsonProcessingException {
+ @Test void testJsonToRex() {
// Test simple literal without inputs
final String jsonString1 = "{\n"
+ " \"literal\": 10,\n"
@@ -777,9 +772,7 @@ class RelWriterTest {
}
@Test void testTrim() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder b = RelBuilder.create(config);
- final RelNode rel =
+ final Function<RelBuilder, RelNode> relFn = b ->
b.scan("EMP")
.project(
b.alias(
@@ -789,36 +782,26 @@ class RelWriterTest {
b.field("ENAME")),
"trimmed_ename"))
.build();
-
- RelJsonWriter jsonWriter = new RelJsonWriter();
- rel.explain(jsonWriter);
- String relJson = jsonWriter.asString();
- final RelOptSchema schema = getSchema(rel);
- final String s = deserializeAndDumpToTextFormat(schema, relJson);
final String expected = ""
+ "LogicalProject(trimmed_ename=[TRIM(FLAG(BOTH), ' ', $1)])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testPlusOperator() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .project(
- builder.call(SqlStdOperatorTable.PLUS,
- builder.field("SAL"),
- builder.literal(10)))
- .build();
- RelJsonWriter jsonWriter = new RelJsonWriter();
- rel.explain(jsonWriter);
- String relJson = jsonWriter.asString();
- String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .project(
+ b.call(SqlStdOperatorTable.PLUS,
+ b.field("SAL"),
+ b.literal(10)))
+ .build();
final String expected = ""
+ "LogicalProject($f0=[+($5, 10)])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@ParameterizedTest
@@ -920,60 +903,43 @@ class RelWriterTest {
}
@Test void testCalc() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RexBuilder rexBuilder = builder.getRexBuilder();
- final LogicalTableScan scan = (LogicalTableScan) builder.scan("EMP").build();
- final RexProgramBuilder programBuilder =
- new RexProgramBuilder(scan.getRowType(), rexBuilder);
- final RelDataTypeField field = scan.getRowType().getField("SAL", false, false);
- programBuilder.addIdentity();
- programBuilder.addCondition(
- rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN,
- new RexInputRef(field.getIndex(), field.getType()),
- builder.literal(10)));
- final LogicalCalc calc = LogicalCalc.create(scan, programBuilder.getProgram());
- String relJson = RelOptUtil.dumpPlan("", calc,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s =
- Frameworks.withPlanner((cluster, relOptSchema, rootSchema) -> {
- final RelJsonReader reader = new RelJsonReader(
- cluster, getSchema(calc), rootSchema);
- RelNode node;
- try {
- node = reader.read(relJson);
- } catch (IOException e) {
- throw TestUtil.rethrow(e);
- }
- return RelOptUtil.dumpPlan("", node, SqlExplainFormat.TEXT,
- SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- });
- final String expected =
- "LogicalCalc(expr#0..7=[{inputs}], expr#8=[10], expr#9=[>($t5, $t8)],"
- + " proj#0..7=[{exprs}], $condition=[$t9])\n"
- + " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .let(b2 -> {
+ final RexBuilder rexBuilder = b2.getRexBuilder();
+ final RelNode scan = b2.build();
+ final RelDataType rowType = scan.getRowType();
+ final RexProgramBuilder programBuilder =
+ new RexProgramBuilder(rowType, rexBuilder);
+ final RelDataTypeField field =
+ rowType.getField("SAL", false, false);
+ assertThat(field, notNullValue());
+ programBuilder.addIdentity();
+ programBuilder.addCondition(
+ rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN,
+ new RexInputRef(field.getIndex(), field.getType()),
+ b2.literal(10)));
+ return LogicalCalc.create(scan, programBuilder.getProgram());
+ });
+ final String expected = ""
+ + "LogicalCalc(expr#0..7=[{inputs}], expr#8=[10], expr#9=[>($t5, $t8)],"
+ + " proj#0..7=[{exprs}], $condition=[$t9])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@ParameterizedTest
@MethodSource("explainFormats")
void testCorrelateQuery(SqlExplainFormat format) {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
final Holder<RexCorrelVariable> v = Holder.empty();
- RelNode relNode = builder.scan("EMP")
+ final Function<RelBuilder, RelNode> relFn = b -> b.scan("EMP")
.variable(v)
.scan("DEPT")
- .filter(
- builder.equals(builder.field(0), builder.field(v.get(), "DEPTNO")))
- .correlate(
- JoinRelType.INNER, v.get().id, builder.field(2, 0, "DEPTNO"))
+ .filter(b.equals(b.field(0), b.field(v.get(), "DEPTNO")))
+ .correlate(JoinRelType.INNER, v.get().id, b.field(2, 0, "DEPTNO"))
.build();
- RelJsonWriter jsonWriter = new RelJsonWriter();
- relNode.explain(jsonWriter);
- final String relJson = jsonWriter.asString();
- String s = deserializeAndDump(getSchema(relNode), relJson, format);
- String expected = null;
+ final String expected;
switch (format) {
case TEXT:
expected = ""
@@ -993,96 +959,82 @@ class RelWriterTest {
+ "($0, $c\\nor0.DEPTNO)\\n\" [label=\"0\"]\n"
+ "}\n";
break;
+ default:
+ throw new AssertionError(format);
}
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .withFormat(format)
+ .assertThatPlan(isLinux(expected));
}
@Test void testOverWithoutPartition() {
- // The rel stands for the sql of "select count(*) over (order by deptno) from EMP"
- final RelNode rel = mockCountOver("EMP", ImmutableList.of(), ImmutableList.of("DEPTNO"));
- String relJson = RelOptUtil.dumpPlan("", rel, SqlExplainFormat.JSON,
- SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ // Equivalent SQL:
+ // SELECT count(*) OVER (ORDER BY deptno) FROM emp
+ final Function<RelBuilder, RelNode> relFn = b ->
+ mockCountOver(b, "EMP", ImmutableList.of(), ImmutableList.of("DEPTNO"));
final String expected = ""
+ "LogicalProject($f0=[COUNT() OVER (ORDER BY $7 NULLS LAST "
+ "ROWS UNBOUNDED PRECEDING)])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testOverWithoutOrderKey() {
- // The rel stands for the sql of "select count(*) over (partition by DEPTNO) from EMP"
- final RelNode rel = mockCountOver("EMP", ImmutableList.of("DEPTNO"), ImmutableList.of());
- String relJson = RelOptUtil.dumpPlan("", rel, SqlExplainFormat.JSON,
- SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ // Equivalent SQL:
+ // SELECT count(*) OVER (PARTITION BY deptno) FROM emp
+ final Function<RelBuilder, RelNode> relFn = b ->
+ mockCountOver(b, "EMP", ImmutableList.of("DEPTNO"), ImmutableList.of());
final String expected = ""
+ "LogicalProject($f0=[COUNT() OVER (PARTITION BY $7)])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testInterval() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
SqlIntervalQualifier sqlIntervalQualifier =
new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.DAY, SqlParserPos.ZERO);
BigDecimal value = new BigDecimal(86400000);
- RexLiteral intervalLiteral = builder.getRexBuilder()
- .makeIntervalLiteral(value, sqlIntervalQualifier);
- final RelNode rel = builder
- .scan("EMP")
+ final Function<RelBuilder, RelNode> relFn = b -> b.scan("EMP")
.project(
- builder.call(
- SqlStdOperatorTable.TUMBLE_END,
- builder.field("HIREDATE"),
- intervalLiteral))
+ b.call(SqlStdOperatorTable.TUMBLE_END,
+ b.field("HIREDATE"),
+ b.getRexBuilder()
+ .makeIntervalLiteral(value, sqlIntervalQualifier)))
.build();
- RelJsonWriter jsonWriter = new RelJsonWriter();
- rel.explain(jsonWriter);
- String relJson = jsonWriter.asString();
- String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
final String expected = ""
+ "LogicalProject($f0=[TUMBLE_END($4, 86400000:INTERVAL DAY)])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testUdf() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .project(
- builder.call(new MockSqlOperatorTable.MyFunction(),
- builder.field("EMPNO")))
- .build();
- String relJson = RelOptUtil.dumpPlan("", rel,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .project(
+ b.call(new MockSqlOperatorTable.MyFunction(),
+ b.field("EMPNO")))
+ .build();
final String expected = ""
+ "LogicalProject($f0=[MYFUN($0)])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@ParameterizedTest
@MethodSource("explainFormats")
void testUDAF(SqlExplainFormat format) {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .project(builder.field("ENAME"), builder.field("DEPTNO"))
- .aggregate(
- builder.groupKey("ENAME"),
- builder.aggregateCall(new MockSqlOperatorTable.MyAggFunc(),
- builder.field("DEPTNO")))
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .project(b.field("ENAME"), b.field("DEPTNO"))
+ .aggregate(b.groupKey("ENAME"),
+ b.aggregateCall(new MockSqlOperatorTable.MyAggFunc(),
+ b.field("DEPTNO")))
.build();
- final String relJson = RelOptUtil.dumpPlan("", rel,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- final String result = deserializeAndDump(getSchema(rel), relJson, format);
- String expected = null;
+ final String expected;
switch (format) {
case TEXT:
expected = ""
@@ -1098,49 +1050,45 @@ class RelWriterTest {
+ "$1\\nDEPTNO = $7\\n\" [label=\"0\"]\n"
+ "}\n";
break;
+ default:
+ throw new AssertionError(format);
}
- assertThat(result, isLinux(expected));
+ relFn(relFn)
+ .withFormat(format)
+ .assertThatPlan(isLinux(expected));
}
@Test void testArrayType() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .project(
- builder.call(new MockSqlOperatorTable.SplitFunction(),
- builder.field("ENAME"), builder.literal(",")))
- .build();
- final String relJson = RelOptUtil.dumpPlan("", rel,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- final String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .project(
+ b.call(new MockSqlOperatorTable.SplitFunction(),
+ b.field("ENAME"), b.literal(",")))
+ .build();
final String expected = ""
+ "LogicalProject($f0=[SPLIT($1, ',')])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testMapType() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- final RelNode rel = builder
- .scan("EMP")
- .project(
- builder.call(new MockSqlOperatorTable.MapFunction(),
- builder.literal("key"), builder.literal("value")))
- .build();
- final String relJson = RelOptUtil.dumpPlan("", rel,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- final String s = deserializeAndDumpToTextFormat(getSchema(rel), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .project(
+ b.call(new MockSqlOperatorTable.MapFunction(),
+ b.literal("key"), b.literal("value")))
+ .build();
final String expected = ""
+ "LogicalProject($f0=[MAP('key', 'value')])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
/** Returns the schema of a {@link org.apache.calcite.rel.core.TableScan}
* in this plan, or null if there are no scans. */
- private RelOptSchema getSchema(RelNode rel) {
+ private static RelOptSchema getSchema(RelNode rel) {
final Holder<@Nullable RelOptSchema> schemaHolder = Holder.empty();
rel.accept(
new RelShuttleImpl() {
@@ -1156,26 +1104,24 @@ class RelWriterTest {
* Deserialize a relnode from the json string by {@link RelJsonReader},
* and dump it to the given format.
*/
- private String deserializeAndDump(
- RelOptSchema schema, String relJson, SqlExplainFormat format) {
- String s =
- Frameworks.withPlanner((cluster, relOptSchema, rootSchema) -> {
- final RelJsonReader reader = new RelJsonReader(
- cluster, schema, rootSchema);
- RelNode node;
- try {
- node = reader.read(relJson);
- } catch (IOException e) {
- throw TestUtil.rethrow(e);
- }
- return RelOptUtil.dumpPlan("", node, format,
- SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- });
- return s;
+ private static String deserializeAndDump(RelOptSchema schema, String relJson,
+ SqlExplainFormat format) {
+ return Frameworks.withPlanner((cluster, relOptSchema, rootSchema) -> {
+ final RelJsonReader reader =
+ new RelJsonReader(cluster, schema, rootSchema);
+ RelNode node;
+ try {
+ node = reader.read(relJson);
+ } catch (IOException e) {
+ throw TestUtil.rethrow(e);
+ }
+ return RelOptUtil.dumpPlan("", node, format,
+ SqlExplainLevel.EXPPLAN_ATTRIBUTES);
+ });
}
- private String deserializeAndDump(RelOptCluster cluster, RelOptSchema schema, String relJson,
- SqlExplainFormat format) {
+ private static String deserializeAndDump(RelOptCluster cluster,
+ RelOptSchema schema, String relJson, SqlExplainFormat format) {
final RelJsonReader reader = new RelJsonReader(cluster, schema, null);
RelNode node;
try {
@@ -1190,7 +1136,8 @@ class RelWriterTest {
* Deserialize a relnode from the json string by {@link RelJsonReader},
* and dump it to text format.
*/
- private String deserializeAndDumpToTextFormat(RelOptSchema schema, String relJson) {
+ private static String deserializeAndDumpToTextFormat(RelOptSchema schema,
+ String relJson) {
return deserializeAndDump(schema, relJson, SqlExplainFormat.TEXT);
}
@@ -1208,11 +1155,8 @@ class RelWriterTest {
* @param orderKeyNames Order by column names, may empty, can not be null
* @return RelNode for the SQL
*/
- private RelNode mockCountOver(String table,
+ private RelNode mockCountOver(RelBuilder builder, String table,
List<String> partitionKeyNames, List<String> orderKeyNames) {
-
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
final RexBuilder rexBuilder = builder.getRexBuilder();
final RelDataType type = rexBuilder.getTypeFactory().createSqlType(SqlTypeName.BIGINT);
List<RexNode> partitionKeys = new ArrayList<>(partitionKeyNames.size());
@@ -1240,179 +1184,173 @@ class RelWriterTest {
}
@Test void testHashDistributionWithoutKeys() {
- final RelNode root = createSortPlan(RelDistributions.hash(Collections.emptyList()));
- final RelJsonWriter writer = new RelJsonWriter();
- root.explain(writer);
- final String json = writer.asString();
- assertThat(json, is(HASH_DIST_WITHOUT_KEYS));
-
- final String s = deserializeAndDumpToTextFormat(getSchema(root), json);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ createSortPlan(b, RelDistributions.hash(Collections.emptyList()));
final String expected =
"LogicalSortExchange(distribution=[hash], collation=[[0]])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatJson(is(HASH_DIST_WITHOUT_KEYS))
+ .assertThatPlan(isLinux(expected));
}
@Test void testWriteSortExchangeWithHashDistribution() {
- final RelNode root = createSortPlan(RelDistributions.hash(Lists.newArrayList(0)));
- final RelJsonWriter writer = new RelJsonWriter();
- root.explain(writer);
- final String json = writer.asString();
- assertThat(json, is(XX3));
-
- final String s = deserializeAndDumpToTextFormat(getSchema(root), json);
- final String expected =
- "LogicalSortExchange(distribution=[hash[0]], collation=[[0]])\n"
- + " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ final Function<RelBuilder, RelNode> relFn = b ->
+ createSortPlan(b, RelDistributions.hash(Lists.newArrayList(0)));
+ final String expected = ""
+ + "LogicalSortExchange(distribution=[hash[0]], collation=[[0]])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ relFn(relFn)
+ .assertThatJson(is(XX3))
+ .assertThatPlan(isLinux(expected));
}
@Test void testWriteSortExchangeWithRandomDistribution() {
- final RelNode root = createSortPlan(RelDistributions.RANDOM_DISTRIBUTED);
- final RelJsonWriter writer = new RelJsonWriter();
- root.explain(writer);
- final String json = writer.asString();
- final String s = deserializeAndDumpToTextFormat(getSchema(root), json);
- final String expected =
- "LogicalSortExchange(distribution=[random], collation=[[0]])\n"
- + " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ final Function<RelBuilder, RelNode> relFn = b ->
+ createSortPlan(b, RelDistributions.RANDOM_DISTRIBUTED);
+ final String expected = ""
+ + "LogicalSortExchange(distribution=[random], collation=[[0]])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testTableModifyInsert() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- RelNode project = builder
- .scan("EMP")
- .project(builder.fields(), ImmutableList.of(), true)
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .project(b.fields(), ImmutableList.of(), true)
+ .let(b2 -> {
+ final RelNode input = b2.build();
+ final RelOptTable table = input.getInput(0).getTable();
+ final LogicalTableModify modify =
+ LogicalTableModify.create(table,
+ (Prepare.CatalogReader) table.getRelOptSchema(),
+ input,
+ TableModify.Operation.INSERT,
+ null,
+ null,
+ false);
+ return b2.push(modify);
+ })
.build();
- LogicalTableModify modify = LogicalTableModify.create(
- project.getInput(0).getTable(),
- (Prepare.CatalogReader) project.getInput(0).getTable().getRelOptSchema(),
- project,
- TableModify.Operation.INSERT,
- null,
- null,
- false);
- String relJson = RelOptUtil.dumpPlan("", modify,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(modify), relJson);
final String expected = ""
+ "LogicalTableModify(table=[[scott, EMP]], operation=[INSERT], flattened=[false])\n"
+ " LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], "
+ "COMM=[$6], DEPTNO=[$7])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testTableModifyUpdate() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- RelNode filter = builder
- .scan("EMP")
- .filter(
- builder.call(
- SqlStdOperatorTable.EQUALS,
- builder.field("JOB"),
- builder.literal("c")))
- .build();
- LogicalTableModify modify = LogicalTableModify.create(
- filter.getInput(0).getTable(),
- (Prepare.CatalogReader) filter.getInput(0).getTable().getRelOptSchema(),
- filter,
- TableModify.Operation.UPDATE,
- ImmutableList.of("ENAME"),
- ImmutableList.of(builder.literal("a")),
- false);
- String relJson = RelOptUtil.dumpPlan("", modify,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(modify), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .filter(
+ b.equals(b.field("JOB"), b.literal("c")))
+ .let(b2 -> {
+ final RelNode filter = b2.build();
+ final RelOptTable table = filter.getInput(0).getTable();
+ final LogicalTableModify modify =
+ LogicalTableModify.create(table,
+ (Prepare.CatalogReader) table.getRelOptSchema(),
+ filter,
+ TableModify.Operation.UPDATE,
+ ImmutableList.of("ENAME"),
+ ImmutableList.of(b2.literal("a")),
+ false);
+ return b2.push(modify);
+ })
+ .build();
final String expected = ""
+ "LogicalTableModify(table=[[scott, EMP]], operation=[UPDATE], updateColumnList=[[ENAME]],"
+ " sourceExpressionList=[['a']], flattened=[false])\n"
+ " LogicalFilter(condition=[=($2, 'c')])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testTableModifyDelete() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- RelNode filter = builder
- .scan("EMP")
- .filter(
- builder.call(
- SqlStdOperatorTable.EQUALS,
- builder.field("JOB"),
- builder.literal("c")))
- .build();
- LogicalTableModify modify = LogicalTableModify.create(
- filter.getInput(0).getTable(),
- (Prepare.CatalogReader) filter.getInput(0).getTable().getRelOptSchema(),
- filter,
- TableModify.Operation.DELETE,
- null,
- null,
- false);
- String relJson = RelOptUtil.dumpPlan("", modify,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(modify), relJson);
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .filter(b.equals(b.field("JOB"), b.literal("c")))
+ .let(b2 -> {
+ final RelNode filter = b2.build();
+ final RelOptTable table = filter.getInput(0).getTable();
+ LogicalTableModify modify =
+ LogicalTableModify.create(table,
+ (Prepare.CatalogReader) table.getRelOptSchema(),
+ filter,
+ TableModify.Operation.DELETE,
+ null,
+ null,
+ false);
+ return b2.push(modify);
+ })
+ .build();
final String expected = ""
+ "LogicalTableModify(table=[[scott, EMP]], operation=[DELETE], flattened=[false])\n"
+ " LogicalFilter(condition=[=($2, 'c')])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
@Test void testTableModifyMerge() {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
- RelNode deptScan = builder.scan("DEPT").build();
- RelNode empScan = builder.scan("EMP").build();
- builder.push(deptScan);
- builder.push(empScan);
- RelNode project = builder
- .join(JoinRelType.LEFT,
- builder.call(
- SqlStdOperatorTable.EQUALS,
- builder.field(2, 0, "DEPTNO"),
- builder.field(2, 1, "DEPTNO")))
- .project(
- builder.literal(0),
- builder.literal("x"),
- builder.literal("x"),
- builder.literal(0),
- builder.literal("20200501 10:00:00"),
- builder.literal(0),
- builder.literal(0),
- builder.literal(0),
- builder.literal("false"),
- builder.field(1, 0, 2),
- builder.field(1, 0, 3),
- builder.field(1, 0, 4),
- builder.field(1, 0, 5),
- builder.field(1, 0, 6),
- builder.field(1, 0, 7),
- builder.field(1, 0, 8),
- builder.field(1, 0, 9),
- builder.field(1, 0, 10),
- builder.literal("a"))
- .build();
- // for sql:
- // merge into emp using dept on emp.deptno = dept.deptno
- // when matched then update set job = 'a'
- // when not matched then insert values(0, 'x', 'x', 0, '20200501 10:00:00', 0, 0, 0, 0)
- LogicalTableModify modify = LogicalTableModify.create(
- empScan.getTable(),
- (Prepare.CatalogReader) empScan.getTable().getRelOptSchema(),
- project,
- TableModify.Operation.MERGE,
- ImmutableList.of("ENAME"),
- null,
- false);
- String relJson = RelOptUtil.dumpPlan("", modify,
- SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
- String s = deserializeAndDumpToTextFormat(getSchema(modify), relJson);
+ final Holder<RelOptTable> emp = Holder.empty();
+ final Holder<RelOptTable> dept = Holder.empty();
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("DEPT")
+ .let(b2 -> {
+ dept.set(requireNonNull(b2.peek().getTable()));
+ return b2;
+ })
+ .scan("EMP")
+ .let(b2 -> {
+ emp.set(requireNonNull(b2.peek().getTable()));
+ return b2;
+ })
+ .join(JoinRelType.LEFT,
+ b.equals(b.field(2, 0, "DEPTNO"), b.field(2, 1, "DEPTNO")))
+ .project(b.literal(0),
+ b.literal("x"),
+ b.literal("x"),
+ b.literal(0),
+ b.literal("20200501 10:00:00"),
+ b.literal(0),
+ b.literal(0),
+ b.literal(0),
+ b.literal("false"),
+ b.field(1, 0, 2),
+ b.field(1, 0, 3),
+ b.field(1, 0, 4),
+ b.field(1, 0, 5),
+ b.field(1, 0, 6),
+ b.field(1, 0, 7),
+ b.field(1, 0, 8),
+ b.field(1, 0, 9),
+ b.field(1, 0, 10),
+ b.literal("a"))
+ .let(b2 -> {
+ // For SQL:
+ // MERGE INTO emp USING dept ON emp.deptno = dept.deptno
+ // WHEN MATCHED THEN
+ // UPDATE SET job = 'a'
+ // WHEN NOT MATCHED THEN
+ // INSERT VALUES (0, 'x', 'x', 0, '20200501 10:00:00',
+ // 0, 0, 0, 0)
+ final RelNode project = b.build();
+ LogicalTableModify modify =
+ LogicalTableModify.create(emp.get(),
+ (Prepare.CatalogReader) emp.get().getRelOptSchema(),
+ project,
+ TableModify.Operation.MERGE,
+ ImmutableList.of("ENAME"),
+ null,
+ false);
+ return b2.push(modify);
+ })
+ .build();
final String expected = ""
+ "LogicalTableModify(table=[[scott, EMP]], operation=[MERGE], "
+ "updateColumnList=[[ENAME]], flattened=[false])\n"
@@ -1422,15 +1360,72 @@ class RelWriterTest {
+ " LogicalJoin(condition=[=($0, $10)], joinType=[left])\n"
+ " LogicalTableScan(table=[[scott, DEPT]])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
- assertThat(s, isLinux(expected));
+ relFn(relFn)
+ .assertThatPlan(isLinux(expected));
}
- private RelNode createSortPlan(RelDistribution distribution) {
- final FrameworkConfig config = RelBuilderTest.config().build();
- final RelBuilder builder = RelBuilder.create(config);
+ private RelNode createSortPlan(RelBuilder builder, RelDistribution distribution) {
return builder.scan("EMP")
.sortExchange(distribution,
RelCollations.of(0))
.build();
}
+
+ /** Test fixture. */
+ static class Fixture {
+ final Function<RelBuilder, RelNode> relFn;
+ final boolean distribution;
+ final SqlExplainFormat format;
+
+ Fixture(Function<RelBuilder, RelNode> relFn, boolean distribution,
+ SqlExplainFormat format) {
+ this.relFn = relFn;
+ this.distribution = distribution;
+ this.format = format;
+ }
+
+ Fixture withDistribution(boolean distribution) {
+ if (distribution == this.distribution) {
+ return this;
+ }
+ return new Fixture(relFn, distribution, format);
+ }
+
+ Fixture withFormat(SqlExplainFormat format) {
+ if (format == this.format) {
+ return this;
+ }
+ return new Fixture(relFn, distribution, format);
+ }
+
+ Fixture assertThatJson(Matcher<String> matcher) {
+ final FrameworkConfig config = RelBuilderTest.config().build();
+ final RelBuilder b = RelBuilder.create(config);
+ RelNode rel = relFn.apply(b);
+ final String relJson = RelOptUtil.dumpPlan("", rel,
+ SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
+ assertThat(relJson, matcher);
+ return this;
+ }
+
+ Fixture assertThatPlan(Matcher<String> matcher) {
+ final FrameworkConfig config = RelBuilderTest.config().build();
+ final RelBuilder b = RelBuilder.create(config);
+ RelNode rel = relFn.apply(b);
+ final String relJson = RelOptUtil.dumpPlan("", rel,
+ SqlExplainFormat.JSON, SqlExplainLevel.EXPPLAN_ATTRIBUTES);
+ final String plan;
+ if (distribution) {
+ VolcanoPlanner planner = new VolcanoPlanner();
+ planner.addRelTraitDef(RelDistributionTraitDef.INSTANCE);
+ RelOptCluster cluster =
+ RelOptCluster.create(planner, b.getRexBuilder());
+ plan = deserializeAndDump(cluster, getSchema(rel), relJson, format);
+ } else {
+ plan = deserializeAndDump(getSchema(rel), relJson, format);
+ }
+ assertThat(plan, matcher);
+ return this;
+ }
+ }
}