docs: @Override
      protected AbstractPlanNode nextPlan() {
  
          // repeat (usually run once) until plans are created
          // or no more plans can be created
          while (m_plans.size() == 0) {
              // get the join order for us to make plans out of
              JoinNode joinTree = m_joinOrders.poll();
  
              // no more join orders => no more plans to generate
              if (joinTree == null) {
                  return null;
              }
  
              // Analyze join and filter conditions
              joinTree.analyzeJoinExpressions(m_parsedStmt.m_noTableSelectionList);
              // a query that is a little too quirky or complicated.
              if (!m_parsedStmt.m_noTableSelectionList.isEmpty()) {
                  throw new PlanningErrorException("Join with filters that do not depend on joined tables is not supported in VoltDB");
              }
  
              if ( ! m_partitioning.wasSpecifiedAsSingle()) {
                  // Now that analyzeJoinExpressions has done its job of properly categorizing
                  // and placing the various filters that the HSQL parser tends to leave in the strangest
                  // configuration, this is the first opportunity to analyze WHERE and JOIN filters'
                  // effects on statement partitioning.
                  // But this causes the analysis to be run based on a particular join order.
                  // Which join orders does this analysis actually need to be run on?
                  // Can it be run on the first join order and be assumed to hold for all join orders?
                  // If there is a join order that fails to generate a single viable plan, is its
                  // determination of partitioning (or partitioning failure) always valid for other
                  // join orders, or should the analysis be repeated on a viable join order
                  // in that case?
                  // For now, analyze each join order independently and when an invalid partitioning is
                  // detected, skip the plan generation for that particular ordering.
                  // If this causes all plans to be skipped, commonly the case, the PlanAssembler
                  // should propagate an error message identifying partitioning as the problem.
                  HashMap<AbstractExpression, Set<AbstractExpression>>
                      valueEquivalence = joinTree.getAllEquivalenceFilters();
                  Collection<StmtTableScan> scans = m_parsedStmt.allScans();
                  m_partitioning.analyzeForMultiPartitionAccess(scans, valueEquivalence);
                  if ( ! m_partitioning.isJoinValid() ) {
                      // The case of more than one independent partitioned table
                      // would result in an illegal plan with more than two fragments.
                      // Don't throw a planning error here, in case the problem is just with this
                      // particular join order, but do leave a hint to the PlanAssembler in case
                      // the failure is unanimous -- a common case.
                      m_recentErrorMsg = m_partitioning.getJoinInvalidReason();
                      // This join order, at least, is not worth trying to plan.
                      continue;
                  }
              }
  
              generateMorePlansForJoinTree(joinTree);
          }
          return m_plans.poll();
      }