Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / store / raw / data / CachedPage.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java
new file mode 100644 (file)
index 0000000..68e3104
--- /dev/null
@@ -0,0 +1,914 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.store.raw.data.CachedPage\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.store.raw.data;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.store.raw.log.LogInstant;\r
+import org.apache.derby.iapi.store.raw.PageKey;\r
+\r
+import org.apache.derby.iapi.services.cache.Cacheable;\r
+import org.apache.derby.iapi.services.cache.CacheManager;\r
+\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.services.io.FormatIdUtil;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.error.ExceptionSeverity;\r
+import java.io.IOException;\r
+\r
+/**\r
+       A base page that is cached.\r
+\r
+       Since there are multiple page formats, use this abstract class to implement\r
+       cacheable interface.\r
+\r
+*/\r
+\r
+public abstract class CachedPage extends BasePage implements Cacheable\r
+{\r
+       protected boolean   alreadyReadPage;    // true when page read by another \r
+                                            // class\r
+\r
+       protected byte[]    pageData;               // the actual page data - this is\r
+                                                                                       // the 'buffer' in the buffer cache\r
+\r
+       // The isDirty flag indicates if the pageData or pageHeader has been\r
+       // modified.  The preDirty flag indicates that the pageData or the\r
+       // pageHeader is about to be modified.  The reason for these 2 flags\r
+       // instead of just one is to accomodate checkpoint.  After a clean\r
+       // (latched) page sends a log record to the log stream but before that page\r
+       // is dirtied by the log operation, a checkpoint could be taken.  If so,\r
+       // then the redoLWM will be after the log record but, without preDirty, the\r
+       // cache cleaning will not have waited for the change.  So the preDirty bit\r
+       // is to stop the cache cleaning from skipping over this (latched) page\r
+       // even though it has not really been modified yet.  \r
+\r
+       protected boolean               isDirty;                // must be set to true whenever the\r
+                                            // pageData array is touched \r
+                                            // directly or indirectly.\r
+\r
+       protected boolean               preDirty;               // set to true if the page is clean\r
+                                                                           // and the pageData array is about \r
+                                            // to be touched directly or \r
+                                            // indirectly.\r
+\r
+\r
+       protected int           initialRowCount;    // keep a running count of rows for\r
+                                                                                   // estimated row count.\r
+\r
+       private long            containerRowCount;      // the number of rows in the\r
+                                                                                       // container when this page is read\r
+                                                                                       // from disk \r
+\r
+       /*\r
+       ** These fields are immutable and can be used by the subclasses directly.\r
+       */\r
+\r
+       /**\r
+               The page cache I live in.\r
+\r
+               <BR> MT - Immutable\r
+       */\r
+       protected CacheManager          pageCache;\r
+\r
+       /**\r
+               The container cache my container lives in.\r
+\r
+               <BR> MT - Immutable\r
+       */\r
+       protected CacheManager          containerCache;\r
+\r
+       /**\r
+               My factory class.\r
+\r
+               <BR> MT - Immutable - \r
+       */\r
+       protected BaseDataFileFactory           dataFactory;  // my factory class.\r
+\r
+\r
+       protected static final int PAGE_FORMAT_ID_SIZE = 4;\r
+\r
+       /*\r
+        * the page need to be written and synced to disk \r
+        */\r
+       public static final int WRITE_SYNC = 1;\r
+\r
+       /*\r
+        * the page need to be write to disk but not synced\r
+        */\r
+       public static final int WRITE_NO_SYNC = 2;\r
+\r
+       public CachedPage()\r
+       {\r
+               super();\r
+       }\r
+\r
+       public final void setFactory(BaseDataFileFactory factory) \r
+    {\r
+               dataFactory     = factory;\r
+               pageCache       = factory.getPageCache();\r
+               containerCache  = factory.getContainerCache();\r
+       }\r
+\r
+       /**\r
+        Initialize a CachedPage.\r
+        <p>\r
+               Initialize the object, ie. perform work normally perfomed in \r
+        constructor.  Called by setIdentity() and createIdentity().\r
+       */\r
+       protected void initialize()\r
+       {\r
+               super.initialize();\r
+               isDirty             = false;\r
+               preDirty            = false;\r
+               initialRowCount     = 0;\r
+               containerRowCount   = 0;\r
+       }\r
+\r
+       /*\r
+       ** Methods of Cacheable\r
+       */\r
+\r
+    /**\r
+     * Find the container and then read the page from that container.\r
+     * <p>\r
+     * This is the way new pages enter the page cache.\r
+     * <p>\r
+     *\r
+        * @return always true, higher levels have already checked the page number \r
+     *         is valid for an open.\r
+     *\r
+     * @exception StandardException Standard Derby policy.\r
+     *\r
+     * @see Cacheable#setIdentity\r
+     **/\r
+       public Cacheable setIdentity(Object key) \r
+        throws StandardException \r
+    {\r
+               if (SanityManager.DEBUG) \r
+        {\r
+                       SanityManager.ASSERT(key instanceof PageKey);\r
+               }\r
+\r
+               initialize();\r
+\r
+               PageKey newIdentity = (PageKey) key;\r
+\r
+               FileContainer myContainer = \r
+            (FileContainer) containerCache.find(newIdentity.getContainerId());\r
+\r
+               setContainerRowCount(myContainer.getEstimatedRowCount(0));\r
+\r
+               try\r
+               {\r
+                       if (!alreadyReadPage)\r
+            {\r
+                // Fill in the pageData array by reading bytes from disk.\r
+                               readPage(myContainer, newIdentity);     \r
+            }\r
+                       else\r
+            {\r
+                // pageData array already filled\r
+                               alreadyReadPage = false;\r
+            }\r
+\r
+                       // if the formatID on disk is not the same as this page instance's\r
+                       // format id, instantiate the real page object\r
+                       int fmtId = getTypeFormatId();\r
+\r
+                       int onPageFormatId = FormatIdUtil.readFormatIdInteger(pageData);\r
+                       if (fmtId != onPageFormatId)\r
+                       {\r
+                               return changeInstanceTo(\r
+                            onPageFormatId, newIdentity).setIdentity(key);\r
+                       }\r
+\r
+                       // this is the correct page instance\r
+                       initFromData(myContainer, newIdentity);\r
+               }\r
+               finally\r
+               {\r
+                       containerCache.release(myContainer);\r
+                       myContainer = null;\r
+               }\r
+\r
+               fillInIdentity(newIdentity);\r
+\r
+               initialRowCount = 0;\r
+\r
+               return this;\r
+       }\r
+\r
+    /**\r
+     * Find the container and then create the page in that container.\r
+     * <p>\r
+     * This is the process of creating a new page in a container, in that\r
+     * case no need to read the page from disk - just need to initialize it\r
+     * in the cache.\r
+     * <p>\r
+     *\r
+        * @return new page, higher levels have already checked the page number is \r
+     *         valid for an open.\r
+     *\r
+     * @param key               Which page is this?\r
+     * @param createParameter   details needed to create page like size, \r
+     *                          format id, ...\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     *\r
+     * @see Cacheable#createIdentity\r
+     **/\r
+       public Cacheable createIdentity(\r
+    Object  key, \r
+    Object  createParameter) \r
+        throws StandardException \r
+    {\r
+\r
+               if (SanityManager.DEBUG) \r
+        {\r
+                       SanityManager.ASSERT(key instanceof PageKey);\r
+               }\r
+\r
+               initialize();\r
+\r
+               PageKey newIdentity = (PageKey) key;\r
+\r
+               PageCreationArgs createArgs = (PageCreationArgs) createParameter;\r
+        int formatId = createArgs.formatId;\r
+\r
+               if (formatId == -1)\r
+        {\r
+                       throw StandardException.newException(\r
+                    SQLState.DATA_UNKNOWN_PAGE_FORMAT, newIdentity);\r
+        }\r
+\r
+               // createArgs[0] contains the integer form of the formatId \r
+               // if it is not the same as this instance's formatId, instantiate the\r
+               // real page object\r
+               if (formatId != getTypeFormatId())\r
+               {\r
+                       return(\r
+                changeInstanceTo(formatId, newIdentity).createIdentity(\r
+                        key, createParameter));\r
+               }\r
+               \r
+               // this is the correct page instance\r
+               initializeHeaders(5);\r
+               createPage(newIdentity, createArgs);\r
+\r
+               fillInIdentity(newIdentity);\r
+\r
+               initialRowCount = 0;\r
+\r
+               /*\r
+                * if we need to grow the container and the page has not been\r
+                * preallocated, writing page before the log is written so that we\r
+                * know if there is an IO error - like running out of disk space - then\r
+                * we don't write out the log record, because if we do, it may fail\r
+                * after the log goes to disk and then the database may not be\r
+                * recoverable. \r
+                *\r
+                * WRITE_SYNC is used when we create the page without first\r
+                *      preallocating it \r
+                * WRITE_NO_SYNC is used when we are preallocating the page - there\r
+                *      will be a SYNC call after all the pages are preallocated\r
+                * 0 means creating a page that has already been preallocated.\r
+                */\r
+        int syncFlag = createArgs.syncFlag;\r
+               if ((syncFlag & WRITE_SYNC) != 0 ||\r
+                       (syncFlag & WRITE_NO_SYNC) != 0)\r
+                       writePage(newIdentity, (syncFlag & WRITE_SYNC) != 0);\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (SanityManager.DEBUG_ON(FileContainer.SPACE_TRACE))\r
+                       {\r
+                               String sync =\r
+                    ((syncFlag & WRITE_SYNC) != 0)     ? "Write_Sync" :\r
+                                       (((syncFlag & WRITE_NO_SYNC) != 0) ? "Write_NO_Sync" :\r
+                                                                                 "No_write");\r
+\r
+                               SanityManager.DEBUG(\r
+                    FileContainer.SPACE_TRACE,\r
+                    "creating new page " + newIdentity + " with " + sync);\r
+                       }\r
+               }\r
+\r
+               return this;\r
+       }\r
+\r
+    /**\r
+     * Convert this page to requested type, as defined by input format id.\r
+     * <p>\r
+     * The current cache entry is a different format id than the requested\r
+     * type, change it.  This object is instantiated to the wrong subtype of \r
+     * cachedPage, this routine will create an object with the correct subtype,\r
+     * and transfer all pertinent information from this to the new correct \r
+     * object.\r
+     * <p>\r
+     *\r
+        * @return The new object created with the input fid and transfered info.\r
+     *\r
+     * @param fid          The format id of the new page.\r
+     * @param newIdentity  The key of the new page.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+       private CachedPage changeInstanceTo(int fid, PageKey newIdentity)\r
+                throws StandardException\r
+       {\r
+               CachedPage realPage;\r
+               try \r
+        {\r
+                       realPage = \r
+                (CachedPage) Monitor.newInstanceFromIdentifier(fid);\r
+\r
+               } \r
+        catch (StandardException se) \r
+        {\r
+            if (se.getSeverity() > ExceptionSeverity.STATEMENT_SEVERITY)\r
+            {\r
+                throw se;\r
+            }\r
+            else\r
+            {\r
+                throw StandardException.newException(\r
+                    SQLState.DATA_UNKNOWN_PAGE_FORMAT, se, newIdentity);\r
+            }\r
+               }\r
+\r
+               realPage.setFactory(dataFactory);\r
+\r
+               // avoid creating the data buffer if possible, transfer it to the new \r
+        // page if this is the first time the page buffer is used, then \r
+        // createPage will create the page array with the correct page size\r
+               if (this.pageData != null) \r
+        {\r
+                       realPage.alreadyReadPage = true;\r
+                       realPage.usePageBuffer(this.pageData);\r
+               }\r
+\r
+        // RESOLVE (12/15/06) - the following code is commented out, but\r
+        // not sure why.\r
+\r
+               // this page should not be used any more, null out all its content and\r
+               // wait for GC to clean it up  \r
+\r
+               //destroyPage();// let this subtype have a chance to get rid of stuff\r
+               //this.pageData = null; // this instance no longer own the data array\r
+               //this.pageCache = null;\r
+               //this.dataFactory = null;\r
+               //this.containerCache = null;\r
+\r
+               return realPage;\r
+       }\r
+\r
+    /**\r
+     * Is the page dirty?\r
+     * <p>\r
+     * The isDirty flag indicates if the pageData or pageHeader has been\r
+     * modified.  The preDirty flag indicates that the pageData or the\r
+     * pageHeader is about to be modified.  The reason for these 2 flags\r
+     * instead of just one is to accomodate checkpoint.  After a clean\r
+     * (latched) page sends a log record to the log stream but before that page\r
+     * is dirtied by the log operation, a checkpoint could be taken.  If so,\r
+     * then the redoLWM will be after the log record but, without preDirty, the\r
+     * cache cleaning will not have waited for the change.  So the preDirty bit\r
+     * is to stop the cache cleaning from skipping over this (latched) page\r
+     * even though it has not really been modified yet.  \r
+     *\r
+        * @return true if the page is dirty.\r
+     *\r
+     * @see Cacheable#isDirty\r
+     **/\r
+       public boolean isDirty() \r
+    {\r
+               synchronized (this) \r
+        {\r
+                       return isDirty || preDirty;\r
+               }\r
+       }\r
+\r
+    /**\r
+     * Has the page or its header been modified.\r
+     * <p>\r
+     * See comment on class header on meaning of isDirty and preDirty bits.\r
+     * <p>\r
+     *\r
+        * @return true if changes have actually been made to the page in memory.\r
+     **/\r
+       public boolean isActuallyDirty() \r
+    {\r
+               synchronized (this) \r
+        {\r
+                       return isDirty;\r
+               }\r
+       }\r
+\r
+    /**\r
+     * Set state to indicate the page or its header is about to be modified.\r
+     * <p>\r
+     * See comment on class header on meaning of isDirty and preDirty bits.\r
+     **/\r
+       public void preDirty()\r
+       {\r
+               synchronized (this) \r
+        {\r
+                       if (!isDirty)\r
+                               preDirty = true;\r
+               }\r
+       }\r
+\r
+    /**\r
+     * Set state to indicate the page or its header has been modified.\r
+     * <p>\r
+     * See comment on class header on meaning of isDirty and preDirty bits.\r
+     * <p>\r
+     **/\r
+       protected void setDirty() \r
+    {\r
+               synchronized (this) \r
+        {\r
+                       isDirty  = true;\r
+                       preDirty = false;\r
+               }\r
+       }\r
+\r
+    /**\r
+     * exclusive latch on page is being released.\r
+     * <p>\r
+     * The only work done in CachedPage is to update the row count on the\r
+     * container if it is too out of sync.\r
+     **/\r
+       protected void releaseExclusive()\r
+       {\r
+               // look at dirty bit without latching, the updating of the row\r
+        // count is just an optimization so does not need the latch.\r
+        //\r
+               // if this page actually has > 1/8 rows of the entire container, then\r
+               // consider updating the row count if it is different.\r
+        //\r
+        // No need to special case allocation pages because it has recordCount \r
+        // of zero, thus the if clause will never be true for an allocation \r
+        // page.\r
+               if (isDirty && !isOverflowPage() &&\r
+                       (containerRowCount / 8) < recordCount())\r
+               {\r
+                       int currentRowCount = internalNonDeletedRecordCount();  \r
+                       int delta           = currentRowCount-initialRowCount;\r
+                       int posDelta        = delta > 0 ? delta : (-delta);\r
+\r
+                       if ((containerRowCount/8) < posDelta)\r
+                       {\r
+                               // This pages delta row count represents a significant change\r
+                // with respect to current container row count so update \r
+                // container row count\r
+                               FileContainer myContainer = null;\r
+\r
+                               try\r
+                               {\r
+                                       myContainer = (FileContainer) \r
+                        containerCache.find(identity.getContainerId());\r
+\r
+                                       if (myContainer != null)\r
+                                       {\r
+                                               myContainer.updateEstimatedRowCount(delta);\r
+                                               setContainerRowCount(\r
+                                myContainer.getEstimatedRowCount(0));\r
+\r
+                                               initialRowCount = currentRowCount;\r
+\r
+                                               // since I have the container, might as well update the\r
+                                               // unfilled information\r
+                                               myContainer.trackUnfilledPage(\r
+                            identity.getPageNumber(), unfilled());\r
+                                       }\r
+                               }\r
+                               catch (StandardException se)\r
+                               {\r
+                                       // do nothing, not sure what could fail but this update\r
+                    // is just an optimization so no need to throw error.\r
+                               }\r
+                               finally\r
+                               {\r
+                                       if (myContainer != null)\r
+                                               containerCache.release(myContainer);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               super.releaseExclusive();\r
+       }\r
+\r
+\r
+    /**\r
+     * Write the page to disk.\r
+     * <p>\r
+     * MP - In a simple world we would just not allow clean until it held the\r
+     *      latch on the page.  But in order to fit into the cache system, we \r
+     *      don't have enough state around to just make clean() latch the page \r
+     *      while doing the I/O - but we still need someway to insure that no\r
+     *      changes happen to the page while the I/O is taking place.  \r
+     *      Also someday it would be fine to allow reads of this page\r
+     *      while the I/O was taking place.  \r
+     *\r
+     *\r
+        * @exception  StandardException  Error writing the page.\r
+     *\r
+     * @see Cacheable#clean\r
+     **/\r
+       public void clean(boolean remove) throws StandardException \r
+    {\r
+\r
+               // must wait for the page to be unlatched\r
+               synchronized (this) \r
+        {\r
+                       if (!isDirty())\r
+                               return;\r
+\r
+                       // is someone else cleaning it\r
+                       while (inClean) \r
+            {\r
+                               try \r
+                {\r
+                                       wait();\r
+                               } \r
+                catch (InterruptedException ie) \r
+                {\r
+                                       throw StandardException.interrupt(ie);\r
+                               }\r
+                       }\r
+\r
+            // page is not "inClean" by other thread at this point.\r
+\r
+                       if (!isDirty())\r
+                               return;\r
+\r
+                       inClean = true;\r
+\r
+                       // If page is in LATCHED state (as opposed to UNLATCH or PRELATCH)\r
+            // wait for the page to move to UNLATCHED state.  See Comments in\r
+            // Generic/BasePage.java describing the interaction of inClean,\r
+            // (owner != null), and preLatch.\r
+                       while ((owner != null) && !preLatch) \r
+            {\r
+                               try \r
+                { \r
+                    wait();\r
+                               } \r
+                catch (InterruptedException ie) \r
+                               {\r
+                                       inClean = false;\r
+                                       throw StandardException.interrupt(ie);\r
+                               }\r
+                       }\r
+\r
+                       // The page is now effectively latched by the cleaner.\r
+                       // We only want to clean the page if the page is actually dirtied,\r
+                       // not when it is just pre-dirtied.\r
+                       if (!isActuallyDirty()) \r
+            {\r
+                // the person who latched it gives up the\r
+                // latch without really dirtying the page\r
+                               preDirty = false; \r
+                               inClean  = false;\r
+                               notifyAll();\r
+                               return;\r
+                       }\r
+               }\r
+\r
+               try\r
+               {\r
+                       writePage(getPageId(), false);\r
+               }\r
+               catch(StandardException se)\r
+               {\r
+            // If we get an error while trying to write a page, current\r
+            // recovery system requires that entire DB is shutdown.  Then\r
+            // when system is rebooted we will run redo recovery which \r
+            // if it does not encounter disk errors will guarantee to recover\r
+            // to a transaction consistent state.  If this write is a \r
+            // persistent device problem, redo recovery will likely fail\r
+            // attempting to the same I/O.  Mark corrupt will stop all further\r
+            // writes of data and log by the system.\r
+                       throw dataFactory.markCorrupt(se);\r
+               }\r
+               finally\r
+               {\r
+                       // if there is something wrong in writing out the page, \r
+            // do not leave it inClean state or it will block the next cleaner \r
+            // forever\r
+\r
+                       synchronized (this) \r
+            {\r
+                               inClean = false;\r
+                               notifyAll();\r
+                       }\r
+               }\r
+       }\r
+\r
+       public void clearIdentity() \r
+    {\r
+               alreadyReadPage = false;\r
+               super.clearIdentity();\r
+       }\r
+\r
+    /**\r
+     * read the page from disk into this CachedPage object.\r
+     * <p>\r
+     * A page is read in from disk into the pageData array of this object,\r
+     * and then put in the cache.\r
+     * <p>\r
+     *\r
+     * @param myContainer the container to read the page from.\r
+     * @param newIdentity indentity (ie. page number) of the page to read\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+       private void readPage(\r
+    FileContainer   myContainer, \r
+    PageKey         newIdentity) \r
+        throws StandardException \r
+       {\r
+               int pagesize = myContainer.getPageSize();\r
+\r
+        // we will reuse the existing page array if it is same size, the\r
+        // cache does support caching various sized pages.\r
+               setPageArray(pagesize);\r
+\r
+               for (int io_retry_count = 0;;)\r
+        {\r
+                       try \r
+            {\r
+                               myContainer.readPage(newIdentity.getPageNumber(), pageData);\r
+                               break;\r
+                       } \r
+            catch (IOException ioe) \r
+            {\r
+                               io_retry_count++;       \r
+                                                               \r
+                               // Retrying read I/O's has been found to be successful sometimes\r
+                // in completing the read without having to fail the calling\r
+                // query, and in some cases avoiding complete db shutdown.\r
+                // Some situations are:\r
+                //     spurious interrupts being sent to thread by clients.\r
+                //     unreliable hardware like a network mounted file system.\r
+                //\r
+                // The only option other than retrying is to fail the I/O \r
+                // immediately and throwing an error, thus performance cost\r
+                // not really a consideration.\r
+                //\r
+                // The retry max of 4 is arbitrary, but has been enough that\r
+                // not many read I/O errors have been reported.\r
+                               if (io_retry_count > 4)\r
+                {\r
+                                       // page cannot be physically read\r
+       \r
+                                       StandardException se = \r
+                                               StandardException.newException(\r
+                                                                  SQLState.FILE_READ_PAGE_EXCEPTION, \r
+                                                                  ioe, newIdentity, new Integer(pagesize));\r
+\r
+                                               \r
+                                   if (dataFactory.getLogFactory().inRFR())\r
+                    {\r
+                        //if in rollforward recovery, it is possible that this \r
+                        //page actually does not exist on the disk yet because\r
+                        //the log record we are proccessing now is actually \r
+                        //creating the page, we will recreate the page if we \r
+                        //are in rollforward recovery, so just throw the \r
+                        //exception.\r
+                        throw se;\r
+                    }\r
+                    else\r
+                    {\r
+                        if (SanityManager.DEBUG)\r
+                        {\r
+                            // by shutting down system in debug mode, maybe\r
+                            // we can catch root cause of the interrupt.\r
+                            throw dataFactory.markCorrupt(se);\r
+                        }\r
+                        else\r
+                        {\r
+                            // No need to shut down runtime database on read\r
+                            // error in delivered system, throwing exception \r
+                            // should be enough.  Thrown exception has nested\r
+                            // IO exception which is root cause of error.\r
+                            throw se;\r
+                        }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+    /**\r
+     * write the page from this CachedPage object to disk.\r
+     * <p>\r
+     *\r
+     * @param identity indentity (ie. page number) of the page to read\r
+     * @param syncMe      does the write of this single page have to be sync'd?\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+       private void writePage(\r
+    PageKey identity, \r
+    boolean syncMe) \r
+                throws StandardException \r
+       {\r
+\r
+        // make subclass write the page format\r
+               writeFormatId(identity); \r
+\r
+        // let subclass have a chance to write any cached data to page data \r
+        // array\r
+               writePage(identity);     \r
+\r
+               // force WAL - and check to see if database is corrupt or is frozen.\r
+               // last log Instant may be null if the page is being forced\r
+               // to disk on a createPage (which violates the WAL protocol actually).\r
+               // See FileContainer.newPage\r
+               LogInstant flushLogTo = getLastLogInstant();\r
+               dataFactory.flush(flushLogTo);\r
+\r
+               if (flushLogTo != null) \r
+        {                                      \r
+                       clearLastLogInstant();\r
+               }\r
+\r
+\r
+               // find the container and file access object\r
+               FileContainer myContainer = \r
+            (FileContainer) containerCache.find(identity.getContainerId());\r
+\r
+               if (myContainer == null)\r
+               {\r
+                       StandardException nested =\r
+                               StandardException.newException(\r
+                                       SQLState.DATA_CONTAINER_VANISHED,\r
+                                       identity.getContainerId());\r
+                       throw dataFactory.markCorrupt(\r
+                               StandardException.newException(\r
+                                       SQLState.FILE_WRITE_PAGE_EXCEPTION, nested,\r
+                                       identity));\r
+               }\r
+\r
+               try\r
+               {\r
+                       myContainer.writePage(\r
+                               identity.getPageNumber(), pageData, syncMe);\r
+\r
+                       //\r
+                       // Do some in memory unlogged bookkeeping tasks while we have\r
+                       // the container.\r
+                       //\r
+\r
+                       if (!isOverflowPage() && isDirty())\r
+                       {\r
+\r
+                               // let the container knows whether this page is a not\r
+                               // filled, non-overflow page\r
+                               myContainer.trackUnfilledPage(\r
+                                       identity.getPageNumber(), unfilled());\r
+\r
+                               // if this is not an overflow page, see if the page's row\r
+                               // count has changed since it come into the cache.\r
+                               //\r
+                               // if the page is not invalid, row count is 0.  Otherwise,\r
+                               // count non-deleted records on page.\r
+                               //\r
+                               // Cannot call nonDeletedRecordCount because the page is\r
+                               // unlatched now even though nobody is changing it\r
+                               int currentRowCount = internalNonDeletedRecordCount();\r
+\r
+                               if (currentRowCount != initialRowCount)\r
+                               {\r
+                                       myContainer.updateEstimatedRowCount(\r
+                                               currentRowCount - initialRowCount);\r
+\r
+                                       setContainerRowCount(\r
+                                               myContainer.getEstimatedRowCount(0));\r
+\r
+                                       initialRowCount = currentRowCount;\r
+                               }\r
+                       }\r
+\r
+               }\r
+               catch (IOException ioe)\r
+               {\r
+                       // page cannot be written\r
+                       throw StandardException.newException(\r
+                               SQLState.FILE_WRITE_PAGE_EXCEPTION,\r
+                               ioe, identity);\r
+               }\r
+               finally\r
+               {\r
+                       containerCache.release(myContainer);\r
+                       myContainer = null;\r
+               }\r
+\r
+               synchronized (this) \r
+        {\r
+            // change page state to not dirty after the successful write\r
+                       isDirty     = false;\r
+                       preDirty    = false;\r
+               }\r
+       }\r
+\r
+       public void setContainerRowCount(long rowCount)\r
+       {\r
+               containerRowCount = rowCount;\r
+       }\r
+\r
+       /**\r
+       ** if the page size is different from the page buffer, then make a\r
+       ** new page buffer and make subclass use the new page buffer\r
+       */\r
+       protected void setPageArray(int pageSize)\r
+       {\r
+               if ((pageData == null) || (pageData.length != pageSize)) \r
+        {\r
+            // Give a chance for garbage collection to free\r
+            // the old array before the new array is allocated.\r
+            // Just in case memory is low.\r
+            pageData = null; \r
+                       pageData = new byte[pageSize];\r
+                               \r
+                       usePageBuffer(pageData);\r
+               }\r
+       }\r
+\r
+\r
+    /**\r
+        * Returns the page data array used to write on disk version.\r
+     *\r
+     * <p>\r
+        * returns the page data array, that is actually written to the disk,\r
+        * when the page is cleaned from the page cache.  Takes care of flushing\r
+     * in-memory information to the array (like page header and format id info).\r
+     * <p>\r
+     *\r
+        * @return The array of bytes that is the on disk version of page.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+       protected byte[] getPageArray() throws StandardException \r
+       {\r
+        // make subclass write the page format\r
+               writeFormatId(identity); \r
+\r
+        // let subclass have a chance to write any cached\r
+        // data to page data array\r
+               writePage(identity);    \r
+\r
+               return pageData;\r
+       }\r
+\r
+       /* methods for subclass of cached page */\r
+\r
+       // use a new pageData buffer, initialize in memory structure that depend on\r
+       // the pageData's size.  The actual disk data may not have not been read in\r
+       // yet so don't look at the content of the buffer\r
+       protected abstract void usePageBuffer(byte[] buffer);\r
+\r
+\r
+       // initialize in memory structure using the read in buffer in pageData\r
+       protected abstract void initFromData(FileContainer container, PageKey id) \r
+        throws StandardException;\r
+\r
+\r
+       // create the page\r
+       protected abstract void createPage(PageKey id, PageCreationArgs args)\r
+        throws StandardException;\r
+\r
+       // page is about to be written, write everything to pageData array\r
+       protected abstract void writePage(PageKey id) throws StandardException;         \r
+\r
+       // write out the formatId to the pageData\r
+       protected abstract void writeFormatId(PageKey identity) \r
+        throws StandardException;\r
+}\r