docs: public static List<Integer> getJoinOrder(JoinGraph graph)
      {
          ImmutableList.Builder<PlanNode> joinOrder = ImmutableList.builder();
  
          Map<PlanNodeId, Integer> priorities = new HashMap<>();
          for (int i = 0; i < graph.size(); i++) {
              priorities.put(graph.getNode(i).getId(), i);
          }
  
          PriorityQueue<PlanNode> nodesToVisit = new PriorityQueue<>(
                  graph.size(),
                  comparing(node -> priorities.get(node.getId())));
          Set<PlanNode> visited = new HashSet<>();
  
          nodesToVisit.add(graph.getNode(0));
  
          while (!nodesToVisit.isEmpty()) {
              PlanNode node = nodesToVisit.poll();
              if (!visited.contains(node)) {
                  visited.add(node);
                  joinOrder.add(node);
                  for (JoinGraph.Edge edge : graph.getEdges(node)) {
                      nodesToVisit.add(edge.getTargetNode());
                  }
              }
  
              if (nodesToVisit.isEmpty() && visited.size() < graph.size()) {
                  // disconnected graph, find new starting point
                  Optional<PlanNode> firstNotVisitedNode = graph.getNodes().stream()
                          .filter(graphNode -> !visited.contains(graphNode))
                          .findFirst();
                  if (firstNotVisitedNode.isPresent()) {
                      nodesToVisit.add(firstNotVisitedNode.get());
                  }
              }
          }
  
          checkState(visited.size() == graph.size());
          return joinOrder.build().stream()
                  .map(node -> priorities.get(node.getId()))
                  .collect(toImmutableList());
      }