docs: private CompiledPlan getNextUnionPlan() {
          String isContentDeterministic = null;
          // Since only the one "best" plan is considered,
          // this method should be called only once.
          if (m_bestAndOnlyPlanWasGenerated) {
              return null;
          }
  
          m_bestAndOnlyPlanWasGenerated = true;
          // Simply return an union plan node with a corresponding union type set
          AbstractPlanNode subUnionRoot = new UnionPlanNode(m_parsedUnion.m_unionType);
          m_recentErrorMsg = null;
  
          ArrayList<CompiledPlan> childrenPlans = new ArrayList<>();
          StatementPartitioning commonPartitioning = null;
  
          // Build best plans for the children first
          int planId = 0;
          for (AbstractParsedStmt parsedChildStmt : m_parsedUnion.m_children) {
              StatementPartitioning partitioning = (StatementPartitioning)m_partitioning.clone();
              PlanSelector planSelector = (PlanSelector) m_planSelector.clone();
              planSelector.m_planId = planId;
              PlanAssembler assembler = new PlanAssembler(m_catalogDb, partitioning, planSelector, m_isLargeQuery);
              CompiledPlan bestChildPlan = assembler.getBestCostPlan(parsedChildStmt);
              partitioning = assembler.m_partitioning;
  
              // make sure we got a winner
              if (bestChildPlan == null) {
                  m_recentErrorMsg = assembler.getErrorMessage();
                  if (m_recentErrorMsg == null) {
                      m_recentErrorMsg = "Unable to plan for statement. Error unknown.";
                  }
                  return null;
              }
  
              childrenPlans.add(bestChildPlan);
              // Remember the content non-determinism message for the
              // first non-deterministic children we find.
              if (isContentDeterministic != null) {
                  isContentDeterministic = bestChildPlan.nondeterminismDetail();
              }
  
              // Make sure that next child's plans won't override current ones.
              planId = planSelector.m_planId;
  
              // Decide whether child statements' partitioning is compatible.
              if (commonPartitioning == null) {
                  commonPartitioning = partitioning;
                  continue;
              }
  
              AbstractExpression statementPartitionExpression = partitioning.singlePartitioningExpression();
              if (commonPartitioning.requiresTwoFragments()) {
                  if (partitioning.requiresTwoFragments() || statementPartitionExpression != null) {
                      // If two child statements need to use a second fragment,
                      // it can't currently be a two-fragment plan.
                      // The coordinator expects a single-table result from each partition.
                      // Also, currently the coordinator of a two-fragment plan is not allowed to
                      // target a particular partition, so neither can the union of the coordinator
                      // and a statement that wants to run single-partition.
                      throw new PlanningErrorException(
                              "Statements are too complex in set operation using multiple partitioned tables.");
                  }
                  // the new statement is apparently a replicated read and has no effect on partitioning
                  continue;
              }
  
              AbstractExpression commonPartitionExpression = commonPartitioning.singlePartitioningExpression();
              if (commonPartitionExpression == null) {
                  // the prior statement(s) were apparently replicated reads
                  // and have no effect on partitioning
                  commonPartitioning = partitioning;
                  continue;
              }
  
              if (partitioning.requiresTwoFragments()) {
                  // Again, currently the coordinator of a two-fragment plan is not allowed to
                  // target a particular partition, so neither can the union of the coordinator
                  // and a statement that wants to run single-partition.
                  throw new PlanningErrorException(
                          "Statements are too complex in set operation using multiple partitioned tables.");
              }
  
              if (statementPartitionExpression == null) {
                  // the new statement is apparently a replicated read and has no effect on partitioning
                  continue;
              }
  
              if ( ! commonPartitionExpression.equals(statementPartitionExpression)) {
                  throw new PlanningErrorException(
                          "Statements use conflicting partitioned table filters in set operation or sub-query.");
              }
          }
  
          if (commonPartitioning != null) {
              m_partitioning = commonPartitioning;
          }
  
          // need to reset plan id for the entire UNION
          m_planSelector.m_planId = planId;
  
          // Add and link children plans
          for (CompiledPlan selectPlan : childrenPlans) {
              subUnionRoot.addAndLinkChild(selectPlan.rootPlanGraph);
          }
  
          // order by
          if (m_parsedUnion.hasOrderByColumns()) {
              subUnionRoot = handleOrderBy(m_parsedUnion, subUnionRoot);
          }
  
          // limit/offset
          if (m_parsedUnion.hasLimitOrOffset()) {
              subUnionRoot = handleUnionLimitOperator(subUnionRoot);
          }
  
          CompiledPlan retval = new CompiledPlan(m_isLargeQuery);
          retval.rootPlanGraph = subUnionRoot;
          retval.setReadOnly(true);
          retval.sql = m_planSelector.m_sql;
          boolean orderIsDeterministic = m_parsedUnion.isOrderDeterministic();
          boolean hasLimitOrOffset = m_parsedUnion.hasLimitOrOffset();
          retval.statementGuaranteesDeterminism(hasLimitOrOffset, orderIsDeterministic, isContentDeterministic);
  
          // compute the cost - total of all children
          retval.cost = 0.0;
          for (CompiledPlan bestChildPlan : childrenPlans) {
              retval.cost += bestChildPlan.cost;
          }
          return retval;
      }