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);