📄 btreescan.java
字号:
} boolean latch_released = !this.getLockingPolicy().lockScan( pos.next_leaf, (LeafControlRow) null, // no other latch currently false /* not for update */, ConglomerateController.LOCK_READ); // get read scan lock. // TESTING CODE: if (SanityManager.DEBUG) { latch_released = test_errors( this, "BTreeScan_positionAtNextPage", true, this.getLockingPolicy(), pos.next_leaf, latch_released); } if (!latch_released) { break; } } // Now that we either have both latch and scan lock on next leaf, or // there is no next leaf we can release scan and latch on current page. if (SanityManager.DEBUG) { if (pos.current_scan_pageno != pos.current_leaf.page.getPageNumber()) SanityManager.THROWASSERT( "pos.current_scan_pageno = " + pos.current_scan_pageno + "pos.current_leaf = " + pos.current_leaf); } // unlock the previous row if doing read. if (pos.current_rh != null) { this.getLockingPolicy().unlockScanRecordAfterRead( pos, init_forUpdate); } this.getLockingPolicy().unlockScan( pos.current_leaf.page.getPageNumber()); pos.current_leaf.release(); pos.current_leaf = pos.next_leaf; pos.current_scan_pageno = (pos.next_leaf == null) ? 0 : pos.next_leaf.page.getPageNumber(); // set up for scan to continue at beginning of next page. pos.current_slot = Page.FIRST_SLOT_NUMBER; pos.current_rh = null; } /** Position scan at "start" position. <p> Positions the scan to the slot just before the first record to be returned from the scan. Returns the start page latched, and sets "current_slot" to the slot number. @exception StandardException Standard exception policy. **/ abstract void positionAtStartPosition( BTreeRowPosition pos) throws StandardException; /** * Do any necessary work to complete the scan. * * @param pos current row position of the scan. * * @exception StandardException Standard exception policy. **/ protected void positionAtDoneScanFromClose( BTreeRowPosition pos) throws StandardException { // call unlockScanRecordAfterRead() before closing, currently // this is only important for releasing RR locks on non-qualified // rows. // // Otherwise the correct behavior happens as part of the close, ie.: // // for READ_UNCOMMITTED there is no lock to release, // for READ_COMMITTED all read locks will be released, // for REPEATABLE_READ or SERIALIZABLE no locks are released. if ((pos.current_rh != null) && !pos.current_rh_qualified) { if (pos.current_leaf == null || pos.current_leaf.page == null) { // If we are being called from a "normal" close then there // will be no latch on current_leaf, get it and do the the // unlock. We may be called sometimes, after an error where // we may have the latch, in this case the transaction is about // to be backed out anyway so don't worry about doing this // unlock (thus why we only do the following code if we // "don't" have lock, ie. pos.current_leaf== null). if (!reposition(pos, false)) { if (SanityManager.DEBUG) { SanityManager.THROWASSERT( "can not fail while holding update row lock."); } } this.getLockingPolicy().unlockScanRecordAfterRead( pos, init_forUpdate); pos.current_rh = null; pos.current_leaf.release(); pos.current_leaf = null; } } // Need to do this unlock in any case, until lock manager provides // a way to release locks associated with a compatibility space. This // scan lock is special, as it is a lock on the btree container rather // than the heap container. The open container on the btree actually // has a null locking policy so the close of that container does not // release this lock, need to explicitly unlock it here or when the // scan is closed as part of the abort the lock will not be released. if (pos.current_scan_pageno != 0) { this.getLockingPolicy().unlockScan(pos.current_scan_pageno); pos.current_scan_pageno = 0; } pos.current_slot = Page.INVALID_SLOT_NUMBER; pos.current_rh = null; pos.current_positionKey = null; this.scan_state = SCAN_DONE; return; } /** * Do work necessary to close a scan. * <p> * This routine can only be called "inline" from other btree routines, * as it counts on the state of the pos to be correct. * <p> * Closing a scan from close() must handle long jumps from exceptions * where the state of pos may not be correct. The easiest case is * a lock timeout which has caused us not to have a latch on a page, * but pos still thinks there is a latch. This is the easiest but * other exceptions can also caused the same state at close() time. **/ protected void positionAtDoneScan( BTreeRowPosition pos) throws StandardException { // Need to do this unlock in any case, until lock manager provides // a way to release locks associated with a compatibility space. This // scan lock is special, as it is a lock on the btree container rather // than the heap container. The open container on the btree actually // has a null locking policy so the close of that container does not // release this lock, need to explicitly unlock it here or when the // scan is closed as part of the abort the lock will not be released. if (pos.current_scan_pageno != 0) { this.getLockingPolicy().unlockScan(pos.current_scan_pageno); pos.current_scan_pageno = 0; } pos.current_slot = Page.INVALID_SLOT_NUMBER; pos.current_rh = null; pos.current_positionKey = null; this.scan_state = SCAN_DONE; return; } /** * process_qualifier - Determine if a row meets all qualifier conditions. * <p> * Check all qualifiers in the qualifier array against row. Return true * if all compares specified by the qualifier array return true, else * return false. * <p> * It is up to caller to make sure qualifier list is non-null. * * @param row The row with the same partial column list as the * row returned by the current scan. * * @exception StandardException Standard exception policy. */ protected boolean process_qualifier( DataValueDescriptor[] row) throws StandardException { boolean row_qualifies = true; Qualifier q; // Process the 2-d qualifier which is structured as follows: // // A two dimensional array is to be used to pass around a AND's and OR's // in conjunctive normal form (CNF). The top slot of the 2 dimensional // array is optimized for the more frequent where no OR's are present. // The first array slot is always a list of AND's to be treated as // described above for single dimensional AND qualifier arrays. The // subsequent slots are to be treated as AND'd arrays or OR's. Thus // the 2 dimensional array qual[][] argument is to be treated as the // following, note if qual.length = 1 then only the first array is // valid and // it is and an array of and clauses: // // (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1]) // and // (qual[1][0] or qual[1][1] ... or qual[1][qual[1].length - 1]) // and // (qual[2][0] or qual[2][1] ... or qual[2][qual[2].length - 1]) // ... // and // (qual[qual.length - 1][0] or qual[1][1] ... or qual[1][2]) // First do the qual[0] which is an array of qualifer terms. if (SanityManager.DEBUG) { // routine should not be called if there is no qualifier SanityManager.ASSERT(this.init_qualifier != null); SanityManager.ASSERT(this.init_qualifier.length > 0); } for (int i = 0; i < this.init_qualifier[0].length; i++) { // process each AND clause row_qualifies = false; // process each OR clause. q = this.init_qualifier[0][i]; // Get the column from the possibly partial row, of the // q.getColumnId()'th column in the full row. DataValueDescriptor columnValue = row[q.getColumnId()]; row_qualifies = columnValue.compare( q.getOperator(), q.getOrderable(), q.getOrderedNulls(), q.getUnknownRV()); if (q.negateCompareResult()) row_qualifies = !row_qualifies; // Once an AND fails the whole Qualification fails - do a return! if (!row_qualifies) return(false); } // all the qual[0] and terms passed, now process the OR clauses for (int and_idx = 1; and_idx < this.init_qualifier.length; and_idx++) { // process each AND clause row_qualifies = false; if (SanityManager.DEBUG) { // Each OR clause must be non-empty. SanityManager.ASSERT(this.init_qualifier[and_idx].length > 0); } for (int or_idx = 0; or_idx < this.init_qualifier[and_idx].length; or_idx++) { // process each OR clause. q = this.init_qualifier[and_idx][or_idx]; // Get the column from the possibly partial row, of the // q.getColumnId()'th column in the full row. DataValueDescriptor columnValue = row[q.getColumnId()]; row_qualifies = columnValue.compare( q.getOperator(), q.getOrderable(), q.getOrderedNulls(), q.getUnknownRV()); if (q.negateCompareResult()) row_qualifies = !row_qualifies; // once one OR qualifies the entire clause is TRUE if (row_qualifies) break; } if (!row_qualifies) break; } return(row_qualifies); } /** * Reposition the scan leaving and reentering the access layer. * <p> * When a scan leaves access it saves the RecordHandle of the record * on the page. There are 2 cases to consider when trying to reposition * the scan when re-entering access: * o ROW has not moved off the page. * If the row has not moved then the RecordHandle we have saved * away is valid, and we just call RawStore to reposition on that * RecordHandle (RawStore takes care of the row moving within * the page). * o ROW has moved off the page. * This can only happen in the case of a btree split. In that * case the splitter will have caused all scans positioned on * this page within the same transaction to save a copy of the * row that the scan was positioned on. Then to reposition the * scan it is necessary to research the tree from the top using * the copy of the row. * * If the scan has saved it's position by key (and thus has given up the * scan lock on the page), there are a few cases where it is possible that * the key no longer exists in the table. In the case of a scan held * open across commit it is easy to imagine that the row the scan was * positioned on could be deleted and subsequently purged from the table * all before the scan resumes. Also in the case of read uncommitted * the scan holds no lock on the current row, so it could be purged - * in the following scenario for instance: read uncommitted transaction 1 * opens scan and positions on row (1,2), transaction 2 deletes (1,2) and * commits, transaction 1 inserts (1,3) which goes to same page as (1,2) * and is going to cause a split, transaction 1 saves scan position as * key, gives up scan lock and then purges row (1, 2), when transaction * 1 resumes scan (1, 2) no longer exists. missing_row_for_key_ok * parameter is added as a sanity check to make sure it ok that * repositioning does not go to same row that we were repositioned on. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -