/**
   * Check and purge committed deleted rows on a page.
   *
   * <p>
   *
   * @return true, if no purging has been done on page, and thus latch can be released before end
   *     transaction. Otherwise the latch on the page can not be released before commit.
   * @param page A non-null, latched page must be passed in. If all rows on page are purged, then
   *     page will be removed and latch released.
   * @exception StandardException Standard exception policy.
   */
  protected final boolean purgeCommittedDeletes(Page page) throws StandardException {
    boolean purgingDone = false;

    // The number records that can be reclaimed is:
    // total recs - recs_not_deleted
    int num_possible_commit_delete = page.recordCount() - page.nonDeletedRecordCount();

    if (num_possible_commit_delete > 0) {
      // loop backward so that purges which affect the slot table
      // don't affect the loop (ie. they only move records we
      // have already looked at).
      for (int slot_no = page.recordCount() - 1; slot_no >= 0; slot_no--) {
        boolean row_is_committed_delete = page.isDeletedAtSlot(slot_no);

        if (row_is_committed_delete) {
          // At this point we only know that the row is
          // deleted, not whether it is committed.

          // see if we can purge the row, by getting an
          // exclusive lock on the row.  If it is marked
          // deleted and we can get this lock, then it
          // must be a committed delete and we can purge
          // it.

          RecordHandle rh =
              page.fetchFromSlot(
                  (RecordHandle) null,
                  slot_no,
                  RowUtil.EMPTY_ROW,
                  RowUtil.EMPTY_ROW_FETCH_DESCRIPTOR,
                  true);

          row_is_committed_delete = this.lockRowAtSlotNoWaitExclusive(rh);

          if (row_is_committed_delete) {
            purgingDone = true;

            page.purgeAtSlot(slot_no, 1, false);
          }
        }
      }
    }
    if (page.recordCount() == 0) {

      // Deallocate the current page with 0 rows on it.
      this.removePage(page);

      // removePage guarantees to unlatch the page even if an
      // exception is thrown. The page is protected against reuse
      // because removePage locks it with a dealloc lock, so it
      // is OK to release the latch even after a purgeAtSlot is
      // called.
      // @see ContainerHandle#removePage

      purgingDone = true;
    }

    return (purgingDone);
  }
  protected long load(
      TransactionManager xact_manager,
      Heap heap,
      boolean createConglom,
      RowLocationRetRowSource rowSource)
      throws StandardException {
    long num_rows_loaded = 0;

    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(
          open_conglom == null, "load expects container handle to be closed on entry.");
    }

    // The individual rows that are inserted are not logged.  To use a
    // logged interface, use insert.  RESOLVE: do we want to allow client
    // to use the load interface even for logged insert?
    int mode = (ContainerHandle.MODE_FORUPDATE | ContainerHandle.MODE_UNLOGGED);

    // If the container is being created in the same operation, don't log
    // page allocation.
    if (createConglom) mode |= ContainerHandle.MODE_CREATE_UNLOGGED;

    OpenConglomerate open_conglom = new OpenHeap();

    if (open_conglom.init(
            (ContainerHandle) null,
            heap,
            heap.format_ids,
            heap.collation_ids,
            xact_manager,
            xact_manager.getRawStoreXact(),
            false,
            mode,
            TransactionController.MODE_TABLE,
            xact_manager
                .getRawStoreXact()
                .newLockingPolicy(
                    LockingPolicy.MODE_CONTAINER,
                    TransactionController.ISOLATION_SERIALIZABLE,
                    true),
            (DynamicCompiledOpenConglomInfo) null)
        == null) {
      throw StandardException.newException(
          SQLState.HEAP_CONTAINER_NOT_FOUND, new Long(heap.getId().getContainerId()));
    }

    this.init(open_conglom);

    // For bulk loading, we always use only brand new page because the row
    // insertion itself is not logged.  We cannot pollute pages with
    // pre-existing data with unlogged rows because nobody is going to wipe
    // out these rows if the transaction rolls back.  We are counting on
    // the allocation page rollback to obliterate these rows if the
    // transaction fails, or, in the CREAT_UNLOGGED case, the whole
    // container to be removed.

    Page page = open_conglom.getContainer().addPage();

    boolean callbackWithRowLocation = rowSource.needsRowLocation();
    RecordHandle rh;
    HeapRowLocation rowlocation;

    if (callbackWithRowLocation) rowlocation = new HeapRowLocation();
    else rowlocation = null;

    FormatableBitSet validColumns = rowSource.getValidColumns();

    try {
      // get the next row and its valid columns from the rowSource
      DataValueDescriptor[] row;
      while ((row = rowSource.getNextRowFromRowSource()) != null) {
        num_rows_loaded++;

        if (SanityManager.DEBUG) {
          // Make sure valid columns are in the list.  The RowUtil
          // call is too expensive to make in a released system for
          // every insert.
          int invalidColumn = RowUtil.columnOutOfRange(row, validColumns, heap.format_ids.length);

          if (invalidColumn >= 0) {
            throw (StandardException.newException(
                SQLState.HEAP_TEMPLATE_MISMATCH,
                new Long(invalidColumn),
                new Long(heap.format_ids.length)));
          }
        }

        // Insert it onto this page as long as it can fit more rows.
        if ((rh =
                page.insert(
                    row,
                    validColumns,
                    Page.INSERT_DEFAULT,
                    AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD))
            == null) {
          // Insert faied, row did not fit.  Get a new page.

          page.unlatch();
          page = null;

          page = open_conglom.getContainer().addPage();

          // RESOLVE (mikem) - no long rows yet so the following code
          // will get an exception from the raw store for a row that
          // does not fit on a page.
          //
          // Multi-thread considerations aside, the raw store will
          // guarantee that any size row will fit on an empty page.
          rh =
              page.insert(
                  row,
                  validColumns,
                  Page.INSERT_OVERFLOW,
                  AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
        }

        // Else, the row fit.  If we are expected to call back with the
        // row location, do so.  All the while keep the page latched
        // and go for the next row.
        if (callbackWithRowLocation) {
          rowlocation.setFrom(rh);
          rowSource.rowLocation(rowlocation);
        }
      }
      page.unlatch();
      page = null;

      // Done with the container, now we need to flush it to disk since
      // it is unlogged.
      if (!heap.isTemporary()) open_conglom.getContainer().flushContainer();
    } finally {
      // If an error happened here, don't bother flushing the
      // container since the changes should be rolled back anyhow.
      close();
    }
    return (num_rows_loaded);
  }
  /**
   * Insert a new row into the heap.
   *
   * <p>Overflow policy: The current heap access method implements an algorithm that optimizes for
   * fetch efficiency vs. space efficiency. A row will not be over flowed unless it is bigger than a
   * page. If it is bigger than a page then it's initial part will be placed on a page and then
   * subsequent parts will be overflowed to other pages.
   *
   * <p>
   *
   * @return The record handle of the inserted row.
   * @param row The row to insert.
   * @exception StandardException Standard exception policy.
   */
  private RecordHandle doInsert(DataValueDescriptor[] row) throws StandardException {
    Page page = null;
    byte insert_mode;

    RecordHandle rh;

    if (SanityManager.DEBUG) {
      Heap heap = (Heap) open_conglom.getConglomerate();
      // Make sure valid columns are in the list.  The RowUtil
      // call is too expensive to make in a released system for
      // every insert.

      int invalidColumn = RowUtil.columnOutOfRange(row, null, heap.format_ids.length);

      if (invalidColumn >= 0) {
        throw (StandardException.newException(
            SQLState.HEAP_TEMPLATE_MISMATCH,
            new Long(invalidColumn),
            new Long(heap.format_ids.length)));
      }
    }

    // Get the last page that was returned for insert or the last page
    // that was allocated.
    page = open_conglom.getContainer().getPageForInsert(0);

    if (page != null) {

      // if there are 0 rows on the page allow the insert to overflow.
      insert_mode = (page.recordCount() == 0) ? Page.INSERT_OVERFLOW : Page.INSERT_DEFAULT;

      // Check to see if there is enough space on the page
      // for the row.
      rh = page.insert(row, null, insert_mode, AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
      page.unlatch();
      page = null;

      // If we have found a page with enough space for the row,
      // insert it and release exclusive access to the page.
      if (rh != null) {
        return rh;
      }
    }

    // If the last inserted page is now full, or RawStore have
    // forgotten what it was, or the row cannot fit on the last
    // inserted page, try to have rawStore get a relatively unfilled
    // page.

    page = open_conglom.getContainer().getPageForInsert(ContainerHandle.GET_PAGE_UNFILLED);

    if (page != null) {
      // Do the insert all over again hoping that it will fit into
      // this page, and if not, allocate a new page.

      // if there are 0 rows on the page allow the insert to overflow.
      insert_mode = (page.recordCount() == 0) ? Page.INSERT_OVERFLOW : Page.INSERT_DEFAULT;

      rh = page.insert(row, null, insert_mode, AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);

      page.unlatch();
      page = null;

      // If we have found a page with enough space for the row,
      // insert it and release exclusive access to the page.
      if (rh != null) {
        return rh;
      }
    }

    page = open_conglom.getContainer().addPage();

    // At this point with long rows the raw store will guarantee
    // that any size row will fit on an empty page.

    rh = page.insert(row, null, Page.INSERT_OVERFLOW, AccessFactoryGlobals.HEAP_OVERFLOW_THRESHOLD);
    page.unlatch();
    page = null;

    if (SanityManager.DEBUG) {
      // a null will only be returned if this page is not empty
      SanityManager.ASSERT(rh != null);
    }

    return rh;
  }