You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by xi...@apache.org on 2022/04/12 07:44:38 UTC
[iotdb] 01/01: complete basic PlanGraphPrinter
This is an automated email from the ASF dual-hosted git repository.
xingtanzjr pushed a commit to branch xingtanzjr/graph_visitor
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 9c7cef73f9cecc0efe6e4fed53b11b6e2eee01a1
Author: Jinrui.Zhang <xi...@gmail.com>
AuthorDate: Tue Apr 12 15:44:20 2022 +0800
complete basic PlanGraphPrinter
---
.../org/apache/iotdb/db/mpp/common/QueryId.java | 2 +-
.../sql/planner/plan/node/PlanGraphPrinter.java | 266 +++++++++++++++++++++
.../db/mpp/sql/planner/plan/node/PlanNode.java | 6 +
.../db/mpp/sql/plan/DistributionPlannerTest.java | 2 +
4 files changed, 275 insertions(+), 1 deletion(-)
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/common/QueryId.java b/server/src/main/java/org/apache/iotdb/db/mpp/common/QueryId.java
index 5bfeb25e26..54905e3c20 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/common/QueryId.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/common/QueryId.java
@@ -48,7 +48,7 @@ public class QueryId {
}
public PlanNodeId genPlanNodeId() {
- return new PlanNodeId(String.format("%s_%d", id, nextPlanNodeIndex++));
+ return new PlanNodeId(String.format("%d", nextPlanNodeIndex++));
}
public PlanFragmentId genPlanFragmentId() {
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanGraphPrinter.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanGraphPrinter.java
new file mode 100644
index 0000000000..7010a58836
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanGraphPrinter.java
@@ -0,0 +1,266 @@
+/*
+ * 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.iotdb.db.mpp.sql.planner.plan.node;
+
+import org.apache.commons.lang3.Validate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PlanGraphPrinter extends PlanVisitor<List<String>, PlanGraphPrinter.GraphContext> {
+
+ private static final String INDENT = " ";
+ private static final String HENG = "─";
+ private static final String SHU = "│";
+ private static final String LEFT_BOTTOM = "└";
+ private static final String RIGHT_BOTTOM = "┘";
+ private static final String LEFT_TOP = "┌";
+ private static final String RIGHT_TOP = "┐";
+ private static final String SHANG = "┴";
+ private static final String XIA = "┬";
+ private static final String CROSS = "┼";
+
+ private static final int BOX_MARGIN = 1;
+ private static final int CONNECTION_LINE_HEIGHT = 2;
+
+ @Override
+ public List<String> visitPlan(PlanNode node, GraphContext context) {
+ List<String> boxValue = new ArrayList<>();
+ boxValue.add(String.format("PlanNode-%s", node.getPlanNodeId().getId()));
+ return render(node, boxValue, context);
+ }
+
+ private List<String> render(PlanNode node, List<String> nodeBoxString, GraphContext context) {
+ Box box = new Box(nodeBoxString);
+ List<List<String>> children = new ArrayList<>();
+ for(PlanNode child : node.getChildren()) {
+ children.add(child.accept(this, context));
+ }
+ box.calculateBoxParams(children);
+
+ box.lines.add(printBoxEdge(box, true));
+ for (String valueLine : nodeBoxString) {
+ StringBuilder line = new StringBuilder();
+ for (int i = 0; i < box.lineWidth; i++) {
+ if (i < box.startPosition) {
+ line.append(INDENT);
+ continue;
+ }
+ if (i > box.endPosition) {
+ line.append(INDENT);
+ continue;
+ }
+ if (i == box.startPosition || i == box.endPosition) {
+ line.append(SHU);
+ continue;
+ }
+ if (i - box.startPosition - 1 < valueLine.length()) {
+ line.append(valueLine.charAt(i - box.startPosition - 1));
+ } else {
+ line.append(INDENT);
+ }
+ }
+ box.lines.add(line.toString());
+ }
+ box.lines.add(printBoxEdge(box, false));
+
+ if (children.size() == 0) {
+ return box.lines;
+ }
+
+ // Print Connection Line
+ if (children.size() == 1) {
+ for (int i = 0; i < CONNECTION_LINE_HEIGHT; i++) {
+ StringBuilder line = new StringBuilder();
+ for (int j = 0; j < box.lineWidth; j++) {
+ line.append(j == box.midPosition ? SHU : INDENT);
+ }
+ box.lines.add(line.toString());
+ }
+ } else {
+ Map<Integer, String> symbolMap = new HashMap<>();
+ Map<Integer, Boolean> childMidPositionMap = new HashMap<>();
+ symbolMap.put(box.midPosition, SHANG);
+ for (int i = 0; i < children.size(); i++) {
+ int childMidPosition = getChildMidPosition(children, i);
+ childMidPositionMap.put(childMidPosition, true);
+ if (childMidPosition == box.midPosition) {
+ symbolMap.put(box.midPosition, CROSS);
+ continue;
+ }
+ symbolMap.put(
+ childMidPosition, i == 0 ? LEFT_TOP : i == children.size() - 1 ? RIGHT_TOP : XIA);
+ }
+ StringBuilder line1 = new StringBuilder();
+ for (int i = 0; i < box.lineWidth; i++) {
+ if (i < getChildMidPosition(children, 0) || i > getChildMidPosition(children, children.size() - 1)) {
+ line1.append(INDENT);
+ continue;
+ }
+ line1.append(symbolMap.getOrDefault(i, HENG));
+ }
+ box.lines.add(line1.toString());
+
+ for (int row = 1; row < CONNECTION_LINE_HEIGHT; row++) {
+ StringBuilder nextLine = new StringBuilder();
+ for (int i = 0; i < box.lineWidth; i++) {
+ nextLine.append(childMidPositionMap.containsKey(i) ? SHU : INDENT);
+ }
+ box.lines.add(nextLine.toString());
+ }
+ }
+
+ for (int i = 0; i < getChildrenLineCount(children); i++) {
+ StringBuilder line = new StringBuilder();
+ for (int j = 0; j < children.size(); j++) {
+ line.append(getLine(children, j, i));
+ if (j != children.size() - 1) {
+ for (int m = 0; m < BOX_MARGIN; m++) {
+ line.append(INDENT);
+ }
+ }
+ }
+ box.lines.add(line.toString());
+ }
+ return box.lines;
+ }
+
+ private String printBoxEdge(Box box, boolean isTopEdge) {
+ StringBuilder line = new StringBuilder();
+ for (int i = 0; i < box.lineWidth; i++) {
+ if (i < box.startPosition) {
+ line.append(INDENT);
+ } else if (i > box.endPosition) {
+ line.append(INDENT);
+ } else if (i == box.startPosition) {
+ line.append(isTopEdge ? LEFT_TOP : LEFT_BOTTOM);
+ } else if (i == box.endPosition) {
+ line.append(isTopEdge ? RIGHT_TOP : RIGHT_BOTTOM);
+ } else {
+ line.append(HENG);
+ }
+ }
+ return line.toString();
+ }
+
+ private String getLine(List<List<String>> children, int child, int line) {
+ if (line < children.get(child).size()) {
+ return children.get(child).get(line);
+ }
+ return genEmptyLine(children.get(child).get(0).length());
+ }
+
+ private String genEmptyLine(int lineWidth) {
+ StringBuilder line = new StringBuilder();
+ for (int i = 0; i < lineWidth; i++) {
+ line.append(INDENT);
+ }
+ return line.toString();
+ }
+
+ private int getChildrenLineCount(List<List<String>> children) {
+ int count = 0;
+ for (List<String> child : children) {
+ count = Math.max(count, child.size());
+ }
+ return count;
+ }
+
+ private static int getChildMidPosition(List<List<String>> children, int idx) {
+ int left = 0;
+ for (int i = 0; i < idx; i++) {
+ left += children.get(i).get(0).length();
+ left += BOX_MARGIN;
+ }
+ left += children.get(idx).get(0).length() / 2;
+ return left;
+ }
+
+ private static class Box {
+ private List<String> boxString;
+ private int boxWidth;
+ private int lineWidth;
+ private List<String> lines;
+ private int startPosition;
+ private int endPosition;
+ private int midPosition;
+
+ public Box(List<String> boxString) {
+ this.boxString = boxString;
+ this.boxWidth = getBoxWidth();
+ this.lines = new ArrayList<>();
+ }
+
+ public int getBoxWidth() {
+ int width = 0;
+ for (String line : boxString) {
+ width = Math.max(width, line.length());
+ }
+ return width + 2;
+ }
+
+ public String getLine(int idx) {
+ if (idx < lines.size()) {
+ return lines.get(idx);
+ }
+ return genEmptyLine(lineWidth);
+ }
+
+ private String genEmptyLine(int lineWidth) {
+ StringBuilder line = new StringBuilder();
+ for (int i = 0; i < lineWidth; i++) {
+ line.append(INDENT);
+ }
+ return line.toString();
+ }
+
+ public void calculateBoxParams(List<List<String>> childBoxStrings) {
+ int childrenWidth = 0;
+ for (List<String> childBoxString : childBoxStrings) {
+ Validate.isTrue(childBoxString.size() > 0, "Lines of box string should be greater than 0");
+ childrenWidth +=childBoxString.get(0).length();
+ }
+ childrenWidth += childBoxStrings.size() > 1 ? (childBoxStrings.size() - 1) * BOX_MARGIN : 0;
+ this.lineWidth = Math.max(this.boxWidth, childrenWidth);
+ this.startPosition = (this.lineWidth - this.boxWidth) / 2;
+ this.endPosition = this.startPosition + this.boxWidth - 1;
+ this.midPosition = this.lineWidth / 2;
+ }
+ }
+
+ public static class GraphContext {
+
+ }
+
+ public static List<String> getGraph(PlanNode node) {
+ return node.accept(new PlanGraphPrinter(), new PlanGraphPrinter.GraphContext());
+ }
+
+ public static void print(PlanNode node) {
+ List<String> lines = getGraph(node);
+ for(String line : lines) {
+ System.out.println(line);
+ }
+ }
+}
+
+
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java
index 97075588c9..6636a5b41d 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/PlanNode.java
@@ -18,6 +18,8 @@
*/
package org.apache.iotdb.db.mpp.sql.planner.plan.node;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
import org.apache.commons.lang.Validate;
@@ -113,4 +115,8 @@ public abstract class PlanNode {
public int hashCode() {
return Objects.hash(id);
}
+
+ public List<String> getBoxString() {
+ return ImmutableList.of(String.format("PlanNode-%s", getPlanNodeId().getId()));
+ }
}
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/DistributionPlannerTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/DistributionPlannerTest.java
index 03db5bb4ff..24b194e3b2 100644
--- a/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/DistributionPlannerTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/sql/plan/DistributionPlannerTest.java
@@ -36,6 +36,7 @@ import org.apache.iotdb.db.mpp.sql.planner.DistributionPlanner;
import org.apache.iotdb.db.mpp.sql.planner.plan.DistributedQueryPlan;
import org.apache.iotdb.db.mpp.sql.planner.plan.LogicalQueryPlan;
import org.apache.iotdb.db.mpp.sql.planner.plan.SubPlan;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanGraphPrinter;
import org.apache.iotdb.db.mpp.sql.planner.plan.node.PlanNode;
import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.LimitNode;
import org.apache.iotdb.db.mpp.sql.planner.plan.node.process.TimeJoinNode;
@@ -160,6 +161,7 @@ public class DistributionPlannerTest {
new DistributionPlanner(analysis, new LogicalQueryPlan(context, root));
PlanNode rootAfterRewrite = planner.rewriteSource();
PlanNode rootWithExchange = planner.addExchangeNode(rootAfterRewrite);
+ PlanGraphPrinter.print(rootWithExchange);
SubPlan subPlan = planner.splitFragment(rootWithExchange);
assertEquals(subPlan.getChildren().size(), 2);
}