/** Creates new DeadlockGraph from plan and checks for cycles */
  public boolean hasDeadlock(List<? extends PlanNode> sinks) {
    this.g = new DeadlockGraph();

    for (PlanNode s : sinks) {
      s.accept(this);
    }

    if (g.hasCycle()) {
      return true;
    } else {
      return false;
    }
  }
  public void resolveDeadlocks(List<? extends PlanNode> sinks) {

    for (PlanNode s : sinks) {
      s.accept(this);
    }
    if (g.hasCycle()) {

      // in the remaining plan is a cycle
      for (DeadlockVertex v : g.vertices) {

        // first strategy to fix -> swap build and probe side
        if (v.getOriginal().getDriverStrategy().equals(DriverStrategy.HYBRIDHASH_BUILD_FIRST)) {

          v.getOriginal().setDriverStrategy(DriverStrategy.HYBRIDHASH_BUILD_SECOND);

          if (hasDeadlock(sinks)) {
            // Didn't fix anything -> revert
            v.getOriginal().setDriverStrategy(DriverStrategy.HYBRIDHASH_BUILD_FIRST);
          } else {
            // deadlock resolved
            break;
          }
        }

        // other direction
        if (v.getOriginal().getDriverStrategy().equals(DriverStrategy.HYBRIDHASH_BUILD_SECOND)) {

          v.getOriginal().setDriverStrategy(DriverStrategy.HYBRIDHASH_BUILD_FIRST);

          if (hasDeadlock(sinks)) {
            // Didn't fix anything -> revert
            v.getOriginal().setDriverStrategy(DriverStrategy.HYBRIDHASH_BUILD_SECOND);
          } else {
            // deadlock resolved
            break;
          }
        }
      }

      // switching build and probe side did not help -> pipeline breaker
      for (DeadlockVertex v : g.vertices) {
        if (v.getOriginal() instanceof DualInputPlanNode) {
          DualInputPlanNode n = (DualInputPlanNode) v.getOriginal();

          // what is the pipelined side? (other side should be a dam, otherwise operator could not
          // be source of deadlock)
          if (!(n.getDriverStrategy().firstDam().equals(DamBehavior.FULL_DAM)
                  || n.getInput1().getLocalStrategy().dams()
                  || n.getInput1().getTempMode().breaksPipeline())
              && (n.getDriverStrategy().secondDam().equals(DamBehavior.FULL_DAM)
                  || n.getInput2().getLocalStrategy().dams()
                  || n.getInput2().getTempMode().breaksPipeline())) {
            n.getInput1().setTempMode(n.getInput1().getTempMode().makePipelineBreaker());
          } else if (!(n.getDriverStrategy().secondDam().equals(DamBehavior.FULL_DAM)
                  || n.getInput2().getLocalStrategy().dams()
                  || n.getInput2().getTempMode().breaksPipeline())
              && (n.getDriverStrategy().firstDam().equals(DamBehavior.FULL_DAM)
                  || n.getInput1().getLocalStrategy().dams()
                  || n.getInput1().getTempMode().breaksPipeline())) {
            n.getInput2().setTempMode(n.getInput2().getTempMode().makePipelineBreaker());
          }

          // Deadlock resolved?
          if (!hasDeadlock(sinks)) {
            break;
          }
        }
      }
    }
  }