summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct70
-rw-r--r--db/btree.h22
-rw-r--r--db/btreecursor.cpp22
-rw-r--r--db/cursor.h14
-rw-r--r--db/dbcommands.cpp38
-rw-r--r--db/matcher.cpp90
-rw-r--r--db/matcher.h61
-rw-r--r--db/namespace.h35
-rw-r--r--db/pdfile.cpp37
-rw-r--r--db/query.cpp10
-rw-r--r--db/queryoptimizer.cpp54
-rw-r--r--db/queryoptimizer.h17
-rw-r--r--db/queryutil.cpp8
-rw-r--r--dbtests/queryoptimizertests.cpp132
-rw-r--r--debian/init.d15
-rw-r--r--jstests/cursor3.js53
-rw-r--r--jstests/in2.js33
-rw-r--r--jstests/index_check3.js6
-rw-r--r--jstests/indexc.js10
-rw-r--r--jstests/objid1.js3
-rw-r--r--jstests/ref.js8
-rw-r--r--shell/utils.js2
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;