diff options
-rw-r--r-- | SConstruct | 70 | ||||
-rw-r--r-- | db/btree.h | 22 | ||||
-rw-r--r-- | db/btreecursor.cpp | 22 | ||||
-rw-r--r-- | db/cursor.h | 14 | ||||
-rw-r--r-- | db/dbcommands.cpp | 38 | ||||
-rw-r--r-- | db/matcher.cpp | 90 | ||||
-rw-r--r-- | db/matcher.h | 61 | ||||
-rw-r--r-- | db/namespace.h | 35 | ||||
-rw-r--r-- | db/pdfile.cpp | 37 | ||||
-rw-r--r-- | db/query.cpp | 10 | ||||
-rw-r--r-- | db/queryoptimizer.cpp | 54 | ||||
-rw-r--r-- | db/queryoptimizer.h | 17 | ||||
-rw-r--r-- | db/queryutil.cpp | 8 | ||||
-rw-r--r-- | dbtests/queryoptimizertests.cpp | 132 | ||||
-rw-r--r-- | debian/init.d | 15 | ||||
-rw-r--r-- | jstests/cursor3.js | 53 | ||||
-rw-r--r-- | jstests/in2.js | 33 | ||||
-rw-r--r-- | jstests/index_check3.js | 6 | ||||
-rw-r--r-- | jstests/indexc.js | 10 | ||||
-rw-r--r-- | jstests/objid1.js | 3 | ||||
-rw-r--r-- | jstests/ref.js | 8 | ||||
-rw-r--r-- | shell/utils.js | 2 |
22 files changed, 466 insertions, 274 deletions
diff --git a/SConstruct b/SConstruct index 212d288cfd4..1b738374123 100644 --- a/SConstruct +++ b/SConstruct @@ -67,6 +67,15 @@ AddOption( "--release", action="store", help="relase build") + +AddOption( "--static", + dest="static", + type="string", + nargs=0, + action="store", + help="fully static build") + + AddOption('--java', dest='javaHome', type='string', @@ -98,15 +107,6 @@ AddOption('--usejvm', action="store", help="use java for javascript" ) -AddOption( "--v8" , - dest="v8home", - type="string", - default="../v8/", - nargs=1, - action="store", - metavar="dir", - help="v8 location") - AddOption( "--d", dest="debugBuild", type="string", @@ -142,6 +142,14 @@ AddOption( "--extrapath", action="store", help="comma seperated list of add'l paths (--extrapath /opt/foo/,/foo" ) + +AddOption( "--boost-compiler", + dest="boostCompiler", + type="string", + nargs=1, + action="store", + help="compiler used for boost (gcc41)" ) + # --- environment setup --- def printLocalInfo(): @@ -176,6 +184,7 @@ if not force64 and os.getcwd().endswith( "mongo-64" ): print( "*** assuming you want a 64-bit build b/c of directory *** " ) force32 = not GetOption( "force32" ) is None release = not GetOption( "release" ) is None +static = not GetOption( "static" ) is None debugBuild = ( not GetOption( "debugBuild" ) is None ) or ( not GetOption( "debugBuildAndLogging" ) is None ) debugLogging = not GetOption( "debugBuildAndLogging" ) is None @@ -185,6 +194,12 @@ nojni = not GetOption( "nojni" ) is None usesm = not GetOption( "usesm" ) is None usejvm = not GetOption( "usejvm" ) is None +boostCompiler = GetOption( "boostCompiler" ) +if boostCompiler is None: + boostCompiler = "" +else: + boostCompiler = "-" + boostCompiler + if ( usesm and usejvm ): print( "can't say usesm and usejvm at the same time" ) Exit(1) @@ -331,6 +346,9 @@ elif "linux2" == os.sys.platform: nix = True + if static: + env.Append( LINKFLAGS=" -static " ) + elif "sunos5" == os.sys.platform: nix = True solaris = True @@ -566,7 +584,10 @@ def doConfigure( myenv , needJava=True , needPcre=True , shell=False ): for b in boostLibs: l = "boost_" + b - myCheckLib( [ l + "-mt" , l ] , release or not shell) + myCheckLib( [ l + boostCompiler + "-mt" , l + boostCompiler ] , release or not shell) + + # this will add it iff it exists and works + myCheckLib( "boost_system" + boostCompiler + "-mt" ) if needJava: for j in javaLibs: @@ -623,9 +644,6 @@ def doConfigure( myenv , needJava=True , needPcre=True , shell=False ): myCheckLib( "execinfo", True ) env.Append( LIBS=[ "execinfo" ] ) - # this will add it iff it exists and works - myCheckLib( "boost_system-mt" ) - return conf.Finish() env = doConfigure( env ) @@ -1087,12 +1105,22 @@ def getDistName( sofar ): if distBuild: from datetime import date today = date.today() - installDir = "mongodb-" + platform + "-" + processor + "-"; + installDir = "mongodb-" + platform + "-" + processor + "-" + if static: + installDir += "static-" installDir += getDistName( installDir ) print "going to make dist: " + installDir # binaries +def checkGlibc(target,source,env): + import subprocess + stringProcess = subprocess.Popen( [ "strings" , str( target[0] ) ] , stdout=subprocess.PIPE ) + stringResult = stringProcess.communicate()[0] + if stringResult.count( "GLIBC_2.4" ) > 0: + print( str( target[0] ) + " has GLIBC_2.4 dependencies!" ) + Exit(-3) + allBinaries = [] def installBinary( e , name ): @@ -1102,10 +1130,15 @@ def installBinary( e , name ): name += ".exe" inst = e.Install( installDir + "/bin" , name ) + + fullInstallName = installDir + "/bin/" + name allBinaries += [ name ] - if linux or solaris: - e.AddPostAction( inst, e.Action( 'strip ' + installDir + "/bin/" + name ) ) + if solaris or linux: + e.AddPostAction( inst, e.Action( 'strip ' + fullInstallName ) ) + + if linux and str( COMMAND_LINE_TARGETS[0] ) == "s3dist": + e.AddPostAction( inst , checkGlibc ) installBinary( env , "mongodump" ) installBinary( env , "mongorestore" ) @@ -1192,7 +1225,10 @@ def s3push( localName , remoteName=None , remotePrefix=None , fixName=True , pla if fixName: (root,dot,suffix) = localName.rpartition( "." ) - name = remoteName + "-" + platform + "-" + processor + remotePrefix + name = remoteName + "-" + platform + "-" + processor + if static: + name += "-static" + name += remotePrefix if dot == "." : name += "." + suffix name = name.lower() diff --git a/db/btree.h b/db/btree.h index 02b94ccb920..a1ace5b0214 100644 --- a/db/btree.h +++ b/db/btree.h @@ -214,11 +214,14 @@ namespace mongo { class BtreeCursor : public Cursor { friend class BtreeBucket; + NamespaceDetails *d; + int idxNo; BSONObj startKey; BSONObj endKey; bool endKeyInclusive_; + bool multikey; // note this must be updated every getmore batch in case someone added a multikey... public: - BtreeCursor( const IndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction ); + BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int direction ); virtual bool ok() { return !bucket.isNull(); } @@ -230,6 +233,23 @@ namespace mongo { virtual void noteLocation(); // updates keyAtKeyOfs... virtual void checkLocation(); + /* used for multikey index traversal to avoid sending back dups. see JSMatcher::matches(). + if a multikey index traversal: + if loc has already been sent, returns true. + otherwise, marks loc as sent. + @param deep - match was against an array, so we know it is multikey. this is legacy and kept + for backwards datafile compatibility. 'deep' can be eliminated next time we + force a data file conversion. 7Jul09 + */ + set<DiskLoc> dups; + virtual bool getsetdup(bool deep, DiskLoc loc) { + if( deep || multikey ) { + pair<set<DiskLoc>::iterator, bool> p = dups.insert(loc); + return !p.second; + } + return false; + } + _KeyNode& _currKeyNode() { assert( !bucket.isNull() ); _KeyNode& kn = bucket.btree()->k(keyOfs); diff --git a/db/btreecursor.cpp b/db/btreecursor.cpp index 745b1a53415..55028036036 100644 --- a/db/btreecursor.cpp +++ b/db/btreecursor.cpp @@ -28,13 +28,19 @@ namespace mongo { DiskLoc maxDiskLoc(0x7fffffff, 0x7fffffff); DiskLoc minDiskLoc(0, 1); - BtreeCursor::BtreeCursor( const IndexDetails &_id, const BSONObj &_startKey, const BSONObj &_endKey, bool endKeyInclusive, int _direction ) : - startKey( _startKey ), - endKey( _endKey ), - endKeyInclusive_( endKeyInclusive ), - indexDetails( _id ), - order( _id.keyPattern() ), - direction( _direction ) { + BtreeCursor::BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails &_id, + const BSONObj &_startKey, const BSONObj &_endKey, bool endKeyInclusive, int _direction ) : + d(_d), idxNo(_idxNo), + startKey( _startKey ), + endKey( _endKey ), + endKeyInclusive_( endKeyInclusive ), + indexDetails( _id ), + order( _id.keyPattern() ), + direction( _direction ) + { + dassert( d->idxNo((IndexDetails&) indexDetails) == idxNo ); + multikey = d->isMultikey(idxNo); + bool found; if ( otherTraceLevel >= 12 ) { if ( otherTraceLevel >= 200 ) { @@ -122,6 +128,8 @@ namespace mongo { if ( eof() ) return; + multikey = d->isMultikey(idxNo); + if ( keyOfs >= 0 ) { BtreeBucket *b = bucket.btree(); diff --git a/db/cursor.h b/db/cursor.h index 84c8527171b..46184aa3e10 100644 --- a/db/cursor.h +++ b/db/cursor.h @@ -90,15 +90,24 @@ namespace mongo { return "abstract?"; } - /* used for multikey index traversal to avoid sending back dups. see JSMatcher::matches() */ + /* used for multikey index traversal to avoid sending back dups. see JSMatcher::matches(). + if a multikey index traversal: + if loc has already been sent, returns true. + otherwise, marks loc as sent. + @param deep - match was against an array, so we know it is multikey. this is legacy and kept + for backwards datafile compatibility. 'deep' can be eliminated next time we + force a data file conversion. 7Jul09 + */ + virtual bool getsetdup(bool deep, DiskLoc loc) = 0; +/* set<DiskLoc> dups; bool getsetdup(DiskLoc loc) { - /* to save mem only call this when there is risk of dups (e.g. when 'deep'/multikey) */ if ( dups.count(loc) > 0 ) return true; dups.insert(loc); return false; } +*/ virtual BSONObj prettyStartKey() const { return BSONObj(); } virtual BSONObj prettyEndKey() const { return BSONObj(); } @@ -178,6 +187,7 @@ namespace mongo { virtual bool tailable() { return tailable_; } + virtual bool getsetdup(bool deep, DiskLoc loc) { return false; } }; /* used for order { $natural: -1 } */ diff --git a/db/dbcommands.cpp b/db/dbcommands.cpp index 8058455ce83..945829086be 100644 --- a/db/dbcommands.cpp +++ b/db/dbcommands.cpp @@ -486,6 +486,23 @@ namespace mongo { } } cmdoplogging; + unsigned removeBit(unsigned b, int x) { + unsigned tmp = b; + return + (tmp & ((1 << x)-1)) | + ((tmp >> (x+1)) << x); + } + + struct DBCommandsUnitTest { + DBCommandsUnitTest() { + assert( removeBit(1, 0) == 0 ); + assert( removeBit(2, 0) == 1 ); + assert( removeBit(2, 1) == 0 ); + assert( removeBit(255, 1) == 127 ); + assert( removeBit(21, 2) == 9 ); + } + } dbc_unittest; + bool deleteIndexes( NamespaceDetails *d, const char *ns, const char *name, string &errmsg, BSONObjBuilder &anObjBuilder, bool mayDeleteIdIndex ) { d->aboutToDeleteAnIndex(); @@ -513,6 +530,8 @@ namespace mongo { d->indexes[ 0 ] = *idIndex; d->nIndexes = 1; } + /* assuming here that id index is not multikey: */ + d->multiKeyIndexBits = 0; } else { // delete just one index @@ -531,7 +550,7 @@ namespace mongo { return false; } d->indexes[x].kill(); - + d->multiKeyIndexBits = removeBit(d->multiKeyIndexBits, x); d->nIndexes--; for ( int i = x; i < d->nIndexes; i++ ) d->indexes[i] = d->indexes[i+1]; @@ -823,7 +842,7 @@ namespace mongo { } } cmdFileMD5; - const IndexDetails *cmdIndexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) { + IndexDetails *cmdIndexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) { if ( ns[ 0 ] == '\0' || min.isEmpty() || max.isEmpty() ) { errmsg = "invalid command syntax (note: min and max are required)"; return 0; @@ -845,15 +864,17 @@ namespace mongo { BSONObj max = jsobj.getObjectField( "max" ); BSONObj keyPattern = jsobj.getObjectField( "keyPattern" ); - const IndexDetails *id = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern ); + IndexDetails *id = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern ); if ( id == 0 ) return false; Timer t; int num = 0; - for( BtreeCursor c( *id, min, max, false, 1 ); c.ok(); c.advance(), ++num ); + NamespaceDetails *d = nsdetails(ns); + int idxNo = d->idxNo(*id); + for( BtreeCursor c( d, idxNo, *id, min, max, false, 1 ); c.ok(); c.advance(), ++num ); num /= 2; - BtreeCursor c( *id, min, max, false, 1 ); + BtreeCursor c( d, idxNo, *id, min, max, false, 1 ); for( ; num; c.advance(), --num ); int ms = t.millis(); if ( ms > 100 ) { @@ -892,10 +913,11 @@ namespace mongo { errmsg = "only one of min or max specified"; return false; } else { - const IndexDetails *id = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern ); - if ( id == 0 ) + IndexDetails *idx = cmdIndexDetailsForRange( ns, errmsg, min, max, keyPattern ); + if ( idx == 0 ) return false; - c.reset( new BtreeCursor( *id, min, max, false, 1 ) ); + NamespaceDetails *d = nsdetails(ns); + c.reset( new BtreeCursor( d, d->idxNo(*idx), *idx, min, max, false, 1 ) ); } Timer t; diff --git a/db/matcher.cpp b/db/matcher.cpp index be68cc96d3b..1ed747364c2 100644 --- a/db/matcher.cpp +++ b/db/matcher.cpp @@ -105,9 +105,6 @@ namespace mongo { }; JSMatcher::~JSMatcher() { - delete in; - delete nin; - delete all; delete where; } @@ -135,8 +132,8 @@ namespace mongo { /* _jsobj - the query pattern */ JSMatcher::JSMatcher(const BSONObj &_jsobj, const BSONObj &constrainIndexKey) : - in(0), nin(0), all(0), where(0), jsobj(_jsobj), haveSize(), nRegex(0) - { + where(0), jsobj(_jsobj), haveSize(), all(), nRegex(0){ + BSONObjIterator i(jsobj); while ( i.moreWithEOO() ) { BSONElement e = i.next(); @@ -189,7 +186,7 @@ namespace mongo { } continue; } - + // greater than / less than... // e.g., e == { a : { $gt : 3 } } // or @@ -244,51 +241,19 @@ namespace mongo { } else if ( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 && fe.type() == Array ) { // $in - uassert( "only 1 $in statement per query supported", in == 0 ); // todo... - in = new set<BSONElement,element_lt>(); - BSONObjIterator i(fe.embeddedObject()); - if ( i.moreWithEOO() ) { - while ( 1 ) { - BSONElement ie = i.next(); - if ( ie.eoo() ) - break; - in->insert(ie); - } - } - addBasic(e, BSONObj::opIN); // e not actually used at the moment for $in + basics.push_back( BasicMatcher( e , BSONObj::opIN , fe.embeddedObject() ) ); ok = true; } else if ( fn[1] == 'n' && fn[2] == 'i' && fn[3] == 'n' && fn[4] == 0 && fe.type() == Array ) { // $nin - uassert( "only 1 $nin statement per query supported", nin == 0 ); // todo... - nin = new set<BSONElement,element_lt>(); - BSONObjIterator i(fe.embeddedObject()); - if ( i.moreWithEOO() ) { - while ( 1 ) { - BSONElement ie = i.next(); - if ( ie.eoo() ) - break; - nin->insert(ie); - } - } - addBasic(e, BSONObj::NIN); // e not actually used at the moment for $nin + basics.push_back( BasicMatcher( e , BSONObj::NIN , fe.embeddedObject() ) ); ok = true; } else if ( fn[1] == 'a' && fn[2] == 'l' && fn[3] == 'l' && fn[4] == 0 && fe.type() == Array ) { // $all - uassert( "only 1 $all statement per query supported", all == 0 ); // todo... - all = new set<BSONElement,element_lt>(); - BSONObjIterator i(fe.embeddedObject()); - if ( i.moreWithEOO() ) { - while ( 1 ) { - BSONElement ie = i.next(); - if ( ie.eoo() ) - break; - all->insert(ie); - } - } - addBasic(e, BSONObj::opALL); // e not actually used at the moment for $all + basics.push_back( BasicMatcher( e , BSONObj::opALL , fe.embeddedObject() ) ); ok = true; + all = true; } else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fe.isNumber() ) { shared_ptr< BSONObjBuilder > b( new BSONObjBuilder() ); @@ -309,7 +274,7 @@ namespace mongo { if ( ok ) continue; } - + // normal, simple case e.g. { a : "foo" } addBasic(e, BSONObj::Equality); } @@ -317,16 +282,15 @@ namespace mongo { constrainIndexKey_ = constrainIndexKey; } - inline int JSMatcher::valuesMatch(const BSONElement& l, const BSONElement& r, int op, bool *deep) { + inline int JSMatcher::valuesMatch(const BSONElement& l, const BSONElement& r, int op, const BasicMatcher& bm, bool *deep) { assert( op != BSONObj::NE && op != BSONObj::NIN ); if ( op == 0 ) return l.valuesEqual(r); - + if ( op == BSONObj::opIN ) { // { $in : [1,2,3] } - int c = in->count(l); - return c; + return bm.myset->count(l); } if ( op == BSONObj::opSIZE ) { @@ -352,10 +316,10 @@ namespace mongo { BSONElement e = i.next(); if ( e.eoo() ) break; - if ( all->count( e ) ) + if ( bm.myset->count( e ) ) matches.insert( e ); } - if ( all->size() == matches.size() ) { + if ( bm.myset->size() == matches.size() ) { if ( deep ) *deep = true; return true; @@ -373,8 +337,8 @@ namespace mongo { return (op & z); } - int JSMatcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, bool *deep) { - int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, deep ); + int JSMatcher::matchesNe(const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, const BasicMatcher& bm, bool *deep) { + int ret = matchesDotted( fieldName, toMatch, obj, BSONObj::Equality, bm, deep ); return -ret; } @@ -392,19 +356,19 @@ namespace mongo { { "a.b" : 3 } means obj.a.b == 3 { a : { $lt : 3 } } means obj.a < 3 { a : { $in : [1,2] } } means [1,2].contains(obj.a) - - return value + + return value -1 mismatch 0 missing element 1 match */ - int JSMatcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, bool *deep, bool isArr) { - + int JSMatcher::matchesDotted(const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, int compareOp, const BasicMatcher& bm , bool *deep, bool isArr) { + if ( compareOp == BSONObj::NE ) - return matchesNe( fieldName, toMatch, obj, deep ); + return matchesNe( fieldName, toMatch, obj, bm, deep ); if ( compareOp == BSONObj::NIN ) { - for( set<BSONElement,element_lt>::const_iterator i = nin->begin(); i != nin->end(); ++i ) { - int ret = matchesNe( fieldName, *i, obj, deep ); + for( set<BSONElement,element_lt>::const_iterator i = bm.myset->begin(); i != bm.myset->end(); ++i ) { + int ret = matchesNe( fieldName, *i, obj, bm, deep ); if ( ret != 1 ) return ret; // code to handle 0 (missing) return value doesn't deal with nin yet @@ -425,7 +389,7 @@ namespace mongo { BSONElement z = ai.next(); if ( z.type() == Object ) { BSONObj eo = z.embeddedObject(); - int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, deep, false); + int cmp = matchesDotted(fieldName, toMatch, eo, compareOp, bm, deep, false); if ( cmp > 0 ) { if ( deep ) *deep = true; return 1; @@ -447,20 +411,20 @@ namespace mongo { return -1; BSONObj eo = se.embeddedObject(); - return matchesDotted(p+1, toMatch, eo, compareOp, deep, se.type() == Array); + return matchesDotted(p+1, toMatch, eo, compareOp, bm, deep, se.type() == Array); } else { e = obj.getField(fieldName); } } if ( ( e.type() != Array || indexed || compareOp == BSONObj::opALL || compareOp == BSONObj::opSIZE ) && - valuesMatch(e, toMatch, compareOp, deep) ) { + valuesMatch(e, toMatch, compareOp, bm, deep) ) { return 1; } else if ( e.type() == Array && compareOp != BSONObj::opALL && compareOp != BSONObj::opSIZE ) { BSONObjIterator ai(e.embeddedObject()); while ( ai.moreWithEOO() ) { BSONElement z = ai.next(); - if ( valuesMatch( z, toMatch, compareOp) ) { + if ( valuesMatch( z, toMatch, compareOp, bm) ) { if ( deep ) *deep = true; return 1; @@ -509,7 +473,7 @@ namespace mongo { BasicMatcher& bm = basics[i]; BSONElement& m = bm.toMatch; // -1=mismatch. 0=missing element. 1=match - int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, deep); + int cmp = matchesDotted(m.fieldName(), m, jsobj, bm.compareOp, bm, deep); if ( cmp < 0 ) return false; if ( cmp == 0 ) { diff --git a/db/matcher.h b/db/matcher.h index 83b64ba7e79..c814fe3c2cb 100644 --- a/db/matcher.h +++ b/db/matcher.h @@ -24,7 +24,7 @@ #include <pcrecpp.h> namespace mongo { - + class RegexMatcher { public: const char *fieldName; @@ -36,11 +36,44 @@ namespace mongo { delete re; } }; + + struct element_lt + { + bool operator()(const BSONElement& l, const BSONElement& r) const + { + int x = (int) l.type() - (int) r.type(); + if ( x == ( NumberInt - NumberDouble ) || x == ( NumberDouble - NumberInt ) ); + else if ( x < 0 ) return true; + else if ( x > 0 ) return false; + return compareElementValues(l,r) < 0; + } + }; + class BasicMatcher { public: + + BasicMatcher(){ + } + + BasicMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareOp( _op ){ + } + + + BasicMatcher( BSONElement _e , int _op , const BSONObj& array ) : toMatch( _e ) , compareOp( _op ){ + + myset.reset( new set<BSONElement,element_lt>() ); + + BSONObjIterator i( array ); + while ( i.more() ) { + BSONElement ie = i.next(); + myset->insert(ie); + } + } + BSONElement toMatch; int compareOp; + shared_ptr< set<BSONElement,element_lt> > myset; }; // SQL where clause equivalent @@ -66,24 +99,13 @@ namespace mongo { int matchesDotted( const char *fieldName, const BSONElement& toMatch, const BSONObj& obj, - int compareOp, bool *deep, bool isArr = false); + int compareOp, const BasicMatcher& bm, bool *deep, bool isArr = false); int matchesNe( const char *fieldName, const BSONElement &toMatch, const BSONObj &obj, - bool *deep); + const BasicMatcher&bm, bool *deep); - struct element_lt - { - bool operator()(const BSONElement& l, const BSONElement& r) const - { - int x = (int) l.type() - (int) r.type(); - if ( x == ( NumberInt - NumberDouble ) || x == ( NumberDouble - NumberInt ) ); - else if ( x < 0 ) return true; - else if ( x > 0 ) return false; - return compareElementValues(l,r) < 0; - } - }; public: static int opDirection(int op) { return op <= BSONObj::LTE ? -1 : 1; @@ -105,17 +127,11 @@ namespace mongo { // TODO May want to selectively ignore these element types based on op type. if ( e.type() == MinKey || e.type() == MaxKey ) return; - BasicMatcher bm; - bm.toMatch = e; - bm.compareOp = c; - basics.push_back(bm); + basics.push_back( BasicMatcher( e , c ) ); } - int valuesMatch(const BSONElement& l, const BSONElement& r, int op, bool *deep=0); + int valuesMatch(const BSONElement& l, const BSONElement& r, int op, const BasicMatcher& bm , bool *deep=0); - set<BSONElement,element_lt> *in; // set if query uses $in - set<BSONElement,element_lt> *nin; // set if query uses $nin - set<BSONElement,element_lt> *all; // set if query uses $all Where *where; // set if query uses $where BSONObj jsobj; // the query pattern. e.g., { name: "joe" } BSONObj constrainIndexKey_; @@ -123,6 +139,7 @@ namespace mongo { vector<BasicMatcher> basics; // int n; // # of basicmatcher items bool haveSize; + bool all; RegexMatcher regexs[4]; int nRegex; diff --git a/db/namespace.h b/db/namespace.h index c8481be773b..5da6ac48600 100644 --- a/db/namespace.h +++ b/db/namespace.h @@ -238,6 +238,8 @@ namespace mongo { // For capped case, signal that we are doing initial extent allocation. if ( capped ) deletedList[ 1 ].setInvalid(); + version = 0; + multiKeyIndexBits = 0; memset(reserved, 0, sizeof(reserved)); } DiskLoc firstExtent; @@ -254,13 +256,44 @@ namespace mongo { int flags; DiskLoc capExtent; DiskLoc capFirstNewRecord; - char reserved[108]; + + /* NamespaceDetails version. So we can do backward compatibility in the future. + */ + unsigned version; + + unsigned multiKeyIndexBits; + + char reserved[100]; enum NamespaceFlags { Flag_HaveIdIndex = 1 << 0, // set when we have _id index (ONLY if ensureIdIndex was called -- 0 if that has never been called) Flag_CappedDisallowDelete = 1 << 1 // set when deletes not allowed during capped table allocation. }; + /* hackish */ + int idxNo(IndexDetails& idx) { + for( int i = 0; i < nIndexes; i++ ) + if( &indexes[i] == &idx ) + return i; + massert("E12000 idxNo fails", false); + return -1; + } + + /* multikey indexes are indexes where there are more than one key in the index + for a single document. see multikey in wiki. + */ + bool isMultikey(int i) { + return (multiKeyIndexBits & (1 << i)) != 0; + } + void setIndexIsMultikey(int i) { + dassert( i < 32 && i <MaxIndexes ); + multiKeyIndexBits |= (1 << i); + } + void clearIndexIsMultikey(int i) { + dassert( i < 32 && i <MaxIndexes ); + multiKeyIndexBits &= ~(1 << i); + } + /* you MUST call when adding an index. see pdfile.cpp */ void addingIndex(const char *thisns, IndexDetails& details); diff --git a/db/pdfile.cpp b/db/pdfile.cpp index 30f49195318..262e926f752 100644 --- a/db/pdfile.cpp +++ b/db/pdfile.cpp @@ -868,6 +868,8 @@ assert( !eloc.isNull() ); IndexChanges& ch = v[i]; idx.getKeysFromObject(oldObj, ch.oldkeys); idx.getKeysFromObject(newObj, ch.newkeys); + if( ch.newkeys.size() > 1 ) + d.setIndexIsMultikey(i); setDifference(ch.oldkeys, ch.newkeys, ch.removed); setDifference(ch.newkeys, ch.oldkeys, ch.added); } @@ -922,8 +924,6 @@ assert( !eloc.isNull() ); d->paddingTooSmall(); if ( database->profile ) ss << " moved "; - // xxx TODO fix dup key error - old copy should not be deleted - deleteRecord(ns, toupdate, dl); insert(ns, objNew.objdata(), objNew.objsize(), false); return; @@ -949,7 +949,7 @@ assert( !eloc.isNull() ); BSONObj idxKey = idx.info.obj().getObjectField("key"); for ( unsigned i = 0; i < changes[x].added.size(); i++ ) { try { - /* TODO xxx dup keys handle */ + /* we did the dupCheck() above. so we don't have to worry about it here. */ idx.head.btree()->bt_insert( idx.head, dl, *changes[x].added[i], idxKey, /*dupsAllowed*/true, idx); @@ -967,11 +967,6 @@ assert( !eloc.isNull() ); } // update in place - /*if ( addID ) { - ((int&)*toupdate->data) = *((int*) buf) + idOld.size(); - memcpy(toupdate->data+4, idOld.rawdata(), idOld.size()); - memcpy(toupdate->data+4+idOld.size(), ((char *)buf)+4, addID-4); - } else {*/ memcpy(toupdate->data, objNew.objdata(), objNew.objsize()); } @@ -987,11 +982,16 @@ assert( !eloc.isNull() ); int deb=0; /* add keys to indexes for a new record */ - inline void _indexRecord(IndexDetails& idx, BSONObj& obj, DiskLoc newRecordLoc, bool dupsAllowed) { + inline void _indexRecord(NamespaceDetails *d, int idxNo, BSONObj& obj, DiskLoc newRecordLoc, bool dupsAllowed) { + IndexDetails& idx = d->indexes[idxNo]; BSONObjSetDefaultOrder keys; idx.getKeysFromObject(obj, keys); BSONObj order = idx.keyPattern(); + int n = 0; for ( BSONObjSetDefaultOrder::iterator i=keys.begin(); i != keys.end(); i++ ) { + if( ++n == 2 ) { + d->setIndexIsMultikey(idxNo); + } assert( !newRecordLoc.isNull() ); try { idx.head.btree()->bt_insert(idx.head, newRecordLoc, @@ -1009,7 +1009,7 @@ assert( !eloc.isNull() ); /* note there are faster ways to build an index in bulk, that can be done eventually */ - void addExistingToIndex(const char *ns, IndexDetails& idx) { + void addExistingToIndex(const char *ns, NamespaceDetails *d, IndexDetails& idx, int idxNo) { bool dupsAllowed = !idx.unique(); Timer t; @@ -1021,7 +1021,7 @@ assert( !eloc.isNull() ); while ( c->ok() ) { BSONObj js = c->current(); try { - _indexRecord(idx, js, c->currLoc(),dupsAllowed); + _indexRecord(d, idxNo, js, c->currLoc(),dupsAllowed); } catch( AssertionException& e ) { l << endl; log(2) << "addExistingToIndex exception " << e.what() << endl; @@ -1042,7 +1042,7 @@ assert( !eloc.isNull() ); for ( int i = 0; i < d->nIndexes; i++ ) { try { bool unique = d->indexes[i].unique(); - _indexRecord(d->indexes[i], obj, newRecordLoc, /*dupsAllowed*/!unique); + _indexRecord(d, i, obj, newRecordLoc, /*dupsAllowed*/!unique); } catch( DBException& ) { // try to roll back previously added index entries @@ -1293,15 +1293,16 @@ assert( !eloc.isNull() ); NamespaceDetailsTransient::get( ns ).registerWriteOp(); if ( tableToIndex ) { - IndexDetails& idxinfo = tableToIndex->indexes[tableToIndex->nIndexes]; - idxinfo.info = loc; - idxinfo.head = BtreeBucket::addHead(idxinfo); - tableToIndex->addingIndex(tabletoidxns.c_str(), idxinfo); + int idxNo = tableToIndex->nIndexes; + IndexDetails& idx = tableToIndex->indexes[idxNo]; + idx.info = loc; + idx.head = BtreeBucket::addHead(idx); + tableToIndex->addingIndex(tabletoidxns.c_str(), idx); try { - addExistingToIndex(tabletoidxns.c_str(), idxinfo); + addExistingToIndex(tabletoidxns.c_str(), tableToIndex, idx, idxNo); } catch( DBException& ) { // roll back this index - string name = idxinfo.indexName(); + string name = idx.indexName(); BSONObjBuilder b; string errmsg; bool ok = deleteIndexes(tableToIndex, tabletoidxns.c_str(), name.c_str(), errmsg, b, true); diff --git a/db/query.cpp b/db/query.cpp index 19eb5d8d5b9..1cdb7bfe332 100644 --- a/db/query.cpp +++ b/db/query.cpp @@ -71,7 +71,7 @@ namespace mongo { bool deep; if ( matcher_->matches(c_->currKey(), rloc, &deep) ) { - if ( !deep || !c_->getsetdup(rloc) ) + if ( !c_->getsetdup(deep, rloc) ) ++count_; } @@ -140,7 +140,7 @@ namespace mongo { } else { c->advance(); // must advance before deleting as the next ptr will die - assert( !deep || !c->getsetdup(rloc) ); // can't be a dup, we deleted it! + assert( !c->getsetdup(deep, rloc) ); // can't be a dup, we deleted it! if ( !justOne ) c->noteLocation(); @@ -822,7 +822,7 @@ namespace mongo { } else { //out() << "matches " << c->currLoc().toString() << ' ' << deep << '\n'; - if ( deep && c->getsetdup(c->currLoc()) ) { + if( c->getsetdup(deep, c->currLoc()) ) { //out() << " but it's a dup \n"; } else { @@ -914,7 +914,7 @@ namespace mongo { bool deep; if ( !matcher_->matches(c_->currKey(), c_->currLoc(), &deep) ) { } - else if ( !deep || !c_->getsetdup(c_->currLoc()) ) { // i.e., check for dups on deep items only + else if( !c_->getsetdup(deep, c_->currLoc()) ) { bool match = true; if ( !fields_.empty() ) { BSONObj js = c_->current(); @@ -1065,7 +1065,7 @@ namespace mongo { } else { DiskLoc cl = c_->currLoc(); - if ( !deep || !c_->getsetdup(cl) ) { // i.e., check for dups on deep items only + if( !c_->getsetdup(deep, cl) ) { BSONObj js = c_->current(); // got a match. assert( js.objsize() >= 0 ); //defensive for segfaults diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index 56896a5abaf..2d6d7033506 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -31,10 +31,13 @@ namespace mongo { return 1; } - QueryPlan::QueryPlan( const FieldBoundSet &fbs, const BSONObj &order, const IndexDetails *index, const BSONObj &startKey, const BSONObj &endKey ) : + QueryPlan::QueryPlan( + NamespaceDetails *_d, int _idxNo, + const FieldBoundSet &fbs, const BSONObj &order, const BSONObj &startKey, const BSONObj &endKey ) : + d(_d), idxNo(_idxNo), fbs_( fbs ), order_( order ), - index_( index ), + index_( 0 ), optimal_( false ), scanAndOrderRequired_( true ), exactKeyMatch_( false ), @@ -43,14 +46,17 @@ namespace mongo { endKey_( endKey ), endKeyInclusive_( endKey_.isEmpty() ), unhelpful_( false ) { - // full table scan case - if ( !index_ ) { + + if( idxNo >= 0 ) { + index_ = &d->indexes[idxNo]; + } else { + // full table scan case if ( order_.isEmpty() || !strcmp( order_.firstElement().fieldName(), "$natural" ) ) scanAndOrderRequired_ = false; return; } - BSONObj idxKey = index->keyPattern(); + BSONObj idxKey = index_->keyPattern(); BSONObjIterator o( order ); BSONObjIterator k( idxKey ); if ( !o.moreWithEOO() ) @@ -120,7 +126,7 @@ namespace mongo { optimal_ = true; if ( exactIndexedQueryCount == fbs.nNontrivialBounds() && orderFieldsUnindexed.size() == 0 && - exactIndexedQueryCount == index->keyPattern().nFields() && + exactIndexedQueryCount == index_->keyPattern().nFields() && exactIndexedQueryCount == fbs.query().nFields() ) { exactKeyMatch_ = true; } @@ -140,7 +146,7 @@ namespace mongo { return findTableScan( fbs_.ns(), order_, startLoc ); massert( "newCursor() with start location not implemented for indexed plans", startLoc.isNull() ); //TODO This constructor should really take a const ref to the index details. - return auto_ptr< Cursor >( new BtreeCursor( *const_cast< IndexDetails* >( index_ ), startKey_, endKey_, endKeyInclusive_, direction_ >= 0 ? 1 : -1 ) ); + return auto_ptr< Cursor >( new BtreeCursor( d, idxNo, *const_cast< IndexDetails* >( index_ ), startKey_, endKey_, endKeyInclusive_, direction_ >= 0 ? 1 : -1 ) ); } auto_ptr< Cursor > QueryPlan::newReverseCursor() const { @@ -166,8 +172,9 @@ namespace mongo { NamespaceDetailsTransient::get( ns() ).registerIndexForPattern( fbs_.pattern( order_ ), indexKey(), nScanned ); } - QueryPlanSet::QueryPlanSet( const char *ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint, bool honorRecordedPlan, const BSONObj &min, const BSONObj &max ) : - fbs_( ns, query ), + QueryPlanSet::QueryPlanSet( const char *_ns, const BSONObj &query, const BSONObj &order, const BSONElement *hint, bool honorRecordedPlan, const BSONObj &min, const BSONObj &max ) : + ns(_ns), + fbs_( _ns, query ), mayRecordPlan_( true ), usingPrerecordedPlan_( false ), hint_( BSONObj() ), @@ -184,14 +191,15 @@ namespace mongo { init(); } - void QueryPlanSet::addHint( const IndexDetails &id ) { + void QueryPlanSet::addHint( IndexDetails &id ) { if ( !min_.isEmpty() || !max_.isEmpty() ) { string errmsg; BSONObj keyPattern = id.keyPattern(); // This reformats min_ and max_ to be used for index lookup. massert( errmsg, indexDetailsForRange( fbs_.ns(), errmsg, min_, max_, keyPattern ) ); } - plans_.push_back( PlanPtr( new QueryPlan( fbs_, order_, &id, min_, max_ ) ) ); + NamespaceDetails *d = nsdetails(ns); + plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(id), fbs_, order_, min_, max_ ) ) ); } void QueryPlanSet::init() { @@ -203,7 +211,7 @@ namespace mongo { NamespaceDetails *d = nsdetails( ns ); if ( !d || !fbs_.matchPossible() ) { // Table scan plan, when no matches are possible - plans_.push_back( PlanPtr( new QueryPlan( fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); return; } @@ -226,7 +234,7 @@ namespace mongo { if ( !strcmp( hintobj.firstElement().fieldName(), "$natural" ) ) { massert( "natural order cannot be specified with $min/$max", min_.isEmpty() && max_.isEmpty() ); // Table scan plan - plans_.push_back( PlanPtr( new QueryPlan( fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); return; } for (int i = 0; i < d->nIndexes; i++ ) { @@ -243,9 +251,9 @@ namespace mongo { if ( !min_.isEmpty() || !max_.isEmpty() ) { string errmsg; BSONObj keyPattern; - const IndexDetails *id = indexDetailsForRange( ns, errmsg, min_, max_, keyPattern ); - massert( errmsg, id ); - plans_.push_back( PlanPtr( new QueryPlan( fbs_, order_, id, min_, max_ ) ) ); + IndexDetails *idx = indexDetailsForRange( ns, errmsg, min_, max_, keyPattern ); + massert( errmsg, idx ); + plans_.push_back( PlanPtr( new QueryPlan( d, d->idxNo(*idx), fbs_, order_, min_, max_ ) ) ); return; } @@ -257,13 +265,13 @@ namespace mongo { oldNScanned_ = NamespaceDetailsTransient::get( ns ).nScannedForPattern( fbs_.pattern( order_ ) ); if ( !strcmp( bestIndex.firstElement().fieldName(), "$natural" ) ) { // Table scan plan - plans_.push_back( PlanPtr( new QueryPlan( fbs_, order_ ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ) ); return; } for (int i = 0; i < d->nIndexes; i++ ) { IndexDetails& ii = d->indexes[i]; if( ii.keyPattern().woCompare(bestIndex) == 0 ) { - plans_.push_back( PlanPtr( new QueryPlan( fbs_, order_, &ii ) ) ); + plans_.push_back( PlanPtr( new QueryPlan( d, i, fbs_, order_ ) ) ); return; } } @@ -284,13 +292,13 @@ namespace mongo { if ( ( fbs_.nNontrivialBounds() == 0 && order_.isEmpty() ) || ( !order_.isEmpty() && !strcmp( order_.firstElement().fieldName(), "$natural" ) ) ) { // Table scan plan - addPlan( PlanPtr( new QueryPlan( fbs_, order_ ) ), checkFirst ); + addPlan( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ), checkFirst ); return; } PlanSet plans; for( int i = 0; i < d->nIndexes; ++i ) { - PlanPtr p( new QueryPlan( fbs_, order_, &d->indexes[ i ] ) ); + PlanPtr p( new QueryPlan( d, i, fbs_, order_ ) ); if ( p->optimal() ) { addPlan( p, checkFirst ); return; @@ -302,7 +310,7 @@ namespace mongo { addPlan( *i, checkFirst ); // Table scan plan - addPlan( PlanPtr( new QueryPlan( fbs_, order_ ) ), checkFirst ); + addPlan( PlanPtr( new QueryPlan( d, -1, fbs_, order_ ) ), checkFirst ); } shared_ptr< QueryOp > QueryPlanSet::runOp( QueryOp &op ) { @@ -495,14 +503,14 @@ namespace mongo { } // NOTE min, max, and keyPattern will be updated to be consistent with the selected index. - const IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) { + IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ) { if ( min.isEmpty() && max.isEmpty() ) { errmsg = "one of min or max must be specified"; return 0; } setClient( ns ); - const IndexDetails *id = 0; + IndexDetails *id = 0; NamespaceDetails *d = nsdetails( ns ); if ( !d ) { errmsg = "ns not found"; diff --git a/db/queryoptimizer.h b/db/queryoptimizer.h index b10657e54cd..ceb50ff2404 100644 --- a/db/queryoptimizer.h +++ b/db/queryoptimizer.h @@ -25,14 +25,16 @@ namespace mongo { class IndexDetails; - class QueryPlan { + class QueryPlan : boost::noncopyable { public: - QueryPlan( const FieldBoundSet &fbs, + QueryPlan(NamespaceDetails *_d, + int _idxNo, // -1 = no index + const FieldBoundSet &fbs, const BSONObj &order, - const IndexDetails *index = 0, const BSONObj &startKey = BSONObj(), const BSONObj &endKey = BSONObj() ); - QueryPlan( const QueryPlan &other ); + +// QueryPlan( const QueryPlan &other ); /* If true, no other index can do better. */ bool optimal() const { return optimal_; } /* ScanAndOrder processing will be required if true */ @@ -56,6 +58,8 @@ namespace mongo { const FieldBound &bound( const char *fieldName ) const { return fbs_.bound( fieldName ); } void registerSelf( long long nScanned ) const; private: + NamespaceDetails *d; + int idxNo; const FieldBoundSet &fbs_; const BSONObj &order_; const IndexDetails *index_; @@ -129,7 +133,7 @@ namespace mongo { plans_.push_back( plan ); } void init(); - void addHint( const IndexDetails &id ); + void addHint( IndexDetails &id ); struct Runner { Runner( QueryPlanSet &plans, QueryOp &op ); shared_ptr< QueryOp > run(); @@ -138,6 +142,7 @@ namespace mongo { static void initOp( QueryOp &op ); static void nextOp( QueryOp &op ); }; + const char *ns; FieldBoundSet fbs_; PlanSet plans_; bool mayRecordPlan_; @@ -151,6 +156,6 @@ namespace mongo { }; // NOTE min, max, and keyPattern will be updated to be consistent with the selected index. - const IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ); + IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSONObj &min, BSONObj &max, BSONObj &keyPattern ); } // namespace mongo diff --git a/db/queryutil.cpp b/db/queryutil.cpp index d217d7f5102..33a5604db3d 100644 --- a/db/queryutil.cpp +++ b/db/queryutil.cpp @@ -85,6 +85,14 @@ namespace mongo { default: break; } + + if ( lower_.isNumber() && upper_.type() == MaxKey ){ + upper_ = addObj( BSON( lower_.fieldName() << numeric_limits<double>::max() ) ).firstElement(); + } + else if ( upper_.isNumber() && lower_.type() == MinKey ){ + lower_ = addObj( BSON( upper_.fieldName() << - numeric_limits<double>::max() ) ).firstElement(); + } + } const FieldBound &FieldBound::operator&=( const FieldBound &other ) { diff --git a/dbtests/queryoptimizertests.cpp b/dbtests/queryoptimizertests.cpp index add030c6d4d..911682dbbee 100644 --- a/dbtests/queryoptimizertests.cpp +++ b/dbtests/queryoptimizertests.cpp @@ -59,6 +59,19 @@ namespace QueryOptimizerTests { } }; + + class NumericBase : public Base { + public: + NumericBase(){ + o = BSON( "min" << -numeric_limits<double>::max() << "max" << numeric_limits<double>::max() ); + } + + virtual BSONElement lower() { return o["min"]; } + virtual BSONElement upper() { return o["max"]; } + private: + BSONObj o; + }; + class Empty : public Base { virtual BSONObj query() { return BSONObj(); } }; @@ -77,7 +90,7 @@ namespace QueryOptimizerTests { virtual BSONObj query() { return BSON( "a" << 1 << "b" << 2 << "a" << 1 ); } }; - class Lt : public Base { + class Lt : public NumericBase { public: Lt() : o_( BSON( "-" << 1 ) ) {} virtual BSONObj query() { return BSON( "a" << LT << 1 ); } @@ -91,7 +104,7 @@ namespace QueryOptimizerTests { virtual bool upperInclusive() { return true; } }; - class Gt : public Base { + class Gt : public NumericBase { public: Gt() : o_( BSON( "-" << 1 ) ) {} virtual BSONObj query() { return BSON( "a" << GT << 1 ); } @@ -192,10 +205,11 @@ namespace QueryOptimizerTests { void run() { FieldBoundSet fbs( "ns", BSON( "a" << GT << 1 << GT << 5 << LT << 10 << "b" << 4 << "c" << LT << 4 << LT << 6 << "d" << GTE << 0 << GT << 0 << "e" << GTE << 0 << LTE << 10 ) ); BSONObj simple = fbs.simplifiedQuery(); + cout << "simple: " << simple << endl; ASSERT( !simple.getObjectField( "a" ).woCompare( fromjson( "{$gt:5,$lt:10}" ) ) ); ASSERT_EQUALS( 4, simple.getIntField( "b" ) ); - ASSERT( !simple.getObjectField( "c" ).woCompare( fromjson( "{$lt:4}" ) ) ); - ASSERT( !simple.getObjectField( "d" ).woCompare( fromjson( "{$gt:0}" ) ) ); + ASSERT( !simple.getObjectField( "c" ).woCompare( BSON("$gte" << -numeric_limits<double>::max() << "$lt" << 4 ) ) ); + ASSERT( !simple.getObjectField( "d" ).woCompare( BSON("$gt" << 0 << "$lte" << numeric_limits<double>::max() ) ) ); ASSERT( !simple.getObjectField( "e" ).woCompare( fromjson( "{$gte:0,$lte:10}" ) ) ); } }; @@ -261,7 +275,7 @@ namespace QueryOptimizerTests { protected: static const char *ns() { return "unittests.QueryPlanTests"; } static NamespaceDetails *nsd() { return nsdetails( ns() ); } - const IndexDetails *index( const BSONObj &key ) { + IndexDetails *index( const BSONObj &key ) { stringstream ss; ss << indexNum_++; string name = ss.str(); @@ -275,6 +289,9 @@ namespace QueryOptimizerTests { assert( false ); return 0; } + int indexno( const BSONObj &key ) { + return nsd()->idxNo( *index(key) ); + } private: dblock lk_; int indexNum_; @@ -283,6 +300,7 @@ namespace QueryOptimizerTests { DBDirectClient Base::client_; // There's a limit of 10 indexes total, make sure not to exceed this in a given test. +#define INDEXNO(x) nsd()->idxNo( *this->index( BSON(x) ) ) #define INDEX(x) this->index( BSON(x) ) auto_ptr< FieldBoundSet > FieldBoundSet_GLOBAL; #define FBS(x) ( FieldBoundSet_GLOBAL.reset( new FieldBoundSet( ns(), x ) ), *FieldBoundSet_GLOBAL ) @@ -290,7 +308,7 @@ namespace QueryOptimizerTests { class NoIndex : public Base { public: void run() { - QueryPlan p( FBS( BSONObj() ), BSONObj(), 0 ); + QueryPlan p( nsd(), -1, FBS( BSONObj() ), BSONObj() ); ASSERT( !p.optimal() ); ASSERT( !p.scanAndOrderRequired() ); ASSERT( !p.exactKeyMatch() ); @@ -307,13 +325,13 @@ namespace QueryOptimizerTests { b2.appendMaxKey( "" ); BSONObj end = b2.obj(); - QueryPlan p( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); - QueryPlan p2( FBS( BSONObj() ), BSON( "a" << 1 << "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 << "b" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); - QueryPlan p3( FBS( BSONObj() ), BSON( "b" << 1 ), INDEX( "a" << 1 ) ); + QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "b" << 1 ) ); ASSERT( p3.scanAndOrderRequired() ); ASSERT( !p3.startKey().woCompare( start ) ); ASSERT( !p3.endKey().woCompare( end ) ); @@ -323,7 +341,7 @@ namespace QueryOptimizerTests { class MoreIndexThanNeeded : public Base { public: void run() { - QueryPlan p( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); } }; @@ -331,13 +349,13 @@ namespace QueryOptimizerTests { class IndexSigns : public Base { public: void run() { - QueryPlan p( FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ), INDEX( "a" << 1 << "b" << -1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << -1 ) , FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT_EQUALS( 1, p.direction() ); - QueryPlan p2( FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) ); ASSERT( p2.scanAndOrderRequired() ); ASSERT_EQUALS( 0, p2.direction() ); - QueryPlan p3( FBS( BSONObj() ), BSON( "_id" << 1 ), index( id_obj ) ); + QueryPlan p3( nsd(), indexno( id_obj ), FBS( BSONObj() ), BSON( "_id" << 1 ) ); ASSERT( !p3.scanAndOrderRequired() ); ASSERT_EQUALS( 1, p3.direction() ); } @@ -354,15 +372,15 @@ namespace QueryOptimizerTests { b2.appendMaxKey( "" ); b2.appendMinKey( "" ); BSONObj end = b2.obj(); - QueryPlan p( FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ), INDEX( "a" << -1 << "b" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ),FBS( BSONObj() ), BSON( "a" << 1 << "b" << -1 ) ); ASSERT( !p.scanAndOrderRequired() ); ASSERT_EQUALS( -1, p.direction() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); - QueryPlan p2( FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ) ); ASSERT( !p2.scanAndOrderRequired() ); ASSERT_EQUALS( -1, p2.direction() ); - QueryPlan p3( FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ), INDEX( "a" << 1 << "b" << -1 ) ); + QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << -1 ), FBS( BSONObj() ), BSON( "a" << -1 << "b" << -1 ) ); ASSERT( p3.scanAndOrderRequired() ); ASSERT_EQUALS( 0, p3.direction() ); } @@ -379,11 +397,11 @@ namespace QueryOptimizerTests { b2.append( "", 3 ); b2.appendMaxKey( "" ); BSONObj end = b2.obj(); - QueryPlan p( FBS( BSON( "a" << 3 ) ), BSONObj(), INDEX( "a" << -1 << "b" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FBS( BSON( "a" << 3 ) ), BSONObj() ); ASSERT( !p.scanAndOrderRequired() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); - QueryPlan p2( FBS( BSON( "a" << 3 ) ), BSONObj(), INDEX( "a" << -1 << "b" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << -1 << "b" << 1 ), FBS( BSON( "a" << 3 ) ), BSONObj() ); ASSERT( !p2.scanAndOrderRequired() ); ASSERT( !p.startKey().woCompare( start ) ); ASSERT( !p.endKey().woCompare( end ) ); @@ -393,11 +411,11 @@ namespace QueryOptimizerTests { class EqualWithOrder : public Base { public: void run() { - QueryPlan p( FBS( BSON( "a" << 4 ) ), BSON( "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 4 ) ), BSON( "b" << 1 ) ); ASSERT( !p.scanAndOrderRequired() ); - QueryPlan p2( FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); - QueryPlan p3( FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 4 ) ), BSON( "a" << 1 << "c" << 1 ) ); ASSERT( p3.scanAndOrderRequired() ); } }; @@ -405,23 +423,23 @@ namespace QueryOptimizerTests { class Optimal : public Base { public: void run() { - QueryPlan p( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( p.optimal() ); - QueryPlan p2( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( p2.optimal() ); - QueryPlan p3( FBS( BSON( "a" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p3( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 ) ), BSON( "a" << 1 ) ); ASSERT( p3.optimal() ); - QueryPlan p4( FBS( BSON( "b" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p4( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSON( "a" << 1 ) ); ASSERT( !p4.optimal() ); - QueryPlan p5( FBS( BSON( "a" << 1 ) ), BSON( "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 ) ), BSON( "b" << 1 ) ); ASSERT( p5.optimal() ); - QueryPlan p6( FBS( BSON( "b" << 1 ) ), BSON( "b" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p6( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSON( "b" << 1 ) ); ASSERT( !p6.optimal() ); - QueryPlan p7( FBS( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p7( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 << "b" << 1 ) ), BSON( "a" << 1 ) ); ASSERT( p7.optimal() ); - QueryPlan p8( FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ) ); ASSERT( p8.optimal() ); - QueryPlan p9( FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); + QueryPlan p9( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSON( "a" << 1 ) ); ASSERT( p9.optimal() ); } }; @@ -429,37 +447,37 @@ namespace QueryOptimizerTests { class MoreOptimal : public Base { public: void run() { - QueryPlan p10( FBS( BSON( "a" << 1 ) ), BSONObj(), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); - ASSERT( p10.optimal() ); - QueryPlan p11( FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSONObj(), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); - ASSERT( p11.optimal() ); - QueryPlan p12( FBS( BSON( "a" << LT << 1 ) ), BSONObj(), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); - ASSERT( p12.optimal() ); - QueryPlan p13( FBS( BSON( "a" << LT << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 << "c" << 1 ) ); - ASSERT( p13.optimal() ); + QueryPlan p10( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 ) ), BSONObj() );
+ ASSERT( p10.optimal() );
+ QueryPlan p11( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << 1 << "b" << LT << 1 ) ), BSONObj() );
+ ASSERT( p11.optimal() );
+ QueryPlan p12( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << LT << 1 ) ), BSONObj() );
+ ASSERT( p12.optimal() );
+ QueryPlan p13( nsd(), INDEXNO( "a" << 1 << "b" << 1 << "c" << 1 ), FBS( BSON( "a" << LT << 1 ) ), BSON( "a" << 1 ) );
+ ASSERT( p13.optimal() ); } }; class KeyMatch : public Base { public: void run() { - QueryPlan p( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( !p.exactKeyMatch() ); - QueryPlan p2( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "b" << 1 << "a" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( !p2.exactKeyMatch() ); - QueryPlan p3( FBS( BSON( "b" << "z" ) ), BSON( "a" << 1 ), INDEX( "b" << 1 << "a" << 1 ) ); + QueryPlan p3( nsd(), INDEXNO( "b" << 1 << "a" << 1 ), FBS( BSON( "b" << "z" ) ), BSON( "a" << 1 ) ); ASSERT( !p3.exactKeyMatch() ); - QueryPlan p4( FBS( BSON( "c" << "y" << "b" << "z" ) ), BSON( "a" << 1 ), INDEX( "b" << 1 << "a" << 1 << "c" << 1 ) ); + QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << "y" << "b" << "z" ) ), BSON( "a" << 1 ) ); ASSERT( !p4.exactKeyMatch() ); - QueryPlan p5( FBS( BSON( "c" << "y" << "b" << "z" ) ), BSONObj(), INDEX( "b" << 1 << "a" << 1 << "c" << 1 ) ); + QueryPlan p5( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << "y" << "b" << "z" ) ), BSONObj() ); ASSERT( !p5.exactKeyMatch() ); - QueryPlan p6( FBS( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), BSONObj(), INDEX( "b" << 1 << "a" << 1 << "c" << 1 ) ); + QueryPlan p6( nsd(), INDEXNO( "b" << 1 << "a" << 1 << "c" << 1 ), FBS( BSON( "c" << LT << "y" << "b" << GT << "z" ) ), BSONObj() ); ASSERT( !p6.exactKeyMatch() ); - QueryPlan p7( FBS( BSONObj() ), BSON( "a" << 1 ), INDEX( "b" << 1 ) ); + QueryPlan p7( nsd(), INDEXNO( "b" << 1 ), FBS( BSONObj() ), BSON( "a" << 1 ) ); ASSERT( !p7.exactKeyMatch() ); - QueryPlan p8( FBS( BSON( "b" << "y" << "a" << "z" ) ), BSONObj(), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p8( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << "y" << "a" << "z" ) ), BSONObj() ); ASSERT( p8.exactKeyMatch() ); - QueryPlan p9( FBS( BSON( "a" << "z" ) ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); + QueryPlan p9( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "z" ) ), BSON( "a" << 1 ) ); ASSERT( p9.exactKeyMatch() ); } }; @@ -467,7 +485,7 @@ namespace QueryOptimizerTests { class MoreKeyMatch : public Base { public: void run() { - QueryPlan p( FBS( BSON( "a" << "r" << "b" << NE << "q" ) ), BSON( "a" << 1 ), INDEX( "a" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "r" << "b" << NE << "q" ) ), BSON( "a" << 1 ) ); ASSERT( !p.exactKeyMatch() ); } }; @@ -475,17 +493,17 @@ namespace QueryOptimizerTests { class ExactKeyQueryTypes : public Base { public: void run() { - QueryPlan p( FBS( BSON( "a" << "b" ) ), BSONObj(), INDEX( "a" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << "b" ) ), BSONObj() ); ASSERT( p.exactKeyMatch() ); - QueryPlan p2( FBS( BSON( "a" << 4 ) ), BSONObj(), INDEX( "a" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << 4 ) ), BSONObj() ); ASSERT( !p2.exactKeyMatch() ); - QueryPlan p3( FBS( BSON( "a" << BSON( "c" << "d" ) ) ), BSONObj(), INDEX( "a" << 1 ) ); + QueryPlan p3( nsd(), INDEXNO( "a" << 1 ), FBS( BSON( "a" << BSON( "c" << "d" ) ) ), BSONObj() ); ASSERT( !p3.exactKeyMatch() ); BSONObjBuilder b; b.appendRegex( "a", "^ddd" ); - QueryPlan p4( FBS( b.obj() ), BSONObj(), INDEX( "a" << 1 ) ); + QueryPlan p4( nsd(), INDEXNO( "a" << 1 ), FBS( b.obj() ), BSONObj() ); ASSERT( !p4.exactKeyMatch() ); - QueryPlan p5( FBS( BSON( "a" << "z" << "b" << 4 ) ), BSONObj(), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p5( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "a" << "z" << "b" << 4 ) ), BSONObj() ); ASSERT( !p5.exactKeyMatch() ); } }; @@ -493,17 +511,17 @@ namespace QueryOptimizerTests { class Unhelpful : public Base { public: void run() { - QueryPlan p( FBS( BSON( "b" << 1 ) ), BSONObj(), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 ) ), BSONObj() ); ASSERT( !p.bound( "a" ).nontrivial() ); ASSERT( p.unhelpful() ); - QueryPlan p2( FBS( BSON( "b" << 1 << "c" << 1 ) ), BSON( "a" << 1 ), INDEX( "a" << 1 << "b" << 1 ) ); + QueryPlan p2( nsd(), INDEXNO( "a" << 1 << "b" << 1 ), FBS( BSON( "b" << 1 << "c" << 1 ) ), BSON( "a" << 1 ) ); ASSERT( !p2.scanAndOrderRequired() ); ASSERT( !p2.bound( "a" ).nontrivial() ); ASSERT( !p2.unhelpful() ); - QueryPlan p3( FBS( BSON( "b" << 1 << "c" << 1 ) ), BSONObj(), INDEX( "b" << 1 ) ); + QueryPlan p3( nsd(), INDEXNO( "b" << 1 ), FBS( BSON( "b" << 1 << "c" << 1 ) ), BSONObj() ); ASSERT( p3.bound( "b" ).nontrivial() ); ASSERT( !p3.unhelpful() ); - QueryPlan p4( FBS( BSON( "c" << 1 << "d" << 1 ) ), BSONObj(), INDEX( "b" << 1 << "c" << 1 ) ); + QueryPlan p4( nsd(), INDEXNO( "b" << 1 << "c" << 1 ), FBS( BSON( "c" << 1 << "d" << 1 ) ), BSONObj() ); ASSERT( !p4.bound( "b" ).nontrivial() ); ASSERT( p4.unhelpful() ); } diff --git a/debian/init.d b/debian/init.d index 8af7a73ceb0..20dd23ca20b 100644 --- a/debian/init.d +++ b/debian/init.d @@ -44,16 +44,23 @@ # # High performance, scalability, and reasonable depth of # functionality are the goals for the project. -# ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin -DAEMON=/bin/mongod -DAEMON_OPTS='--dbpath /var/lib/mongodb run' +DAEMON=/usr/bin/mongod +DATA=/var/lib/mongodb +DAEMON_OPTS="--dbpath $DATA run" NAME=MongoDB DESC=database -test -x $DAEMON || exit 0 +if test ! -x $DAEMON; then + echo "Could not find $DAEMON" + exit 0 +fi + +if test ! -x $DATA; then + mkdir $DATA || exit 0 +fi . /lib/lsb/init-functions diff --git a/jstests/cursor3.js b/jstests/cursor3.js index fd487d7bfea..d23264c94ba 100644 --- a/jstests/cursor3.js +++ b/jstests/cursor3.js @@ -1,34 +1,35 @@ // Test inequality bounds combined with ordering for a single-field index. // BUG 1079 (fixed) -function checkResults( expected, cursor ) { - assert.eq( expected.length, cursor.count() ); +testNum = 1; + +function checkResults( expected, cursor , testNum ) { + assert.eq( expected.length, cursor.count() , "testNum: " + testNum + " A : " + tojson( cursor.toArray() ) + " " + tojson( cursor.explain() ) ); for( i = 0; i < expected.length; ++i ) { - assert.eq( expected[ i ], cursor[ i ][ "a" ] ); + assert.eq( expected[ i ], cursor[ i ][ "a" ] , "testNum: " + testNum + " B" ); } } -function testConstrainedFindWithOrdering( db ) { - r = db.ed_db_cursor3_cfwo; - r.drop() - - r.save( { a: 0 } ); - r.save( { a: 1 } ); - r.save( { a: 2 } ); - r.ensureIndex( { a: 1 } ); - - checkResults( [ 1 ], r.find( { a: 1 } ).sort( { a: 1 } ).hint( { a: 1 } ) ); - checkResults( [ 1 ], r.find( { a: 1 } ).sort( { a: -1 } ).hint( { a: 1 } ) ); - - checkResults( [ 1, 2 ], r.find( { a: { $gt: 0 } } ).sort( { a: 1 } ).hint( { a: 1 } ) ); - checkResults( [ 2, 1 ], r.find( { a: { $gt: 0 } } ).sort( { a: -1 } ).hint( { a: 1 } ) ); - checkResults( [ 1, 2 ], r.find( { a: { $gte: 1 } } ).sort( { a: 1 } ).hint( { a: 1 } ) ); - checkResults( [ 2, 1 ], r.find( { a: { $gte: 1 } } ).sort( { a: -1 } ).hint( { a: 1 } ) ); - - checkResults( [ 0, 1 ], r.find( { a: { $lt: 2 } } ).sort( { a: 1 } ).hint( { a: 1 } ) ); - checkResults( [ 1, 0 ], r.find( { a: { $lt: 2 } } ).sort( { a: -1 } ).hint( { a: 1 } ) ); - checkResults( [ 0, 1 ], r.find( { a: { $lte: 1 } } ).sort( { a: 1 } ).hint( { a: 1 } ) ); - checkResults( [ 1, 0 ], r.find( { a: { $lte: 1 } } ).sort( { a: -1 } ).hint( { a: 1 } ) ); -} +t = db.cursor3; +t.drop() + +t.save( { a: 0 } ); +t.save( { a: 1 } ); +t.save( { a: 2 } ); + +t.ensureIndex( { a: 1 } ); + + + +checkResults( [ 1 ], t.find( { a: 1 } ).sort( { a: 1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 1 ], t.find( { a: 1 } ).sort( { a: -1 } ).hint( { a: 1 } ) , testNum++ ) + +checkResults( [ 1, 2 ], t.find( { a: { $gt: 0 } } ).sort( { a: 1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 2, 1 ], t.find( { a: { $gt: 0 } } ).sort( { a: -1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 1, 2 ], t.find( { a: { $gte: 1 } } ).sort( { a: 1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 2, 1 ], t.find( { a: { $gte: 1 } } ).sort( { a: -1 } ).hint( { a: 1 } ) , testNum++ ) -testConstrainedFindWithOrdering( db ); +checkResults( [ 0, 1 ], t.find( { a: { $lt: 2 } } ).sort( { a: 1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 1, 0 ], t.find( { a: { $lt: 2 } } ).sort( { a: -1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 0, 1 ], t.find( { a: { $lte: 1 } } ).sort( { a: 1 } ).hint( { a: 1 } ) , testNum++ ) +checkResults( [ 1, 0 ], t.find( { a: { $lte: 1 } } ).sort( { a: -1 } ).hint( { a: 1 } ) , testNum++ ) diff --git a/jstests/in2.js b/jstests/in2.js new file mode 100644 index 00000000000..66b90daa25a --- /dev/null +++ b/jstests/in2.js @@ -0,0 +1,33 @@ + +t = db.in2; + +function go( name , index ){ + + t.drop(); + + t.save( { a : 1 , b : 1 } ); + t.save( { a : 1 , b : 2 } ); + t.save( { a : 1 , b : 3 } ); + + t.save( { a : 1 , b : 1 } ); + t.save( { a : 2 , b : 2 } ); + t.save( { a : 3 , b : 3 } ); + + t.save( { a : 1 , b : 1 } ); + t.save( { a : 2 , b : 1 } ); + t.save( { a : 3 , b : 1 } ); + + if ( index ) + t.ensureIndex( index ); + + assert.eq( 7 , t.find( { a : { $in : [ 1 , 2 ] } } ).count() , name + " A" ); + + assert.eq( 6 , t.find( { a : { $in : [ 1 , 2 ] } , b : { $in : [ 1 , 2 ] } } ).count() , name + " B" ); +} + +go( "no index" ); +go( "index on a" , { a : 1 } ); +go( "index on b" , { b : 1 } ); +go( "index on a&b" , { a : 1 , b : 1 } ); + + diff --git a/jstests/index_check3.js b/jstests/index_check3.js index f1615fe0944..62352e5d61c 100644 --- a/jstests/index_check3.js +++ b/jstests/index_check3.js @@ -29,8 +29,8 @@ for ( var i=0; i<100; i++ ){ t.ensureIndex( { foo : 1 } ); -//printjson( t.find( { foo : { $lt : 50 } } ).explain() ); +printjson( t.find( { foo : { $lt : 50 } } ).explain() ); assert.gt( 30 , t.find( { foo : { $lt : 50 } } ).explain().nscanned , "lt" ) -//printjson( t.find( { foo : { $gt : 50 } } ).explain() ); -//assert.gt( 30 , t.find( { foo : { $gt : 50 } } ).explain().nscanned , "gt" ) +printjson( t.find( { foo : { $gt : 50 } } ).explain() ); +assert.gt( 30 , t.find( { foo : { $gt : 50 } } ).explain().nscanned , "gt" ) diff --git a/jstests/indexc.js b/jstests/indexc.js index 1c1cc0e119d..b099e2d2823 100644 --- a/jstests/indexc.js +++ b/jstests/indexc.js @@ -12,9 +12,9 @@ for ( var i=1; i<100; i++ ){ assert.eq( 50 , t.find( { ts : { $lt : mid } } ).itcount() , "A" ); assert.eq( 50 , t.find( { ts : { $lt : mid } } ).sort( { ts : 1 } ).itcount() , "B" ); -//t.ensureIndex( { ts : 1 , cats : 1 } ); -//t.ensureIndex( { cats : 1 } ); +t.ensureIndex( { ts : 1 , cats : 1 } ); +t.ensureIndex( { cats : 1 } ); -// multi-key bug -//assert.eq( 50 , t.find( { ts : { $lt : mid } } ).itcount() , "C" ); -//assert.eq( 50 , t.find( { ts : { $lt : mid } } ).sort( { ts : 1 } ).itcount() , "D" ); +// multi-key bug was firing here (related to getsetdup()): +assert.eq( 50 , t.find( { ts : { $lt : mid } } ).itcount() , "C" ); +assert.eq( 50 , t.find( { ts : { $lt : mid } } ).sort( { ts : 1 } ).itcount() , "D" ); diff --git a/jstests/objid1.js b/jstests/objid1.js index f0b62ef5d0c..dea31eed0d8 100644 --- a/jstests/objid1.js +++ b/jstests/objid1.js @@ -11,3 +11,6 @@ t.save( { a : a } ) assert( t.findOne().a.isObjectId , "C" ); assert.eq( a.str , t.findOne().a.str , "D" ); +x = { a : new ObjectId() }; +eval( " y = " + tojson( x ) ); +assert.eq( x.a.str , y.a.str , "E" ); diff --git a/jstests/ref.js b/jstests/ref.js index 1c7201ace4e..77d6038f182 100644 --- a/jstests/ref.js +++ b/jstests/ref.js @@ -9,13 +9,11 @@ db.otherthings.save(other); db.things.save( { name : "abc" } ); x = db.things.findOne(); -x.o = other; +x.o = new DBRef( "otherthings" , other._id ); db.things.save(x); -assert( db.things.findOne().o.n == 1, "dbref broken 2" ); +assert( db.things.findOne().o.fetch().n == 1, "dbref broken 2" ); other.n++; db.otherthings.save(other); -//print( tojson( db.things.findOne() ) ); -print("ref.js: needs line uncommented after fixing bug:"); -//assert( db.things.findOne().o.n == 2, "dbrefs broken" ); +assert( db.things.findOne().o.fetch().n == 2, "dbrefs broken" ); diff --git a/shell/utils.js b/shell/utils.js index 204ddec5919..ef2889fc867 100644 --- a/shell/utils.js +++ b/shell/utils.js @@ -188,7 +188,7 @@ ObjectId.prototype.toString = function(){ } ObjectId.prototype.tojson = function(){ - return "\"" + this.str + "\""; + return " ObjectId( \"" + this.str + "\") "; } ObjectId.prototype.isObjectId = true; |