--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.access.btree.ControlRow\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.access.btree;\r
+\r
+\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.services.io.TypedFormat;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+import org.apache.derby.iapi.store.access.RowUtil;\r
+\r
+import org.apache.derby.iapi.store.raw.AuxObject;\r
+import org.apache.derby.iapi.store.raw.FetchDescriptor;\r
+import org.apache.derby.iapi.store.raw.Page;\r
+import org.apache.derby.iapi.store.raw.ContainerHandle;\r
+import org.apache.derby.iapi.store.raw.RecordHandle;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.types.SQLLongint;\r
+import org.apache.derby.impl.store.access.StorableFormatId;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+\r
+import org.apache.derby.impl.store.access.conglomerate.ConglomerateUtil;\r
+\r
+/**\r
+\r
+Base class for leaf and branch control rows.\r
+<P>\r
+<B>Concurrency Notes</B>\r
+<P>\r
+All access through control rows is serialized by an exclusive latch on \r
+the page the control row is for. The page is latched when the control\r
+row is "gotten" (ControlRow#Get), and unlatched when the control row\r
+is released (ControlRow#release).\r
+<P>\r
+<B>To Do List</B>\r
+<UL>\r
+<LI> <I>[NOTE1]</I>\r
+The code is arranged to fault in fields from the row as necessary.\r
+many of the fields of a control row are rarely used (left sibling, parent).\r
+The accessors fault in the underlying column only when\r
+requested by allocating the appropriate object and calling fetchFromSlot and\r
+only fetching the requested field.\r
+<LI> <I>[NOTE2]</I> \r
+Currently, all the fields of the control row are stored as StorableU8s\r
+for simplicity. This is too few bits to hold the long page numbers, and\r
+too many to hold the version, level, and isRoot flag. Some consideration\r
+will have to be given to the appropriate storage format for these values.\r
+<LI> <I>[NOTE3]</I>\r
+The implementation relies on the existance of page "auxiliary" pointers \r
+which keep Object versions of the control row.\r
+<P>\r
+@see ControlRow#get\r
+@see ControlRow#release\r
+\r
+**/\r
+\r
+public abstract class ControlRow implements AuxObject, TypedFormat\r
+{\r
+ /**\r
+ * Version indentifier of the control row within the page. \r
+ * <p>\r
+ * This is the format id of the control row. The format id is currently\r
+ * one of either StoredFormatIds.ACCESS_BTREE_LEAFCONTROLROW_ID or\r
+ * StoredFormatIds.ACCESS_BTREE_BRANCHCONTROLROW_ID.\r
+ **/\r
+ private StorableFormatId version = null;\r
+\r
+ /**\r
+ * Pointer to page which is "left" at the current level.\r
+ * <p>\r
+ * Currently all pages at a level are doubly linked. The leftmost page\r
+ * in a level has a leftSiblingPageNumber == \r
+ * ContainerHandle.INVALID_PAGE_NUMBER. All key values on the page which\r
+ * is left must precede the first key value on the current page.\r
+ **/\r
+ private SQLLongint leftSiblingPageNumber;\r
+\r
+ /**\r
+ * Pointer to page which is "right" at the current level.\r
+ * <p>\r
+ * Currently all pages at a level are doubly linked. The rightmost page\r
+ * in a level has a rightSiblingPageNumber == \r
+ * ContainerHandle.INVALID_PAGE_NUMBER. All key values on the page which\r
+ * is right of the current page must follow the last key value on the \r
+ * current page.\r
+ **/\r
+ private SQLLongint rightSiblingPageNumber;\r
+\r
+ /**\r
+ * The parent page of the current page.\r
+ * <p>\r
+ * For consistency checking it is useful to maintain the parentPageNumber\r
+ * field of the current page. The root page has a value of \r
+ * ContainerHandle.INVALID_PAGE_NUMBER in it's parentPageNumber field.\r
+ * <p>\r
+ * RESOLVE (mikem) - we need to come up with some way to not maintain these,\r
+ * maybe by providing a property on secondary index or a different 2nd \r
+ * index.\r
+ *\r
+ **/\r
+ private SQLLongint parentPageNumber; // for consistency checking\r
+\r
+\r
+ /**\r
+ * The level of the btree.\r
+ * <p>\r
+ * The leaf level of the btree is 0. The first branch level (parent level\r
+ * of the leaf), is level 1. The height of the btree is (level + 1).\r
+ * <p>\r
+ * The smallest btree is a one page btree with only a leaf, and no branch\r
+ * pages. \r
+ **/\r
+ private SQLLongint level;\r
+\r
+ /**\r
+ * Is this page the root of the btree?\r
+ * <p>\r
+ * Currently "1" if the page is the root page, else "0".\r
+ * <p>\r
+ * RESOLVE (mikem) When real datatype come about, this value should \r
+ * probably be just a bit in some status word.\r
+ **/\r
+ private SQLLongint isRoot = null;\r
+\r
+ /**\r
+ * A copy of the Conglomerate that describes the owning conglom.\r
+ * <p>\r
+ * This information is used during logical undo to get the type information\r
+ * so that rows can be compared and searched for. We may be able to get\r
+ * away with a subset of the information stored in the conglomerate.\r
+ * <p>\r
+ * RESOLVE (mikem) - change this to only store the info on the root page.\r
+ **/\r
+ private BTree btree = null;\r
+\r
+ /**\r
+ * The page that this control row describes.\r
+ **/\r
+ protected Page page;\r
+\r
+ /**\r
+ * The page that this control row describes.\r
+ **/\r
+ protected DataValueDescriptor row[];\r
+\r
+ /**\r
+ * row used to replace fetchFieldFromSlot() calls.\r
+ **/\r
+ protected DataValueDescriptor[] scratch_row;\r
+\r
+ /**\r
+ * FetchDescriptor used to replace fetchFieldFromSlot() calls.\r
+ **/\r
+ protected FetchDescriptor fetchDesc;\r
+\r
+ /**\r
+ * In memory hint about whether to use the last_search_result hint during\r
+ * search.\r
+ **/\r
+ transient protected boolean use_last_search_result_hint = false;\r
+\r
+ /**\r
+ * In memory hint about where to begin the binary search to find a key\r
+ * on the the current control page.\r
+ **/\r
+ transient protected int last_search_result = 0;\r
+\r
+ /**\r
+ * Column number assignments for columns of the control row.\r
+ * <p>\r
+ * The control row is stored as the first row in a btree page. The row\r
+ * is an array of columns. The Control row columns are the columns numbered\r
+ * from ControlRow.CR_COLID_FIRST through ControlRow.CR_COLID_LAST. The\r
+ * classes which implement the concrete derived classes of ControlRow may\r
+ * add columns to the control row, but they must be added after the \r
+ * ControlRow columns.\r
+ **/\r
+ protected static final int CR_COLID_FIRST = 0;\r
+ protected static final int CR_VERSION_COLID = CR_COLID_FIRST + 0;\r
+ protected static final int CR_LEFTSIB_COLID = CR_COLID_FIRST + 1;\r
+ protected static final int CR_RIGHTSIB_COLID = CR_COLID_FIRST + 2;\r
+ protected static final int CR_PARENT_COLID = CR_COLID_FIRST + 3;\r
+ protected static final int CR_LEVEL_COLID = CR_COLID_FIRST + 4;\r
+ protected static final int CR_ISROOT_COLID = CR_COLID_FIRST + 5;\r
+ protected static final int CR_CONGLOM_COLID = CR_COLID_FIRST + 6;\r
+ protected static final int CR_COLID_LAST = CR_CONGLOM_COLID;\r
+ protected static final int CR_NCOLUMNS = CR_COLID_LAST + 1;\r
+\r
+ /**\r
+ * bit sets used to fetch single columns at a time.\r
+ **/\r
+ protected static final FormatableBitSet CR_VERSION_BITSET = \r
+ new FormatableBitSet(CR_VERSION_COLID + 1);\r
+ protected static final FormatableBitSet CR_LEFTSIB_BITSET = \r
+ new FormatableBitSet(CR_LEFTSIB_COLID + 1);\r
+ protected static final FormatableBitSet CR_RIGHTSIB_BITSET =\r
+ new FormatableBitSet(CR_RIGHTSIB_COLID + 1);\r
+ protected static final FormatableBitSet CR_PARENT_BITSET =\r
+ new FormatableBitSet(CR_PARENT_COLID + 1);\r
+ protected static final FormatableBitSet CR_LEVEL_BITSET =\r
+ new FormatableBitSet(CR_LEVEL_COLID + 1);\r
+ protected static final FormatableBitSet CR_ISROOT_BITSET =\r
+ new FormatableBitSet(CR_ISROOT_COLID + 1);\r
+ protected static final FormatableBitSet CR_CONGLOM_BITSET =\r
+ new FormatableBitSet(CR_CONGLOM_COLID + 1);\r
+\r
+\r
+ /**\r
+ * Values passed in the flag argument to splitFor.\r
+ **/\r
+ /* row causing split would be last row on leaf page */\r
+ public static final int SPLIT_FLAG_LAST_ON_PAGE = 0x000000001;\r
+ /* row causing split would be last row in table */\r
+ public static final int SPLIT_FLAG_LAST_IN_TABLE = 0x000000002;\r
+ /* row causing split would be first row on page */\r
+ public static final int SPLIT_FLAG_FIRST_ON_PAGE = 0x000000004;\r
+ /* row causing split would be first row in table */\r
+ public static final int SPLIT_FLAG_FIRST_IN_TABLE = 0x000000008;\r
+\r
+ /**\r
+ * The slot at which all control rows reside.\r
+ **/\r
+ protected static final int CR_SLOT = 0;\r
+\r
+ /*\r
+ ** Constructors of ControlRow\r
+ */\r
+\r
+ static \r
+ {\r
+ CR_VERSION_BITSET.set(CR_VERSION_COLID);\r
+ CR_LEFTSIB_BITSET.set(CR_LEFTSIB_COLID);\r
+ CR_RIGHTSIB_BITSET.set(CR_RIGHTSIB_COLID);\r
+ CR_PARENT_BITSET.set(CR_PARENT_COLID);\r
+ CR_LEVEL_BITSET.set(CR_LEVEL_COLID);\r
+ CR_ISROOT_BITSET.set(CR_ISROOT_COLID);\r
+ CR_CONGLOM_BITSET.set(CR_CONGLOM_COLID);\r
+ }\r
+\r
+ /**\r
+ * No arg constructor.\r
+ * <p>\r
+ * GetControlRowForPage() will call this constructor when it uses the \r
+ * monitor to create a control row dynamically given a given format id.\r
+ **/\r
+ protected ControlRow()\r
+ {\r
+ this.scratch_row = \r
+ new DataValueDescriptor[getNumberOfControlRowColumns()];\r
+\r
+ this.fetchDesc = \r
+ new FetchDescriptor(\r
+ this.scratch_row.length, (FormatableBitSet) null, (Qualifier[][]) null);\r
+ }\r
+\r
+ /**\r
+ * Constructor for making a new control row as part of allocating a new\r
+ * page. Fills in all the fields but does not write them anywhere.\r
+ * <p>\r
+ * <P>\r
+ * Changes to this constructor will probably require changes to the\r
+ * corresponding accessor(s).\r
+ *\r
+ * @param btree Static information about the btree.\r
+ * @param page The page described by this control row.\r
+ * @param parent The parent page of this page, "null" if this page is \r
+ * root or if not maintaining parent links.\r
+ * @param isRoot Is this page the root of the tree?\r
+ *\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected ControlRow(\r
+ OpenBTree btree,\r
+ Page page, \r
+ int level, \r
+ ControlRow parent,\r
+ boolean isRoot\r
+ )\r
+ throws StandardException\r
+ {\r
+ // The caller is expected to have latched the pages.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(page.isLatched());\r
+ SanityManager.ASSERT(parent == null || parent.page.isLatched());\r
+ }\r
+\r
+ // Maintain which page this control row describes.\r
+ this.page = page;\r
+\r
+ // Page numbers start out "invalid". Presumably the caller will\r
+ // link the page into a page chain as one of its next steps.\r
+ leftSiblingPageNumber = \r
+ new SQLLongint(btree.container.INVALID_PAGE_NUMBER);\r
+ rightSiblingPageNumber = \r
+ new SQLLongint(btree.container.INVALID_PAGE_NUMBER);\r
+\r
+ // Remember the parent if there is one and we're remembering parents.\r
+ parentPageNumber = new SQLLongint( \r
+ (parent == null ? \r
+ btree.container.INVALID_PAGE_NUMBER : \r
+ parent.page.getPageNumber()));\r
+\r
+ // All pages start out not being root pages. The caller will setIsRoot\r
+ // if this is going to be a root page. Zero means false - see \r
+ // getIsRoot/setIsRoot.\r
+ this.isRoot = new SQLLongint(isRoot ? 1 : 0);\r
+\r
+ // set the rest of the state, as passed in.\r
+ this.level = new SQLLongint(level);\r
+ this.version = new StorableFormatId(getTypeFormatId());\r
+\r
+ // If it is a root page then store the real btree conglomerate, if it\r
+ // is not a root page then set up an "empty" btree conglomerate which\r
+ // will be stored as "null".\r
+ this.btree = \r
+ (isRoot ? \r
+ btree.getConglomerate() : \r
+ (BTree) Monitor.newInstanceFromIdentifier(\r
+ btree.getConglomerate().getTypeFormatId()));\r
+\r
+ // Initialize the object array to be used for interacting with raw\r
+ // store to insert, fetch, and update the control row.\r
+ this.row = new DataValueDescriptor[getNumberOfControlRowColumns()];\r
+ this.row[CR_VERSION_COLID] = this.version;\r
+ this.row[CR_LEFTSIB_COLID] = this.leftSiblingPageNumber;\r
+ this.row[CR_RIGHTSIB_COLID] = this.rightSiblingPageNumber;\r
+ this.row[CR_PARENT_COLID] = this.parentPageNumber;\r
+ this.row[CR_LEVEL_COLID] = this.level;\r
+ this.row[CR_ISROOT_COLID] = this.isRoot;\r
+ this.row[CR_CONGLOM_COLID] = this.btree;\r
+\r
+\r
+ // Make the control row the aux object for the page so control row\r
+ // getters end up with the same row.\r
+ page.setAuxObject(this);\r
+ }\r
+\r
+ /**\r
+ * Constructor for making a control row for an existing page.\r
+ * <p>\r
+ * Not all the fields are filled in; their values will get faulted in from \r
+ * the page as necessary.\r
+ * <p>\r
+ * Classes which extend ControlRow must delegate to this constructor\r
+ * and may want to override it as well.\r
+ * Changes to this constructor will probably require changes to the\r
+ * corresponding accessor(s).\r
+ *\r
+ * @param container Open container \r
+ * @param page The page described by this control row.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected ControlRow(ContainerHandle container, Page page)\r
+ throws StandardException\r
+ {\r
+ System.out.println("ControlRow construct 2.");\r
+\r
+ // The caller is expected to have latched the pages.\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(page.isLatched());\r
+\r
+ // Remember the page.\r
+ this.page = page;\r
+\r
+ // The rest of the fields are left null; they'll get faulted\r
+ // in if/when necessary. See the accessors.\r
+ }\r
+\r
+ /* Private/Protected methods of ControlRow: */\r
+\r
+ /**\r
+ * Get version of the control row.\r
+ * <p>\r
+ * Returns the version of the control row, faulting it in from the page\r
+ * if necessary.\r
+ *\r
+ * @return version of the control row.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected int getVersion()\r
+ throws StandardException\r
+ {\r
+ if (this.version == null)\r
+ {\r
+ // Fault in the version.\r
+ this.version = new StorableFormatId();\r
+\r
+ scratch_row[CR_VERSION_COLID] = this.version;\r
+\r
+ fetchDesc.setValidColumns(CR_VERSION_BITSET);\r
+\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+ }\r
+ return this.version.getValue();\r
+ }\r
+\r
+ /**\r
+ * Set version of the control row.\r
+ * <p>\r
+ * Sets the version of the control row. Updates both the in-memory \r
+ * control row and the disk copy.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected void setVersion(int version)\r
+ throws StandardException\r
+ {\r
+ // Store the field.\r
+ if (this.version == null)\r
+ this.version = new StorableFormatId();\r
+ this.version.setValue(version);\r
+\r
+ // Write the field through to the underlying row.\r
+ this.page.updateFieldAtSlot(\r
+ CR_SLOT, CR_VERSION_COLID, this.version, null);\r
+ }\r
+\r
+ /**\r
+ * Get the control row for this page's left sibling, or null if there is no\r
+ * left sibling (which probably means it's the leftmost page at its level).\r
+ * Since right-to-left traversal of an index level is deadlock-prone, this \r
+ * method will only get get the left sibling if it can latch it without\r
+ * waiting.\r
+ *\r
+ * @exception WaitError if the latch request would have had to wait.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public ControlRow getLeftSibling(OpenBTree btree)\r
+ throws StandardException, WaitError\r
+ {\r
+ ControlRow cr;\r
+ \r
+ long pageno = this.getleftSiblingPageNumber();\r
+\r
+ // Is there a left sibling?\r
+ if (pageno == ContainerHandle.INVALID_PAGE_NUMBER)\r
+ return null;\r
+\r
+ // Try to get the control row without waiting\r
+ cr = ControlRow.getNoWait(btree, pageno);\r
+ if (cr == null)\r
+ throw new WaitError();\r
+\r
+ return cr;\r
+ }\r
+\r
+ protected void setLeftSibling(ControlRow leftsib)\r
+ throws StandardException\r
+ {\r
+ long left_sib_pageno = \r
+ (leftsib == null ? ContainerHandle.INVALID_PAGE_NUMBER : \r
+ leftsib.page.getPageNumber());\r
+ \r
+ // Store the field.\r
+ if (leftSiblingPageNumber == null)\r
+ leftSiblingPageNumber = new SQLLongint(left_sib_pageno);\r
+ else\r
+ this.leftSiblingPageNumber.setValue(left_sib_pageno);\r
+\r
+ // Write the field through to the underlying row\r
+ try\r
+ {\r
+ this.page.updateFieldAtSlot(\r
+ CR_SLOT, CR_LEFTSIB_COLID, this.leftSiblingPageNumber, null);\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ // Since this is an update of a fixed length field it should \r
+ // never fail, but it has happened enough that an assert helps\r
+ // with debugging.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "setLeftSibling got an exception: " +\r
+ "control_row = " + this +\r
+ "trying to update field number " + CR_LEFTSIB_COLID + \r
+ "to new value " + this.leftSiblingPageNumber, se);\r
+ }\r
+ throw(se);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Return the control row for this page's right sibling. Unlike getting\r
+ the left sibling, it's ok to wait for the right sibling latch since\r
+ left-to-right is the deadlock-free ordering.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ protected ControlRow getRightSibling(OpenBTree open_btree)\r
+ throws StandardException\r
+ {\r
+ long pageno = this.getrightSiblingPageNumber();\r
+\r
+ // Return the control row for the page.\r
+ if (pageno == ContainerHandle.INVALID_PAGE_NUMBER)\r
+ return null;\r
+ else\r
+ return ControlRow.get(open_btree, pageno);\r
+ }\r
+\r
+ // This method will have to update the row.\r
+ protected void setRightSibling(ControlRow rightsib)\r
+ throws StandardException\r
+ {\r
+ long right_sib_pageno = \r
+ (rightsib == null ? ContainerHandle.INVALID_PAGE_NUMBER : \r
+ rightsib.page.getPageNumber());\r
+ \r
+ // Store the field.\r
+ if (rightSiblingPageNumber == null)\r
+ rightSiblingPageNumber = new SQLLongint(right_sib_pageno);\r
+ else\r
+ this.rightSiblingPageNumber.setValue(right_sib_pageno);\r
+\r
+ // Write the field through to the underlying row\r
+ try\r
+ {\r
+ this.page.updateFieldAtSlot(\r
+ CR_SLOT, CR_RIGHTSIB_COLID, this.rightSiblingPageNumber, null);\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ // Since this is an update of a fixed length field it should \r
+ // never fail, but it has happened enough that an assert helps\r
+ // with debugging.\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "setRightSibling got an exception: " +\r
+ "control_row = " + this +\r
+ "trying to update field number " + CR_RIGHTSIB_COLID + \r
+ "to new value " + this.rightSiblingPageNumber, se);\r
+ }\r
+ throw(se);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Get the page number of the left sibling. Fault it's value in if it\r
+ hasn't been yet.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public long getleftSiblingPageNumber()\r
+ throws StandardException\r
+ {\r
+ if (this.leftSiblingPageNumber == null)\r
+ {\r
+ // Fault in the page number.\r
+ this.leftSiblingPageNumber = new SQLLongint();\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(scratch_row != null);\r
+\r
+ scratch_row[CR_LEFTSIB_COLID] = this.leftSiblingPageNumber;\r
+\r
+ fetchDesc.setValidColumns(CR_LEFTSIB_BITSET);\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+\r
+ }\r
+\r
+ return(leftSiblingPageNumber.getLong());\r
+ }\r
+\r
+ /**\r
+ Get the page number of the right sibling. Fault it's value in if it\r
+ hasn't been yet.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ protected long getrightSiblingPageNumber()\r
+ throws StandardException\r
+ {\r
+ if (this.rightSiblingPageNumber == null)\r
+ {\r
+ // Fault in the page number.\r
+ this.rightSiblingPageNumber = new SQLLongint();\r
+\r
+ scratch_row[CR_RIGHTSIB_COLID] = this.rightSiblingPageNumber;\r
+\r
+ fetchDesc.setValidColumns(CR_RIGHTSIB_BITSET);\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+ }\r
+\r
+ return(rightSiblingPageNumber.getLong());\r
+ }\r
+\r
+ /**\r
+ Get the page number of the parent, if it's being maintained.\r
+ Note that there is intentionally no way to get the control\r
+ row for the parent page - the b-tree code NEVER traverses\r
+ up the tree, even in consistency checks.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ protected long getParentPageNumber()\r
+ throws StandardException\r
+ {\r
+ if (this.parentPageNumber == null)\r
+ {\r
+ // Fault in the page number.\r
+ this.parentPageNumber = new SQLLongint();\r
+\r
+ scratch_row[CR_PARENT_COLID] = this.parentPageNumber;\r
+\r
+ fetchDesc.setValidColumns(CR_PARENT_BITSET);\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+ }\r
+\r
+ // See NOTE3 about converting from int to long.\r
+ long pageno = parentPageNumber.getLong();\r
+ return pageno;\r
+ }\r
+ \r
+ void setParent(long parent)\r
+ throws StandardException\r
+ {\r
+ // Store the field.\r
+ if (parentPageNumber == null)\r
+ parentPageNumber = new SQLLongint();\r
+ this.parentPageNumber.setValue(parent);\r
+\r
+ // Write the field through to the underlying row\r
+ try\r
+ {\r
+ this.page.updateFieldAtSlot(\r
+ CR_SLOT, CR_PARENT_COLID, this.parentPageNumber, null);\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ // Since this is an update of a fixed length field it should \r
+ // never fail, but it has happened enough that an assert helps\r
+ // with debugging.\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "setParent got an exception: " +\r
+ "control_row = " + this +\r
+ "trying to update field number " + CR_PARENT_COLID + \r
+ "to new value " + this.parentPageNumber, se);\r
+ }\r
+ throw(se);\r
+ }\r
+\r
+ return;\r
+ }\r
+\r
+ protected int getLevel()\r
+ throws StandardException\r
+ {\r
+ if (this.level == null)\r
+ {\r
+ // Fault in the level\r
+ this.level = new SQLLongint();\r
+\r
+ scratch_row[CR_LEVEL_COLID] = this.level;\r
+\r
+ fetchDesc.setValidColumns(CR_LEVEL_BITSET);\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+ }\r
+\r
+ return((int) this.level.getLong());\r
+ }\r
+\r
+ protected void setLevel(int newlevel)\r
+ throws StandardException\r
+ {\r
+ // Store the field.\r
+ if (this.level == null)\r
+ this.level = new SQLLongint();\r
+ this.level.setValue((long) newlevel);\r
+\r
+ // Write the field through to the underlying row.\r
+ this.page.updateFieldAtSlot(CR_SLOT, CR_LEVEL_COLID, this.level, null);\r
+ }\r
+\r
+ protected boolean getIsRoot()\r
+ throws StandardException\r
+ {\r
+ // convert 1 to true, 0 to false;\r
+ \r
+ if (this.isRoot == null)\r
+ {\r
+ // Fault in the level\r
+ this.isRoot = new SQLLongint();\r
+\r
+ scratch_row[CR_ISROOT_COLID] = this.isRoot;\r
+\r
+ fetchDesc.setValidColumns(CR_ISROOT_BITSET);\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+ }\r
+\r
+ return((this.isRoot.getLong() == 1));\r
+ }\r
+ \r
+ protected void setIsRoot(boolean isRoot)\r
+ throws StandardException\r
+ {\r
+ // RESOLVE (mmm) - need to store more efficiently //\r
+\r
+ // Store the field.\r
+ if (this.isRoot == null)\r
+ this.isRoot = new SQLLongint();\r
+\r
+ this.isRoot.setValue((isRoot) ? 1 : 0);\r
+\r
+ // Write the field through to the underlying row.\r
+ this.page.updateFieldAtSlot(\r
+ CR_SLOT, CR_ISROOT_COLID, this.isRoot, null);\r
+ }\r
+\r
+ /**\r
+ * Get format id information for row on page.\r
+ * <p>\r
+ * Returns the format id information for a row on the page. faulting it \r
+ * in from the page if necessary.\r
+ *\r
+ * @return format id of a row on the page.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public BTree getConglom(int format_id)\r
+ throws StandardException\r
+ {\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // this call is only valid on root pages. If called on non\r
+ // root pages it will return a "null" conglom object.\r
+ SanityManager.ASSERT(\r
+ (this.page.getPageNumber() == BTree.ROOTPAGEID) && getIsRoot());\r
+ }\r
+\r
+ if (this.btree == null)\r
+ {\r
+ // use format id to create empty instance of Conglomerate class\r
+ this.btree = (BTree) Monitor.newInstanceFromIdentifier(format_id);\r
+\r
+ scratch_row[CR_CONGLOM_COLID] = this.btree;\r
+\r
+ fetchDesc.setValidColumns(CR_CONGLOM_BITSET);\r
+ this.page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, scratch_row, fetchDesc, false); \r
+ }\r
+ return this.btree;\r
+ }\r
+\r
+ /**\r
+ * Set the conglomerate field in the btree.\r
+ * <p>\r
+ * Sets the btree field of the control row. Updates just the disk copy. \r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private void setConglom(BTree btree)\r
+ throws StandardException\r
+ {\r
+ // Store the field. Delay faulting value into object until getConlgom()\r
+ // call, which in general only happens during recovery.\r
+\r
+ // Write the field through to the underlying row.\r
+ this.page.updateFieldAtSlot(CR_SLOT, CR_CONGLOM_COLID, btree, null);\r
+ }\r
+\r
+ /*\r
+ ** Methods for getting control rows from pages.\r
+ */\r
+\r
+ /**\r
+ Get the control row from the given page in the b-tree.\r
+ The returned control row will be of the correct type\r
+ for the page (i.e., either a LeafControlRow or a\r
+ BranchControlRow).\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public static ControlRow get(OpenBTree open_btree, long pageNumber)\r
+ throws StandardException\r
+ {\r
+ return(ControlRow.get(open_btree.container, pageNumber));\r
+ }\r
+\r
+ public static ControlRow get(ContainerHandle container, long pageNumber)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(container != null, \r
+ "ControlRow.Get() is being called on a closed container.");\r
+ }\r
+\r
+ // Get the page, waiting if necessary. The page is returned latched.\r
+ Page page = container.getPage(pageNumber);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (page == null)\r
+ SanityManager.THROWASSERT(\r
+ "No page at pagenumber: " + pageNumber +\r
+ "; ContainerHandle = " + container);\r
+ }\r
+\r
+ // Return the corresponding control row.\r
+ return getControlRowForPage(container, page);\r
+ }\r
+\r
+ /**\r
+ Get the control row for the given page if the latch on the\r
+ page can be obtained without waiting, else return null.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public static ControlRow getNoWait(\r
+ OpenBTree open_btree, \r
+ long pageNumber)\r
+ throws StandardException\r
+ {\r
+ // Try to get the page without waiting. If we would have\r
+ // to wait, return null.\r
+ Page page = open_btree.container.getUserPageNoWait(pageNumber);\r
+ if (page == null)\r
+ return null; \r
+\r
+ // Got the page without waiting. Return the corresponding\r
+ // control row.\r
+ return getControlRowForPage(open_btree.container, page);\r
+ }\r
+\r
+ protected static ControlRow getControlRowForPage(\r
+ ContainerHandle container,\r
+ Page page)\r
+ throws StandardException\r
+ {\r
+ ControlRow cr = null;\r
+\r
+ // See if the control row is still cached with the page\r
+ // If so, just use the cached control row.\r
+ AuxObject auxobject = page.getAuxObject();\r
+ if (auxobject != null)\r
+ return (ControlRow) auxobject;\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(page.recordCount() >= 1);\r
+\r
+ // No cached control row, so create a new one.\r
+ \r
+ // Use the version field to determine the type of the row to\r
+ // create. This routine depends on the version field being the same\r
+ // number column in all control rows.\r
+ \r
+ StorableFormatId version = new StorableFormatId();\r
+\r
+ DataValueDescriptor[] version_ret = new DataValueDescriptor[1];\r
+ \r
+ version_ret[0] = version;\r
+\r
+ // TODO (mikem) - get rid of this new.\r
+\r
+ page.fetchFromSlot(\r
+ (RecordHandle) null, CR_SLOT, version_ret,\r
+ new FetchDescriptor(1, CR_VERSION_BITSET, (Qualifier[][]) null), \r
+ false); \r
+\r
+ // use format id to create empty instance of right Conglomerate class\r
+ cr = (ControlRow) Monitor.newInstanceFromIdentifier(version.getValue());\r
+ cr.page = page;\r
+\r
+ // call page specific initialization.\r
+ cr.controlRowInit();\r
+\r
+ // cache this Control row with the page in the cache.\r
+ page.setAuxObject(cr);\r
+\r
+ return(cr);\r
+ }\r
+\r
+ /**\r
+ Release this control row's resources.\r
+ **/\r
+ public void release()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(page != null);\r
+\r
+ if (page != null)\r
+ page.unlatch();\r
+\r
+ // It might be nice to set the page object to null, but\r
+ // since there might be multiple control rows on this\r
+ // page, we'd have to maintain a use count. Rather than\r
+ // doing that we'll let the garbage collector earn its\r
+ // keep. We are also expecting that the raw store will\r
+ // throw errors if we attempt to use an unlatched page.\r
+ }\r
+\r
+ /**\r
+ Search this index page.\r
+ <P>\r
+ This method is very performance sensitive. It is the intention that no\r
+ object allocations occur during the execution of this method.\r
+ <P>\r
+ This method performs a binary search on the page and finds the entry i on\r
+ the page such that entry[i] <= key < entry[i+1]. The result of the search\r
+ is filled into the passed in params structure.\r
+\r
+ @param params the parameters of the search\r
+\r
+ @exception StandardException could be thrown by underlying raw store \r
+ operations.\r
+\r
+ @see SearchParameters\r
+ **/\r
+ protected void searchForEntry(SearchParameters params)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // System.out.println("searchForEntry() enter: params:" + params);\r
+ // System.out.println("searchForEntry() enter: this:" + this);\r
+ // System.out.println("searchForEntry() enter: this page:" + debugPage(params.btree));\r
+ }\r
+\r
+\r
+ // leftrange and rightrange indicates the range of slots to search.\r
+ // The range starts as all the slots on the page not including slot\r
+ // 0 which is the control row.\r
+ int leftrange = 1;\r
+ int rightrange = page.recordCount() - 1;\r
+\r
+ // leftslot and rightslot if non-zero, mean that the key has been\r
+ // compared to the row at that slot. If non-zero the key must be\r
+ // greater than the key at leftslot and the key must be lest than\r
+ // the key at rightslot.\r
+ int leftslot = 0;\r
+ int rightslot = rightrange + 1;\r
+\r
+ int midslot;\r
+ int compare_ret;\r
+\r
+ // search until you either exactly find the key, or you have \r
+ // compared 2 adjacent rows and found the value must exist between\r
+ // the 2.\r
+\r
+\r
+ if (this.use_last_search_result_hint)\r
+ {\r
+ // make sure to set midslot to point to somwhere in the legal range.\r
+ midslot = \r
+ ((this.last_search_result == 0) ? 1 : this.last_search_result);\r
+\r
+ if (midslot > rightrange)\r
+ midslot = rightrange;\r
+ }\r
+ else\r
+ {\r
+ // if we don't think we have a good hint where to start the search\r
+ // just go to the middle.\r
+ midslot = (leftrange + rightrange) / 2;\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ((leftslot != (rightslot - 1)) &&\r
+ !(midslot >= leftrange && midslot <= rightrange))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "midslot = " + midslot +\r
+ ";leftrange = " + leftrange +\r
+ ";rightrange = " + rightrange);\r
+ }\r
+ }\r
+\r
+ \r
+ while (leftslot != (rightslot - 1))\r
+ {\r
+ // Compare the index row to the key.\r
+ compare_ret = \r
+ compareIndexRowFromPageToKey(\r
+ this,\r
+ midslot,\r
+ params.template, params.searchKey, \r
+ params.btree.getConglomerate().nUniqueColumns, \r
+ params.partial_key_match_op,\r
+ params.btree.getConglomerate().ascDescInfo);\r
+\r
+ if (compare_ret == 0)\r
+ {\r
+ // Found exact match\r
+ params.resultSlot = midslot;\r
+ params.resultExact = true;\r
+\r
+ // update the hints based on result of the search.\r
+ use_last_search_result_hint = \r
+ (midslot == this.last_search_result) ? true : false;\r
+ this.last_search_result = midslot;\r
+\r
+ return;\r
+ }\r
+ else if (compare_ret > 0)\r
+ {\r
+ // key falls to the left of midslot\r
+ rightslot = midslot;\r
+ rightrange = midslot - 1;\r
+ }\r
+ else\r
+ {\r
+ // key falls to the right of midslot\r
+ leftslot = midslot;\r
+ leftrange = midslot + 1;\r
+ }\r
+\r
+ midslot = (leftrange + rightrange) / 2;\r
+ //midslot = (leftrange + rightrange) >> 1;\r
+ }\r
+\r
+ // update the hints based on result of the search.\r
+ this.use_last_search_result_hint = \r
+ (leftslot == this.last_search_result);\r
+ this.last_search_result = leftslot;\r
+\r
+ // no exact match found, leftslot will point at the slot on the\r
+ // page just before where the row should be inserted. In the case\r
+ // where the key is before rows on the page then leftslot will be\r
+ // 0 (an empty page is a special case of this).\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (leftslot != rightslot - 1)\r
+ SanityManager.THROWASSERT(\r
+ "leftslot = " + leftslot + "; rightslot = " + rightslot);\r
+ }\r
+\r
+ params.resultSlot = leftslot;\r
+ params.resultExact = false;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // System.out.println("searchForEntry() exit: params:" + params);\r
+ }\r
+\r
+ return;\r
+ }\r
+\r
+ protected void searchForEntryBackward(SearchParameters params)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // System.out.println("searchForEntry() enter: params:" + params);\r
+ // System.out.println("searchForEntry() enter: this:" + this);\r
+ // System.out.println("searchForEntry() enter: this page:" + debugPage(params.btree));\r
+ }\r
+\r
+\r
+ // leftrange and rightrange indicates the range of slots to search.\r
+ // The range starts as all the slots on the page not including slot\r
+ // 0 which is the control row.\r
+ int leftrange = 1;\r
+ int rightrange = page.recordCount() - 1;\r
+\r
+ // leftslot and rightslot if non-zero, mean that the key has been\r
+ // compared to the row at that slot. If non-zero the key must be\r
+ // greater than the key at leftslot and the key must be lest than\r
+ // the key at rightslot.\r
+ int leftslot = 0;\r
+ int rightslot = rightrange + 1;\r
+\r
+ int midslot;\r
+ int compare_ret;\r
+\r
+ // search until you either exactly find the key, or you have \r
+ // compared 2 adjacent rows and found the value must exist between\r
+ // the 2.\r
+\r
+\r
+ if (this.use_last_search_result_hint)\r
+ {\r
+ // make sure to set midslot to point to somwhere in the legal range.\r
+ midslot = \r
+ ((this.last_search_result == 0) ? 1 : this.last_search_result);\r
+\r
+ if (midslot > rightrange)\r
+ midslot = rightrange;\r
+ }\r
+ else\r
+ {\r
+ // if we don't think we have a good hint where to start the search\r
+ // just go to the middle.\r
+ midslot = (leftrange + rightrange) / 2;\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ((leftslot != (rightslot - 1)) &&\r
+ !(midslot >= leftrange && midslot <= rightrange))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "midslot = " + midslot +\r
+ ";leftrange = " + leftrange +\r
+ ";rightrange = " + rightrange);\r
+ }\r
+ }\r
+\r
+ \r
+ while (leftslot != (rightslot - 1))\r
+ {\r
+ // Compare the index row to the key.\r
+ compare_ret = \r
+ compareIndexRowFromPageToKey(\r
+ this,\r
+ midslot,\r
+ params.template, params.searchKey, \r
+ params.btree.getConglomerate().nUniqueColumns, \r
+ params.partial_key_match_op,\r
+ params.btree.getConglomerate().ascDescInfo);\r
+\r
+ if (compare_ret == 0)\r
+ {\r
+ // Found exact match\r
+ params.resultSlot = midslot;\r
+ params.resultExact = true;\r
+\r
+ // update the hints based on result of the search.\r
+ use_last_search_result_hint = \r
+ (midslot == this.last_search_result) ? true : false;\r
+ this.last_search_result = midslot;\r
+\r
+ return;\r
+ }\r
+ else if (compare_ret > 0)\r
+ {\r
+ // key falls to the left of midslot\r
+ rightslot = midslot;\r
+ rightrange = midslot - 1;\r
+ }\r
+ else\r
+ {\r
+ // key falls to the right of midslot\r
+ leftslot = midslot;\r
+ leftrange = midslot + 1;\r
+ }\r
+\r
+ midslot = (leftrange + rightrange) / 2;\r
+ //midslot = (leftrange + rightrange) >> 1;\r
+ }\r
+\r
+ // update the hints based on result of the search.\r
+ this.use_last_search_result_hint = \r
+ (leftslot == this.last_search_result);\r
+ this.last_search_result = leftslot;\r
+\r
+ // no exact match found, leftslot will point at the slot on the\r
+ // page just before where the row should be inserted. In the case\r
+ // where the key is before rows on the page then leftslot will be\r
+ // 0 (an empty page is a special case of this).\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (leftslot != rightslot - 1)\r
+ SanityManager.THROWASSERT(\r
+ "leftslot = " + leftslot + "; rightslot = " + rightslot);\r
+ }\r
+\r
+ params.resultSlot = leftslot;\r
+ params.resultExact = false;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // System.out.println("searchForEntry() exit: params:" + params);\r
+ }\r
+\r
+ return;\r
+ }\r
+\r
+ /**\r
+ Compare two orderable rows, considering nCompareCols, and return -1, 0, or 1\r
+ depending on whether the first row (indexrow) is less than, equal to, or \r
+ greater than the second (key). The key may have fewer columns present \r
+ than nCompareCols.\r
+\r
+ In such a case, if all the columns of the partial key match all of the \r
+ corresponding columns in the index row, then the value passed in in \r
+ partialKeyOrder is returned. The caller should pass in partialKeyOrder=1 \r
+ if the index rows which match a partial key should be considered to be \r
+ greater than the partial key, and -1 if they should be considered to be \r
+ less.\r
+\r
+ This routine only reads objects off the page if it needs them, so if a \r
+ multi-part key differs in the first column the subsequent columns are not\r
+ read.\r
+\r
+ @param indexpage Controlrow of page to get target row from.\r
+ @param slot Slot to get control row from.\r
+ @param indexrow template of the target row (the row in the index).\r
+ @param key the (possibly partial) search key.\r
+ @param nCompareCols the number of columns to compare.\r
+ @param partialKeyOrder what to return on a partial key match.\r
+ @param ascOrDesc column sort order information\r
+ @throws StandardException if lower levels have a problem.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public static int compareIndexRowFromPageToKey(\r
+ ControlRow indexpage,\r
+ int slot,\r
+ DataValueDescriptor[] indexrow, \r
+ DataValueDescriptor[] key,\r
+ int nCompareCols, \r
+ int partialKeyOrder,\r
+ boolean[] ascOrDesc)\r
+ throws StandardException\r
+ {\r
+ int compare_result;\r
+\r
+ // Get the actual number of key columns present\r
+ // in the partial key.\r
+ int partialKeyCols = key.length;\r
+\r
+ // Fetch entire index row from page.\r
+ // RESOLVE (mikem) - it may be more efficient to fetch just the\r
+ // columns you need, but there is overhead currently in raw\r
+ // store, since to get to the n'th column you have to walk \r
+ // through the preceding n-1 columns.\r
+ indexpage.page.fetchFromSlot(\r
+ (RecordHandle) null, slot, indexrow, \r
+ (FetchDescriptor) null,\r
+ true);\r
+\r
+ // Compare corresponding columns in the index row and the key.\r
+ for (int i = 0; i < nCompareCols; i++)\r
+ {\r
+ // See if we have run out of partial key columns.\r
+ if (i >= partialKeyCols)\r
+ {\r
+ // All the columns of the partial key match, and \r
+ // there are more columns in the index row. We\r
+ // want to return -1 or 1, depending on whether the\r
+ // caller wants to direct the search to the beginning\r
+ // of this key range or the beginning of the next\r
+ // one. If the caller passes in -1, the index row\r
+ // will appear less than the partial key, sending the\r
+ // search to the next range ("to the right"). If the\r
+ // caller passes in 1, the index row will appear\r
+ // to be greater than the search key, sending the search\r
+ // to the beginning of the range ("to the left").\r
+ return partialKeyOrder;\r
+ }\r
+\r
+ // Get the corresponding columns to compare\r
+\r
+ // Orderable indexcol = (Orderable) indexrow[i];\r
+ // Orderable keycol = (Orderable) key[i];\r
+\r
+ // Compare them.\r
+ // int r = indexcol.compare(keycol);\r
+\r
+ int r = indexrow[i].compare(key[i]);\r
+\r
+ // If the columns don't compare equal, we're done.\r
+ // Return the sense of the comparison.\r
+ if (r != 0)\r
+ {\r
+ //coulmns could have been sorted in ascending or descending\r
+ //order. depending on ascending/descending order search \r
+ //direction will change.\r
+\r
+ if (ascOrDesc[i]) // true - Ascending order\r
+ return r;\r
+ else\r
+ return -r;\r
+ }\r
+ }\r
+\r
+ // We made it through all the columns, and they must have\r
+ // all compared equal. So return that the rows compare equal.\r
+ return 0;\r
+ }\r
+\r
+ public static int compareIndexRowToKey(\r
+ DataValueDescriptor[] indexrow, \r
+ DataValueDescriptor[] key,\r
+ int nCompareCols, \r
+ int partialKeyOrder,\r
+ boolean[] ascOrDesc)\r
+ throws StandardException\r
+ {\r
+ // Get the actual number of key columns present\r
+ // in the partial key.\r
+ int partialKeyCols = key.length;\r
+\r
+ // Compare corresponding columns in the index row and the key.\r
+ for (int i = 0; i < nCompareCols; i++)\r
+ {\r
+ // See if we have run out of partial key columns.\r
+ if (i >= partialKeyCols)\r
+ {\r
+ // All the columns of the partial key match, and \r
+ // there are more columns in the index row. We\r
+ // want to return -1 or 1, depending on whether the\r
+ // caller wants to direct the search to the beginning\r
+ // of this key range or the beginning of the next\r
+ // one. If the caller passes in -1, the index row\r
+ // will appear less than the partial key, sending the\r
+ // search to the next range ("to the right"). If the\r
+ // caller passes in 1, the index row will appear\r
+ // to be greater than the search key, sending the search\r
+ // to the beginning of the range ("to the left").\r
+ return partialKeyOrder;\r
+ }\r
+\r
+ // Get the corresponding columns to compare\r
+ DataValueDescriptor indexcol = indexrow[i];\r
+ DataValueDescriptor keycol = key[i];\r
+\r
+ // Compare them.\r
+ int r = indexcol.compare(keycol);\r
+\r
+ // If the columns don't compare equal, we're done.\r
+ // Return the sense of the comparison.\r
+ if (r != 0)\r
+ {\r
+ if (ascOrDesc[i]) // true - column in ascending order\r
+ return r;\r
+ else\r
+ return -r;\r
+ }\r
+ }\r
+\r
+ // We made it through all the columns, and they must have\r
+ // all compared equal. So return that the rows compare equal.\r
+ return 0;\r
+ }\r
+\r
+ /**\r
+ ** Perform consistency checks which are common to all\r
+ ** pages that derive from ControlRow (both leaf and \r
+ ** branch pages). The checks are:\r
+ ** <menu>\r
+ ** <li> This page thinks the parent argument is actually\r
+ ** its parent.\r
+ ** <li> The level of this page is 1 less than the level of\r
+ ** the parent.\r
+ ** <li> All the rows on the page are in order.\r
+ ** <li> Both left and right siblings, if they exist, are at\r
+ ** the same level of this page.\r
+ ** <li> This page is the left sibling of its right sibling,\r
+ ** and it's the right sibling of its left sibling.\r
+ ** <li> The last row on the left sibling is < the first\r
+ ** row on this page.\r
+ ** <li> The first row on the right sibling is > than the\r
+ ** the last row on this page.\r
+ ** </menu>\r
+ ** Note that these last two are really only true if there\r
+ ** are never duplicate keys.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/ \r
+ protected void checkGeneric(\r
+ OpenBTree btree, \r
+ ControlRow parent,\r
+ boolean check_other_pages)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(this.page.recordCount() >= 1);\r
+ SanityManager.ASSERT(!this.page.isDeletedAtSlot(0));\r
+\r
+ // Make sure that we think we're a child of the parent.\r
+ if (((parent != null) &&\r
+ (parent.page.getPageNumber() != this.getParentPageNumber())))\r
+ SanityManager.THROWASSERT(this + " not child of " + parent);\r
+\r
+ // Check this page's level.\r
+ if (((parent != null) &&\r
+ (parent.getLevel() != this.getLevel() + 1)))\r
+ SanityManager.THROWASSERT(this +\r
+ " at wrong level when compared to parent:" + parent);\r
+ \r
+ // Check rows are in order.\r
+ checkRowOrder(btree, parent);\r
+\r
+ // Check siblings.\r
+ if (check_other_pages)\r
+ checkSiblings(btree);\r
+ }\r
+ }\r
+\r
+ /**\r
+ ** Check that all rows on the page are in order. This\r
+ ** means that each key is > than the previous key.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ protected boolean checkRowOrder(OpenBTree btree, ControlRow parent)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ RecordHandle lesser_handle = null;\r
+ RecordHandle greater_handle = null;\r
+ DataValueDescriptor[] lesser = getRowTemplate(btree);\r
+ DataValueDescriptor[] greater = getRowTemplate(btree);\r
+ boolean is_consistent = true;\r
+ \r
+ \r
+ int numslots = page.recordCount();\r
+ for (int i = ControlRow.CR_SLOT + 1; (i + 1) < numslots; i++)\r
+ {\r
+ lesser_handle = \r
+ page.fetchFromSlot(\r
+ (RecordHandle) null, i, lesser, \r
+ (FetchDescriptor) null, true); \r
+ greater_handle = \r
+ page.fetchFromSlot(\r
+ (RecordHandle) null, i + 1, greater, \r
+ (FetchDescriptor) null, true); \r
+\r
+ SanityManager.ASSERT(btree.getConglomerate().nUniqueColumns <= \r
+ btree.getConglomerate().nKeyFields);\r
+ int compare_result = \r
+ compareIndexRowToKey(\r
+ lesser, greater,\r
+ btree.getConglomerate().nUniqueColumns, 0,\r
+ btree.getConglomerate().ascDescInfo);\r
+\r
+ // >= 0 means that lesser >= greater\r
+ if (compare_result >= 0)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "Bad order of rows found in conglomerate: " + btree +\r
+ "\n." +\r
+ "compare result = " + compare_result + ". " + \r
+ "nKeyFields = " + btree.getConglomerate().nKeyFields +\r
+ ".\n" + \r
+ this + " rows " + (i) + " and " + (i + 1) +\r
+ " out of order.\n" +\r
+ "row[" + i + "] + " + RowUtil.toString(lesser) + "\n" + \r
+ "row[" + (i + 1) + "] + " + RowUtil.toString(greater) +\r
+ "\ndump of page = " + \r
+ debugPage(btree) +\r
+ "\ndump of parent page = " + \r
+ ((parent != null) ? \r
+ parent.debugPage(btree) : "null parent") +\r
+ "\rawstore dump = " + this.page);\r
+\r
+ is_consistent = false;\r
+ }\r
+ }\r
+ return(is_consistent);\r
+ }\r
+ else\r
+ {\r
+ return(true);\r
+ }\r
+ }\r
+\r
+ protected boolean compareRowsOnSiblings(\r
+ OpenBTree btree,\r
+ ControlRow left_sib,\r
+ ControlRow right_sib)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ boolean is_consistent = true;\r
+\r
+ // Check that left last row is < right page's first row.\r
+ if (left_sib.page.recordCount() > 1 && \r
+ right_sib.page.recordCount() > 1)\r
+ {\r
+ DataValueDescriptor[] left_lastrow = getRowTemplate(btree);\r
+ DataValueDescriptor[] right_firstrow = getRowTemplate(btree);\r
+\r
+ RecordHandle left_lastrow_handle = \r
+ left_sib.page.fetchFromSlot(\r
+ (RecordHandle) null, left_sib.page.recordCount() - 1, \r
+ left_lastrow, \r
+ (FetchDescriptor) null, true); \r
+\r
+ RecordHandle right_firstrow_handle = \r
+ right_sib.page.fetchFromSlot(\r
+ (RecordHandle) null, 1, right_firstrow, \r
+ (FetchDescriptor) null, true); \r
+\r
+ int r = \r
+ compareIndexRowToKey(\r
+ left_lastrow, right_firstrow,\r
+ btree.getConglomerate().nUniqueColumns,\r
+ 0, btree.getConglomerate().ascDescInfo);\r
+\r
+ if (r >= 0)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "last row on left page " + \r
+ left_sib.page.getPageNumber() + \r
+ " > than first row on right page " + \r
+ right_sib.page.getPageNumber() + "\n" + \r
+ "left last row = " + RowUtil.toString(left_lastrow) +\r
+ "right first row = " + RowUtil.toString(right_firstrow)+\r
+ left_sib + " last > first of " + right_sib);\r
+\r
+ is_consistent = false;\r
+ }\r
+ }\r
+ return(is_consistent);\r
+ }\r
+ else\r
+ {\r
+ return(true);\r
+ }\r
+ }\r
+\r
+ /**\r
+ ** Perform checks on the siblings of this page: make sure\r
+ ** that they're at the same level as this page, that they're\r
+ ** mutually linked together, and that the first/last keys\r
+ ** on sibling pages are in order.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ protected void checkSiblings(OpenBTree btree)\r
+ throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // Get this page's left sibling.\r
+ ControlRow leftsib = null;\r
+ ControlRow rightsib = null;\r
+\r
+ try\r
+ {\r
+ try\r
+ {\r
+ leftsib = this.getLeftSibling(btree);\r
+ }\r
+ catch (WaitError e)\r
+ {\r
+ // In a normal system it may be possible to not get\r
+ // the left sibling (some other thread either user\r
+ // or daemon cache cleaner thread) may already have\r
+ // the latch on the page, and waiting on it could cause\r
+ // a latch/latch deadlock. So for now just give up\r
+ // doing the consistency check in this case.\r
+ //\r
+ // RESOLVE (mikem) - We could do fancier things than \r
+ // ignore this error, but the only safe way to wait for\r
+ // a right to left latch is to release current latch which\r
+ // will complicate all the code, and this is only a sanity\r
+ // check.\r
+\r
+ // SanityManager.DEBUG_PRINT(\r
+ // "ControlRow.checkSiblings",\r
+ // this + " left sibling deadlock");\r
+\r
+ // give up on checking the left sibling.\r
+ leftsib = null;\r
+ }\r
+\r
+ // There may not be a left sibling; if there is, check it out.\r
+ if (leftsib != null)\r
+ {\r
+ // Check that it's at the same level as this page.\r
+ if (leftsib.getLevel() != this.getLevel())\r
+ SanityManager.THROWASSERT(\r
+ (leftsib + "not at same level as " + this));\r
+\r
+ // Check that its right sibling is this page.\r
+ long hopefullythis_pageno = \r
+ leftsib.getrightSiblingPageNumber();\r
+\r
+ if (hopefullythis_pageno != this.page.getPageNumber())\r
+ SanityManager.THROWASSERT(\r
+ "right sibling of " + leftsib + " isn't " + this);\r
+\r
+ // Check that its last row is < this page's first row.\r
+ compareRowsOnSiblings(btree, leftsib, this);\r
+\r
+ // Done looking at the left sibling.\r
+ leftsib.release();\r
+ leftsib = null;\r
+ }\r
+\r
+ // Get the right sibling page.\r
+ rightsib = this.getRightSibling(btree);\r
+\r
+ // There may not be a right sibling; if there is, check it out.\r
+ if (rightsib != null)\r
+ {\r
+ // Check that it's at the same level as this page.\r
+ if (rightsib.getLevel() != this.getLevel())\r
+ SanityManager.THROWASSERT(\r
+ rightsib + "not at same level as " + this);\r
+\r
+ // Check that its left sibling is this page.\r
+ long hopefullythis_pageno = \r
+ rightsib.getleftSiblingPageNumber();\r
+\r
+ if (hopefullythis_pageno != this.page.getPageNumber())\r
+ SanityManager.THROWASSERT(\r
+ "left sibling of " + rightsib + " isn't " + this);\r
+\r
+ // Check that its first row is > this page's last row.\r
+ compareRowsOnSiblings(btree, this, rightsib);\r
+\r
+ // Done looking at it.\r
+ rightsib.release();\r
+ rightsib = null;\r
+ }\r
+ }\r
+ finally\r
+ {\r
+ if (leftsib != null)\r
+ leftsib.release();\r
+ if (rightsib != null)\r
+ rightsib.release();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ ** Link this page to the right of the target page.\r
+ ** <P>\r
+ ** Upon entry, this page and the target must be\r
+ ** latched. Upon exit, this page and the target\r
+ ** remain latched.\r
+ ** <P>\r
+ ** This method carefully acquires pages from left\r
+ ** to right in order to avoid deadlocks.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ */\r
+ void linkRight(OpenBTree btree, ControlRow target)\r
+ throws StandardException\r
+ {\r
+ ControlRow rightSibling = null;\r
+\r
+ try\r
+ {\r
+ rightSibling = target.getRightSibling(btree);\r
+ this.setRightSibling(rightSibling);\r
+ this.setLeftSibling(target);\r
+ if (rightSibling != null)\r
+ rightSibling.setLeftSibling(this);\r
+ target.setRightSibling(this);\r
+ }\r
+ finally\r
+ {\r
+ if (rightSibling != null)\r
+ rightSibling.release();\r
+ }\r
+ }\r
+\r
+ /**\r
+ ** Unlink this page from its siblings. This method\r
+ ** will give up and return false rather than run the\r
+ ** risk of a deadlock.\r
+ ** <P>\r
+ ** On entry this page must be latched. The siblings\r
+ ** are latched and unlatched during the operation. Upon\r
+ ** exit, this page will remain latched, but unlinked from\r
+ ** its siblings and deallocated from the container.\r
+ ** <P>\r
+ ** The seemingly odd situation that this page will be\r
+ ** returned latched but deallocated is intentional.\r
+ ** The container will not be able to reuse this page\r
+ ** until the latch is released, and the caller may still\r
+ ** need to read information out of it.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ boolean unlink(OpenBTree btree)\r
+ throws StandardException\r
+ {\r
+ ControlRow leftsib = null;\r
+ ControlRow rightsib = null;\r
+\r
+ \r
+ try \r
+ {\r
+ // Try to get the left sibling, and give up if \r
+ // it can't be obtained without waiting.\r
+\r
+ try\r
+ {\r
+ leftsib = this.getLeftSibling(btree);\r
+ }\r
+ catch (WaitError e)\r
+ {\r
+ return false;\r
+ }\r
+\r
+ // We can wait for the right sibling since it's\r
+ // in the deadlock-free direction.\r
+\r
+ rightsib = this.getRightSibling(btree);\r
+\r
+ // Change the links that pointed to this page to\r
+ // point to the appropriate sibling.\r
+\r
+ if (leftsib != null)\r
+ leftsib.setRightSibling(rightsib);\r
+ if (rightsib != null)\r
+ rightsib.setLeftSibling(leftsib);\r
+\r
+ // Deallocate the page.\r
+ // Would need to clear out aux object here.\r
+ //\r
+ // RESOLVE (mikem) - how to deallocate a page. //\r
+ btree.container.removePage(this.page);\r
+\r
+ // After removePage call the current page is unlatched, and should\r
+ // not be referenced anymore.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(!this.page.isLatched());\r
+ }\r
+\r
+ return true;\r
+ }\r
+ finally\r
+ {\r
+ // Unlatch the siblings.\r
+ if (leftsib != null)\r
+ leftsib.release();\r
+ if (rightsib != null)\r
+ rightsib.release();\r
+ }\r
+ }\r
+\r
+ public Page getPage()\r
+ {\r
+ return(page);\r
+ }\r
+\r
+ /**\r
+ * Get the row.\r
+ * <p>\r
+ * Return the object array that represents the control row for use\r
+ * in raw store fetch, insert, and/or update.\r
+ *\r
+ * @return The row.\r
+ *\r
+ **/\r
+ protected final DataValueDescriptor[] getRow()\r
+ {\r
+ return(row);\r
+ }\r
+\r
+ /*\r
+ * The following methods must be implemented by all\r
+ * control rows.\r
+ */\r
+\r
+ /**\r
+ * Check consistency of the page and its children, returning the number of \r
+ * pages seen, and throwing errors if inconsistencies are found.\r
+ * <p>\r
+ *\r
+ * @return The identifier to be used to open the conglomerate later.\r
+ *\r
+ * @param btree The open btree to associate latches/locks with.\r
+ * @param parent The parent page of this page, "null" if this page is \r
+ * root or if not maintaining parent links.\r
+ * @param check_other_pages\r
+ * Should the consistency check go to other pages (this \r
+ * option breaks the latch protocol).\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ abstract protected int checkConsistency(\r
+ OpenBTree btree, \r
+ ControlRow parent,\r
+ boolean check_other_pages)\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Return the left child pointer for the page.\r
+ * <p>\r
+ * Leaf pages don't have children, so they override this and return null.\r
+ *\r
+ * @return The page which is the leftmost child of this page.\r
+ *\r
+ * @param btree The open btree to associate latches/locks with.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected abstract ControlRow getLeftChild(OpenBTree btree)\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Return the right child pointer for the page.\r
+ * <p>\r
+ * Leaf pages don't have children, so they override this and return null.\r
+ *\r
+ * @return The page which is the rightmost child of this page.\r
+ *\r
+ * @param btree The open btree to associate latches/locks with.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected abstract ControlRow getRightChild(OpenBTree btree)\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Perform page specific initialization.\r
+ * <p>\r
+ *\r
+ **/\r
+ protected abstract void controlRowInit();\r
+\r
+ /**\r
+ * Is the current page the leftmost leaf of tree?\r
+ * <p>\r
+ *\r
+ * @return true if the current page is the leftmost leaf of the tree,\r
+ * else return false.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public abstract boolean isLeftmostLeaf()\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Is the current page the rightmost leaf of tree?\r
+ * <p>\r
+ *\r
+ * @return true if the current page is the rightmost leaf of the tree,\r
+ * else return false.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public abstract boolean isRightmostLeaf()\r
+ throws StandardException;\r
+\r
+ /**\r
+ ** Perform a recursive search, ultimately returning the latched\r
+ ** leaf page and row slot after which the given key belongs.\r
+ ** The slot is returned in the result structure. If the key\r
+ ** exists on the page, the resultExact field will be true. Otherwise,\r
+ ** resultExact field will be false, and the row slot returned will be\r
+ ** the one immediately preceding the position at which the key\r
+ ** belongs.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public abstract ControlRow search(\r
+ SearchParameters search_params)\r
+ throws StandardException;\r
+ /**\r
+ * Get the number of columns in the control row. \r
+ * <p>\r
+ * Control rows all share the first columns as defined by this class and\r
+ * then add columns to the end of the control row. For instance a branch\r
+ * control row add a child page pointer field.\r
+ * <p>\r
+ *\r
+ * @return The total number of columns in the control row.\r
+ **/\r
+ protected abstract int getNumberOfControlRowColumns();\r
+ \r
+ /**\r
+ * Search and return the left most leaf page.\r
+ * <p>\r
+ * Perform a recursive search, ultimately returning the\r
+ * leftmost leaf page which is the first leaf page in the\r
+ * leaf sibling chain. (This method might better be called\r
+ * getFirstLeafPage()).\r
+ *\r
+ * @return The leftmost leaf page.\r
+ *\r
+ * @param btree The open btree to associate latches/locks with.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected abstract ControlRow searchLeft(OpenBTree btree)\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Search and return the right most leaf page.\r
+ * <p>\r
+ * Perform a recursive search, ultimately returning the\r
+ * rightmost leaf page which is the last leaf page in the\r
+ * leaf sibling chain. (This method might better be called\r
+ * getLastLeafPage()).\r
+ *\r
+ * @return The rightmost leaf page.\r
+ *\r
+ * @param btree The open btree to associate latches/locks with.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected abstract ControlRow searchRight(OpenBTree btree)\r
+ throws StandardException;\r
+\r
+ /**\r
+ ** Perform a recursive shrink operation for the key.\r
+ ** If this method returns true, the caller should\r
+ ** remove the corresponding entry for the page.\r
+ ** This routine is not guaranteed to successfully\r
+ ** shrink anything. The page lead to by the key might\r
+ ** turn out not to be empty by the time shrink gets\r
+ ** there, and shrinks will give up if there is a deadlock.\r
+ ** <P>\r
+ ** As currently implemented shrinkFor must be executed while holding\r
+ ** an exclusive container lock on the entire table. It is expected that\r
+ ** this call is made within an internal transaction which has been called\r
+ ** by a post commit thread. Latches are released by the code. The raw \r
+ ** store guarantees that deallocated pages are not seen by other xacts\r
+ ** until the transaction has been committed. \r
+ ** <P>\r
+ ** Note that a non-table level lock implementation must hold latches on\r
+ ** pages affected until end transaction.\r
+ ** <p>\r
+ ** On entry, the current page is latched. On exit, all pages will have\r
+ ** been unlatched. \r
+ ** \r
+ ** @exception StandardException Standard exception policy.\r
+ **/\r
+ protected abstract boolean shrinkFor(\r
+ OpenBTree btree, \r
+ DataValueDescriptor[] key)\r
+ throws StandardException;\r
+\r
+ /**\r
+ * Perform a top down split pass making room for the the key in "row".\r
+ * <p>\r
+ * Perform a split such that a subsequent call to insert\r
+ * given the argument index row will likely find room for it. Since \r
+ * latches are released the client must code for the case where another\r
+ * user has grabbed the space made available by the split pass and be\r
+ * ready to do another split.\r
+ * <p>\r
+ *\r
+ * @return page number of the newly allocated leaf page created by split.\r
+ *\r
+ * @param open_btree The open btree to associate latches with.\r
+ * @param template A scratch area to use while searching for split pass.\r
+ * @param parentpage The parent page of the current page in the split pass.\r
+ * starts at null for root.\r
+ * @param row The key to make room for during the split pass.\r
+ * @param flag A flag used to direct where point of split should be\r
+ * chosen.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ protected abstract long splitFor(\r
+ OpenBTree open_btree, \r
+ DataValueDescriptor[] template, \r
+ BranchControlRow parentpage, \r
+ DataValueDescriptor[] row,\r
+ int flag)\r
+ throws StandardException;\r
+\r
+ /**\r
+ ** Recursively print the tree starting at current node in tree.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public abstract void printTree(\r
+ OpenBTree btree) \r
+ throws StandardException;\r
+ \r
+\r
+\r
+ /*\r
+ ** Methods of AuxObject\r
+ */\r
+\r
+ /**\r
+ Called when the page is being evicted from cache or when a rollback\r
+ happened on the page and may possibly have changed the control row's \r
+ value\r
+\r
+ @see AuxObject#auxObjectInvalidated\r
+ **/\r
+ public void auxObjectInvalidated()\r
+ {\r
+ version = null;\r
+ leftSiblingPageNumber = null;\r
+ rightSiblingPageNumber = null;\r
+ parentPageNumber = null;\r
+ level = null;\r
+ isRoot = null;\r
+ page = null;\r
+ }\r
+\r
+ /**\r
+ * Return a new template for reading a data row from the current page.\r
+ * <p>\r
+ * Default implementation for rows which are the same as the conglomerates\r
+ * template, sub-classes can alter if underlying template is different\r
+ * (for instance branch rows add an extra field at the end).\r
+ *\r
+ * @return Newly allocated template.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public DataValueDescriptor[] getRowTemplate(OpenBTree open_btree)\r
+ throws StandardException\r
+ {\r
+ return(open_btree.getConglomerate().createTemplate(\r
+ open_btree.getRawTran()));\r
+ }\r
+\r
+\r
+ /**\r
+ ** Debug toString() method's.\r
+ **/\r
+\r
+ /**\r
+ * Dump complete information about control row and rows on the page.\r
+ * <p>\r
+ *\r
+ * @return string with all info.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public String debugPage(\r
+ OpenBTree open_btree)\r
+ throws StandardException\r
+ {\r
+ String ret_str;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ StringBuffer string = new StringBuffer(4096);\r
+ string.append(this.toString());\r
+ string.append("\n");\r
+\r
+ DataValueDescriptor[] row = getRowTemplate(open_btree);\r
+\r
+ string.append(\r
+ ConglomerateUtil.debugPage(\r
+ page, ControlRow.CR_SLOT + 1, false, row));\r
+\r
+ ret_str = string.toString();\r
+ }\r
+ else\r
+ {\r
+ ret_str = null;\r
+ }\r
+\r
+ return(ret_str);\r
+ }\r
+\r
+ /**\r
+ * The standard toString().\r
+ * <p>\r
+ * This is a concise print out of the info in the control row, does not\r
+ * include anything the page.\r
+ * <p>\r
+ * \r
+ **/\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ StringBuffer string = new StringBuffer(4096);\r
+\r
+ try {\r
+\r
+\r
+ // LEAF, BRANCH, LEAF-ROOT, BRANCH-ROOT\r
+ string.append((getLevel() == 0) ? "\nLEAF" : "\nBRANCH");\r
+ string.append((getIsRoot()) ? "-ROOT" : "");\r
+\r
+ // (PAGE NUMBER)(LEVEL):num recs \r
+ // example: (107)(lev=2):num recs = 16\r
+ string.append("(");\r
+ string.append(this.page.getPageNumber());\r
+ string.append(")(lev=");\r
+ string.append(level);\r
+ string.append("): num recs = ");\r
+ string.append(this.page.recordCount());\r
+ string.append("\n");\r
+\r
+ // rest of info\r
+ string.append("\t");\r
+\r
+ string.append("left = ");\r
+ string.append(getleftSiblingPageNumber());\r
+ string.append(";");\r
+\r
+ string.append("right = ");\r
+ string.append(getrightSiblingPageNumber());\r
+ string.append(";");\r
+\r
+ string.append("parent = ");\r
+ string.append(getParentPageNumber());\r
+ string.append(";");\r
+\r
+ string.append("isRoot = ");\r
+ string.append(getIsRoot());\r
+ string.append(";");\r
+\r
+ }\r
+ catch (Throwable t)\r
+ {\r
+ string.append(\r
+ "error encountered while doing ControlRow.toString()");\r
+ }\r
+\r
+ return(string.toString());\r
+ }\r
+ else\r
+ {\r
+ return(null);\r
+ }\r
+ }\r
+}\r