📄 btreecontroller.java
字号:
split_open_btree.close(); } split_xact.commit(); split_xact.destroy(); return(new_leaf_pageno); } /** Insert a row into the conglomerate. @param rowToInsert The row to insert into the conglomerate. The stored representations of the row's columns are copied into a new row somewhere in the conglomerate. @return Returns 0 if insert succeeded. Returns ConglomerateController.ROWISDUPLICATE if conglomerate supports uniqueness checks and has been created to disallow duplicates, and the row inserted had key columns which were duplicate of a row already in the table. Other insert failures will raise StandardException's. @exception StandardException Standard exception policy. **/ private int doIns(DataValueDescriptor[] rowToInsert) throws StandardException { LeafControlRow targetleaf = null; LeafControlRow save_targetleaf = null; int insert_slot = 0; int result_slot = 0; int ret_val = 0; boolean reclaim_deleted_rows_attempted = false; if (scratch_template == null) scratch_template = runtime_mem.get_template(); if (SanityManager.DEBUG) this.isIndexableRowConsistent(rowToInsert); // Create the objects needed for the insert. // RESOLVE (mikem) - should we cache this in the controller? SearchParameters sp = new SearchParameters( rowToInsert, SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH, scratch_template, this, false); // RowLocation column is in last column of template. FetchDescriptor lock_fetch_desc = RowUtil.getFetchDescriptorConstant( scratch_template.length - 1); RowLocation lock_row_loc = (RowLocation) scratch_template[scratch_template.length - 1]; // Row locking - lock the row being inserted. if (get_insert_row_lock) { // I don't hold any latch yet so I can wait on this lock, so I // don't care about return value from this call. This // lock can only wait if the base table row was inserted in a // separate transaction which never happens in sql tables, but // does happen in the sparse indexes that synchronization builds. this.getLockingPolicy().lockNonScanRow( this.getConglomerate(), (LeafControlRow) null, (LeafControlRow) null, rowToInsert, (ConglomerateController.LOCK_INS | ConglomerateController.LOCK_UPD)); } while (true) { // Search the location at which the new row should be inserted. if (SanityManager.DEBUG) SanityManager.ASSERT(this.container != null); targetleaf = (LeafControlRow) ControlRow.Get(this, BTree.ROOTPAGEID).search(sp); // Row locking - first lock row previous to row being inserted: // o if (sp.resultExact) then the row must be deleted and // we will be replacing it with the new row, lock // the row before the slot as the previous key. // o else // we will be inserting after the current slot so // lock the current slot as the previous key. // int slot_after_previous = (sp.resultExact ? sp.resultSlot : sp.resultSlot + 1); boolean latch_released = false; latch_released = !this.getLockingPolicy().lockNonScanPreviousRow( this.getConglomerate(), targetleaf, slot_after_previous, lock_fetch_desc, scratch_template, lock_row_loc, this, (ConglomerateController.LOCK_INS_PREVKEY | ConglomerateController.LOCK_UPD), TransactionManager.LOCK_INSTANT_DURATION); // special test to see if latch release code works if (SanityManager.DEBUG) { latch_released = test_errors( this, "BTreeController_doIns", false, this.getLockingPolicy(), targetleaf, latch_released); } if (latch_released) { // Had to release latch in order to get the lock, probably // because of a forward scanner, research tree, and try again. targetleaf = null; continue; } // If the row is there already, simply undelete it. // The rationale for this is, since the index does // not support duplicates, the only way we could // find a duplicate is if we found a deleted row. // If we could lock it, then no other transaction // is deleting it; either this transaction deleted // it earlier, or it's simply a row that the space // reclaimer hasn't reclaimed yet. // Since inserts are done directly (i.e., not to a // location provided by a scan, we will see the // deleted row). if (sp.resultExact) { result_slot = insert_slot = sp.resultSlot; if (this.getConglomerate().nKeyFields != this.getConglomerate().nUniqueColumns) { // The key fields match, but not the row location. We // must wait on the lock on the other row location before // preceding, so as to serialize behind any work being done // to the row as part of another transaction. latch_released = !this.getLockingPolicy().lockNonScanRowOnPage( this.getConglomerate(), targetleaf, insert_slot, lock_fetch_desc, scratch_template, lock_row_loc, ConglomerateController.LOCK_UPD); if (latch_released) { // Had to release latch in order to get the lock, // probably to wait for deleting xact to commit or // abort. Research tree, and try again. targetleaf = null; continue; } } // The row better be deleted, or something is very wrong. if (!(targetleaf.page.isDeletedAtSlot(insert_slot))) { // attempt to insert a duplicate into the index. ret_val = ConglomerateController.ROWISDUPLICATE; break; } else { if (this.getConglomerate().nKeyFields == this.getConglomerate().nUniqueColumns) { // The row that we found deleted is exactly the new row. targetleaf.page.deleteAtSlot( insert_slot, false, this.btree_undo); break; } else if (this.getConglomerate().nUniqueColumns == (this.getConglomerate().nKeyFields - 1)) { // The row that we found deleted has matching keys // which form the unique key fields, // but the nonkey fields may differ (for now the // heap rowlocation is the only nonkey field // allowed). // RESOLVE BT39 (mikem) - when/if heap row location // is not fixed we must handle update failing for // out of space and split if it does. For now // if the update fails because of lack of space // an exception is thrown and the statement is // backed out. Should not happen very often. targetleaf.page.deleteAtSlot( insert_slot, false, this.btree_undo); boolean update_succeeded = true; try { int rowloc_index = this.getConglomerate().nKeyFields - 1; targetleaf.page.updateFieldAtSlot( insert_slot, rowloc_index, (DataValueDescriptor) RowUtil.getColumn( rowToInsert, (FormatableBitSet) null, rowloc_index), this.btree_undo); } catch (StandardException se) { // check if the exception is for out of space if (!se.getMessageId().equals(SQLState.DATA_NO_SPACE_FOR_RECORD)) { throw se; } // The statement exception is // because the update failed for out of // space (ie. the field got longer and there // is no room on the page for the expanded // field). Address this error by falling // through the code and doing a split. update_succeeded = false; // update failed. targetleaf.page.deleteAtSlot( insert_slot, true, this.btree_undo); } if (update_succeeded) break; } else { // Can only happen with non key fields in the btree. throw( StandardException.newException( SQLState.BTREE_UNIMPLEMENTED_FEATURE)); } } } else if (targetleaf.page.recordCount() - 1 < this.getConglomerate().maxRowsPerPage) { // The row wasn't there, so try to insert it // on the page returned by the search. insert_slot = sp.resultSlot + 1; result_slot = insert_slot + 1; // By default maxRowsPerPage is set to MAXINT, some tests // set it small to cause splitting to happen quicker with // less data. if (targetleaf.page.insertAtSlot( insert_slot, rowToInsert, (FormatableBitSet) null, this.btree_undo, Page.INSERT_DEFAULT, AccessFactoryGlobals.BTREE_OVERFLOW_THRESHOLD) != null) { // Insert succeeded, so we're done. break; } // RESOLVE (mikem) - another long row issue. // For now if a row does not fit on a page and there // is only the control row on the page and at most one // other row on the page, throw an exception if (targetleaf.page.recordCount() <= 2) { throw StandardException.newException( SQLState.BTREE_NO_SPACE_FOR_KEY); } // start splitting ... } // Create some space by splitting pages. // determine where in page/table row causing split would go int flag = 0; if (insert_slot == 1) { flag |= ControlRow.SPLIT_FLAG_FIRST_ON_PAGE; if (targetleaf.isLeftmostLeaf()) flag |= ControlRow.SPLIT_FLAG_FIRST_IN_TABLE; } else if (insert_slot == targetleaf.page.recordCount()) { flag |= ControlRow.SPLIT_FLAG_LAST_ON_PAGE; if (targetleaf.isRightmostLeaf()) flag |= ControlRow.SPLIT_FLAG_LAST_IN_TABLE; } long targetleaf_pageno = targetleaf.page.getPageNumber(); if ((targetleaf.page.recordCount() - targetleaf.page.nonDeletedRecordCount()) <= 0) { // Don't do reclaim work if there are no deleted records. reclaim_deleted_rows_attempted = true; } BranchRow branchrow = BranchRow.createBranchRowFromOldLeafRow( rowToInsert, targetleaf_pageno); // Release the target page because (a) it may change as a // result of the split, (b) the latch ordering requires us // to acquire latches from top to bottom, and (c) this // loop should be done in a system transaction. targetleaf.release(); targetleaf = null; start_xact_and_dosplit( !reclaim_deleted_rows_attempted, targetleaf_pageno, scratch_template, branchrow.getRow(), flag); // only attempt to reclaim deleted rows once, otherwise the // split loop could loop forever, trying to reclaim a deleted // row that was not committed. reclaim_deleted_rows_attempted = true; // RESOLVE (mikem) possible optimization could be to save // split location and look there first, if this has // already caused a split. Or even return a latched page // from splitFor(). For now just execute the loop again // searching the tree for somewhere to put the row. } // set in-memory hint of where last row on page was inserted. targetleaf.last_search_result = result_slot; // Check that page just updated is consistent. if (SanityManager.DEBUG) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -