gortiz commented on code in PR #13733:
URL: https://github.com/apache/pinot/pull/13733#discussion_r1771374971


##########
pinot-query-planner/src/main/java/org/apache/pinot/query/planner/explain/PlanNodeSorter.java:
##########
@@ -0,0 +1,229 @@
+/**
+ * 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.pinot.query.planner.explain;
+
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeSet;
+import org.apache.pinot.common.proto.Plan;
+import org.apache.pinot.query.planner.plannode.AggregateNode;
+import org.apache.pinot.query.planner.plannode.ExchangeNode;
+import org.apache.pinot.query.planner.plannode.ExplainedNode;
+import org.apache.pinot.query.planner.plannode.FilterNode;
+import org.apache.pinot.query.planner.plannode.JoinNode;
+import org.apache.pinot.query.planner.plannode.MailboxReceiveNode;
+import org.apache.pinot.query.planner.plannode.MailboxSendNode;
+import org.apache.pinot.query.planner.plannode.PlanNode;
+import org.apache.pinot.query.planner.plannode.PlanNodeVisitor;
+import org.apache.pinot.query.planner.plannode.ProjectNode;
+import org.apache.pinot.query.planner.plannode.SetOpNode;
+import org.apache.pinot.query.planner.plannode.SortNode;
+import org.apache.pinot.query.planner.plannode.TableScanNode;
+import org.apache.pinot.query.planner.plannode.ValueNode;
+import org.apache.pinot.query.planner.plannode.WindowNode;
+
+
+/**
+ * A utility class used to sort the plan nodes in a deterministic order.
+ *
+ * Any comparator can be passed to the sort method to sort the plan nodes, 
although the default comparator
+ * is used to sort the plan nodes based on the type and the attributes of the 
node.
+ *
+ * Only nodes that are simplifiable will be sorted. See {@link 
ExplainNodeSimplifier} for more information.
+ */
+public class PlanNodeSorter {
+
+  private PlanNodeSorter() {
+  }
+
+  /**
+   * Applies a default comparator to sort the plan nodes.
+   *
+   * The result may be the same as the input if the plan nodes are already 
sorted.
+   */
+  public static PlanNode sort(PlanNode planNode) {
+    return planNode.visit(new Sorter(), DefaultComparator.INSTANCE);
+  }
+
+  public static PlanNode sort(PlanNode planNode, Comparator<PlanNode> 
comparator) {
+    return planNode.visit(Sorter.INSTANCE, comparator);
+  }
+
+  private static class Sorter implements PlanNodeVisitor<PlanNode, 
Comparator<PlanNode>> {
+    private static final Sorter INSTANCE = new Sorter();
+
+    private PlanNode defaultNode(PlanNode node, Comparator<PlanNode> 
comparator) {
+      List<PlanNode> inputs = node.getInputs();
+      List<PlanNode> newInputs = applyToChildren(inputs, comparator);
+      return inputs != newInputs ? node.withInputs(newInputs) : node;
+    }
+
+    @Override
+    public PlanNode visitAggregate(AggregateNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitFilter(FilterNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitJoin(JoinNode node, Comparator<PlanNode> comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitMailboxReceive(MailboxReceiveNode node, 
Comparator<PlanNode> comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitMailboxSend(MailboxSendNode node, 
Comparator<PlanNode> comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitProject(ProjectNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitSort(SortNode node, Comparator<PlanNode> comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitTableScan(TableScanNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitValue(ValueNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitWindow(WindowNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitSetOp(SetOpNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitExchange(ExchangeNode node, Comparator<PlanNode> 
comparator) {
+      return defaultNode(node, comparator);
+    }
+
+    @Override
+    public PlanNode visitExplained(ExplainedNode node, Comparator<PlanNode> 
comparator) {
+      if (!node.getTitle().contains(ExplainNodeSimplifier.COMBINE) || 
node.getInputs().size() <= 1) {
+        return defaultNode(node, comparator);
+      }
+      List<PlanNode> simplifiedChildren = new 
ArrayList<>(applyToChildren(node.getInputs(), comparator));
+      simplifiedChildren.sort(comparator);
+      if (simplifiedChildren.equals(node.getInputs())) {
+        return node;
+      }
+      return node.withInputs(simplifiedChildren);
+    }
+
+    private List<PlanNode> applyToChildren(List<PlanNode> children, 
Comparator<PlanNode> comparator) {
+      List<PlanNode> simplifiedChildren = null;
+      for (int i = 0; i < children.size(); i++) {
+        PlanNode child = children.get(i);
+        PlanNode newChild = child.visit(this, comparator);
+        if (child != newChild) {
+          if (simplifiedChildren == null) {
+            simplifiedChildren = new ArrayList<>(children);
+          }
+          simplifiedChildren.set(i, newChild);
+        }
+      }
+      return simplifiedChildren != null ? simplifiedChildren : children;
+    }
+  }
+
+  public static class DefaultComparator implements Comparator<PlanNode> {
+    public static final DefaultComparator INSTANCE = new DefaultComparator();
+
+    private DefaultComparator() {
+    }
+
+    @Override
+    public int compare(PlanNode o1, PlanNode o2) {
+      if (!(o1 instanceof ExplainedNode) || !(o2 instanceof ExplainedNode)) {
+        return 
o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
+      }
+      ExplainedNode node1 = (ExplainedNode) o1;
+      ExplainedNode node2 = (ExplainedNode) o2;
+
+      int cmp = node1.getTitle().compareTo(node2.getTitle());
+      if (cmp != 0) {
+        return cmp;
+      }
+      TreeSet<String> allKeys = new TreeSet<>(Sets.union(
+          node1.getAttributes().keySet(), node2.getAttributes().keySet()));
+      for (String key : allKeys) {
+        Plan.ExplainNode.AttributeValue value1 = 
node1.getAttributes().get(key);
+        Plan.ExplainNode.AttributeValue value2 = 
node2.getAttributes().get(key);
+        if (value1 == null) {
+          // there is an attribute in node2 with a _smaller_ key that is not 
in node1
+          return -1;
+        }
+        if (value2 == null) {
+          // there is an attribute in node1 with a _smaller_ key that is not 
in node2
+          return 1;
+        }
+        int cmp2 = value1.getValueCase().compareTo(value2.getValueCase());
+        if (cmp2 != 0) {
+          return cmp2;
+        }
+        int cmp3;
+        switch (value1.getValueCase()) {

Review Comment:
   Changed in  
https://github.com/apache/pinot/pull/13733/commits/272263125a022bf5419ca85c71d766f6f976a83a



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to