You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by hy...@apache.org on 2020/05/04 13:06:20 UTC
[calcite] branch master updated: [CALCITE-3921] Support TableModify
json serialization and deserialization (Wang Yanlin)
This is an automated email from the ASF dual-hosted git repository.
hyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new ac7d2b6 [CALCITE-3921] Support TableModify json serialization and deserialization (Wang Yanlin)
ac7d2b6 is described below
commit ac7d2b6b98334b80e2a43ffd9a79f36ea547e410
Author: yanlin-Lynn <19...@163.com>
AuthorDate: Tue Apr 14 20:45:21 2020 +0800
[CALCITE-3921] Support TableModify json serialization and deserialization (Wang Yanlin)
Close #1915
---
.../org/apache/calcite/rel/core/TableModify.java | 25 +++-
.../calcite/rel/externalize/RelEnumTypes.java | 2 +
.../calcite/rel/externalize/RelJsonReader.java | 3 +
.../calcite/rel/logical/LogicalTableModify.java | 8 ++
.../org/apache/calcite/plan/RelWriterTest.java | 149 +++++++++++++++++++++
5 files changed, 185 insertions(+), 2 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
index de625c8..162eb2d 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
@@ -23,9 +23,11 @@ import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.prepare.Prepare;
+import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.externalize.RelEnumTypes;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -122,7 +124,11 @@ public abstract class TableModify extends SingleRel {
Preconditions.checkArgument(sourceExpressionList.size()
== updateColumnList.size());
} else {
- Preconditions.checkArgument(updateColumnList == null);
+ if (operation == Operation.MERGE) {
+ Objects.requireNonNull(updateColumnList);
+ } else {
+ Preconditions.checkArgument(updateColumnList == null);
+ }
Preconditions.checkArgument(sourceExpressionList == null);
}
if (table.getRelOptSchema() != null) {
@@ -131,6 +137,21 @@ public abstract class TableModify extends SingleRel {
this.flattened = flattened;
}
+ /**
+ * Creates a TableModify by parsing serialized output.
+ */
+ protected TableModify(RelInput input) {
+ this(input.getCluster(),
+ input.getTraitSet(),
+ input.getTable("table"),
+ (Prepare.CatalogReader) input.getTable("table").getRelOptSchema(),
+ input.getInput(),
+ input.getEnum("operation", Operation.class),
+ input.getStringList("updateColumnList"),
+ input.getExpressionList("sourceExpressionList"),
+ input.getBoolean("flattened", false));
+ }
+
//~ Methods ----------------------------------------------------------------
public Prepare.CatalogReader getCatalogReader() {
@@ -220,7 +241,7 @@ public abstract class TableModify extends SingleRel {
@Override public RelWriter explainTerms(RelWriter pw) {
return super.explainTerms(pw)
.item("table", table.getQualifiedName())
- .item("operation", getOperation())
+ .item("operation", RelEnumTypes.fromEnum(getOperation()))
.itemIf("updateColumnList", updateColumnList, updateColumnList != null)
.itemIf("sourceExpressionList", sourceExpressionList,
sourceExpressionList != null)
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java
index f793efc..d386f13 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelEnumTypes.java
@@ -17,6 +17,7 @@
package org.apache.calcite.rel.externalize;
import org.apache.calcite.avatica.util.TimeUnitRange;
+import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.sql.JoinConditionType;
import org.apache.calcite.sql.JoinType;
import org.apache.calcite.sql.SqlExplain;
@@ -69,6 +70,7 @@ public abstract class RelEnumTypes {
register(enumByName, SqlSelectKeyword.class);
register(enumByName, SqlTrimFunction.Flag.class);
register(enumByName, TimeUnitRange.class);
+ register(enumByName, TableModify.Operation.class);
ENUM_BY_NAME = enumByName.build();
}
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
index ce7c37a..a4db821 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
@@ -199,6 +199,9 @@ public class RelJsonReader {
public List<RexNode> getExpressionList(String tag) {
@SuppressWarnings("unchecked")
final List<Object> jsonNodes = (List) jsonRel.get(tag);
+ if (jsonNodes == null) {
+ return null;
+ }
final List<RexNode> nodes = new ArrayList<>();
for (Object jsonNode : jsonNodes) {
nodes.add(relJson.toRex(this, jsonNode));
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java
index 122c5f1..6a84e23 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableModify.java
@@ -21,6 +21,7 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.prepare.Prepare;
+import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rex.RexNode;
@@ -47,6 +48,13 @@ public final class LogicalTableModify extends TableModify {
sourceExpressionList, flattened);
}
+ /**
+ * Creates a LogicalTableModify by parsing serialized output.
+ */
+ public LogicalTableModify(RelInput input) {
+ super(input);
+ }
+
@Deprecated // to be removed before 2.0
public LogicalTableModify(RelOptCluster cluster, RelOptTable table,
Prepare.CatalogReader schema, RelNode input, Operation operation,
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 f99753f..a564ddd 100644
--- a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
@@ -18,6 +18,7 @@ package org.apache.calcite.plan;
import org.apache.calcite.adapter.java.ReflectiveSchema;
import org.apache.calcite.avatica.util.TimeUnit;
+import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelDistribution;
import org.apache.calcite.rel.RelDistributions;
@@ -25,6 +26,7 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttleImpl;
import org.apache.calcite.rel.core.AggregateCall;
import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rel.core.TableScan;
import org.apache.calcite.rel.externalize.RelJsonReader;
import org.apache.calcite.rel.externalize.RelJsonWriter;
@@ -32,6 +34,7 @@ import org.apache.calcite.rel.logical.LogicalAggregate;
import org.apache.calcite.rel.logical.LogicalCalc;
import org.apache.calcite.rel.logical.LogicalFilter;
import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalTableModify;
import org.apache.calcite.rel.logical.LogicalTableScan;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
@@ -895,6 +898,152 @@ class RelWriterTest {
assertThat(s, 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)
+ .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));
+ }
+
+ @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 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));
+ }
+
+ @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 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));
+ }
+
+ @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 String expected = ""
+ + "LogicalTableModify(table=[[scott, EMP]], operation=[MERGE], "
+ + "updateColumnList=[[ENAME]], flattened=[false])\n"
+ + " LogicalProject($f0=[0], $f1=['x'], $f2=['x'], $f3=[0], $f4=['20200501 10:00:00'], "
+ + "$f5=[0], $f6=[0], $f7=[0], $f8=['false'], LOC=[$2], EMPNO=[$3], ENAME=[$4], JOB=[$5], "
+ + "MGR=[$6], HIREDATE=[$7], SAL=[$8], COMM=[$9], DEPTNO=[$10], $f18=['a'])\n"
+ + " LogicalJoin(condition=[=($0, $10)], joinType=[left])\n"
+ + " LogicalTableScan(table=[[scott, DEPT]])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(s, isLinux(expected));
+ }
+
private RelNode createSortPlan(RelDistribution distribution) {
final FrameworkConfig config = RelBuilderTest.config().build();
final RelBuilder builder = RelBuilder.create(config);