/**
   * Extract out and return the subquery, with a PRN on top. (See FromSubquery.preprocess() for more
   * details.)
   *
   * @param numTables The number of tables in the DML Statement
   * @return ResultSetNode at top of extracted tree.
   * @exception StandardException Thrown on error
   */
  public ResultSetNode extractSubquery(int numTables) throws StandardException {
    JBitSet newJBS;
    ResultSetNode newPRN;

    newPRN =
        (ResultSetNode)
            getNodeFactory()
                .getNode(
                    C_NodeTypes.PROJECT_RESTRICT_NODE,
                    subquery, /* Child ResultSet */
                    resultColumns, /* Projection */
                    null, /* Restriction */
                    null, /* Restriction as PredicateList */
                    null, /* Subquerys in Projection */
                    null, /* Subquerys in Restriction */
                    tableProperties,
                    getContextManager());

    /* Set up the PRN's referencedTableMap */
    newJBS = new JBitSet(numTables);
    newJBS.set(tableNumber);
    newPRN.setReferencedTableMap(newJBS);
    ((FromTable) newPRN).setTableNumber(tableNumber);

    return newPRN;
  }
  /**
   * This overload variant of optimizeStatement is used by subclass CursorNode (as well as a minion
   * for the no-arg variant).
   *
   * @param offset Any OFFSET row count, or null
   * @param fetchFirst Any FETCH FIRST row count or null
   * @exception StandardException Thrown on error
   * @see DMLStatementNode#optimizeStatement()
   */
  protected void optimizeStatement(ValueNode offset, ValueNode fetchFirst)
      throws StandardException {
    resultSet = resultSet.preprocess(getCompilerContext().getNumTables(), null, (FromList) null);
    resultSet = resultSet.optimize(getDataDictionary(), null, 1.0d);

    resultSet = resultSet.modifyAccessPaths();

    // Any OFFSET/FETCH FIRST narrowing must be done *after* any rewrite of
    // the query tree (if not, underlying GROUP BY fails), but *before* the
    // final scroll insensitive result node set is added - that one needs
    // to sit on top - so now is the time.
    //
    // This example statement fails if we wrap *before* the optimization
    // above:
    //     select max(a) from t1 group by b fetch first row only
    //
    // A java.sql.ResultSet#previous on a scrollable result set will fail
    // if we don't wrap *after* the ScrollInsensitiveResultSetNode below.
    //
    // We need only wrap the RowCountNode set if at least one of the
    // clauses is present.

    if (offset != null || fetchFirst != null) {
      resultSet = wrapRowCountNode(resultSet, offset, fetchFirst);
    }

    /* If this is a cursor, then we
     * need to generate a new ResultSetNode to enable the scrolling
     * on top of the tree before modifying the access paths.
     */
    if (this instanceof CursorNode) {
      ResultColumnList siRCList;
      ResultColumnList childRCList;
      ResultSetNode siChild = resultSet;

      /* We get a shallow copy of the ResultColumnList and its
       * ResultColumns.  (Copy maintains ResultColumn.expression for now.)
       */
      siRCList = resultSet.getResultColumns();
      childRCList = siRCList.copyListAndObjects();
      resultSet.setResultColumns(childRCList);

      /* Replace ResultColumn.expression with new VirtualColumnNodes
       * in the ScrollInsensitiveResultSetNode's ResultColumnList.  (VirtualColumnNodes include
       * pointers to source ResultSetNode, this, and source ResultColumn.)
       */
      siRCList.genVirtualColumnNodes(resultSet, childRCList);

      /* Finally, we create the new ScrollInsensitiveResultSetNode */
      resultSet =
          (ResultSetNode)
              getNodeFactory()
                  .getNode(
                      C_NodeTypes.SCROLL_INSENSITIVE_RESULT_SET_NODE,
                      resultSet,
                      siRCList,
                      null,
                      getContextManager());
      // Propagate the referenced table map if it's already been created
      if (siChild.getReferencedTableMap() != null) {
        resultSet.setReferencedTableMap((JBitSet) siChild.getReferencedTableMap().clone());
      }
    }
  }