/** * Copyright (C) 2012 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "mongo/db/intervalbtreecursor.h" #include "mongo/db/btree.h" #include "mongo/db/kill_current_op.h" #include "mongo/db/namespace_details-inl.h" #include "mongo/db/pdfile.h" namespace mongo { unordered_set IntervalBtreeCursor::_activeCursors; SimpleMutex IntervalBtreeCursor::_activeCursorsMutex("active_interval_btree_cursors"); /** * Advance 'loc' until it does not reference an unused key, or the end of the btree is reached. */ static void skipUnused( BtreeKeyLocation* loc ) { // While loc points to an unused key ... while( !loc->bucket.isNull() && loc->bucket.btree()->k( loc->pos ).isUnused() ) { // ... advance loc to the next key in the btree. loc->bucket = loc->bucket.btree()->advance( loc->bucket, loc->pos, 1, __FUNCTION__ ); } } IntervalBtreeCursor* IntervalBtreeCursor::make( NamespaceDetails* namespaceDetails, const IndexDetails& indexDetails, const BSONObj& lowerBound, bool lowerBoundInclusive, const BSONObj& upperBound, bool upperBoundInclusive ) { if ( indexDetails.version() != 1 ) { // Only v1 indexes are supported. return NULL; } auto_ptr ret( new IntervalBtreeCursor( namespaceDetails, indexDetails, lowerBound, lowerBoundInclusive, upperBound, upperBoundInclusive ) ); ret->init(); return ret.release(); } IntervalBtreeCursor::IntervalBtreeCursor( NamespaceDetails* namespaceDetails, const IndexDetails& indexDetails, const BSONObj& lowerBound, bool lowerBoundInclusive, const BSONObj& upperBound, bool upperBoundInclusive ) : _namespaceDetails( *namespaceDetails ), _indexNo( namespaceDetails->idxNo( indexDetails ) ), _indexDetails( indexDetails ), _ordering( Ordering::make( _indexDetails.keyPattern() ) ), _lowerBound( lowerBound ), _lowerBoundInclusive( lowerBoundInclusive ), _upperBound( upperBound ), _upperBoundInclusive( upperBoundInclusive ), _currRecoverable( _indexDetails, _ordering, _curr ), _nscanned(), _multikeyFlag() { SimpleMutex::scoped_lock lock(_activeCursorsMutex); _activeCursors.insert(this); } IntervalBtreeCursor::~IntervalBtreeCursor() { SimpleMutex::scoped_lock lock(_activeCursorsMutex); _activeCursors.erase(this); } void IntervalBtreeCursor::aboutToDeleteBucket(const DiskLoc& bucket) { SimpleMutex::scoped_lock lock(_activeCursorsMutex); for (unordered_set::iterator i = _activeCursors.begin(); i != _activeCursors.end(); ++i) { IntervalBtreeCursor* ic = *i; if (bucket == ic->_curr.bucket) { ic->_currRecoverable.invalidateInitialLocation(); } } } void IntervalBtreeCursor::init() { _multikeyFlag = _namespaceDetails.isMultikey( _indexNo ); _curr = locateKey( _lowerBound, !_lowerBoundInclusive ); skipUnused( &_curr ); relocateEnd(); if ( ok() ) { _nscanned = 1; } } bool IntervalBtreeCursor::ok() { return !_curr.bucket.isNull(); } DiskLoc IntervalBtreeCursor::currLoc() { if ( eof() ) { return DiskLoc(); } return _curr.bucket.btree()->keyNode( _curr.pos ).recordLoc; } bool IntervalBtreeCursor::advance() { RARELY killCurrentOp.checkForInterrupt(); if ( eof() ) { return false; } // Advance _curr to the next key in the btree. _curr.bucket = _curr.bucket.btree()->advance( _curr.bucket, _curr.pos, 1, __FUNCTION__ ); skipUnused( &_curr ); if ( _curr == _end ) { // _curr has reached _end, so iteration is complete. _curr.bucket.Null(); } else { ++_nscanned; } return ok(); } BSONObj IntervalBtreeCursor::currKey() const { if ( _curr.bucket.isNull() ) { return BSONObj(); } return _curr.bucket.btree()->keyNode( _curr.pos ).key.toBson(); } void IntervalBtreeCursor::noteLocation() { _currRecoverable = LogicalBtreePosition( _indexDetails, _ordering, _curr ); _currRecoverable.init(); } void IntervalBtreeCursor::checkLocation() { _multikeyFlag = _namespaceDetails.isMultikey( _indexNo ); _curr = _currRecoverable.currentLocation(); skipUnused( &_curr ); relocateEnd(); } bool IntervalBtreeCursor::getsetdup( DiskLoc loc ) { // TODO _multikeyFlag may be set part way through an iteration by checkLocation(). In this // case results returned earlier, when _multikeyFlag was false, will not be deduped. This // is an old issue with all mongo btree cursor implementations. return _multikeyFlag && !_dups.insert( loc ).second; } BSONObj IntervalBtreeCursor::prettyIndexBounds() const { return BSON( "lower" << _lowerBound.replaceFieldNames( _indexDetails.keyPattern() ) << "upper" << _upperBound.replaceFieldNames( _indexDetails.keyPattern() ) ); } BtreeKeyLocation IntervalBtreeCursor::locateKey( const BSONObj& key, bool afterKey ) { bool found; BtreeKeyLocation ret; // To find the first btree location equal to the specified key, specify a record location of // minDiskLoc, which is below any actual Record location. To find the first btree location // greater than the specified key, specify a record location of maxDiskLoc, which is above // any actual Record location. DiskLoc targetRecord = afterKey ? maxDiskLoc : minDiskLoc; // Find the requested location in the btree. ret.bucket = _indexDetails.head.btree()->locate( _indexDetails, _indexDetails.head, key, _ordering, ret.pos, found, targetRecord, 1 ); return ret; } void IntervalBtreeCursor::relocateEnd() { if ( eof() ) { return; } // If the current key is above the upper bound ... int32_t cmp = currKey().woCompare( _upperBound, _ordering, false ); if ( cmp > 0 || ( cmp == 0 && !_upperBoundInclusive ) ) { // ... then iteration is complete. _curr.bucket.Null(); return; } // Otherwise, relocate _end. _end = locateKey( _upperBound, _upperBoundInclusive ); skipUnused( &_end ); } } // namespace mongo