--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.store.access.btree.BTreeController\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
+import java.util.Properties;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;\r
+import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;\r
+import org.apache.derby.iapi.store.access.AccessFactoryGlobals;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.RowLocationRetRowSource;\r
+import org.apache.derby.iapi.store.access.RowUtil;\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import org.apache.derby.iapi.store.raw.ContainerHandle;\r
+import org.apache.derby.iapi.store.raw.FetchDescriptor;\r
+import org.apache.derby.iapi.store.raw.LockingPolicy;\r
+import org.apache.derby.iapi.store.raw.Page;\r
+import org.apache.derby.iapi.store.raw.Transaction;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.impl.store.access.conglomerate.ConglomerateUtil;\r
+import org.apache.derby.impl.store.access.conglomerate.TemplateRow;\r
+\r
+/**\r
+\r
+ A b-tree controller corresponds to an instance of an open b-tree conglomerate.\r
+ <P>\r
+ <B>Concurrency Notes<\B>\r
+ <P>\r
+ The concurrency rules are derived from OpenBTree.\r
+ <P>\r
+ @see OpenBTree\r
+\r
+**/\r
+\r
+public class BTreeController extends OpenBTree implements ConglomerateController\r
+{\r
+\r
+ transient DataValueDescriptor[] scratch_template = null;\r
+\r
+ /**\r
+ * Whether to get lock on the row being inserted, usually this lock\r
+ * has already been gotten when the row was inserted into the base table.\r
+ **/\r
+ boolean get_insert_row_lock;\r
+\r
+ /* Constructors: */\r
+\r
+ public BTreeController()\r
+ {\r
+ }\r
+\r
+ /*\r
+ ** private Methods of BTreeController\r
+ */\r
+\r
+ /**\r
+ * Attempt to reclaim committed deleted rows from the page.\r
+ * <p>\r
+ * Get exclusive latch on page, and then loop backward through\r
+ * page searching for deleted rows which are committed. The routine\r
+ * assumes that it is called from a transaction which cannot have \r
+ * deleted any rows on the page. For each deleted row on the page\r
+ * it attempts to get an exclusive lock on the deleted row, NOWAIT.\r
+ * If it succeeds, and since this row did not delete the row then the\r
+ * row must have been deleted by a transaction which has committed, so\r
+ * it is safe to purge the row. It then purges the row from the page.\r
+ * <p>\r
+ * Note that this routine may remove all rows from the page, it will not\r
+ * attempt a merge in this situation. This is because this routine is\r
+ * called from split which is attempting an insert on the given page, so\r
+ * it would be a waste to merge the page only to split it again to allow\r
+ * the insert of the row causing the split.\r
+ *\r
+ * @return true if at least one row was purged.\r
+ *\r
+ * @param open_btree The already open btree to use to get latch on page.\r
+ * @param pageno The page number of the leaf to attempt the reclaim on.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private boolean reclaim_deleted_rows(\r
+ OpenBTree open_btree,\r
+ long pageno)\r
+ throws StandardException\r
+ {\r
+ boolean purged_at_least_one_row = false;\r
+ ControlRow controlRow = null; \r
+\r
+ try\r
+ {\r
+\r
+ if ((controlRow = ControlRow.get(open_btree, pageno)) == null)\r
+ return(false);\r
+\r
+ LeafControlRow leaf = (LeafControlRow) controlRow;\r
+\r
+ BTreeLockingPolicy btree_locking_policy = \r
+ open_btree.getLockingPolicy();\r
+\r
+\r
+ // The number records that can be reclaimed is:\r
+ // total recs - control row - recs_not_deleted\r
+ int num_possible_commit_delete = \r
+ leaf.page.recordCount() - 1 - leaf.page.nonDeletedRecordCount();\r
+\r
+ if ((num_possible_commit_delete > 0) &&\r
+ (btree_locking_policy.lockScanForReclaimSpace(leaf)))\r
+ {\r
+ // Need to get an exclusive scan lock on the page before we can\r
+ // do any sort of purges, otherwise other concurrent scans would\r
+ // not work. If we can't get the lock NOWAIT, just give up on\r
+ // purging rows and do the split without reclaiming rows.\r
+\r
+ Page page = leaf.page;\r
+\r
+\r
+ // RowLocation column is in last column of template.\r
+ FetchDescriptor lock_fetch_desc = \r
+ RowUtil.getFetchDescriptorConstant(\r
+ scratch_template.length - 1);\r
+\r
+ // loop backward so that purges which affect the slot table \r
+ // don't affect the loop (ie. they only move records we \r
+ // have already looked at).\r
+ for (int slot_no = page.recordCount() - 1; \r
+ slot_no > 0; \r
+ slot_no--) \r
+ {\r
+ if (page.isDeletedAtSlot(slot_no))\r
+ {\r
+ // try to get an exclusive lock on the row, if we can \r
+ // then the row is a committed deleted row and it is \r
+ // safe to purge it.\r
+ if (btree_locking_policy.lockScanCommittedDeletedRow(\r
+ open_btree, leaf, scratch_template, \r
+ lock_fetch_desc, slot_no))\r
+ {\r
+ // the row is a committed deleted row, purge it.\r
+ page.purgeAtSlot(slot_no, 1, true);\r
+\r
+ purged_at_least_one_row = true;\r
+ }\r
+ }\r
+ }\r
+\r
+ }\r
+ }\r
+ catch (java.lang.ClassCastException cce)\r
+ {\r
+ // because we give up the latch on the leaf before entering this\r
+ // routine, the page might change from a leaf to branch. If that\r
+ // happens this routine will get a ClassCastException, and we\r
+ // just give up trying to reclaim space.\r
+ }\r
+ finally\r
+ {\r
+ if (controlRow != null)\r
+ controlRow.release();\r
+\r
+ return(purged_at_least_one_row);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Start an internal transaction and do the split.\r
+ * <p>\r
+ * This routine starts a new transaction, and handles any errors that\r
+ * may come during the transaction. This transation must not obtain any\r
+ * locks as they are likely to conflict with the current user transaction.\r
+ * <p>\r
+ * If attempt_to_reclaim_deleted_rows is true this routine will \r
+ * attempt to reclaim space on the leaf page input, by purging \r
+ * committed deleted rows from the leaf. If it succeeds in purging at\r
+ * least one row, then it will commit the internal transaction and return\r
+ * without actually performing a split. \r
+ *\r
+ * @param scratch_template A scratch template used to search a page.\r
+ * @param rowToInsert The row to insert, make sure during split to\r
+ * make room for this row.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private long \r
+ start_xact_and_dosplit(\r
+ boolean attempt_to_reclaim_deleted_rows,\r
+ long leaf_pageno,\r
+ DataValueDescriptor[] scratch_template, \r
+ DataValueDescriptor[] rowToInsert,\r
+ int flag)\r
+ throws StandardException\r
+ {\r
+ TransactionManager split_xact = null;\r
+ OpenBTree split_open_btree = null;\r
+ ControlRow root = null;\r
+\r
+ // Get an internal transaction to be used for the split.\r
+ split_xact = this.init_open_user_scans.getInternalTransaction();\r
+\r
+ // open the btree again so that actions on it take place in the\r
+ // split_xact, don't get any locks in this transaction.\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (((getOpenMode() & ContainerHandle.MODE_FORUPDATE) !=\r
+ ContainerHandle.MODE_FORUPDATE))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "Container not opened with update should not cause split");\r
+ }\r
+ }\r
+\r
+\r
+ boolean do_split = true;\r
+ if (attempt_to_reclaim_deleted_rows)\r
+ {\r
+ // Get lock on base table.\r
+\r
+ ConglomerateController base_cc = null;\r
+\r
+ try\r
+ {\r
+ base_cc = \r
+ this.getConglomerate().lockTable(\r
+ split_xact, \r
+ (ContainerHandle.MODE_FORUPDATE |\r
+ ContainerHandle.MODE_LOCK_NOWAIT), \r
+ TransactionController.MODE_RECORD,\r
+ TransactionController.ISOLATION_REPEATABLE_READ);\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ // any error just don't try to reclaim deleted rows. The\r
+ // expected error is that we can't get the lock, which the\r
+ // current interface throws as a containerNotFound exception.\r
+ }\r
+\r
+ if (base_cc != null)\r
+ {\r
+ // we got IX lock on the base table, so can try reclaim space.\r
+\r
+\r
+ // We can only reclaim space by opening the btree in row lock \r
+ // mode. Table level lock row recovery is hard as we can't \r
+ // determine if the deleted rows we encounter have been \r
+ // deleted by our parent caller and have been committed or \r
+ // not. We will have to get those rows offline.\r
+ split_open_btree = new OpenBTree();\r
+ split_open_btree.init(\r
+ this.init_open_user_scans, \r
+ split_xact, \r
+ null, // open the container.\r
+ split_xact.getRawStoreXact(), \r
+ false,\r
+ (ContainerHandle.MODE_FORUPDATE | \r
+ ContainerHandle.MODE_LOCK_NOWAIT),\r
+ TransactionManager.MODE_RECORD,\r
+ this.getConglomerate().getBtreeLockingPolicy(\r
+ split_xact.getRawStoreXact(), \r
+ TransactionController.MODE_RECORD,\r
+ LockingPolicy.MODE_RECORD,\r
+ TransactionController.ISOLATION_REPEATABLE_READ, \r
+ (ConglomerateController) base_cc, \r
+ split_open_btree),\r
+ this.getConglomerate(), \r
+ (LogicalUndo) null,\r
+ (DynamicCompiledOpenConglomInfo) null);\r
+\r
+ // don't split if we reclaim any rows.\r
+ do_split = !reclaim_deleted_rows(split_open_btree, leaf_pageno);\r
+\r
+ split_open_btree.close();\r
+ }\r
+ }\r
+\r
+ long new_leaf_pageno = leaf_pageno; \r
+ if (do_split)\r
+ {\r
+ split_open_btree = new OpenBTree();\r
+ split_open_btree.init(\r
+ this.init_open_user_scans, \r
+ split_xact, \r
+ null, // open the container.\r
+ split_xact.getRawStoreXact(), \r
+ false,\r
+ getOpenMode(), // use same mode this controller\r
+ // was opened with\r
+ TransactionManager.MODE_NONE,\r
+ this.getConglomerate().getBtreeLockingPolicy(\r
+ split_xact.getRawStoreXact(), \r
+ this.init_lock_level,\r
+ LockingPolicy.MODE_RECORD,\r
+ TransactionController.ISOLATION_REPEATABLE_READ, \r
+ (ConglomerateController) null, // no base row locks during split\r
+ split_open_btree),\r
+ this.getConglomerate(), \r
+ (LogicalUndo) null,\r
+ (DynamicCompiledOpenConglomInfo) null);\r
+\r
+\r
+ // Get the root page back, and perform a split following the\r
+ // to-be-inserted key. The split releases the root page latch.\r
+ root = ControlRow.get(split_open_btree, BTree.ROOTPAGEID);\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(root.page.isLatched());\r
+\r
+ new_leaf_pageno = \r
+ root.splitFor(\r
+ split_open_btree, scratch_template, \r
+ null, rowToInsert, flag);\r
+\r
+ split_open_btree.close();\r
+ }\r
+\r
+ split_xact.commit();\r
+\r
+ split_xact.destroy();\r
+\r
+ return(new_leaf_pageno);\r
+ }\r
+\r
+ /**\r
+ Insert a row into the conglomerate.\r
+\r
+ @param rowToInsert The row to insert into the conglomerate. The stored\r
+ representations of the row's columns are copied into a new row\r
+ somewhere in the conglomerate.\r
+\r
+ @return Returns 0 if insert succeeded. Returns \r
+ ConglomerateController.ROWISDUPLICATE if conglomerate supports uniqueness\r
+ checks and has been created to disallow duplicates, and the row inserted\r
+ had key columns which were duplicate of a row already in the table. Other\r
+ insert failures will raise StandardException's.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ private int doIns(DataValueDescriptor[] rowToInsert)\r
+ throws StandardException\r
+ {\r
+ LeafControlRow targetleaf = null;\r
+ LeafControlRow save_targetleaf = null;\r
+ int insert_slot = 0;\r
+ int result_slot = 0;\r
+ int ret_val = 0;\r
+ boolean reclaim_deleted_rows_attempted = false;\r
+\r
+ if (scratch_template == null)\r
+ {\r
+ scratch_template = runtime_mem.get_template(getRawTran());\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ this.isIndexableRowConsistent(rowToInsert);\r
+\r
+ // Create the objects needed for the insert.\r
+ // RESOLVE (mikem) - should we cache this in the controller?\r
+ SearchParameters sp = \r
+ new SearchParameters(\r
+ rowToInsert,\r
+ SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH,\r
+ scratch_template, this, false);\r
+\r
+ // RowLocation column is in last column of template.\r
+ FetchDescriptor lock_fetch_desc = \r
+ RowUtil.getFetchDescriptorConstant(\r
+ scratch_template.length - 1);\r
+ RowLocation lock_row_loc = \r
+ (RowLocation) scratch_template[scratch_template.length - 1];\r
+\r
+ // Row locking - lock the row being inserted.\r
+\r
+ if (get_insert_row_lock)\r
+ {\r
+ // I don't hold any latch yet so I can wait on this lock, so I\r
+ // don't care about return value from this call. This\r
+ // lock can only wait if the base table row was inserted in a\r
+ // separate transaction which never happens in sql tables, but\r
+ // does happen in the sparse indexes that synchronization builds.\r
+ \r
+ this.getLockingPolicy().lockNonScanRow(\r
+ this.getConglomerate(),\r
+ (LeafControlRow) null,\r
+ (LeafControlRow) null,\r
+ rowToInsert, \r
+ (ConglomerateController.LOCK_INS | \r
+ ConglomerateController.LOCK_UPD));\r
+ }\r
+\r
+ while (true)\r
+ {\r
+ // Search the location at which the new row should be inserted.\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(this.container != null);\r
+\r
+ targetleaf = (LeafControlRow)\r
+ ControlRow.get(this, BTree.ROOTPAGEID).search(sp);\r
+\r
+\r
+ // Row locking - first lock row previous to row being inserted:\r
+ // o if (sp.resultExact) then the row must be deleted and\r
+ // we will be replacing it with the new row, lock\r
+ // the row before the slot as the previous key.\r
+ // o else \r
+ // we will be inserting after the current slot so\r
+ // lock the current slot as the previous key.\r
+ //\r
+ int slot_after_previous = \r
+ (sp.resultExact ? sp.resultSlot : sp.resultSlot + 1);\r
+\r
+ boolean latch_released = false;\r
+\r
+ latch_released = \r
+ !this.getLockingPolicy().lockNonScanPreviousRow(\r
+ this.getConglomerate(),\r
+ targetleaf, \r
+ slot_after_previous, \r
+ lock_fetch_desc,\r
+ scratch_template,\r
+ lock_row_loc,\r
+ this, \r
+ (ConglomerateController.LOCK_INS_PREVKEY |\r
+ ConglomerateController.LOCK_UPD),\r
+ TransactionManager.LOCK_INSTANT_DURATION);\r
+\r
+ // special test to see if latch release code works\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ latch_released = \r
+ test_errors(\r
+ this,\r
+ "BTreeController_doIns", false,\r
+ this.getLockingPolicy(), \r
+ targetleaf, latch_released);\r
+ }\r
+\r
+ if (latch_released)\r
+ {\r
+ // Had to release latch in order to get the lock, probably \r
+ // because of a forward scanner, research tree, and try again.\r
+ targetleaf = null;\r
+ continue;\r
+ }\r
+\r
+ // If the row is there already, simply undelete it.\r
+ // The rationale for this is, since the index does\r
+ // not support duplicates, the only way we could\r
+ // find a duplicate is if we found a deleted row.\r
+ // If we could lock it, then no other transaction\r
+ // is deleting it; either this transaction deleted\r
+ // it earlier, or it's simply a row that the space\r
+ // reclaimer hasn't reclaimed yet.\r
+ // Since inserts are done directly (i.e., not to a\r
+ // location provided by a scan, we will see the \r
+ // deleted row).\r
+ if (sp.resultExact)\r
+ {\r
+ result_slot = insert_slot = sp.resultSlot;\r
+\r
+ if (this.getConglomerate().nKeyFields != \r
+ this.getConglomerate().nUniqueColumns)\r
+ {\r
+ // The key fields match, but not the row location. We\r
+ // must wait on the lock on the other row location before\r
+ // preceding, so as to serialize behind any work being done\r
+ // to the row as part of another transaction.\r
+\r
+ latch_released = \r
+ !this.getLockingPolicy().lockNonScanRowOnPage(\r
+ this.getConglomerate(), targetleaf, insert_slot, \r
+ lock_fetch_desc, scratch_template, lock_row_loc,\r
+ ConglomerateController.LOCK_UPD);\r
+\r
+ if (latch_released)\r
+ {\r
+ // Had to release latch in order to get the lock, \r
+ // probably to wait for deleting xact to commit or \r
+ // abort. Research tree, and try again.\r
+ targetleaf = null;\r
+ continue;\r
+ }\r
+ }\r
+\r
+ // The row better be deleted, or something is very wrong.\r
+\r
+ if (!(targetleaf.page.isDeletedAtSlot(insert_slot)))\r
+ {\r
+ // attempt to insert a duplicate into the index.\r
+ ret_val = ConglomerateController.ROWISDUPLICATE;\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ if (this.getConglomerate().nKeyFields == \r
+ this.getConglomerate().nUniqueColumns)\r
+ {\r
+ // The row that we found deleted is exactly the new row.\r
+ targetleaf.page.deleteAtSlot(\r
+ insert_slot, false, this.btree_undo);\r
+\r
+ break;\r
+ }\r
+ else if (this.getConglomerate().nUniqueColumns == \r
+ (this.getConglomerate().nKeyFields - 1))\r
+ {\r
+ // The row that we found deleted has matching keys\r
+ // which form the unique key fields,\r
+ // but the nonkey fields may differ (for now the\r
+ // heap rowlocation is the only nonkey field \r
+ // allowed).\r
+ \r
+ // RESOLVE BT39 (mikem) - when/if heap row location\r
+ // is not fixed we must handle update failing for\r
+ // out of space and split if it does. For now\r
+ // if the update fails because of lack of space\r
+ // an exception is thrown and the statement is \r
+ // backed out. Should not happen very often.\r
+ targetleaf.page.deleteAtSlot(\r
+ insert_slot, false, this.btree_undo);\r
+\r
+ boolean update_succeeded = true;\r
+\r
+ try \r
+ {\r
+ int rowloc_index = \r
+ this.getConglomerate().nKeyFields - 1;\r
+ targetleaf.page.updateFieldAtSlot(\r
+ insert_slot, rowloc_index, \r
+ (DataValueDescriptor) RowUtil.getColumn(\r
+ rowToInsert, \r
+ (FormatableBitSet) null, rowloc_index),\r
+ this.btree_undo);\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ // check if the exception is for out of space\r
+ if (!se.getMessageId().equals(SQLState.DATA_NO_SPACE_FOR_RECORD))\r
+ {\r
+ throw se;\r
+ }\r
+\r
+ // The statement exception is\r
+ // because the update failed for out of\r
+ // space (ie. the field got longer and there\r
+ // is no room on the page for the expanded\r
+ // field). Address this error by falling\r
+ // through the code and doing a split.\r
+ update_succeeded = false; // update failed.\r
+ targetleaf.page.deleteAtSlot(\r
+ insert_slot, true, this.btree_undo);\r
+ }\r
+\r
+ if (update_succeeded)\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ // Can only happen with non key fields in the btree.\r
+ throw(\r
+ StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE));\r
+ }\r
+ }\r
+ }\r
+ else if (targetleaf.page.recordCount() - 1 < \r
+ this.getConglomerate().maxRowsPerPage)\r
+ {\r
+ // The row wasn't there, so try to insert it\r
+ // on the page returned by the search.\r
+ insert_slot = sp.resultSlot + 1;\r
+ result_slot = insert_slot + 1;\r
+\r
+ // By default maxRowsPerPage is set to MAXINT, some tests\r
+ // set it small to cause splitting to happen quicker with\r
+ // less data.\r
+\r
+ if (targetleaf.page.insertAtSlot(\r
+ insert_slot, \r
+ rowToInsert, (FormatableBitSet) null,\r
+ this.btree_undo,\r
+ Page.INSERT_DEFAULT,\r
+ AccessFactoryGlobals.BTREE_OVERFLOW_THRESHOLD) != null)\r
+ {\r
+ // Insert succeeded, so we're done.\r
+\r
+ break;\r
+ }\r
+\r
+ // RESOLVE (mikem) - another long row issue.\r
+ // For now if a row does not fit on a page and there \r
+ // is only the control row on the page and at most one\r
+ // other row on the page, throw an exception\r
+\r
+ if (targetleaf.page.recordCount() <= 2)\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_NO_SPACE_FOR_KEY);\r
+ }\r
+\r
+ // start splitting ...\r
+ }\r
+\r
+ \r
+ // Create some space by splitting pages.\r
+\r
+ // determine where in page/table row causing split would go\r
+ int flag = 0;\r
+ if (insert_slot == 1)\r
+ {\r
+ flag |= ControlRow.SPLIT_FLAG_FIRST_ON_PAGE;\r
+ if (targetleaf.isLeftmostLeaf())\r
+ flag |= ControlRow.SPLIT_FLAG_FIRST_IN_TABLE;\r
+ }\r
+ else if (insert_slot == targetleaf.page.recordCount())\r
+ {\r
+ flag |= ControlRow.SPLIT_FLAG_LAST_ON_PAGE;\r
+ if (targetleaf.isRightmostLeaf())\r
+ flag |= ControlRow.SPLIT_FLAG_LAST_IN_TABLE;\r
+ }\r
+\r
+ long targetleaf_pageno = targetleaf.page.getPageNumber();\r
+\r
+ if ((targetleaf.page.recordCount() - \r
+ targetleaf.page.nonDeletedRecordCount()) <= 0)\r
+ {\r
+ // Don't do reclaim work if there are no deleted records.\r
+ reclaim_deleted_rows_attempted = true;\r
+ }\r
+\r
+ BranchRow branchrow = \r
+ BranchRow.createBranchRowFromOldLeafRow(\r
+ rowToInsert, targetleaf_pageno);\r
+\r
+ // Release the target page because (a) it may change as a \r
+ // result of the split, (b) the latch ordering requires us \r
+ // to acquire latches from top to bottom, and (c) this \r
+ // loop should be done in a system transaction.\r
+ targetleaf.release();\r
+ targetleaf = null;\r
+\r
+ start_xact_and_dosplit(\r
+ !reclaim_deleted_rows_attempted, targetleaf_pageno, \r
+ scratch_template, branchrow.getRow(), flag);\r
+\r
+ // only attempt to reclaim deleted rows once, otherwise the\r
+ // split loop could loop forever, trying to reclaim a deleted\r
+ // row that was not committed.\r
+ reclaim_deleted_rows_attempted = true;\r
+\r
+ // RESOLVE (mikem) possible optimization could be to save\r
+ // split location and look there first, if this has \r
+ // already caused a split. Or even return a latched page\r
+ // from splitFor(). For now just execute the loop again\r
+ // searching the tree for somewhere to put the row.\r
+ }\r
+\r
+ // set in-memory hint of where last row on page was inserted.\r
+ targetleaf.last_search_result = result_slot;\r
+\r
+ // Check that page just updated is consistent.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (SanityManager.DEBUG_ON("enableBtreeConsistencyCheck"))\r
+ {\r
+ targetleaf.checkConsistency(this, null, true);\r
+ }\r
+ }\r
+\r
+ // Done with the target page.\r
+ targetleaf.release();\r
+ targetleaf = null;\r
+\r
+ // return the status about insert - 0 is ok, or duplicate status.\r
+ return(ret_val);\r
+ }\r
+\r
+ /**\r
+ * Just insert the row on the current page/slot if it fits.\r
+ * <p>\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private boolean do_load_insert(\r
+ DataValueDescriptor[] rowToInsert,\r
+ LeafControlRow leaf,\r
+ int insert_slot)\r
+ throws StandardException\r
+ {\r
+ LeafControlRow old_leaf = null;\r
+ boolean row_inserted = false;\r
+ int num_rows_on_page = leaf.page.recordCount() - 1;\r
+\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(insert_slot == leaf.page.recordCount());\r
+ SanityManager.ASSERT(\r
+ leaf.getrightSiblingPageNumber() == \r
+ ContainerHandle.INVALID_PAGE_NUMBER);\r
+ this.isIndexableRowConsistent(rowToInsert);\r
+ }\r
+\r
+ if (num_rows_on_page < this.getConglomerate().maxRowsPerPage)\r
+ {\r
+ // By default maxRowsPerPage is set to MAXINT, some tests\r
+ // set it small to cause splitting to happen quicker with\r
+ // less data.\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // Caller should have sorted and done duplicate checking.\r
+\r
+ if (insert_slot > 1)\r
+ {\r
+ // verify that the row inserted is >= than previous row.\r
+ int compare_result =\r
+ ControlRow.compareIndexRowFromPageToKey(\r
+ leaf,\r
+ insert_slot - 1,\r
+ scratch_template,\r
+ rowToInsert,\r
+ this.getConglomerate().nUniqueColumns,\r
+ 0,\r
+ this.getConglomerate().ascDescInfo);\r
+ \r
+ if (compare_result >= 0)\r
+ {\r
+ // Rows must be presented in order, so the row we are\r
+ // inserting must always be greater than the previous \r
+ // row on the page.\r
+ SanityManager.THROWASSERT("result = " + compare_result);\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ if (leaf.page.insertAtSlot(\r
+ insert_slot, \r
+ rowToInsert, \r
+ (FormatableBitSet) null, \r
+ this.btree_undo,\r
+ Page.INSERT_DEFAULT,\r
+ AccessFactoryGlobals.BTREE_OVERFLOW_THRESHOLD) != null)\r
+ {\r
+ // Insert succeeded, so we're done.\r
+ row_inserted = true;\r
+ }\r
+ else\r
+ {\r
+ // RESOLVE (mikem) - another long row issue.\r
+ // For now if a row does not fit on a page and there \r
+ // is only the control row on the page and at most one\r
+ // other row on the page, throw an exception\r
+\r
+ if (leaf.page.recordCount() <= 2)\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_NO_SPACE_FOR_KEY);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Check that page just updated is consistent.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (SanityManager.DEBUG_ON("enableBtreeConsistencyCheck"))\r
+ {\r
+ leaf.checkConsistency(this, null, true);\r
+ }\r
+ }\r
+\r
+ return(row_inserted);\r
+ }\r
+\r
+ /**\r
+ * Create room to insert a row to the right of the largest key in table.\r
+ * <p>\r
+ * Perform a split pass on the tree which will move the largest key in\r
+ * leaf right to a new leaf, splitting parent branch pages as necessary.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ private LeafControlRow do_load_split(\r
+ DataValueDescriptor[] rowToInsert,\r
+ LeafControlRow leaf)\r
+ throws StandardException\r
+ {\r
+ LeafControlRow new_leaf = null;\r
+\r
+ BranchRow branchrow = \r
+ BranchRow.createBranchRowFromOldLeafRow(\r
+ rowToInsert, leaf.page.getPageNumber());\r
+\r
+ // Release the target page because (a) it may change as a \r
+ // result of the split, (b) the latch ordering requires us \r
+ // to acquire latches from top to bottom, and (c) this \r
+ // loop should be done in a system transaction.\r
+ long old_leafpage = leaf.page.getPageNumber();\r
+\r
+ leaf.release();\r
+ leaf = null;\r
+ \r
+ long new_leaf_pageno = \r
+ start_xact_and_dosplit(\r
+ false /* don't try to reclaim deleted rows */,\r
+ old_leafpage,\r
+ scratch_template, \r
+ branchrow.getRow(), \r
+ (ControlRow.SPLIT_FLAG_LAST_ON_PAGE | \r
+ ControlRow.SPLIT_FLAG_LAST_IN_TABLE));\r
+\r
+ new_leaf = (LeafControlRow) ControlRow.get(this, new_leaf_pageno);\r
+\r
+ // The leaf must be the rightmost leaf in the table, the first time\r
+ // the root grows from leaf to branch it will be a leaf with many\r
+ // rows which will probably have to be split soon, after that it will\r
+ // be a leaf with only one row. The current algorithm requires that\r
+ // there be at least one row for duplicate checking (the duplicate\r
+ // checking code does not handle going left to the previous leaf) - \r
+ // this is the way the split at rightmost leaf row works currently.\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (new_leaf.getrightSiblingPageNumber() != \r
+ ContainerHandle.INVALID_PAGE_NUMBER)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "new_leaf.getrightSiblingPageNumber() = " + \r
+ new_leaf.getrightSiblingPageNumber());\r
+ }\r
+ if (new_leaf.page.recordCount() <= 1)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "new_leaf.page.recordCount() = " + \r
+ new_leaf.page.recordCount());\r
+ }\r
+ }\r
+\r
+ return(new_leaf);\r
+ }\r
+\r
+\r
+\r
+ /*\r
+ ** public Methods of BTreeController\r
+ */\r
+\r
+ /**\r
+ Initialize the controller for use.\r
+ <p>\r
+ Any changes to this method will probably have to be reflected in close as \r
+ well.\r
+ <p>\r
+ Currently delegates to OpenBTree. If the btree controller ends up not \r
+ having any state of its own, we can remove this method (the VM will \r
+ dispatch to OpenBTree), gaining some small efficiency. For now, this \r
+ method remains for clarity. \r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public void init(\r
+ TransactionManager xact_manager,\r
+ boolean hold,\r
+ ContainerHandle container,\r
+ Transaction rawtran, \r
+ int open_mode,\r
+ int lock_level,\r
+ BTreeLockingPolicy btree_locking_policy,\r
+ BTree conglomerate,\r
+ LogicalUndo undo,\r
+ StaticCompiledOpenConglomInfo static_info,\r
+ DynamicCompiledOpenConglomInfo dynamic_info)\r
+ throws StandardException\r
+ {\r
+ get_insert_row_lock = \r
+ ((open_mode & \r
+ TransactionController.OPENMODE_BASEROW_INSERT_LOCKED) == 0);\r
+\r
+ super.init(\r
+ xact_manager, xact_manager, \r
+ container, rawtran, hold, open_mode,\r
+ lock_level, btree_locking_policy,\r
+ conglomerate, undo, dynamic_info);\r
+ }\r
+\r
+ /*\r
+ ** Methods of ConglomerateController\r
+ */\r
+\r
+ /**\r
+ Close the conglomerate controller.\r
+ <p>\r
+ Any changes to this method will probably have to be reflected in close as \r
+ well.\r
+ <p>\r
+ Currently delegates to OpenBTree. If the btree controller ends up not \r
+ having any state of its own, we can remove this method (the VM will \r
+ dispatch to OpenBTree), gaining some small efficiency. For now, this \r
+ method remains for clarity. \r
+\r
+ @see ConglomerateController#close\r
+ **/\r
+ public void close()\r
+ throws StandardException\r
+ {\r
+ super.close();\r
+\r
+ // If we are closed due to catching an error in the middle of init,\r
+ // xact_manager may not be set yet. \r
+ if (getXactMgr() != null)\r
+ getXactMgr().closeMe(this);\r
+ }\r
+\r
+ /**\r
+ * Close conglomerate controller as part of terminating a transaction.\r
+ * <p>\r
+ * Use this call to close the conglomerate controller resources as part of\r
+ * committing or aborting a transaction. The normal close() routine may \r
+ * do some cleanup that is either unnecessary, or not correct due to the \r
+ * unknown condition of the controller following a transaction ending error.\r
+ * Use this call when closing all controllers as part of an abort of a \r
+ * transaction.\r
+ * <p)\r
+ * This call is meant to only be used internally by the Storage system,\r
+ * clients of the storage system should use the simple close() interface.\r
+ * <p>\r
+ * RESOLVE (mikem) - move this call to ConglomerateManager so it is\r
+ * obvious that non-access clients should not call this.\r
+ *\r
+ * @param closeHeldScan If true, means to close controller even if\r
+ * it has been opened to be kept opened \r
+ * across commit. This is\r
+ * used to close these controllers on abort.\r
+ *\r
+ * @return boolean indicating that the close has resulted in a real close\r
+ * of the controller. A held scan will return false if \r
+ * called by closeForEndTransaction(false), otherwise it \r
+ * will return true. A non-held scan will always return \r
+ * true.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public boolean closeForEndTransaction(boolean closeHeldScan)\r
+ throws StandardException\r
+ {\r
+ super.close();\r
+\r
+ if ((!getHold()) || closeHeldScan) \r
+ {\r
+ // If we are closed due to catching an error in the middle of init,\r
+ // xact_manager may not be set yet. \r
+ if (getXactMgr() != null)\r
+ getXactMgr().closeMe(this);\r
+\r
+ return(true);\r
+ }\r
+ else\r
+ {\r
+ return(false);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Insert a row into the conglomerate.\r
+ @see ConglomerateController#insert\r
+\r
+ @param row The row to insert into the conglomerate. The stored\r
+ representations of the row's columns are copied into a new row\r
+ somewhere in the conglomerate.\r
+\r
+ @return Returns 0 if insert succeeded. Returns \r
+ ConglomerateController.ROWISDUPLICATE if conglomerate supports uniqueness\r
+ checks and has been created to disallow duplicates, and the row inserted\r
+ had key columns which were duplicate of a row already in the table. Other\r
+ insert failures will raise StandardException's.\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public int insert(DataValueDescriptor[] row) \r
+ throws StandardException\r
+ {\r
+\r
+ if (isClosed())\r
+ {\r
+ if (getHold())\r
+ {\r
+ reopen();\r
+ }\r
+ else\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_IS_CLOSED,\r
+ new Long(err_containerid));\r
+ } \r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(this.container != null);\r
+\r
+ TemplateRow.checkPartialColumnTypes(\r
+ this.getConglomerate().format_ids, \r
+ (FormatableBitSet) null, (int []) null, row);\r
+ }\r
+\r
+ return doIns(row);\r
+ }\r
+\r
+ /**\r
+ Return whether this is a keyed conglomerate.\r
+ <p>\r
+ All b-trees are keyed.\r
+ @see ConglomerateController#isKeyed\r
+ **/\r
+ public boolean isKeyed()\r
+ {\r
+ return(true);\r
+ }\r
+\r
+ /*\r
+ * Request the system properties associated with a table. \r
+ * <p>\r
+ * Request the value of properties that are associated with a table. The\r
+ * following properties can be requested:\r
+ * derby.storage.pageSize \r
+ * derby.storage.pageReservedSpace\r
+ * derby.storage.minimumRecordSize\r
+ * derby.storage.initialPages\r
+ * <p>\r
+ * To get the value of a particular property add it to the property list,\r
+ * and on return the value of the property will be set to it's current \r
+ * value. For example:\r
+ *\r
+ * get_prop(ConglomerateController cc)\r
+ * {\r
+ * Properties prop = new Properties();\r
+ * prop.put("derby.storage.pageSize", "");\r
+ * cc.getTableProperties(prop);\r
+ *\r
+ * System.out.println(\r
+ * "table's page size = " + \r
+ * prop.getProperty("derby.storage.pageSize");\r
+ * }\r
+ *\r
+ * @param prop Property list to fill in.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public void getTableProperties(Properties prop)\r
+ throws StandardException\r
+ {\r
+ if (this.container == null)\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_IS_CLOSED,\r
+ new Long(err_containerid));\r
+ }\r
+\r
+ container.getContainerProperties(prop);\r
+\r
+ return;\r
+ }\r
+\r
+ /**\r
+ * Request set of properties associated with a table. \r
+ * <p>\r
+ * Returns a property object containing all properties that the store\r
+ * knows about, which are stored persistently by the store. This set\r
+ * of properties may vary from implementation to implementation of the\r
+ * store.\r
+ * <p>\r
+ * This call is meant to be used only for internal query of the properties\r
+ * by jbms, for instance by language during bulk insert so that it can\r
+ * create a new conglomerate which exactly matches the properties that\r
+ * the original container was created with. This call should not be used\r
+ * by the user interface to present properties to users as it may contain\r
+ * properties that are meant to be internal to jbms. Some properties are \r
+ * meant only to be specified by jbms code and not by users on the command\r
+ * line.\r
+ * <p>\r
+ * Note that not all properties passed into createConglomerate() are stored\r
+ * persistently, and that set may vary by store implementation.\r
+ *\r
+ * @param prop Property list to add properties to. If null, routine will\r
+ * create a new Properties object, fill it in and return it.\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public Properties getInternalTablePropertySet(Properties prop)\r
+ throws StandardException\r
+ {\r
+ Properties ret_properties = \r
+ ConglomerateUtil.createRawStorePropertySet(prop);\r
+\r
+ getTableProperties(ret_properties);\r
+\r
+ return(ret_properties);\r
+ }\r
+\r
+ /**\r
+ * Load rows from rowSource into the opened btree.\r
+ * <p>\r
+ * Efficiently load rows into the already opened btree. The btree must\r
+ * be table locked, as no row locks will be requested by this routine. \r
+ * On exit from this routine the conglomerate will be closed (on both\r
+ * error or success).\r
+ * <p>\r
+ * This routine does an almost bottom up build of a btree. It assumes\r
+ * all rows arrive in sorted order, and inserts them directly into the\r
+ * next (to the right) spot in the current leaf until there is no space.\r
+ * Then it calls the generic split code to add the next leaf (RESOLVE - \r
+ * in the future we could optimize this to split bottom up rather than\r
+ * top down for create index).\r
+ *\r
+ * @exception StandardException Standard exception policy. If conglomerate\r
+ * supports uniqueness checks and has been \r
+ * created to disallow duplicates, and one of \r
+ * the rows being loaded had key columns which\r
+ * were duplicate of a row already in the \r
+ * conglomerate, then raise \r
+ * SQLState.STORE_CONGLOMERATE_DUPLICATE_KEY_EXCEPTION.\r
+ *\r
+ * @see org.apache.derby.iapi.store.access.conglomerate.Conglomerate#load\r
+ **/\r
+ public long load(\r
+ TransactionManager xact_manager,\r
+ boolean createConglom,\r
+ RowLocationRetRowSource rowSource)\r
+ throws StandardException\r
+ {\r
+ long num_rows_loaded = 0;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(createConglom,\r
+ "Cannot load a btree incrementally - it must either be entirely logged, or entirely not logged. Doesn't make sense to log only the allocation when one cannot guarantee to not touch any pre-existing pages");\r
+ }\r
+\r
+ if (scratch_template == null)\r
+ {\r
+ scratch_template = runtime_mem.get_template(getRawTran());\r
+ }\r
+\r
+ LeafControlRow current_leaf = null;\r
+\r
+ try \r
+ {\r
+ // Btree must just have been created and empty, so there must\r
+ // be one root leaf page which is empty except for the control row.\r
+ current_leaf = \r
+ (LeafControlRow) ControlRow.get(this, BTree.ROOTPAGEID);\r
+ int current_insert_slot = 1;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // root must be empty except for the control row.\r
+ SanityManager.ASSERT(current_leaf.page.recordCount() == 1);\r
+ }\r
+ \r
+ // now loop thru the row source and insert into the btree\r
+ FormatableBitSet validColumns = rowSource.getValidColumns();\r
+ \r
+ // get the next row and its valid columns from the rowSource\r
+ DataValueDescriptor[] row;\r
+ while ((row = rowSource.getNextRowFromRowSource()) != null)\r
+ {\r
+ num_rows_loaded++;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ validColumns == null, "Does not support partial row");\r
+ }\r
+\r
+ while (true)\r
+ {\r
+ if (do_load_insert(row, current_leaf, current_insert_slot))\r
+ {\r
+ // row inserted successfully.\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ // if insert fails, do a split pass. There is an edge\r
+ // case where multiple split passes are necessary if\r
+ // branch splits are necessary, thus the loop. It is\r
+ // most likely that only a single split pass will be\r
+ // necessary.\r
+ current_leaf = do_load_split(row, current_leaf);\r
+\r
+ current_insert_slot = current_leaf.page.recordCount();\r
+ }\r
+ }\r
+ current_insert_slot++;\r
+ }\r
+\r
+ current_leaf.release();\r
+ current_leaf = null;\r
+\r
+ // Loading done, must flush all pages to disk since it is unlogged.\r
+ if (!this.getConglomerate().isTemporary())\r
+ container.flushContainer();\r
+ }\r
+ finally\r
+ {\r
+ this.close();\r
+ }\r
+\r
+ return(num_rows_loaded);\r
+ }\r
+\r
+ /*\r
+ ** Methods of ConglomerateController which are not supported.\r
+ */\r
+\r
+ /**\r
+ Delete a row from the conglomerate. \r
+ @see ConglomerateController#delete\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public boolean delete(RowLocation loc)\r
+ throws StandardException\r
+ {\r
+ throw(StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE));\r
+ }\r
+\r
+ /**\r
+ Fetch the row at the given location.\r
+ @see ConglomerateController#fetch\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public boolean fetch(\r
+ RowLocation loc, \r
+ DataValueDescriptor[] row, \r
+ FormatableBitSet validColumns) \r
+ throws StandardException\r
+ {\r
+ throw(StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE));\r
+ }\r
+\r
+ /**\r
+ Fetch the row at the given location.\r
+ @see ConglomerateController#fetch\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public boolean fetch(\r
+ RowLocation loc, \r
+ DataValueDescriptor[] row, \r
+ FormatableBitSet validColumns,\r
+ boolean waitForLock) \r
+ throws StandardException\r
+ {\r
+ throw(StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE));\r
+ }\r
+\r
+ /**\r
+ Insert a row into the conglomerate, and store its location in the\r
+ provided template row location.\r
+\r
+ Unimplemented by btree.\r
+\r
+ @see ConglomerateController#insertAndFetchLocation\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public void insertAndFetchLocation(\r
+ DataValueDescriptor[] row,\r
+ RowLocation templateRowLocation)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+\r
+ /**\r
+ Return a row location object of the correct type to be\r
+ used in calls to insertAndFetchLocation.\r
+\r
+ @see ConglomerateController#newRowLocationTemplate\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public RowLocation newRowLocationTemplate()\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+\r
+ /**\r
+ * Lock the given row location.\r
+ * <p>\r
+ * Should only be called by access.\r
+ * <p>\r
+ * This call can be made on a ConglomerateController that was opened\r
+ * for locking only.\r
+ * <p>\r
+ * RESOLVE (mikem) - move this call to ConglomerateManager so it is\r
+ * obvious that non-access clients should not call this.\r
+ *\r
+ * @return true if lock was granted, only can be false if wait was false.\r
+ *\r
+ * @param loc The "RowLocation" which describes the exact row to lock.\r
+ * @param wait Should the lock call wait to be granted?\r
+ *\r
+ * @exception StandardException Standard exception policy.\r
+ **/\r
+ public boolean lockRow(\r
+ RowLocation loc,\r
+ int lock_operation,\r
+ boolean wait,\r
+ int lock_duration)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+\r
+ public boolean lockRow(\r
+ long page_num,\r
+ int record_id,\r
+ int lock_operation,\r
+ boolean wait,\r
+ int lock_duration)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+\r
+ public void unlockRowAfterRead(\r
+ RowLocation loc,\r
+ boolean forUpdate,\r
+ boolean row_qualifies)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+\r
+ /**\r
+ Replace the entire row at the given location. \r
+ @see ConglomerateController#replace\r
+\r
+ @exception StandardException Standard exception policy.\r
+ **/\r
+ public boolean replace(\r
+ RowLocation loc, \r
+ DataValueDescriptor[] row, \r
+ FormatableBitSet validColumns)\r
+ throws StandardException\r
+ {\r
+ throw StandardException.newException(\r
+ SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+ }\r
+}\r