docs: private CompiledPlan getNextInsertPlan() {
          // there's really only one way to do an insert, so just
          // do it the right way once, then return null after that
          if (m_bestAndOnlyPlanWasGenerated) {
              return null;
          }
          m_bestAndOnlyPlanWasGenerated = true;
  
          // The child of the insert node produces rows containing values
          // from one of
          //   - A VALUES clause.  In this case the child node is a MaterializeNode
          //   - a SELECT statement as in "INSERT INTO ... SELECT ...".  In this case
          //       the child node is the root of an arbitrary subplan.
  
          // figure out which table we're inserting into
          assert (m_parsedInsert.m_tableList.size() == 1);
          Table targetTable = m_parsedInsert.m_tableList.get(0);
          StmtSubqueryScan subquery = m_parsedInsert.getSubqueryScan();
          CompiledPlan retval = null;
          String isContentDeterministic = null;
          if (subquery != null) {
              isContentDeterministic = subquery.calculateContentDeterminismMessage();
              if (subquery.getBestCostPlan() == null) {
                  // Seems like this should really be caught earlier
                  // in getBestCostPlan, above.
                  throw new PlanningErrorException("INSERT INTO ... SELECT subquery could not be planned: "
                          + m_recentErrorMsg);
  
              }
  
              boolean targetIsExportTable = tableListIncludesExportOnly(m_parsedInsert.m_tableList);
              InsertSubPlanAssembler subPlanAssembler =
                      new InsertSubPlanAssembler(m_catalogDb, m_parsedInsert, m_partitioning,
                              targetIsExportTable);
              AbstractPlanNode subplan = subPlanAssembler.nextPlan();
              if (subplan == null) {
                  throw new PlanningErrorException(subPlanAssembler.m_recentErrorMsg);
              }
  
              assert(m_partitioning.isJoinValid());
  
              //  Use the subquery's plan as the basis for the insert plan.
              retval = subquery.getBestCostPlan();
          }
          else {
              retval = new CompiledPlan(m_isLargeQuery);
          }
          retval.setReadOnly(false);
  
          // Iterate over each column in the table we're inserting into:
          //   - Make sure we're supplying values for columns that require it.
          //     For a normal INSERT, these are the usual non-nullable values that
          //     don't have a default value.
          //     For an UPSERT, the (only) required values are the primary key
          //     components. Other required values can be supplied from the
          //     existing row in "UPDATE mode". If some other value is required
          //     for an INSERT, UPSERT's "INSERT mode" will throw a runtime
          //     constraint violation as the INSERT operation tries to set the
          //     non-nullable column to null.
          //   - Set partitioning expressions for VALUES (...) case.
          //     TODO: it would be good someday to do the same kind of processing
          //      for the INSERT ... SELECT ... case, by analyzing the subquery.
          if (m_parsedInsert.m_isUpsert) {
              boolean hasPrimaryKey = false;
              for (Constraint constraint : targetTable.getConstraints()) {
                  if (constraint.getType() != ConstraintType.PRIMARY_KEY.getValue()) {
                      continue;
                  }
  
                  hasPrimaryKey = true;
                  boolean targetsPrimaryKey = false;
                  for (ColumnRef colRef : constraint.getIndex().getColumns()) {
                      int primary = colRef.getColumn().getIndex();
                      for (Column targetCol : m_parsedInsert.m_columns.keySet()) {
                          if (targetCol.getIndex() == primary) {
                              targetsPrimaryKey = true;
                              break;
                          }
                      }
                      if (! targetsPrimaryKey) {
                          throw new PlanningErrorException("UPSERT on table \"" +
                                  targetTable.getTypeName() +
                                  "\" must specify a value for primary key \"" +
                                  colRef.getColumn().getTypeName() + "\".");
                      }
                  }
              }
              if (! hasPrimaryKey) {
                  throw new PlanningErrorException("UPSERT is not allowed on table \"" +
                          targetTable.getTypeName() + "\" that has no primary key.");
              }
          }
          CatalogMap<Column> targetTableColumns = targetTable.getColumns();
          for (Column col : targetTableColumns) {
              boolean needsValue = (!m_parsedInsert.m_isUpsert) &&
                      (col.getNullable() == false) && (col.getDefaulttype() == 0);
              if (needsValue && !m_parsedInsert.m_columns.containsKey(col)) {
                  // This check could be done during parsing?
                  throw new PlanningErrorException("Column " + col.getName()
                          + " has no default and is not nullable.");
              }
  
              // hint that this statement can be executed SP.
              if (col.equals(m_partitioning.getPartitionColForDML()) && subquery == null) {
                  // When AdHoc insert-into-select is supported, we'll need to be able to infer
                  // partitioning of the sub-select
                  AbstractExpression expr = m_parsedInsert.getExpressionForPartitioning(col);
                  String fullColumnName = targetTable.getTypeName() + "." + col.getTypeName();
                  m_partitioning.addPartitioningExpression(fullColumnName, expr, expr.getValueType());
              }
          }
  
          NodeSchema matSchema = null;
          if (subquery == null) {
              matSchema = new NodeSchema();
          }
  
          int[] fieldMap = new int[m_parsedInsert.m_columns.size()];
          int i = 0;
  
          // The insert statement's set of columns are contained in a LinkedHashMap,
          // meaning that we'll iterate over the columns here in the order that the user
          // specified them in the original SQL.  (If the statement didn't specify any
          // columns, then all the columns will be in the map in schema order.)
          //   - Build the field map, used by insert executor to build tuple to execute
          //   - For VALUES(...) insert statements, build the materialize node's schema
          for (Map.Entry<Column, AbstractExpression> e : m_parsedInsert.m_columns.entrySet()) {
              Column col = e.getKey();
              fieldMap[i] = col.getIndex();
  
              if (matSchema != null) {
                  AbstractExpression valExpr = e.getValue();
                  valExpr.setInBytes(col.getInbytes());
  
                  // Patch over any mismatched expressions with an explicit cast.
                  // Most impossible-to-cast type combinations should have already been caught by the
                  // parser, but there are also runtime checks in the casting code
                  // -- such as for out of range values.
                  valExpr = castExprIfNeeded(valExpr, col);
  
                  matSchema.addColumn(
                          AbstractParsedStmt.TEMP_TABLE_NAME,
                          AbstractParsedStmt.TEMP_TABLE_NAME,
                          col.getTypeName(), col.getTypeName(),
                          valExpr);
              }
  
              i++;
          }
  
          // the root of the insert plan may be an InsertPlanNode, or
          // it may be a scan plan node.  We may do an inline InsertPlanNode
          // as well.  All inlining of insert nodes will be done later,
          // in a microoptimzation.  We can't do it here, since we
          // may need to remove uneeded projection nodes.
          InsertPlanNode insertNode = new InsertPlanNode(m_parsedInsert.m_isUpsert);
          insertNode.setTargetTableName(targetTable.getTypeName());
          if (subquery != null) {
              insertNode.setSourceIsPartitioned(! subquery.getIsReplicated());
          }
  
          // The field map tells the insert node
          // where to put values produced by child into the row to be inserted.
          insertNode.setFieldMap(fieldMap);
  
          if (matSchema != null) {
              MaterializePlanNode matNode =
                      new MaterializePlanNode(matSchema);
              // connect the insert and the materialize nodes together
              insertNode.addAndLinkChild(matNode);
              retval.statementGuaranteesDeterminism(false, true, isContentDeterministic);
          }
          else {
              insertNode.addAndLinkChild(retval.rootPlanGraph);
          }
  
          if (m_partitioning.wasSpecifiedAsSingle() || m_partitioning.isInferredSingle()) {
              insertNode.setMultiPartition(false);
              retval.rootPlanGraph = insertNode;
              return retval;
          }
  
          insertNode.setMultiPartition(true);
          // Add a compensating sum of modified tuple counts or a limit 1
          // AND a send on top of a union-like receive node.
          boolean isReplicated = targetTable.getIsreplicated();
          retval.rootPlanGraph = addCoordinatorToDMLNode(insertNode, isReplicated);
          return retval;
      }