/**
   * read the page from disk into this CachedPage object.
   *
   * <p>A page is read in from disk into the pageData array of this object, and then put in the
   * cache.
   *
   * <p>
   *
   * @param myContainer the container to read the page from.
   * @param newIdentity indentity (ie. page number) of the page to read
   * @exception StandardException Standard exception policy.
   */
  private void readPage(FileContainer myContainer, PageKey newIdentity) throws StandardException {
    int pagesize = myContainer.getPageSize();

    // we will reuse the existing page array if it is same size, the
    // cache does support caching various sized pages.
    setPageArray(pagesize);

    for (int io_retry_count = 0; ; ) {
      try {
        myContainer.readPage(newIdentity.getPageNumber(), pageData);
        break;
      } catch (IOException ioe) {
        io_retry_count++;

        // Retrying read I/O's has been found to be successful sometimes
        // in completing the read without having to fail the calling
        // query, and in some cases avoiding complete db shutdown.
        // Some situations are:
        //     spurious interrupts being sent to thread by clients.
        //     unreliable hardware like a network mounted file system.
        //
        // The only option other than retrying is to fail the I/O
        // immediately and throwing an error, thus performance cost
        // not really a consideration.
        //
        // The retry max of 4 is arbitrary, but has been enough that
        // not many read I/O errors have been reported.
        if (io_retry_count > 4) {
          // page cannot be physically read

          StandardException se =
              StandardException.newException(
                  SQLState.FILE_READ_PAGE_EXCEPTION, ioe, newIdentity, new Integer(pagesize));

          if (dataFactory.getLogFactory().inRFR()) {
            // if in rollforward recovery, it is possible that this
            // page actually does not exist on the disk yet because
            // the log record we are proccessing now is actually
            // creating the page, we will recreate the page if we
            // are in rollforward recovery, so just throw the
            // exception.
            throw se;
          } else {
            if (SanityManager.DEBUG) {
              // by shutting down system in debug mode, maybe
              // we can catch root cause of the interrupt.
              throw dataFactory.markCorrupt(se);
            } else {
              // No need to shut down runtime database on read
              // error in delivered system, throwing exception
              // should be enough.  Thrown exception has nested
              // IO exception which is root cause of error.
              throw se;
            }
          }
        }
      }
    }
  }
  /**
   * Find the container and then read the page from that container.
   *
   * <p>This is the way new pages enter the page cache.
   *
   * <p>
   *
   * @return always true, higher levels have already checked the page number is valid for an open.
   * @exception StandardException Standard Derby policy.
   * @see Cacheable#setIdentity
   */
  public Cacheable setIdentity(Object key) throws StandardException {
    if (SanityManager.DEBUG) {
      SanityManager.ASSERT(key instanceof PageKey);
    }

    initialize();

    PageKey newIdentity = (PageKey) key;

    FileContainer myContainer = (FileContainer) containerCache.find(newIdentity.getContainerId());

    setContainerRowCount(myContainer.getEstimatedRowCount(0));

    try {
      if (!alreadyReadPage) {
        // Fill in the pageData array by reading bytes from disk.
        readPage(myContainer, newIdentity);
      } else {
        // pageData array already filled
        alreadyReadPage = false;
      }

      // if the formatID on disk is not the same as this page instance's
      // format id, instantiate the real page object
      int fmtId = getTypeFormatId();

      int onPageFormatId = FormatIdUtil.readFormatIdInteger(pageData);
      if (fmtId != onPageFormatId) {
        return changeInstanceTo(onPageFormatId, newIdentity).setIdentity(key);
      }

      // this is the correct page instance
      initFromData(myContainer, newIdentity);
    } finally {
      containerCache.release(myContainer);
      myContainer = null;
    }

    fillInIdentity(newIdentity);

    initialRowCount = 0;

    return this;
  }
  /**
   * write the page from this CachedPage object to disk.
   *
   * <p>
   *
   * @param identity indentity (ie. page number) of the page to read
   * @param syncMe does the write of this single page have to be sync'd?
   * @exception StandardException Standard exception policy.
   */
  private void writePage(PageKey identity, boolean syncMe) throws StandardException {

    // make subclass write the page format
    writeFormatId(identity);

    // let subclass have a chance to write any cached data to page data
    // array
    writePage(identity);

    // force WAL - and check to see if database is corrupt or is frozen.
    // last log Instant may be null if the page is being forced
    // to disk on a createPage (which violates the WAL protocol actually).
    // See FileContainer.newPage
    LogInstant flushLogTo = getLastLogInstant();
    dataFactory.flush(flushLogTo);

    if (flushLogTo != null) {
      clearLastLogInstant();
    }

    // find the container and file access object
    FileContainer myContainer = (FileContainer) containerCache.find(identity.getContainerId());

    if (myContainer == null) {
      StandardException nested =
          StandardException.newException(
              SQLState.DATA_CONTAINER_VANISHED, identity.getContainerId());
      throw dataFactory.markCorrupt(
          StandardException.newException(SQLState.FILE_WRITE_PAGE_EXCEPTION, nested, identity));
    }

    try {
      myContainer.writePage(identity.getPageNumber(), pageData, syncMe);

      //
      // Do some in memory unlogged bookkeeping tasks while we have
      // the container.
      //

      if (!isOverflowPage() && isDirty()) {

        // let the container knows whether this page is a not
        // filled, non-overflow page
        myContainer.trackUnfilledPage(identity.getPageNumber(), unfilled());

        // if this is not an overflow page, see if the page's row
        // count has changed since it come into the cache.
        //
        // if the page is not invalid, row count is 0.	Otherwise,
        // count non-deleted records on page.
        //
        // Cannot call nonDeletedRecordCount because the page is
        // unlatched now even though nobody is changing it
        int currentRowCount = internalNonDeletedRecordCount();

        if (currentRowCount != initialRowCount) {
          myContainer.updateEstimatedRowCount(currentRowCount - initialRowCount);

          setContainerRowCount(myContainer.getEstimatedRowCount(0));

          initialRowCount = currentRowCount;
        }
      }

    } catch (IOException ioe) {
      // page cannot be written
      throw StandardException.newException(SQLState.FILE_WRITE_PAGE_EXCEPTION, ioe, identity);
    } finally {
      containerCache.release(myContainer);
      myContainer = null;
    }

    synchronized (this) {
      // change page state to not dirty after the successful write
      isDirty = false;
      preDirty = false;
    }
  }