2008-07-30 20:08:54 +02:00
|
|
|
// btreecursor.cpp
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copyright (C) 2008 10gen Inc.
|
2008-12-29 02:28:49 +01:00
|
|
|
*
|
2008-07-30 20:08:54 +02:00
|
|
|
* 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.
|
2008-12-29 02:28:49 +01:00
|
|
|
*
|
2008-07-30 20:08:54 +02:00
|
|
|
* 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.
|
2008-12-29 02:28:49 +01:00
|
|
|
*
|
2008-07-30 20:08:54 +02:00
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "btree.h"
|
|
|
|
#include "pdfile.h"
|
2008-12-09 15:54:51 +01:00
|
|
|
#include "jsobj.h"
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-14 23:09:51 +01:00
|
|
|
namespace mongo {
|
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
extern int otherTraceLevel;
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
DiskLoc maxDiskLoc(0x7fffffff, 0x7fffffff);
|
|
|
|
DiskLoc minDiskLoc(0, 1);
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
BtreeCursor::BtreeCursor(IndexDetails& _id, const BSONObj& k, int _direction, BSONObj& _query) :
|
2008-11-18 21:47:37 +01:00
|
|
|
// query(_query),
|
2009-01-15 16:17:11 +01:00
|
|
|
indexDetails(_id),
|
|
|
|
order(_id.keyPattern()),
|
|
|
|
direction(_direction)
|
|
|
|
{
|
2008-07-30 20:08:54 +02:00
|
|
|
//otherTraceLevel = 999;
|
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
bool found;
|
|
|
|
if ( otherTraceLevel >= 12 ) {
|
|
|
|
if ( otherTraceLevel >= 200 ) {
|
2009-01-15 17:26:38 +01:00
|
|
|
out() << "::BtreeCursor() qtl>200. validating entire index." << endl;
|
2009-01-15 16:17:11 +01:00
|
|
|
indexDetails.head.btree()->fullValidate(indexDetails.head, order);
|
|
|
|
}
|
|
|
|
else {
|
2009-01-15 17:26:38 +01:00
|
|
|
out() << "BTreeCursor(). dumping head bucket" << endl;
|
2009-01-15 16:17:11 +01:00
|
|
|
indexDetails.head.btree()->dump();
|
|
|
|
}
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
findExtremeKeys( _query );
|
|
|
|
if ( !k.isEmpty() )
|
|
|
|
startKey = k;
|
2008-12-29 02:28:49 +01:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
bucket = indexDetails.head.btree()->
|
|
|
|
locate(indexDetails.head, startKey, order, keyOfs, found, direction > 0 ? minDiskLoc : maxDiskLoc, direction);
|
2008-12-29 02:28:49 +01:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
checkUnused();
|
|
|
|
}
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2008-12-09 20:42:47 +01:00
|
|
|
// Given a query, find the lowest and highest keys along our index that could
|
|
|
|
// potentially match the query. These lowest and highest keys will be mapped
|
2009-01-15 19:04:25 +01:00
|
|
|
// to startKey_ and endKey_ based on the value of direction.
|
2009-01-15 16:17:11 +01:00
|
|
|
void BtreeCursor::findExtremeKeys( const BSONObj &query ) {
|
|
|
|
BSONObjBuilder startBuilder;
|
|
|
|
BSONObjBuilder endBuilder;
|
2009-01-15 19:04:25 +01:00
|
|
|
vector< string >fields;
|
|
|
|
getIndexFields( fields );
|
|
|
|
for ( vector<string>::iterator i = fields.begin(); i != fields.end(); ++i ) {
|
2009-01-15 16:17:11 +01:00
|
|
|
const char * field = i->c_str();
|
|
|
|
BSONElement k = indexDetails.keyPattern().getFieldDotted( field );
|
|
|
|
int number = (int) k.number(); // returns 0.0 if not numeric
|
|
|
|
bool forward = ( ( number >= 0 ? 1 : -1 ) * direction > 0 );
|
|
|
|
BSONElement lowest = minKey.firstElement();
|
|
|
|
BSONElement highest = maxKey.firstElement();
|
|
|
|
BSONElement e = query.getFieldDotted( field );
|
|
|
|
if ( !e.eoo() && e.type() != RegEx ) {
|
|
|
|
if ( getGtLtOp( e ) == JSMatcher::Equality )
|
|
|
|
lowest = highest = e;
|
|
|
|
else
|
|
|
|
findExtremeInequalityValues( e, lowest, highest );
|
|
|
|
}
|
|
|
|
startBuilder.appendAs( forward ? lowest : highest, "" );
|
|
|
|
endBuilder.appendAs( forward ? highest : lowest, "" );
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2009-01-15 16:17:11 +01:00
|
|
|
startKey = startBuilder.doneAndDecouple();
|
|
|
|
endKey = endBuilder.doneAndDecouple();
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2008-12-09 15:54:51 +01:00
|
|
|
|
2008-12-11 15:48:14 +01:00
|
|
|
// Find lowest and highest possible key values given all $gt, $gte, $lt, and
|
|
|
|
// $lte elements in e. The values of lowest and highest should be
|
|
|
|
// preinitialized, for example to minKey.firstElement() and maxKey.firstElement().
|
2009-01-15 16:17:11 +01:00
|
|
|
void BtreeCursor::findExtremeInequalityValues( const BSONElement &e,
|
|
|
|
BSONElement &lowest,
|
|
|
|
BSONElement &highest ) {
|
|
|
|
BSONObjIterator i( e.embeddedObject() );
|
|
|
|
while ( 1 ) {
|
|
|
|
BSONElement s = i.next();
|
|
|
|
if ( s.eoo() )
|
|
|
|
break;
|
|
|
|
int op = s.getGtLtOp();
|
|
|
|
if ( ( op == JSMatcher::LT || op == JSMatcher::LTE ) &&
|
|
|
|
( s.woCompare( highest, false ) < 0 ) )
|
|
|
|
highest = s;
|
|
|
|
else if ( ( op == JSMatcher::GT || op == JSMatcher::GTE ) &&
|
|
|
|
( s.woCompare( lowest, false ) > 0 ) )
|
|
|
|
lowest = s;
|
|
|
|
}
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2008-12-11 15:48:14 +01:00
|
|
|
|
2008-12-09 20:42:47 +01:00
|
|
|
// Expand all field names in key to use dotted notation.
|
2009-01-15 19:04:25 +01:00
|
|
|
void BtreeCursor::getFields( const BSONObj &key, vector< string > &fields ) {
|
2009-01-15 16:17:11 +01:00
|
|
|
BSONObjIterator i( key );
|
|
|
|
while ( 1 ) {
|
|
|
|
BSONElement k = i.next();
|
|
|
|
if ( k.eoo() )
|
|
|
|
break;
|
|
|
|
bool addedSubfield = false;
|
|
|
|
if ( k.type() == Object ) {
|
2009-01-15 19:04:25 +01:00
|
|
|
vector< string > subFields;
|
2009-01-15 16:17:11 +01:00
|
|
|
getFields( k.embeddedObject(), subFields );
|
2009-01-15 19:04:25 +01:00
|
|
|
for ( vector< string >::iterator i = subFields.begin(); i != subFields.end(); ++i ) {
|
2009-01-15 16:17:11 +01:00
|
|
|
addedSubfield = true;
|
2009-01-15 19:04:25 +01:00
|
|
|
fields.push_back( k.fieldName() + string( "." ) + *i );
|
2009-01-15 16:17:11 +01:00
|
|
|
}
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2009-01-15 16:17:11 +01:00
|
|
|
if ( !addedSubfield )
|
2009-01-15 19:04:25 +01:00
|
|
|
fields.push_back( k.fieldName() );
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
|
|
|
}
|
2009-01-15 16:17:11 +01:00
|
|
|
|
|
|
|
/* skip unused keys. */
|
|
|
|
void BtreeCursor::checkUnused() {
|
|
|
|
int u = 0;
|
|
|
|
while ( 1 ) {
|
|
|
|
if ( !ok() )
|
|
|
|
break;
|
|
|
|
BtreeBucket *b = bucket.btree();
|
|
|
|
_KeyNode& kn = b->k(keyOfs);
|
|
|
|
if ( kn.isUsed() )
|
|
|
|
break;
|
|
|
|
bucket = b->advance(bucket, keyOfs, direction, "checkUnused");
|
|
|
|
u++;
|
|
|
|
}
|
|
|
|
if ( u > 10 )
|
|
|
|
OCCASIONALLY log() << "btree unused skipped:" << u << '\n';
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2008-12-09 20:42:47 +01:00
|
|
|
// Return a value in the set {-1, 0, 1} to represent the sign of parameter i.
|
2009-01-15 16:17:11 +01:00
|
|
|
int sgn( int i ) {
|
|
|
|
if ( i == 0 )
|
|
|
|
return 0;
|
|
|
|
return i > 0 ? 1 : -1;
|
|
|
|
}
|
2008-12-09 15:54:51 +01:00
|
|
|
|
2009-01-15 19:04:25 +01:00
|
|
|
// Check if the current key is beyond endKey_.
|
2009-01-15 16:17:11 +01:00
|
|
|
void BtreeCursor::checkEnd() {
|
|
|
|
if ( bucket.isNull() )
|
|
|
|
return;
|
|
|
|
int cmp = sgn( endKey.woCompare( currKey(), order ) );
|
|
|
|
if ( cmp != 0 && cmp != direction )
|
|
|
|
bucket = DiskLoc();
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
bool BtreeCursor::advance() {
|
|
|
|
if ( bucket.isNull() )
|
|
|
|
return false;
|
|
|
|
bucket = bucket.btree()->advance(bucket, keyOfs, direction, "BtreeCursor::advance");
|
|
|
|
checkUnused();
|
|
|
|
checkEnd();
|
|
|
|
return !bucket.isNull();
|
|
|
|
}
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
void BtreeCursor::noteLocation() {
|
|
|
|
if ( !eof() ) {
|
|
|
|
BSONObj o = bucket.btree()->keyAt(keyOfs).copy();
|
|
|
|
keyAtKeyOfs = o;
|
|
|
|
locAtKeyOfs = bucket.btree()->k(keyOfs).recordLoc;
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
/* Since the last noteLocation(), our key may have moved around, and that old cached
|
|
|
|
information may thus be stale and wrong (although often it is right). We check
|
|
|
|
that here; if we have moved, we have to search back for where we were at.
|
|
|
|
|
|
|
|
i.e., after operations on the index, the BtreeCursor's cached location info may
|
|
|
|
be invalid. This function ensures validity, so you should call it before using
|
|
|
|
the cursor if other writers have used the database since the last noteLocation
|
|
|
|
call.
|
2008-12-29 02:28:49 +01:00
|
|
|
*/
|
2009-01-15 16:17:11 +01:00
|
|
|
void BtreeCursor::checkLocation() {
|
|
|
|
if ( eof() )
|
|
|
|
return;
|
2008-12-29 02:28:49 +01:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
if ( keyOfs >= 0 ) {
|
|
|
|
BtreeBucket *b = bucket.btree();
|
|
|
|
|
|
|
|
assert( !keyAtKeyOfs.isEmpty() );
|
|
|
|
|
|
|
|
// Note keyAt() returns an empty BSONObj if keyOfs is now out of range,
|
|
|
|
// which is possible as keys may have been deleted.
|
|
|
|
if ( b->keyAt(keyOfs).woEqual(keyAtKeyOfs) &&
|
|
|
|
b->k(keyOfs).recordLoc == locAtKeyOfs ) {
|
|
|
|
if ( !b->k(keyOfs).isUsed() ) {
|
|
|
|
/* we were deleted but still exist as an unused
|
|
|
|
marker key. advance.
|
|
|
|
*/
|
|
|
|
checkUnused();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2008-12-29 02:28:49 +01:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
/* normally we don't get to here. when we do, old position is no longer
|
|
|
|
valid and we must refind where we left off (which is expensive)
|
|
|
|
*/
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
bool found;
|
2008-07-30 20:08:54 +02:00
|
|
|
|
2009-01-15 16:17:11 +01:00
|
|
|
/* TODO: Switch to keep indexdetails and do idx.head! */
|
|
|
|
bucket = indexDetails.head.btree()->locate(indexDetails.head, keyAtKeyOfs, order, keyOfs, found, locAtKeyOfs, direction);
|
|
|
|
RARELY log() << " key seems to have moved in the index, refinding. found:" << found << endl;
|
|
|
|
if ( found )
|
|
|
|
checkUnused();
|
2008-12-29 02:28:49 +01:00
|
|
|
}
|
2009-01-15 16:17:11 +01:00
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
struct BtreeUnitTest {
|
|
|
|
BtreeUnitTest() {
|
|
|
|
assert( minDiskLoc.compare(maxDiskLoc) < 0 );
|
|
|
|
}
|
|
|
|
} btut;
|
2009-01-14 23:09:51 +01:00
|
|
|
|
|
|
|
} // namespace mongo
|