diff options
Diffstat (limited to 'dbtests/btreetests.cpp')
-rw-r--r-- | dbtests/btreetests.cpp | 1702 |
1 files changed, 20 insertions, 1682 deletions
diff --git a/dbtests/btreetests.cpp b/dbtests/btreetests.cpp index 4da7375..44c5474 100644 --- a/dbtests/btreetests.cpp +++ b/dbtests/btreetests.cpp @@ -24,1688 +24,26 @@ #include "dbtests.h" -namespace BtreeTests { - - const char* ns() { - return "unittests.btreetests"; - } - - // dummy, valid record loc - const DiskLoc recordLoc() { - return DiskLoc( 0, 2 ); - } - - class Ensure { - public: - Ensure() { - _c.ensureIndex( ns(), BSON( "a" << 1 ), false, "testIndex" ); - } - ~Ensure() { - _c.dropIndexes( ns() ); - } - private: - DBDirectClient _c; - }; - - class Base : public Ensure { - public: - Base() : - _context( ns() ) { - { - bool f = false; - assert( f = true ); - massert( 10402 , "assert is misdefined", f); - } - } - virtual ~Base() {} - static string bigNumString( long long n, int len = 800 ) { - char sub[17]; - sprintf( sub, "%.16llx", n ); - string val( len, ' ' ); - for( int i = 0; i < len; ++i ) { - val[ i ] = sub[ i % 16 ]; - } - return val; - } - protected: - const BtreeBucket* bt() { - return id().head.btree(); - } - DiskLoc dl() { - return id().head; - } - IndexDetails& id() { - NamespaceDetails *nsd = nsdetails( ns() ); - assert( nsd ); - return nsd->idx( 1 ); - } - void checkValid( int nKeys ) { - ASSERT( bt() ); - ASSERT( bt()->isHead() ); - bt()->assertValid( order(), true ); - ASSERT_EQUALS( nKeys, bt()->fullValidate( dl(), order(), 0, true ) ); - } - void dump() { - bt()->dumpTree( dl(), order() ); - } - void insert( BSONObj &key ) { - bt()->bt_insert( dl(), recordLoc(), key, Ordering::make(order()), true, id(), true ); - getDur().commitIfNeeded(); - } - bool unindex( BSONObj &key ) { - getDur().commitIfNeeded(); - return bt()->unindex( dl(), id(), key, recordLoc() ); - } - static BSONObj simpleKey( char c, int n = 1 ) { - BSONObjBuilder builder; - string val( n, c ); - builder.append( "a", val ); - return builder.obj(); - } - void locate( BSONObj &key, int expectedPos, - bool expectedFound, const DiskLoc &expectedLocation, - int direction = 1 ) { - int pos; - bool found; - DiskLoc location = - bt()->locate( id(), dl(), key, Ordering::make(order()), pos, found, recordLoc(), direction ); - ASSERT_EQUALS( expectedFound, found ); - ASSERT( location == expectedLocation ); - ASSERT_EQUALS( expectedPos, pos ); - } - bool present( BSONObj &key, int direction ) { - int pos; - bool found; - bt()->locate( id(), dl(), key, Ordering::make(order()), pos, found, recordLoc(), direction ); - return found; - } - BSONObj order() { - return id().keyPattern(); - } - const BtreeBucket *child( const BtreeBucket *b, int i ) { - assert( i <= b->nKeys() ); - DiskLoc d; - if ( i == b->nKeys() ) { - d = b->getNextChild(); - } - else { - d = const_cast< DiskLoc& >( b->keyNode( i ).prevChildBucket ); - } - assert( !d.isNull() ); - return d.btree(); - } - void checkKey( char i ) { - stringstream ss; - ss << i; - checkKey( ss.str() ); - } - void checkKey( const string &k ) { - BSONObj key = BSON( "" << k ); -// log() << "key: " << key << endl; - ASSERT( present( key, 1 ) ); - ASSERT( present( key, -1 ) ); - } - private: - dblock lk_; - Client::Context _context; - }; - - class Create : public Base { - public: - void run() { - checkValid( 0 ); - } - }; - - class SimpleInsertDelete : public Base { - public: - void run() { - BSONObj key = simpleKey( 'z' ); - insert( key ); - - checkValid( 1 ); - locate( key, 0, true, dl() ); - - unindex( key ); - - checkValid( 0 ); - locate( key, 0, false, DiskLoc() ); - } - }; - - class SplitUnevenBucketBase : public Base { - public: - virtual ~SplitUnevenBucketBase() {} - void run() { - for ( int i = 0; i < 10; ++i ) { - BSONObj shortKey = simpleKey( shortToken( i ), 1 ); - insert( shortKey ); - BSONObj longKey = simpleKey( longToken( i ), 800 ); - insert( longKey ); - } - checkValid( 20 ); - ASSERT_EQUALS( 1, bt()->nKeys() ); - checkSplit(); - } - protected: - virtual char shortToken( int i ) const = 0; - virtual char longToken( int i ) const = 0; - static char leftToken( int i ) { - return 'a' + i; - } - static char rightToken( int i ) { - return 'z' - i; - } - virtual void checkSplit() = 0; - }; - - class SplitRightHeavyBucket : public SplitUnevenBucketBase { - private: - virtual char shortToken( int i ) const { - return leftToken( i ); - } - virtual char longToken( int i ) const { - return rightToken( i ); - } - virtual void checkSplit() { - ASSERT_EQUALS( 15, child( bt(), 0 )->nKeys() ); - ASSERT_EQUALS( 4, child( bt(), 1 )->nKeys() ); - } - }; - - class SplitLeftHeavyBucket : public SplitUnevenBucketBase { - private: - virtual char shortToken( int i ) const { - return rightToken( i ); - } - virtual char longToken( int i ) const { - return leftToken( i ); - } - virtual void checkSplit() { - ASSERT_EQUALS( 4, child( bt(), 0 )->nKeys() ); - ASSERT_EQUALS( 15, child( bt(), 1 )->nKeys() ); - } - }; - - class MissingLocate : public Base { - public: - void run() { - for ( int i = 0; i < 3; ++i ) { - BSONObj k = simpleKey( 'b' + 2 * i ); - insert( k ); - } - - locate( 1, 'a', 'b', dl() ); - locate( 1, 'c', 'd', dl() ); - locate( 1, 'e', 'f', dl() ); - locate( 1, 'g', 'g' + 1, DiskLoc() ); // of course, 'h' isn't in the index. - - // old behavior - // locate( -1, 'a', 'b', dl() ); - // locate( -1, 'c', 'd', dl() ); - // locate( -1, 'e', 'f', dl() ); - // locate( -1, 'g', 'f', dl() ); - - locate( -1, 'a', 'a' - 1, DiskLoc() ); // of course, 'a' - 1 isn't in the index - locate( -1, 'c', 'b', dl() ); - locate( -1, 'e', 'd', dl() ); - locate( -1, 'g', 'f', dl() ); - } - private: - void locate( int direction, char token, char expectedMatch, - DiskLoc expectedLocation ) { - BSONObj k = simpleKey( token ); - int expectedPos = ( expectedMatch - 'b' ) / 2; - Base::locate( k, expectedPos, false, expectedLocation, direction ); - } - }; - - class MissingLocateMultiBucket : public Base { - public: - void run() { - for ( int i = 0; i < 8; ++i ) { - insert( i ); - } - insert( 9 ); - insert( 8 ); -// dump(); - BSONObj straddle = key( 'i' ); - locate( straddle, 0, false, dl(), 1 ); - straddle = key( 'k' ); - locate( straddle, 0, false, dl(), -1 ); - } - private: - BSONObj key( char c ) { - return simpleKey( c, 800 ); - } - void insert( int i ) { - BSONObj k = key( 'b' + 2 * i ); - Base::insert( k ); - } - }; - - class SERVER983 : public Base { - public: - void run() { - for ( int i = 0; i < 10; ++i ) { - insert( i ); - } -// dump(); - BSONObj straddle = key( 'o' ); - locate( straddle, 0, false, dl(), 1 ); - straddle = key( 'q' ); - locate( straddle, 0, false, dl(), -1 ); - } - private: - BSONObj key( char c ) { - return simpleKey( c, 800 ); - } - void insert( int i ) { - BSONObj k = key( 'b' + 2 * i ); - Base::insert( k ); - } - }; - - class DontReuseUnused : public Base { - public: - void run() { - for ( int i = 0; i < 10; ++i ) { - insert( i ); - } -// dump(); - BSONObj root = key( 'p' ); - unindex( root ); - Base::insert( root ); - locate( root, 0, true, bt()->getNextChild(), 1 ); - } - private: - BSONObj key( char c ) { - return simpleKey( c, 800 ); - } - void insert( int i ) { - BSONObj k = key( 'b' + 2 * i ); - Base::insert( k ); - } - }; - - class PackUnused : public Base { - public: - void run() { - for ( long long i = 0; i < 1000000; i += 1000 ) { - insert( i ); - } -// dump(); - string orig, after; - { - stringstream ss; - bt()->shape( ss ); - orig = ss.str(); - } - vector< string > toDel; - vector< string > other; - BSONObjBuilder start; - start.appendMinKey( "a" ); - BSONObjBuilder end; - end.appendMaxKey( "a" ); - auto_ptr< BtreeCursor > c( new BtreeCursor( nsdetails( ns() ), 1, id(), start.done(), end.done(), false, 1 ) ); - while( c->ok() ) { - if ( !c->currKeyNode().prevChildBucket.isNull() ) { - toDel.push_back( c->currKey().firstElement().valuestr() ); - } - else { - other.push_back( c->currKey().firstElement().valuestr() ); - } - c->advance(); - } - ASSERT( toDel.size() > 0 ); - for( vector< string >::const_iterator i = toDel.begin(); i != toDel.end(); ++i ) { - BSONObj o = BSON( "a" << *i ); - unindex( o ); - } - ASSERT( other.size() > 0 ); - for( vector< string >::const_iterator i = other.begin(); i != other.end(); ++i ) { - BSONObj o = BSON( "a" << *i ); - unindex( o ); - } - - int unused = 0; - ASSERT_EQUALS( 0, bt()->fullValidate( dl(), order(), &unused, true ) ); - - for ( long long i = 50000; i < 50100; ++i ) { - insert( i ); - } - - int unused2 = 0; - ASSERT_EQUALS( 100, bt()->fullValidate( dl(), order(), &unused2, true ) ); - -// log() << "old unused: " << unused << ", new unused: " << unused2 << endl; -// - ASSERT( unused2 <= unused ); - } - protected: - void insert( long long n ) { - string val = bigNumString( n ); - BSONObj k = BSON( "a" << val ); - Base::insert( k ); - } - }; - - class DontDropReferenceKey : public PackUnused { - public: - void run() { - // with 80 root node is full - for ( long long i = 0; i < 80; i += 1 ) { - insert( i ); - } - - BSONObjBuilder start; - start.appendMinKey( "a" ); - BSONObjBuilder end; - end.appendMaxKey( "a" ); - BSONObj l = bt()->keyNode( 0 ).key; - string toInsert; - auto_ptr< BtreeCursor > c( new BtreeCursor( nsdetails( ns() ), 1, id(), start.done(), end.done(), false, 1 ) ); - while( c->ok() ) { - if ( c->currKey().woCompare( l ) > 0 ) { - toInsert = c->currKey().firstElement().valuestr(); - break; - } - c->advance(); - } - // too much work to try to make this happen through inserts and deletes - // we are intentionally manipulating the btree bucket directly here - getDur().writingDiskLoc( const_cast< DiskLoc& >( bt()->keyNode( 1 ).prevChildBucket ) ) = DiskLoc(); - getDur().writingInt( const_cast< DiskLoc& >( bt()->keyNode( 1 ).recordLoc ).GETOFS() ) |= 1; // make unused - BSONObj k = BSON( "a" << toInsert ); - Base::insert( k ); - } - }; - - class MergeBuckets : public Base { - public: - virtual ~MergeBuckets() {} - void run() { - for ( int i = 0; i < 10; ++i ) { - insert( i ); - } -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - int expectedCount = 10 - unindexKeys(); -// dump(); - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - int unused = 0; - ASSERT_EQUALS( expectedCount, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - } - protected: - BSONObj key( char c ) { - return simpleKey( c, 800 ); - } - void insert( int i ) { - BSONObj k = key( 'b' + 2 * i ); - Base::insert( k ); - } - virtual int unindexKeys() = 0; - }; - - class MergeBucketsLeft : public MergeBuckets { - virtual int unindexKeys() { - BSONObj k = key( 'b' ); - unindex( k ); - k = key( 'b' + 2 ); - unindex( k ); - k = key( 'b' + 4 ); - unindex( k ); - k = key( 'b' + 6 ); - unindex( k ); - return 4; - } - }; - - class MergeBucketsRight : public MergeBuckets { - virtual int unindexKeys() { - BSONObj k = key( 'b' + 2 * 9 ); - unindex( k ); - return 1; - } - }; - - // deleting from head won't coalesce yet -// class MergeBucketsHead : public MergeBuckets { -// virtual BSONObj unindexKey() { return key( 'p' ); } -// }; - - class MergeBucketsDontReplaceHead : public Base { - public: - void run() { - for ( int i = 0; i < 18; ++i ) { - insert( i ); - } - // dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = key( 'a' + 17 ); - unindex( k ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - int unused = 0; - ASSERT_EQUALS( 17, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - } - private: - BSONObj key( char c ) { - return simpleKey( c, 800 ); - } - void insert( int i ) { - BSONObj k = key( 'a' + i ); - Base::insert( k ); - } - }; - - // Tool to construct custom trees for tests. - class ArtificialTree : public BtreeBucket { - public: - void push( const BSONObj &key, const DiskLoc &child ) { - pushBack( dummyDiskLoc(), key, Ordering::make( BSON( "a" << 1 ) ), child ); - } - void setNext( const DiskLoc &child ) { - nextChild = child; - } - static DiskLoc make( IndexDetails &id ) { - DiskLoc ret = addBucket( id ); - is( ret )->init(); - getDur().commitIfNeeded(); - return ret; - } - static ArtificialTree *is( const DiskLoc &l ) { - return static_cast< ArtificialTree * >( l.btreemod() ); - } - static DiskLoc makeTree( const string &spec, IndexDetails &id ) { - return makeTree( fromjson( spec ), id ); - } - static DiskLoc makeTree( const BSONObj &spec, IndexDetails &id ) { - DiskLoc node = make( id ); - ArtificialTree *n = ArtificialTree::is( node ); - BSONObjIterator i( spec ); - while( i.more() ) { - BSONElement e = i.next(); - DiskLoc child; - if ( e.type() == Object ) { - child = makeTree( e.embeddedObject(), id ); - } - if ( e.fieldName() == string( "_" ) ) { - n->setNext( child ); - } - else { - n->push( BSON( "" << expectedKey( e.fieldName() ) ), child ); - } - } - n->fixParentPtrs( node ); - return node; - } - static void setTree( const string &spec, IndexDetails &id ) { - set( makeTree( spec, id ), id ); - } - static void set( const DiskLoc &l, IndexDetails &id ) { - ArtificialTree::is( id.head )->deallocBucket( id.head, id ); - getDur().writingDiskLoc(id.head) = l; - } - static string expectedKey( const char *spec ) { - if ( spec[ 0 ] != '$' ) { - return spec; - } - char *endPtr; - // parsing a long long is a pain, so just allow shorter keys for now - unsigned long long num = strtol( spec + 1, &endPtr, 16 ); - int len = 800; - if( *endPtr == '$' ) { - len = strtol( endPtr + 1, 0, 16 ); - } - return Base::bigNumString( num, len ); - } - static void checkStructure( const BSONObj &spec, const IndexDetails &id, const DiskLoc node ) { - ArtificialTree *n = ArtificialTree::is( node ); - BSONObjIterator j( spec ); - for( int i = 0; i < n->n; ++i ) { - ASSERT( j.more() ); - BSONElement e = j.next(); - KeyNode kn = n->keyNode( i ); - string expected = expectedKey( e.fieldName() ); - ASSERT( present( id, BSON( "" << expected ), 1 ) ); - ASSERT( present( id, BSON( "" << expected ), -1 ) ); - ASSERT_EQUALS( expected, kn.key.firstElement().valuestr() ); - if ( kn.prevChildBucket.isNull() ) { - ASSERT( e.type() == jstNULL ); - } - else { - ASSERT( e.type() == Object ); - checkStructure( e.embeddedObject(), id, kn.prevChildBucket ); - } - } - if ( n->nextChild.isNull() ) { - // maybe should allow '_' field with null value? - ASSERT( !j.more() ); - } - else { - BSONElement e = j.next(); - ASSERT_EQUALS( string( "_" ), e.fieldName() ); - ASSERT( e.type() == Object ); - checkStructure( e.embeddedObject(), id, n->nextChild ); - } - ASSERT( !j.more() ); - } - static void checkStructure( const string &spec, const IndexDetails &id ) { - checkStructure( fromjson( spec ), id, id.head ); - } - static bool present( const IndexDetails &id, const BSONObj &key, int direction ) { - int pos; - bool found; - id.head.btree()->locate( id, id.head, key, Ordering::make(id.keyPattern()), pos, found, recordLoc(), direction ); - return found; - } - int headerSize() const { return BtreeBucket::headerSize(); } - int packedDataSize( int pos ) const { return BtreeBucket::packedDataSize( pos ); } - void fixParentPtrs( const DiskLoc &thisLoc ) { BtreeBucket::fixParentPtrs( thisLoc ); } - void forcePack() { - topSize += emptySize; - emptySize = 0; - setNotPacked(); - } - private: - DiskLoc dummyDiskLoc() const { return DiskLoc( 0, 2 ); } - }; - - /** - * We could probably refactor the following tests, but it's easier to debug - * them in the present state. - */ - - class MergeBucketsDelInternal : public Base { - public: - void run() { - ArtificialTree::setTree( "{d:{b:{a:null},bb:null,_:{c:null}},_:{f:{e:null},_:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 8, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "bb" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 7, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 5, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{b:{a:null},d:{c:null},f:{e:null},_:{g:null}}", id() ); - } - }; - - class MergeBucketsRightNull : public Base { - public: - void run() { - ArtificialTree::setTree( "{d:{b:{a:null},bb:null,cc:{c:null}},_:{f:{e:null},h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "bb" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 9, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 5, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{b:{a:null},cc:{c:null},d:null,f:{e:null},h:{g:null}}", id() ); - } - }; - - // not yet handling this case - class DontMergeSingleBucket : public Base { - public: - void run() { - ArtificialTree::setTree( "{d:{b:{a:null},c:null}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 4, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "c" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 3, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{d:{b:{a:null}}}", id() ); - } - }; - - class ParentMergeNonRightToLeft : public Base { - public: - void run() { - ArtificialTree::setTree( "{d:{b:{a:null},bb:null,cc:{c:null}},i:{f:{e:null},h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 11, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "bb" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - // child does not currently replace parent in this case - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{i:{b:{a:null},cc:{c:null},d:null,f:{e:null},h:{g:null}}}", id() ); - } - }; - - class ParentMergeNonRightToRight : public Base { - public: - void run() { - ArtificialTree::setTree( "{d:{b:{a:null},cc:{c:null}},i:{f:{e:null},ff:null,h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 11, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "ff" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - // child does not currently replace parent in this case - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{i:{b:{a:null},cc:{c:null},d:null,f:{e:null},h:{g:null}}}", id() ); - } - }; - - class CantMergeRightNoMerge : public Base { - public: - void run() { - ArtificialTree::setTree( "{d:{b:{a:null},bb:null,cc:{c:null}},dd:null,_:{f:{e:null},h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 11, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "bb" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{d:{b:{a:null},cc:{c:null}},dd:null,_:{f:{e:null},h:{g:null}}}", id() ); - } - }; - - class CantMergeLeftNoMerge : public Base { - public: - void run() { - ArtificialTree::setTree( "{c:{b:{a:null}},d:null,_:{f:{e:null},g:null}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 7, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 5, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "g" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 6, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 5, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{c:{b:{a:null}},d:null,_:{f:{e:null}}}", id() ); - } - }; - - class MergeOption : public Base { - public: - void run() { - ArtificialTree::setTree( "{c:{b:{a:null}},f:{e:{d:null},ee:null},_:{h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 9, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "ee" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 8, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{c:{b:{a:null}},_:{e:{d:null},f:null,h:{g:null}}}", id() ); - } - }; - - class ForceMergeLeft : public Base { - public: - void run() { - ArtificialTree::setTree( "{c:{b:{a:null}},f:{e:{d:null},ee:null},ff:null,_:{h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "ee" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 9, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{f:{b:{a:null},c:null,e:{d:null}},ff:null,_:{h:{g:null}}}", id() ); - } - }; - - class ForceMergeRight : public Base { - public: - void run() { - ArtificialTree::setTree( "{c:{b:{a:null}},cc:null,f:{e:{d:null},ee:null},_:{h:{g:null}}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 7, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "ee" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 9, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{c:{b:{a:null}},cc:null,_:{e:{d:null},f:null,h:{g:null}}}", id() ); - } - }; - - class RecursiveMerge : public Base { - public: - void run() { - ArtificialTree::setTree( "{h:{e:{b:{a:null},c:null,d:null},g:{f:null}},j:{i:null}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 10, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "c" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 9, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - // height is not currently reduced in this case - ArtificialTree::checkStructure( "{j:{g:{b:{a:null},d:null,e:null,f:null},h:null,i:null}}", id() ); - } - }; - - class RecursiveMergeRightBucket : public Base { - public: - void run() { - ArtificialTree::setTree( "{h:{e:{b:{a:null},c:null,d:null},g:{f:null}},_:{i:null}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 9, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "c" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 8, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{g:{b:{a:null},d:null,e:null,f:null},h:null,i:null}", id() ); - } - }; - - class RecursiveMergeDoubleRightBucket : public Base { - public: - void run() { - ArtificialTree::setTree( "{h:{e:{b:{a:null},c:null,d:null},_:{f:null}},_:{i:null}}", id() ); -// dump(); - string ns = id().indexNamespace(); - ASSERT_EQUALS( 8, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - - BSONObj k = BSON( "" << "c" ); - assert( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 7, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - // no recursion currently in this case - ArtificialTree::checkStructure( "{h:{b:{a:null},d:null,e:null,f:null},_:{i:null}}", id() ); - } - }; - - class MergeSizeBase : public Base { - public: - MergeSizeBase() : _count() {} - virtual ~MergeSizeBase() {} - void run() { - typedef ArtificialTree A; - A::set( A::make( id() ), id() ); - A* root = A::is( dl() ); - DiskLoc left = A::make( id() ); - root->push( biggestKey( 'm' ), left ); - _count = 1; - A* l = A::is( left ); - DiskLoc right = A::make( id() ); - root->setNext( right ); - A* r = A::is( right ); - root->fixParentPtrs( dl() ); - - ASSERT_EQUALS( bigSize(), bigSize() / 2 * 2 ); - fillToExactSize( l, leftSize(), 'a' ); - fillToExactSize( r, rightSize(), 'n' ); - ASSERT( leftAdditional() <= 2 ); - if ( leftAdditional() >= 2 ) { - l->push( bigKey( 'k' ), DiskLoc() ); - } - if ( leftAdditional() >= 1 ) { - l->push( bigKey( 'l' ), DiskLoc() ); - } - ASSERT( rightAdditional() <= 2 ); - if ( rightAdditional() >= 2 ) { - r->push( bigKey( 'y' ), DiskLoc() ); - } - if ( rightAdditional() >= 1 ) { - r->push( bigKey( 'z' ), DiskLoc() ); - } - _count += leftAdditional() + rightAdditional(); - -// dump(); - - initCheck(); - string ns = id().indexNamespace(); - const char *keys = delKeys(); - for( const char *i = keys; *i; ++i ) { - int unused = 0; - ASSERT_EQUALS( _count, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = bigKey( *i ); - unindex( k ); -// dump(); - --_count; - } - -// dump(); - - int unused = 0; - ASSERT_EQUALS( _count, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - validate(); - if ( !merge() ) { - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - } - else { - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - } - } - protected: - virtual int leftAdditional() const { return 2; } - virtual int rightAdditional() const { return 2; } - virtual void initCheck() {} - virtual void validate() {} - virtual int leftSize() const = 0; - virtual int rightSize() const = 0; - virtual const char * delKeys() const { return "klyz"; } - virtual bool merge() const { return true; } - void fillToExactSize( ArtificialTree *t, int targetSize, char startKey ) { - int size = 0; - while( size < targetSize ) { - int space = targetSize - size; - int nextSize = space - sizeof( _KeyNode ); - assert( nextSize > 0 ); - BSONObj newKey = key( startKey++, nextSize ); - t->push( newKey, DiskLoc() ); - size += newKey.objsize() + sizeof( _KeyNode ); - _count += 1; - } - ASSERT_EQUALS( t->packedDataSize( 0 ), targetSize ); - } - static BSONObj key( char a, int size ) { - if ( size >= bigSize() ) { - return bigKey( a ); - } - return simpleKey( a, size - ( bigSize() - 801 ) ); - } - static BSONObj bigKey( char a ) { - return simpleKey( a, 801 ); - } - static BSONObj biggestKey( char a ) { - int size = BtreeBucket::getKeyMax() - bigSize() + 801; - return simpleKey( a, size ); - } - static int bigSize() { - return bigKey( 'a' ).objsize(); - } - static int biggestSize() { - return biggestKey( 'a' ).objsize(); - } - int _count; - }; - - class MergeSizeJustRightRight : public MergeSizeBase { - protected: - virtual int rightSize() const { return BtreeBucket::getLowWaterMark() - 1; } - virtual int leftSize() const { return BtreeBucket::bodySize() - biggestSize() - sizeof( _KeyNode ) - ( BtreeBucket::getLowWaterMark() - 1 ); } - }; - - class MergeSizeJustRightLeft : public MergeSizeBase { - protected: - virtual int leftSize() const { return BtreeBucket::getLowWaterMark() - 1; } - virtual int rightSize() const { return BtreeBucket::bodySize() - biggestSize() - sizeof( _KeyNode ) - ( BtreeBucket::getLowWaterMark() - 1 ); } - virtual const char * delKeys() const { return "yzkl"; } - }; - - class MergeSizeRight : public MergeSizeJustRightRight { - virtual int rightSize() const { return MergeSizeJustRightRight::rightSize() - 1; } - virtual int leftSize() const { return MergeSizeJustRightRight::leftSize() + 1; } - }; - - class MergeSizeLeft : public MergeSizeJustRightLeft { - virtual int rightSize() const { return MergeSizeJustRightLeft::rightSize() + 1; } - virtual int leftSize() const { return MergeSizeJustRightLeft::leftSize() - 1; } - }; - - class NoMergeBelowMarkRight : public MergeSizeJustRightRight { - virtual int rightSize() const { return MergeSizeJustRightRight::rightSize() + 1; } - virtual int leftSize() const { return MergeSizeJustRightRight::leftSize() - 1; } - virtual bool merge() const { return false; } - }; - - class NoMergeBelowMarkLeft : public MergeSizeJustRightLeft { - virtual int rightSize() const { return MergeSizeJustRightLeft::rightSize() - 1; } - virtual int leftSize() const { return MergeSizeJustRightLeft::leftSize() + 1; } - virtual bool merge() const { return false; } - }; - - class MergeSizeRightTooBig : public MergeSizeJustRightLeft { - virtual int rightSize() const { return MergeSizeJustRightLeft::rightSize() + 1; } - virtual bool merge() const { return false; } - }; - - class MergeSizeLeftTooBig : public MergeSizeJustRightRight { - virtual int leftSize() const { return MergeSizeJustRightRight::leftSize() + 1; } - virtual bool merge() const { return false; } - }; - - class BalanceOneLeftToRight : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$1:null,$2:null,$3:null,$4:null,$5:null,$6:null},b:{$20:null,$30:null,$40:null,$50:null,a:null},_:{c:null}}", id() ); - ASSERT_EQUALS( 14, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x40 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 13, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$6:{$1:null,$2:null,$3:null,$4:null,$5:null},b:{$10:null,$20:null,$30:null,$50:null,a:null},_:{c:null}}", id() ); - } - }; - - class BalanceOneRightToLeft : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$1:null,$2:null,$3:null,$4:null},b:{$20:null,$30:null,$40:null,$50:null,$60:null,$70:null},_:{c:null}}", id() ); - ASSERT_EQUALS( 13, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x3 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$20:{$1:null,$2:null,$4:null,$10:null},b:{$30:null,$40:null,$50:null,$60:null,$70:null},_:{c:null}}", id() ); - } - }; - - class BalanceThreeLeftToRight : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$20:{$1:{$0:null},$3:{$2:null},$5:{$4:null},$7:{$6:null},$9:{$8:null},$11:{$10:null},$13:{$12:null},_:{$14:null}},b:{$30:null,$40:{$35:null},$50:{$45:null}},_:{c:null}}", id() ); - ASSERT_EQUALS( 23, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 14, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x30 ) ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 22, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 14, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$9:{$1:{$0:null},$3:{$2:null},$5:{$4:null},$7:{$6:null},_:{$8:null}},b:{$11:{$10:null},$13:{$12:null},$20:{$14:null},$40:{$35:null},$50:{$45:null}},_:{c:null}}", id() ); - } - }; - - class BalanceThreeRightToLeft : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$20:{$1:{$0:null},$3:{$2:null},$5:null,_:{$14:null}},b:{$30:{$25:null},$40:{$35:null},$50:{$45:null},$60:{$55:null},$70:{$65:null},$80:{$75:null},$90:{$85:null},$100:{$95:null}},_:{c:null}}", id() ); - ASSERT_EQUALS( 25, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 15, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x5 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 24, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 15, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$50:{$1:{$0:null},$3:{$2:null},$20:{$14:null},$30:{$25:null},$40:{$35:null},_:{$45:null}},b:{$60:{$55:null},$70:{$65:null},$80:{$75:null},$90:{$85:null},$100:{$95:null}},_:{c:null}}", id() ); - } - }; - - class BalanceSingleParentKey : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$1:null,$2:null,$3:null,$4:null,$5:null,$6:null},_:{$20:null,$30:null,$40:null,$50:null,a:null}}", id() ); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x40 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 11, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$6:{$1:null,$2:null,$3:null,$4:null,$5:null},_:{$10:null,$20:null,$30:null,$50:null,a:null}}", id() ); - } - }; - - class PackEmpty : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null}", id() ); - BSONObj k = BSON( "" << "a" ); - ASSERT( unindex( k ) ); - ArtificialTree *t = ArtificialTree::is( dl() ); - t->forcePack(); - Tester::checkEmpty( t, id() ); - } - class Tester : public ArtificialTree { - public: - static void checkEmpty( ArtificialTree *a, const IndexDetails &id ) { - Tester *t = static_cast< Tester * >( a ); - ASSERT_EQUALS( 0, t->n ); - ASSERT( !( t->flags & Packed ) ); - Ordering o = Ordering::make( id.keyPattern() ); - int zero = 0; - t->_packReadyForMod( o, zero ); - ASSERT_EQUALS( 0, t->n ); - ASSERT_EQUALS( 0, t->topSize ); - ASSERT_EQUALS( BtreeBucket::bodySize(), t->emptySize ); - ASSERT( t->flags & Packed ); - } - }; - }; - - class PackedDataSizeEmpty : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null}", id() ); - BSONObj k = BSON( "" << "a" ); - ASSERT( unindex( k ) ); - ArtificialTree *t = ArtificialTree::is( dl() ); - t->forcePack(); - Tester::checkEmpty( t, id() ); - } - class Tester : public ArtificialTree { - public: - static void checkEmpty( ArtificialTree *a, const IndexDetails &id ) { - Tester *t = static_cast< Tester * >( a ); - ASSERT_EQUALS( 0, t->n ); - ASSERT( !( t->flags & Packed ) ); - int zero = 0; - ASSERT_EQUALS( 0, t->packedDataSize( zero ) ); - ASSERT( !( t->flags & Packed ) ); - } - }; - }; - - class BalanceSingleParentKeyPackParent : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$1:null,$2:null,$3:null,$4:null,$5:null,$6:null},_:{$20:null,$30:null,$40:null,$50:null,a:null}}", id() ); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - // force parent pack - ArtificialTree::is( dl() )->forcePack(); - BSONObj k = BSON( "" << bigNumString( 0x40 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 11, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$6:{$1:null,$2:null,$3:null,$4:null,$5:null},_:{$10:null,$20:null,$30:null,$50:null,a:null}}", id() ); - } - }; - - class BalanceSplitParent : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10$10:{$1:null,$2:null,$3:null,$4:null},$100:{$20:null,$30:null,$40:null,$50:null,$60:null,$70:null,$80:null},$200:null,$300:null,$400:null,$500:null,$600:null,$700:null,$800:null,$900:null,_:{c:null}}", id() ); - ASSERT_EQUALS( 22, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x3 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 21, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 6, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$500:{$30:{$1:null,$2:null,$4:null,$10$10:null,$20:null},$100:{$40:null,$50:null,$60:null,$70:null,$80:null},$200:null,$300:null,$400:null},_:{$600:null,$700:null,$800:null,$900:null,_:{c:null}}}", id() ); - } - }; - - class RebalancedSeparatorBase : public Base { - public: - void run() { - ArtificialTree::setTree( treeSpec(), id() ); - modTree(); - Tester::checkSeparator( id(), expectedSeparator() ); - } - virtual string treeSpec() const = 0; - virtual int expectedSeparator() const = 0; - virtual void modTree() {} - struct Tester : public ArtificialTree { - static void checkSeparator( const IndexDetails& id, int expected ) { - ASSERT_EQUALS( expected, static_cast< Tester * >( id.head.btreemod() )->rebalancedSeparatorPos( id.head, 0 ) ); - } - }; - }; - - class EvenRebalanceLeft : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$7:{$1:null,$2$31f:null,$3:null,$4$31f:null,$5:null,$6:null},_:{$8:null,$9:null,$10$31e:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class EvenRebalanceLeftCusp : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$6:{$1:null,$2$31f:null,$3:null,$4$31f:null,$5:null},_:{$7:null,$8:null,$9$31e:null,$10:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class EvenRebalanceRight : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$3:{$1:null,$2$31f:null},_:{$4$31f:null,$5:null,$6:null,$7:null,$8$31e:null,$9:null,$10:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class EvenRebalanceRightCusp : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$4$31f:{$1:null,$2$31f:null,$3:null},_:{$5:null,$6:null,$7$31e:null,$8:null,$9:null,$10:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class EvenRebalanceCenter : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$5:{$1:null,$2$31f:null,$3:null,$4$31f:null},_:{$6:null,$7$31e:null,$8:null,$9:null,$10:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class OddRebalanceLeft : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$6$31f:{$1:null,$2:null,$3:null,$4:null,$5:null},_:{$7:null,$8:null,$9:null,$10:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class OddRebalanceRight : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$4:{$1:null,$2:null,$3:null},_:{$5:null,$6:null,$7:null,$8$31f:null,$9:null,$10:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class OddRebalanceCenter : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$5:{$1:null,$2:null,$3:null,$4:null},_:{$6:null,$7:null,$8:null,$9:null,$10$31f:null}}"; } - virtual int expectedSeparator() const { return 4; } - }; - - class RebalanceEmptyRight : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$a:{$1:null,$2:null,$3:null,$4:null,$5:null,$6:null,$7:null,$8:null,$9:null},_:{$b:null}}"; } - virtual void modTree() { - BSONObj k = BSON( "" << bigNumString( 0xb ) ); - ASSERT( unindex( k ) ); - } - virtual int expectedSeparator() const { return 4; } - }; - - class RebalanceEmptyLeft : public RebalancedSeparatorBase { - virtual string treeSpec() const { return "{$a:{$1:null},_:{$11:null,$12:null,$13:null,$14:null,$15:null,$16:null,$17:null,$18:null,$19:null}}"; } - virtual void modTree() { - BSONObj k = BSON( "" << bigNumString( 0x1 ) ); - ASSERT( unindex( k ) ); - } - virtual int expectedSeparator() const { return 4; } - }; - - class NoMoveAtLowWaterMarkRight : public MergeSizeJustRightRight { - virtual int rightSize() const { return MergeSizeJustRightRight::rightSize() + 1; } - virtual void initCheck() { _oldTop = bt()->keyNode( 0 ).key; } - virtual void validate() { ASSERT_EQUALS( _oldTop, bt()->keyNode( 0 ).key ); } - virtual bool merge() const { return false; } - protected: - BSONObj _oldTop; - }; - - class MoveBelowLowWaterMarkRight : public NoMoveAtLowWaterMarkRight { - virtual int rightSize() const { return MergeSizeJustRightRight::rightSize(); } - virtual int leftSize() const { return MergeSizeJustRightRight::leftSize() + 1; } - // different top means we rebalanced - virtual void validate() { ASSERT( !( _oldTop == bt()->keyNode( 0 ).key ) ); } - }; - - class NoMoveAtLowWaterMarkLeft : public MergeSizeJustRightLeft { - virtual int leftSize() const { return MergeSizeJustRightLeft::leftSize() + 1; } - virtual void initCheck() { _oldTop = bt()->keyNode( 0 ).key; } - virtual void validate() { ASSERT_EQUALS( _oldTop, bt()->keyNode( 0 ).key ); } - virtual bool merge() const { return false; } - protected: - BSONObj _oldTop; - }; - - class MoveBelowLowWaterMarkLeft : public NoMoveAtLowWaterMarkLeft { - virtual int leftSize() const { return MergeSizeJustRightLeft::leftSize(); } - virtual int rightSize() const { return MergeSizeJustRightLeft::rightSize() + 1; } - // different top means we rebalanced - virtual void validate() { ASSERT( !( _oldTop == bt()->keyNode( 0 ).key ) ); } - }; - - class PreferBalanceLeft : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$1:null,$2:null,$3:null,$4:null,$5:null,$6:null},$20:{$11:null,$12:null,$13:null,$14:null},_:{$30:null}}", id() ); - ASSERT_EQUALS( 13, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x12 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$5:{$1:null,$2:null,$3:null,$4:null},$20:{$6:null,$10:null,$11:null,$13:null,$14:null},_:{$30:null}}", id() ); - } - }; - - class PreferBalanceRight : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$1:null},$20:{$11:null,$12:null,$13:null,$14:null},_:{$31:null,$32:null,$33:null,$34:null,$35:null,$36:null}}", id() ); - ASSERT_EQUALS( 13, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x12 ) ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$10:{$1:null},$31:{$11:null,$13:null,$14:null,$20:null},_:{$32:null,$33:null,$34:null,$35:null,$36:null}}", id() ); - } - }; - - class RecursiveMergeThenBalance : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:{$5:{$1:null,$2:null},$8:{$6:null,$7:null}},_:{$20:null,$30:null,$40:null,$50:null,$60:null,$70:null,$80:null,$90:null}}", id() ); - ASSERT_EQUALS( 15, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 5, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x7 ) ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 14, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$40:{$8:{$1:null,$2:null,$5:null,$6:null},$10:null,$20:null,$30:null},_:{$50:null,$60:null,$70:null,$80:null,$90:null}}", id() ); - } - }; - - class MergeRightEmpty : public MergeSizeBase { - protected: - virtual int rightAdditional() const { return 1; } - virtual int leftAdditional() const { return 1; } - virtual const char * delKeys() const { return "lz"; } - virtual int rightSize() const { return 0; } - virtual int leftSize() const { return BtreeBucket::bodySize() - biggestSize() - sizeof( _KeyNode ); } - }; - - class MergeMinRightEmpty : public MergeSizeBase { - protected: - virtual int rightAdditional() const { return 1; } - virtual int leftAdditional() const { return 0; } - virtual const char * delKeys() const { return "z"; } - virtual int rightSize() const { return 0; } - virtual int leftSize() const { return bigSize() + sizeof( _KeyNode ); } - }; - - class MergeLeftEmpty : public MergeSizeBase { - protected: - virtual int rightAdditional() const { return 1; } - virtual int leftAdditional() const { return 1; } - virtual const char * delKeys() const { return "zl"; } - virtual int leftSize() const { return 0; } - virtual int rightSize() const { return BtreeBucket::bodySize() - biggestSize() - sizeof( _KeyNode ); } - }; - - class MergeMinLeftEmpty : public MergeSizeBase { - protected: - virtual int leftAdditional() const { return 1; } - virtual int rightAdditional() const { return 0; } - virtual const char * delKeys() const { return "l"; } - virtual int leftSize() const { return 0; } - virtual int rightSize() const { return bigSize() + sizeof( _KeyNode ); } - }; - - class BalanceRightEmpty : public MergeRightEmpty { - protected: - virtual int leftSize() const { return BtreeBucket::bodySize() - biggestSize() - sizeof( _KeyNode ) + 1; } - virtual bool merge() const { return false; } - virtual void initCheck() { _oldTop = bt()->keyNode( 0 ).key; } - virtual void validate() { ASSERT( !( _oldTop == bt()->keyNode( 0 ).key ) ); } - private: - BSONObj _oldTop; - }; - - class BalanceLeftEmpty : public MergeLeftEmpty { - protected: - virtual int rightSize() const { return BtreeBucket::bodySize() - biggestSize() - sizeof( _KeyNode ) + 1; } - virtual bool merge() const { return false; } - virtual void initCheck() { _oldTop = bt()->keyNode( 0 ).key; } - virtual void validate() { ASSERT( !( _oldTop == bt()->keyNode( 0 ).key ) ); } - private: - BSONObj _oldTop; - }; - - class DelEmptyNoNeighbors : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{b:{a:null}}", id() ); - ASSERT_EQUALS( 2, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 2, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "a" ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 1, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{b:null}", id() ); - } - }; - - class DelEmptyEmptyNeighbors : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,c:{b:null},d:null}", id() ); - ASSERT_EQUALS( 4, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 2, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "b" ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 3, bt()->fullValidate( dl(), order(), 0, true ) ); - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{a:null,c:null,d:null}", id() ); - } - }; - - class DelInternal : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,c:{b:null},d:null}", id() ); - int unused = 0; - ASSERT_EQUALS( 4, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 2, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "c" ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 3, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{a:null,b:null,d:null}", id() ); - } - }; - - class DelInternalReplaceWithUnused : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,c:{b:null},d:null}", id() ); - getDur().writingInt( const_cast< DiskLoc& >( bt()->keyNode( 1 ).prevChildBucket.btree()->keyNode( 0 ).recordLoc ).GETOFS() ) |= 1; // make unused - int unused = 0; - ASSERT_EQUALS( 3, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 1, unused ); - ASSERT_EQUALS( 2, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "c" ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - unused = 0; - ASSERT_EQUALS( 2, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 1, unused ); - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - // doesn't discriminate between used and unused - ArtificialTree::checkStructure( "{a:null,b:null,d:null}", id() ); - } - }; - - class DelInternalReplaceRight : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,_:{b:null}}", id() ); - int unused = 0; - ASSERT_EQUALS( 2, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 2, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "a" ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - unused = 0; - ASSERT_EQUALS( 1, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 1, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{b:null}", id() ); - } - }; - - class DelInternalPromoteKey : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,y:{d:{c:{b:null}},_:{e:null}},z:null}", id() ); - int unused = 0; - ASSERT_EQUALS( 7, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 5, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "y" ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - unused = 0; - ASSERT_EQUALS( 6, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{a:null,e:{c:{b:null},d:null},z:null}", id() ); - } - }; - - class DelInternalPromoteRightKey : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,_:{e:{c:null},_:{f:null}}}", id() ); - int unused = 0; - ASSERT_EQUALS( 4, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "a" ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - unused = 0; - ASSERT_EQUALS( 3, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 2, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{c:null,_:{e:null,f:null}}", id() ); - } - }; - - class DelInternalReplacementPrevNonNull : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,d:{c:{b:null}},e:null}", id() ); - int unused = 0; - ASSERT_EQUALS( 5, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "d" ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 4, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 1, unused ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{a:null,d:{c:{b:null}},e:null}", id() ); - ASSERT( bt()->keyNode( 1 ).recordLoc.getOfs() & 1 ); // check 'unused' key - } - }; - - class DelInternalReplacementNextNonNull : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{a:null,_:{c:null,_:{d:null}}}", id() ); - int unused = 0; - ASSERT_EQUALS( 3, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << "a" ); - // dump(); - ASSERT( unindex( k ) ); - // dump(); - ASSERT_EQUALS( 2, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 1, unused ); - ASSERT_EQUALS( 3, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{a:null,_:{c:null,_:{d:null}}}", id() ); - ASSERT( bt()->keyNode( 0 ).recordLoc.getOfs() & 1 ); // check 'unused' key - } - }; - - class DelInternalSplitPromoteLeft : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:null,$20:null,$30$10:{$25:{$23:null},_:{$27:null}},$40:null,$50:null,$60:null,$70:null,$80:null,$90:null,$100:null}", id() ); - int unused = 0; - ASSERT_EQUALS( 13, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x30, 0x10 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$60:{$10:null,$20:null,$27:{$23:null,$25:null},$40:null,$50:null},_:{$70:null,$80:null,$90:null,$100:null}}", id() ); - } - }; - - class DelInternalSplitPromoteRight : public Base { - public: - void run() { - string ns = id().indexNamespace(); - ArtificialTree::setTree( "{$10:null,$20:null,$30:null,$40:null,$50:null,$60:null,$70:null,$80:null,$90:null,$100$10:{$95:{$93:null},_:{$97:null}}}", id() ); - int unused = 0; - ASSERT_EQUALS( 13, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - BSONObj k = BSON( "" << bigNumString( 0x100, 0x10 ) ); -// dump(); - ASSERT( unindex( k ) ); -// dump(); - ASSERT_EQUALS( 12, bt()->fullValidate( dl(), order(), &unused, true ) ); - ASSERT_EQUALS( 0, unused ); - ASSERT_EQUALS( 4, nsdetails( ns.c_str() )->stats.nrecords ); - ArtificialTree::checkStructure( "{$80:{$10:null,$20:null,$30:null,$40:null,$50:null,$60:null,$70:null},_:{$90:null,$97:{$93:null,$95:null}}}", id() ); - } - }; - - class All : public Suite { - public: - All() : Suite( "btree" ) { - } +#define BtreeBucket BtreeBucket<V0> +#define btree btree<V0> +#define btreemod btreemod<V0> +#define testName "btree" +#define BTVERSION 0 +namespace BtreeTests0 { + #include "btreetests.inl" +} - void setupTests() { - add< Create >(); - add< SimpleInsertDelete >(); - add< SplitRightHeavyBucket >(); - add< SplitLeftHeavyBucket >(); - add< MissingLocate >(); - add< MissingLocateMultiBucket >(); - add< SERVER983 >(); - add< DontReuseUnused >(); - add< PackUnused >(); - add< DontDropReferenceKey >(); - add< MergeBucketsLeft >(); - add< MergeBucketsRight >(); -// add< MergeBucketsHead >(); - add< MergeBucketsDontReplaceHead >(); - add< MergeBucketsDelInternal >(); - add< MergeBucketsRightNull >(); - add< DontMergeSingleBucket >(); - add< ParentMergeNonRightToLeft >(); - add< ParentMergeNonRightToRight >(); - add< CantMergeRightNoMerge >(); - add< CantMergeLeftNoMerge >(); - add< MergeOption >(); - add< ForceMergeLeft >(); - add< ForceMergeRight >(); - add< RecursiveMerge >(); - add< RecursiveMergeRightBucket >(); - add< RecursiveMergeDoubleRightBucket >(); - add< MergeSizeJustRightRight >(); - add< MergeSizeJustRightLeft >(); - add< MergeSizeRight >(); - add< MergeSizeLeft >(); - add< NoMergeBelowMarkRight >(); - add< NoMergeBelowMarkLeft >(); - add< MergeSizeRightTooBig >(); - add< MergeSizeLeftTooBig >(); - add< BalanceOneLeftToRight >(); - add< BalanceOneRightToLeft >(); - add< BalanceThreeLeftToRight >(); - add< BalanceThreeRightToLeft >(); - add< BalanceSingleParentKey >(); - add< PackEmpty >(); - add< PackedDataSizeEmpty >(); - add< BalanceSingleParentKeyPackParent >(); - add< BalanceSplitParent >(); - add< EvenRebalanceLeft >(); - add< EvenRebalanceLeftCusp >(); - add< EvenRebalanceRight >(); - add< EvenRebalanceRightCusp >(); - add< EvenRebalanceCenter >(); - add< OddRebalanceLeft >(); - add< OddRebalanceRight >(); - add< OddRebalanceCenter >(); - add< RebalanceEmptyRight >(); - add< RebalanceEmptyLeft >(); - add< NoMoveAtLowWaterMarkRight >(); - add< MoveBelowLowWaterMarkRight >(); - add< NoMoveAtLowWaterMarkLeft >(); - add< MoveBelowLowWaterMarkLeft >(); - add< PreferBalanceLeft >(); - add< PreferBalanceRight >(); - add< RecursiveMergeThenBalance >(); - add< MergeRightEmpty >(); - add< MergeMinRightEmpty >(); - add< MergeLeftEmpty >(); - add< MergeMinLeftEmpty >(); - add< BalanceRightEmpty >(); - add< BalanceLeftEmpty >(); - add< DelEmptyNoNeighbors >(); - add< DelEmptyEmptyNeighbors >(); - add< DelInternal >(); - add< DelInternalReplaceWithUnused >(); - add< DelInternalReplaceRight >(); - add< DelInternalPromoteKey >(); - add< DelInternalPromoteRightKey >(); - add< DelInternalReplacementPrevNonNull >(); - add< DelInternalReplacementNextNonNull >(); - add< DelInternalSplitPromoteLeft >(); - add< DelInternalSplitPromoteRight >(); - } - } myall; +#undef BtreeBucket +#undef btree +#undef btreemod +#define BtreeBucket BtreeBucket<V1> +#define btree btree<V1> +#define btreemod btreemod<V1> +#undef testName +#define testName "btree1" +#undef BTVERSION +#define BTVERSION 1 +namespace BtreeTests1 { + #include "btreetests.inl" } |