summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests/querytests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/dbtests/querytests.cpp')
-rw-r--r--src/mongo/dbtests/querytests.cpp1408
1 files changed, 1408 insertions, 0 deletions
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
new file mode 100644
index 00000000000..9416ae20723
--- /dev/null
+++ b/src/mongo/dbtests/querytests.cpp
@@ -0,0 +1,1408 @@
+// querytests.cpp : query.{h,cpp} unit tests.
+
+/**
+ * Copyright (C) 2008 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pch.h"
+#include "../db/ops/query.h"
+#include "../db/dbhelpers.h"
+#include "../db/clientcursor.h"
+
+#include "../db/instance.h"
+#include "../db/json.h"
+#include "../db/lasterror.h"
+
+#include "../util/timer.h"
+
+#include "dbtests.h"
+
+namespace mongo {
+ extern int __findingStartInitialTimeout;
+}
+
+namespace QueryTests {
+
+ class Base {
+ dblock lk;
+ Client::Context _context;
+ public:
+ Base() : _context( ns() ) {
+ addIndex( fromjson( "{\"a\":1}" ) );
+ }
+ ~Base() {
+ try {
+ boost::shared_ptr<Cursor> c = theDataFileMgr.findAll( ns() );
+ vector< DiskLoc > toDelete;
+ for(; c->ok(); c->advance() )
+ toDelete.push_back( c->currLoc() );
+ for( vector< DiskLoc >::iterator i = toDelete.begin(); i != toDelete.end(); ++i )
+ theDataFileMgr.deleteRecord( ns(), i->rec(), *i, false );
+ DBDirectClient cl;
+ cl.dropIndexes( ns() );
+ }
+ catch ( ... ) {
+ FAIL( "Exception while cleaning up collection" );
+ }
+ }
+ protected:
+ static const char *ns() {
+ return "unittests.querytests";
+ }
+ static void addIndex( const BSONObj &key ) {
+ BSONObjBuilder b;
+ b.append( "name", key.firstElementFieldName() );
+ b.append( "ns", ns() );
+ b.append( "key", key );
+ BSONObj o = b.done();
+ stringstream indexNs;
+ indexNs << "unittests.system.indexes";
+ theDataFileMgr.insert( indexNs.str().c_str(), o.objdata(), o.objsize() );
+ }
+ static void insert( const char *s ) {
+ insert( fromjson( s ) );
+ }
+ static void insert( const BSONObj &o ) {
+ theDataFileMgr.insert( ns(), o.objdata(), o.objsize() );
+ }
+ };
+
+ class FindOne : public Base {
+ public:
+ void run() {
+ addIndex( BSON( "b" << 1 ) );
+ addIndex( BSON( "c" << 1 ) );
+ insert( BSON( "b" << 2 << "_id" << 0 ) );
+ insert( BSON( "c" << 3 << "_id" << 1 ) );
+ BSONObj query = fromjson( "{$or:[{b:2},{c:3}]}" );
+ BSONObj ret;
+ // Check findOne() returning object.
+ ASSERT( Helpers::findOne( ns(), query, ret, true ) );
+ ASSERT_EQUALS( string( "b" ), ret.firstElement().fieldName() );
+ // Cross check with findOne() returning location.
+ ASSERT_EQUALS( ret, Helpers::findOne( ns(), query, true ).obj() );
+ }
+ };
+
+ class FindOneRequireIndex : public Base {
+ public:
+ void run() {
+ insert( BSON( "b" << 2 << "_id" << 0 ) );
+ BSONObj query = fromjson( "{b:2}" );
+ BSONObj ret;
+
+ // Check findOne() returning object, allowing unindexed scan.
+ ASSERT( Helpers::findOne( ns(), query, ret, false ) );
+ // Check findOne() returning location, allowing unindexed scan.
+ ASSERT_EQUALS( ret, Helpers::findOne( ns(), query, false ).obj() );
+
+ // Check findOne() returning object, requiring indexed scan without index.
+ ASSERT_THROWS( Helpers::findOne( ns(), query, ret, true ), MsgAssertionException );
+ // Check findOne() returning location, requiring indexed scan without index.
+ ASSERT_THROWS( Helpers::findOne( ns(), query, true ), MsgAssertionException );
+
+ addIndex( BSON( "b" << 1 ) );
+ // Check findOne() returning object, requiring indexed scan with index.
+ ASSERT( Helpers::findOne( ns(), query, ret, false ) );
+ // Check findOne() returning location, requiring indexed scan with index.
+ ASSERT_EQUALS( ret, Helpers::findOne( ns(), query, false ).obj() );
+ }
+ };
+
+ class FindOneEmptyObj : public Base {
+ public:
+ void run() {
+ // We don't normally allow empty objects in the database, but test that we can find
+ // an empty object (one might be allowed inside a reserved namespace at some point).
+ dblock lk;
+ Client::Context ctx( "unittests.querytests" );
+ // Set up security so godinsert command can run.
+ cc().getAuthenticationInfo()->isLocalHost = true;
+ DBDirectClient cl;
+ BSONObj info;
+ ASSERT( cl.runCommand( "unittests", BSON( "godinsert" << "querytests" << "obj" << BSONObj() ), info ) );
+ insert( BSONObj() );
+ BSONObj query;
+ BSONObj ret;
+ ASSERT( Helpers::findOne( ns(), query, ret, false ) );
+ ASSERT( ret.isEmpty() );
+ ASSERT_EQUALS( ret, Helpers::findOne( ns(), query, false ).obj() );
+ }
+ };
+
+ class ClientBase {
+ public:
+ ClientBase() {
+ mongo::lastError.reset( new LastError() );
+ }
+ ~ClientBase() {
+ //mongo::lastError.release();
+ }
+ protected:
+ static void insert( const char *ns, BSONObj o ) {
+ client_.insert( ns, o );
+ }
+ static void update( const char *ns, BSONObj q, BSONObj o, bool upsert = 0 ) {
+ client_.update( ns, Query( q ), o, upsert );
+ }
+ static bool error() {
+ return !client_.getPrevError().getField( "err" ).isNull();
+ }
+ DBDirectClient &client() const { return client_; }
+
+ static DBDirectClient client_;
+ };
+ DBDirectClient ClientBase::client_;
+
+ class BoundedKey : public ClientBase {
+ public:
+ ~BoundedKey() {
+ client().dropCollection( "unittests.querytests.BoundedKey" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.BoundedKey";
+ insert( ns, BSON( "a" << 1 ) );
+ BSONObjBuilder a;
+ a.appendMaxKey( "$lt" );
+ BSONObj limit = a.done();
+ ASSERT( !client().findOne( ns, QUERY( "a" << limit ) ).isEmpty() );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT( !client().findOne( ns, QUERY( "a" << limit ).hint( BSON( "a" << 1 ) ) ).isEmpty() );
+ }
+ };
+
+ class GetMore : public ClientBase {
+ public:
+ ~GetMore() {
+ client().dropCollection( "unittests.querytests.GetMore" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.GetMore";
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ insert( ns, BSON( "a" << 3 ) );
+ auto_ptr< DBClientCursor > cursor = client().query( ns, BSONObj(), 2 );
+ long long cursorId = cursor->getCursorId();
+ cursor->decouple();
+ cursor.reset();
+ cursor = client().getMore( ns, cursorId );
+ ASSERT( cursor->more() );
+ ASSERT_EQUALS( 3, cursor->next().getIntField( "a" ) );
+ }
+ };
+
+ class PositiveLimit : public ClientBase {
+ public:
+ const char* ns;
+ PositiveLimit() : ns("unittests.querytests.PositiveLimit") {}
+ ~PositiveLimit() {
+ client().dropCollection( ns );
+ }
+
+ void testLimit(int limit) {
+ ASSERT_EQUALS(client().query( ns, BSONObj(), limit )->itcount(), limit);
+ }
+ void run() {
+ for(int i=0; i<1000; i++)
+ insert( ns, BSON( GENOID << "i" << i ) );
+
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 1 )->itcount(), 1);
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 10 )->itcount(), 10);
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 101 )->itcount(), 101);
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 999 )->itcount(), 999);
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 1000 )->itcount(), 1000);
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 1001 )->itcount(), 1000);
+ ASSERT_EQUALS( client().query(ns, BSONObj(), 0 )->itcount(), 1000);
+ }
+ };
+
+ class ReturnOneOfManyAndTail : public ClientBase {
+ public:
+ ~ReturnOneOfManyAndTail() {
+ client().dropCollection( "unittests.querytests.ReturnOneOfManyAndTail" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.ReturnOneOfManyAndTail";
+ client().createCollection( ns, 1024, true );
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, QUERY( "a" << GT << 0 ).hint( BSON( "$natural" << 1 ) ), 1, 0, 0, QueryOption_CursorTailable );
+ // If only one result requested, a cursor is not saved.
+ ASSERT_EQUALS( 0, c->getCursorId() );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 1, c->next().getIntField( "a" ) );
+ }
+ };
+
+ class TailNotAtEnd : public ClientBase {
+ public:
+ ~TailNotAtEnd() {
+ client().dropCollection( "unittests.querytests.TailNotAtEnd" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailNotAtEnd";
+ client().createCollection( ns, 2047, true );
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ insert( ns, BSON( "a" << 2 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ ASSERT( 0 != c->getCursorId() );
+ while( c->more() )
+ c->next();
+ ASSERT( 0 != c->getCursorId() );
+ insert( ns, BSON( "a" << 3 ) );
+ insert( ns, BSON( "a" << 4 ) );
+ insert( ns, BSON( "a" << 5 ) );
+ insert( ns, BSON( "a" << 6 ) );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 3, c->next().getIntField( "a" ) );
+ }
+ };
+
+ class EmptyTail : public ClientBase {
+ public:
+ ~EmptyTail() {
+ client().dropCollection( "unittests.querytests.EmptyTail" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.EmptyTail";
+ client().createCollection( ns, 1900, true );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ ASSERT_EQUALS( 0, c->getCursorId() );
+ ASSERT( c->isDead() );
+ insert( ns, BSON( "a" << 0 ) );
+ c = client().query( ns, QUERY( "a" << 1 ).hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ ASSERT( 0 != c->getCursorId() );
+ ASSERT( !c->isDead() );
+ }
+ };
+
+ class TailableDelete : public ClientBase {
+ public:
+ ~TailableDelete() {
+ client().dropCollection( "unittests.querytests.TailableDelete" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailableDelete";
+ client().createCollection( ns, 8192, true, 2 );
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ c->next();
+ c->next();
+ ASSERT( !c->more() );
+ insert( ns, BSON( "a" << 2 ) );
+ insert( ns, BSON( "a" << 3 ) );
+ ASSERT( !c->more() );
+ ASSERT_EQUALS( 0, c->getCursorId() );
+ }
+ };
+
+ class TailableInsertDelete : public ClientBase {
+ public:
+ ~TailableInsertDelete() {
+ client().dropCollection( "unittests.querytests.TailableInsertDelete" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailableInsertDelete";
+ client().createCollection( ns, 1330, true );
+ insert( ns, BSON( "a" << 0 ) );
+ insert( ns, BSON( "a" << 1 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, Query().hint( BSON( "$natural" << 1 ) ), 2, 0, 0, QueryOption_CursorTailable );
+ c->next();
+ c->next();
+ ASSERT( !c->more() );
+ insert( ns, BSON( "a" << 2 ) );
+ client().remove( ns, QUERY( "a" << 1 ) );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 2, c->next().getIntField( "a" ) );
+ ASSERT( !c->more() );
+ }
+ };
+
+ class TailCappedOnly : public ClientBase {
+ public:
+ ~TailCappedOnly() {
+ client().dropCollection( "unittest.querytests.TailCappedOnly" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.TailCappedOnly";
+ client().insert( ns, BSONObj() );
+ auto_ptr< DBClientCursor > c = client().query( ns, BSONObj(), 0, 0, 0, QueryOption_CursorTailable );
+ ASSERT( c->isDead() );
+ ASSERT( !client().getLastError().empty() );
+ }
+ };
+
+ class TailableQueryOnId : public ClientBase {
+ public:
+ ~TailableQueryOnId() {
+ client().dropCollection( "unittests.querytests.TailableQueryOnId" );
+ }
+
+ void insertA(const char* ns, int a) {
+ BSONObjBuilder b;
+ b.appendOID("_id", 0, true);
+ b.appendOID("value", 0, true);
+ b.append("a", a);
+ insert(ns, b.obj());
+ }
+
+ void run() {
+ const char *ns = "unittests.querytests.TailableQueryOnId";
+ BSONObj info;
+ client().runCommand( "unittests", BSON( "create" << "querytests.TailableQueryOnId" << "capped" << true << "size" << 8192 << "autoIndexId" << true ), info );
+ insertA( ns, 0 );
+ insertA( ns, 1 );
+ auto_ptr< DBClientCursor > c1 = client().query( ns, QUERY( "a" << GT << -1 ), 0, 0, 0, QueryOption_CursorTailable );
+ OID id;
+ id.init("000000000000000000000000");
+ auto_ptr< DBClientCursor > c2 = client().query( ns, QUERY( "value" << GT << id ), 0, 0, 0, QueryOption_CursorTailable );
+ c1->next();
+ c1->next();
+ ASSERT( !c1->more() );
+ c2->next();
+ c2->next();
+ ASSERT( !c2->more() );
+ insertA( ns, 2 );
+ ASSERT( c1->more() );
+ ASSERT_EQUALS( 2, c1->next().getIntField( "a" ) );
+ ASSERT( !c1->more() );
+ ASSERT( c2->more() );
+ ASSERT_EQUALS( 2, c2->next().getIntField( "a" ) ); // SERVER-645
+ ASSERT( !c2->more() );
+ ASSERT( !c2->isDead() );
+ }
+ };
+
+ class OplogReplayMode : public ClientBase {
+ public:
+ ~OplogReplayMode() {
+ client().dropCollection( "unittests.querytests.OplogReplayMode" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.OplogReplayMode";
+ insert( ns, BSON( "ts" << 0 ) );
+ insert( ns, BSON( "ts" << 1 ) );
+ insert( ns, BSON( "ts" << 2 ) );
+ auto_ptr< DBClientCursor > c = client().query( ns, QUERY( "ts" << GT << 1 ).hint( BSON( "$natural" << 1 ) ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 2, c->next().getIntField( "ts" ) );
+ ASSERT( !c->more() );
+
+ insert( ns, BSON( "ts" << 3 ) );
+ c = client().query( ns, QUERY( "ts" << GT << 1 ).hint( BSON( "$natural" << 1 ) ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 2, c->next().getIntField( "ts" ) );
+ ASSERT( c->more() );
+ }
+ };
+
+ class BasicCount : public ClientBase {
+ public:
+ ~BasicCount() {
+ client().dropCollection( "unittests.querytests.BasicCount" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.BasicCount";
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ count( 0 );
+ insert( ns, BSON( "a" << 3 ) );
+ count( 0 );
+ insert( ns, BSON( "a" << 4 ) );
+ count( 1 );
+ insert( ns, BSON( "a" << 5 ) );
+ count( 1 );
+ insert( ns, BSON( "a" << 4 ) );
+ count( 2 );
+ }
+ private:
+ void count( unsigned long long c ) const {
+ ASSERT_EQUALS( c, client().count( "unittests.querytests.BasicCount", BSON( "a" << 4 ) ) );
+ }
+ };
+
+ class ArrayId : public ClientBase {
+ public:
+ ~ArrayId() {
+ client().dropCollection( "unittests.querytests.ArrayId" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.ArrayId";
+ client().ensureIndex( ns, BSON( "_id" << 1 ) );
+ ASSERT( !error() );
+ client().insert( ns, fromjson( "{'_id':[1,2]}" ) );
+ ASSERT( error() );
+ }
+ };
+
+ class UnderscoreNs : public ClientBase {
+ public:
+ ~UnderscoreNs() {
+ client().dropCollection( "unittests.querytests._UnderscoreNs" );
+ }
+ void run() {
+ ASSERT( !error() );
+ const char *ns = "unittests.querytests._UnderscoreNs";
+ ASSERT( client().findOne( ns, "{}" ).isEmpty() );
+ client().insert( ns, BSON( "a" << 1 ) );
+ ASSERT_EQUALS( 1, client().findOne( ns, "{}" ).getIntField( "a" ) );
+ ASSERT( !error() );
+ }
+ };
+
+ class EmptyFieldSpec : public ClientBase {
+ public:
+ ~EmptyFieldSpec() {
+ client().dropCollection( "unittests.querytests.EmptyFieldSpec" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.EmptyFieldSpec";
+ client().insert( ns, BSON( "a" << 1 ) );
+ ASSERT( !client().findOne( ns, "" ).isEmpty() );
+ BSONObj empty;
+ ASSERT( !client().findOne( ns, "", &empty ).isEmpty() );
+ }
+ };
+
+ class MultiNe : public ClientBase {
+ public:
+ ~MultiNe() {
+ client().dropCollection( "unittests.querytests.Ne" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.Ne";
+ client().insert( ns, fromjson( "{a:[1,2]}" ) );
+ ASSERT( client().findOne( ns, fromjson( "{a:{$ne:1}}" ) ).isEmpty() );
+ BSONObj spec = fromjson( "{a:{$ne:1,$ne:2}}" );
+ ASSERT( client().findOne( ns, spec ).isEmpty() );
+ }
+ };
+
+ class EmbeddedNe : public ClientBase {
+ public:
+ ~EmbeddedNe() {
+ client().dropCollection( "unittests.querytests.NestedNe" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.NestedNe";
+ client().insert( ns, fromjson( "{a:[{b:1},{b:2}]}" ) );
+ ASSERT( client().findOne( ns, fromjson( "{'a.b':{$ne:1}}" ) ).isEmpty() );
+ }
+ };
+
+ class EmbeddedNumericTypes : public ClientBase {
+ public:
+ ~EmbeddedNumericTypes() {
+ client().dropCollection( "unittests.querytests.NumericEmbedded" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.NumericEmbedded";
+ client().insert( ns, BSON( "a" << BSON ( "b" << 1 ) ) );
+ ASSERT( ! client().findOne( ns, BSON( "a" << BSON ( "b" << 1.0 ) ) ).isEmpty() );
+ client().ensureIndex( ns , BSON( "a" << 1 ) );
+ ASSERT( ! client().findOne( ns, BSON( "a" << BSON ( "b" << 1.0 ) ) ).isEmpty() );
+ }
+ };
+
+ class AutoResetIndexCache : public ClientBase {
+ public:
+ ~AutoResetIndexCache() {
+ client().dropCollection( "unittests.querytests.AutoResetIndexCache" );
+ }
+ static const char *ns() { return "unittests.querytests.AutoResetIndexCache"; }
+ static const char *idxNs() { return "unittests.system.indexes"; }
+ void index() const { ASSERT( !client().findOne( idxNs(), BSON( "name" << NE << "_id_" ) ).isEmpty() ); }
+ void noIndex() const {
+ BSONObj o = client().findOne( idxNs(), BSON( "name" << NE << "_id_" ) );
+ if( !o.isEmpty() ) {
+ cout << o.toString() << endl;
+ ASSERT( false );
+ }
+ }
+ void checkIndex() {
+ client().ensureIndex( ns(), BSON( "a" << 1 ) );
+ index();
+ }
+ void run() {
+ client().dropDatabase( "unittests" );
+ noIndex();
+ checkIndex();
+ client().dropCollection( ns() );
+ noIndex();
+ checkIndex();
+ client().dropDatabase( "unittests" );
+ noIndex();
+ checkIndex();
+ }
+ };
+
+ class UniqueIndex : public ClientBase {
+ public:
+ ~UniqueIndex() {
+ client().dropCollection( "unittests.querytests.UniqueIndex" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.UniqueIndex";
+ client().ensureIndex( ns, BSON( "a" << 1 ), true );
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ ASSERT_EQUALS( 1U, client().count( ns, BSONObj() ) );
+ client().dropCollection( ns );
+ client().ensureIndex( ns, BSON( "b" << 1 ), true );
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSONObj() ) );
+ }
+ };
+
+ class UniqueIndexPreexistingData : public ClientBase {
+ public:
+ ~UniqueIndexPreexistingData() {
+ client().dropCollection( "unittests.querytests.UniqueIndexPreexistingData" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.UniqueIndexPreexistingData";
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ), true );
+ ASSERT_EQUALS( 0U, client().count( "unittests.system.indexes", BSON( "ns" << ns << "name" << NE << "_id_" ) ) );
+ }
+ };
+
+ class SubobjectInArray : public ClientBase {
+ public:
+ ~SubobjectInArray() {
+ client().dropCollection( "unittests.querytests.SubobjectInArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.SubobjectInArray";
+ client().insert( ns, fromjson( "{a:[{b:{c:1}}]}" ) );
+ ASSERT( !client().findOne( ns, BSON( "a.b.c" << 1 ) ).isEmpty() );
+ ASSERT( !client().findOne( ns, fromjson( "{'a.c':null}" ) ).isEmpty() );
+ }
+ };
+
+ class Size : public ClientBase {
+ public:
+ ~Size() {
+ client().dropCollection( "unittests.querytests.Size" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.Size";
+ client().insert( ns, fromjson( "{a:[1,2,3]}" ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT( client().query( ns, QUERY( "a" << mongo::SIZE << 3 ).hint( BSON( "a" << 1 ) ) )->more() );
+ }
+ };
+
+ class FullArray : public ClientBase {
+ public:
+ ~FullArray() {
+ client().dropCollection( "unittests.querytests.IndexedArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.IndexedArray";
+ client().insert( ns, fromjson( "{a:[1,2,3]}" ) );
+ ASSERT( client().query( ns, Query( "{a:[1,2,3]}" ) )->more() );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT( client().query( ns, Query( "{a:{$in:[1,[1,2,3]]}}" ).hint( BSON( "a" << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{a:[1,2,3]}" ).hint( BSON( "a" << 1 ) ) )->more() ); // SERVER-146
+ }
+ };
+
+ class InsideArray : public ClientBase {
+ public:
+ ~InsideArray() {
+ client().dropCollection( "unittests.querytests.InsideArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.InsideArray";
+ client().insert( ns, fromjson( "{a:[[1],2]}" ) );
+ check( "$natural" );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ check( "a" ); // SERVER-146
+ }
+ private:
+ void check( const string &hintField ) {
+ const char *ns = "unittests.querytests.InsideArray";
+ ASSERT( client().query( ns, Query( "{a:[[1],2]}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{a:[1]}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{a:2}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( !client().query( ns, Query( "{a:1}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ }
+ };
+
+ class IndexInsideArrayCorrect : public ClientBase {
+ public:
+ ~IndexInsideArrayCorrect() {
+ client().dropCollection( "unittests.querytests.IndexInsideArrayCorrect" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.IndexInsideArrayCorrect";
+ client().insert( ns, fromjson( "{'_id':1,a:[1]}" ) );
+ client().insert( ns, fromjson( "{'_id':2,a:[[1]]}" ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ ASSERT_EQUALS( 1, client().query( ns, Query( "{a:[1]}" ).hint( BSON( "a" << 1 ) ) )->next().getIntField( "_id" ) );
+ }
+ };
+
+ class SubobjArr : public ClientBase {
+ public:
+ ~SubobjArr() {
+ client().dropCollection( "unittests.querytests.SubobjArr" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.SubobjArr";
+ client().insert( ns, fromjson( "{a:[{b:[1]}]}" ) );
+ check( "$natural" );
+ client().ensureIndex( ns, BSON( "a" << 1 ) );
+ check( "a" );
+ }
+ private:
+ void check( const string &hintField ) {
+ const char *ns = "unittests.querytests.SubobjArr";
+ ASSERT( client().query( ns, Query( "{'a.b':1}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ ASSERT( client().query( ns, Query( "{'a.b':[1]}" ).hint( BSON( hintField << 1 ) ) )->more() );
+ }
+ };
+
+ class MinMax : public ClientBase {
+ public:
+ MinMax() : ns( "unittests.querytests.MinMax" ) {}
+ ~MinMax() {
+ client().dropCollection( "unittests.querytests.MinMax" );
+ }
+ void run() {
+ client().ensureIndex( ns, BSON( "a" << 1 << "b" << 1 ) );
+ client().insert( ns, BSON( "a" << 1 << "b" << 1 ) );
+ client().insert( ns, BSON( "a" << 1 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 2 << "b" << 1 ) );
+ client().insert( ns, BSON( "a" << 2 << "b" << 2 ) );
+
+ ASSERT_EQUALS( 4, count( client().query( ns, BSONObj() ) ) );
+ BSONObj hints[] = { BSONObj(), BSON( "a" << 1 << "b" << 1 ) };
+ for( int i = 0; i < 2; ++i ) {
+ check( 0, 0, 3, 3, 4, hints[ i ] );
+ check( 1, 1, 2, 2, 3, hints[ i ] );
+ check( 1, 2, 2, 2, 2, hints[ i ] );
+ check( 1, 2, 2, 1, 1, hints[ i ] );
+
+ auto_ptr< DBClientCursor > c = query( 1, 2, 2, 2, hints[ i ] );
+ BSONObj obj = c->next();
+ ASSERT_EQUALS( 1, obj.getIntField( "a" ) );
+ ASSERT_EQUALS( 2, obj.getIntField( "b" ) );
+ obj = c->next();
+ ASSERT_EQUALS( 2, obj.getIntField( "a" ) );
+ ASSERT_EQUALS( 1, obj.getIntField( "b" ) );
+ ASSERT( !c->more() );
+ }
+ }
+ private:
+ auto_ptr< DBClientCursor > query( int minA, int minB, int maxA, int maxB, const BSONObj &hint ) {
+ Query q;
+ q = q.minKey( BSON( "a" << minA << "b" << minB ) ).maxKey( BSON( "a" << maxA << "b" << maxB ) );
+ if ( !hint.isEmpty() )
+ q.hint( hint );
+ return client().query( ns, q );
+ }
+ void check( int minA, int minB, int maxA, int maxB, int expectedCount, const BSONObj &hint = empty_ ) {
+ ASSERT_EQUALS( expectedCount, count( query( minA, minB, maxA, maxB, hint ) ) );
+ }
+ int count( auto_ptr< DBClientCursor > c ) {
+ int ret = 0;
+ while( c->more() ) {
+ ++ret;
+ c->next();
+ }
+ return ret;
+ }
+ const char *ns;
+ static BSONObj empty_;
+ };
+ BSONObj MinMax::empty_;
+
+ class MatchCodeCodeWScope : public ClientBase {
+ public:
+ MatchCodeCodeWScope() : _ns( "unittests.querytests.MatchCodeCodeWScope" ) {}
+ ~MatchCodeCodeWScope() {
+ client().dropCollection( "unittests.querytests.MatchCodeCodeWScope" );
+ }
+ void run() {
+ checkMatch();
+ client().ensureIndex( _ns, BSON( "a" << 1 ) );
+ checkMatch();
+ // Use explain queries to check index bounds.
+ {
+ BSONObj explain = client().findOne( _ns, QUERY( "a" << BSON( "$type" << (int)Code ) ).explain() );
+ BSONObjBuilder lower;
+ lower.appendCode( "", "" );
+ BSONObjBuilder upper;
+ upper.appendCodeWScope( "", "", BSONObj() );
+ ASSERT( lower.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 0 ] ) );
+ ASSERT( upper.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 1 ] ) );
+ }
+ {
+ BSONObj explain = client().findOne( _ns, QUERY( "a" << BSON( "$type" << (int)CodeWScope ) ).explain() );
+ BSONObjBuilder lower;
+ lower.appendCodeWScope( "", "", BSONObj() );
+ // This upper bound may change if a new bson type is added.
+ BSONObjBuilder upper;
+ upper << "" << BSON( "$maxElement" << 1 );
+ ASSERT( lower.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 0 ] ) );
+ ASSERT( upper.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 1 ] ) );
+ }
+ }
+ private:
+ void checkMatch() {
+ client().remove( _ns, BSONObj() );
+
+ client().insert( _ns, code() );
+ client().insert( _ns, codeWScope() );
+
+ ASSERT_EQUALS( 1U, client().count( _ns, code() ) );
+ ASSERT_EQUALS( 1U, client().count( _ns, codeWScope() ) );
+
+ ASSERT_EQUALS( 1U, client().count( _ns, BSON( "a" << BSON( "$type" << (int)Code ) ) ) );
+ ASSERT_EQUALS( 1U, client().count( _ns, BSON( "a" << BSON( "$type" << (int)CodeWScope ) ) ) );
+ }
+ BSONObj code() const {
+ BSONObjBuilder codeBuilder;
+ codeBuilder.appendCode( "a", "return 1;" );
+ return codeBuilder.obj();
+ }
+ BSONObj codeWScope() const {
+ BSONObjBuilder codeWScopeBuilder;
+ codeWScopeBuilder.appendCodeWScope( "a", "return 1;", BSONObj() );
+ return codeWScopeBuilder.obj();
+ }
+ const char *_ns;
+ };
+
+ class MatchDBRefType : public ClientBase {
+ public:
+ MatchDBRefType() : _ns( "unittests.querytests.MatchDBRefType" ) {}
+ ~MatchDBRefType() {
+ client().dropCollection( "unittests.querytests.MatchDBRefType" );
+ }
+ void run() {
+ checkMatch();
+ client().ensureIndex( _ns, BSON( "a" << 1 ) );
+ checkMatch();
+ }
+ private:
+ void checkMatch() {
+ client().remove( _ns, BSONObj() );
+ client().insert( _ns, dbref() );
+ ASSERT_EQUALS( 1U, client().count( _ns, dbref() ) );
+ ASSERT_EQUALS( 1U, client().count( _ns, BSON( "a" << BSON( "$type" << (int)DBRef ) ) ) );
+ }
+ BSONObj dbref() const {
+ BSONObjBuilder b;
+ OID oid;
+ b.appendDBRef( "a", "ns", oid );
+ return b.obj();
+ }
+ const char *_ns;
+ };
+
+ class DirectLocking : public ClientBase {
+ public:
+ void run() {
+ dblock lk;
+ Client::Context ctx( "unittests.DirectLocking" );
+ client().remove( "a.b", BSONObj() );
+ ASSERT_EQUALS( "unittests", cc().database()->name );
+ }
+ const char *ns;
+ };
+
+ class FastCountIn : public ClientBase {
+ public:
+ ~FastCountIn() {
+ client().dropCollection( "unittests.querytests.FastCountIn" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.FastCountIn";
+ client().insert( ns, BSON( "i" << "a" ) );
+ client().ensureIndex( ns, BSON( "i" << 1 ) );
+ ASSERT_EQUALS( 1U, client().count( ns, fromjson( "{i:{$in:['a']}}" ) ) );
+ }
+ };
+
+ class EmbeddedArray : public ClientBase {
+ public:
+ ~EmbeddedArray() {
+ client().dropCollection( "unittests.querytests.EmbeddedArray" );
+ }
+ void run() {
+ const char *ns = "unittests.querytests.EmbeddedArray";
+ client().insert( ns, fromjson( "{foo:{bar:['spam']}}" ) );
+ client().insert( ns, fromjson( "{foo:{bar:['spam','eggs']}}" ) );
+ client().insert( ns, fromjson( "{bar:['spam']}" ) );
+ client().insert( ns, fromjson( "{bar:['spam','eggs']}" ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSON( "bar" << "spam" ) ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSON( "foo.bar" << "spam" ) ) );
+ }
+ };
+
+ class DifferentNumbers : public ClientBase {
+ public:
+ ~DifferentNumbers() {
+ client().dropCollection( "unittests.querytests.DifferentNumbers" );
+ }
+ void t( const char * ns ) {
+ auto_ptr< DBClientCursor > cursor = client().query( ns, Query().sort( "7" ) );
+ while ( cursor->more() ) {
+ BSONObj o = cursor->next();
+ assert( o.valid() );
+ //cout << " foo " << o << endl;
+ }
+
+ }
+ void run() {
+ const char *ns = "unittests.querytests.DifferentNumbers";
+ { BSONObjBuilder b; b.append( "7" , (int)4 ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.append( "7" , (long long)2 ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.appendNull( "7" ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.append( "7" , "b" ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.appendNull( "8" ); client().insert( ns , b.obj() ); }
+ { BSONObjBuilder b; b.append( "7" , (double)3.7 ); client().insert( ns , b.obj() ); }
+
+ t(ns);
+ client().ensureIndex( ns , BSON( "7" << 1 ) );
+ t(ns);
+ }
+ };
+
+ class CollectionBase : public ClientBase {
+ public:
+
+ CollectionBase( string leaf ) {
+ _ns = "unittests.querytests.";
+ _ns += leaf;
+ client().dropCollection( ns() );
+ }
+
+ virtual ~CollectionBase() {
+ client().dropCollection( ns() );
+ }
+
+ int count() {
+ return (int) client().count( ns() );
+ }
+
+ const char * ns() {
+ return _ns.c_str();
+ }
+
+ private:
+ string _ns;
+ };
+
+ class SymbolStringSame : public CollectionBase {
+ public:
+ SymbolStringSame() : CollectionBase( "symbolstringsame" ) {}
+
+ void run() {
+ { BSONObjBuilder b; b.appendSymbol( "x" , "eliot" ); b.append( "z" , 17 ); client().insert( ns() , b.obj() ); }
+ ASSERT_EQUALS( 17 , client().findOne( ns() , BSONObj() )["z"].number() );
+ {
+ BSONObjBuilder b;
+ b.appendSymbol( "x" , "eliot" );
+ ASSERT_EQUALS( 17 , client().findOne( ns() , b.obj() )["z"].number() );
+ }
+ ASSERT_EQUALS( 17 , client().findOne( ns() , BSON( "x" << "eliot" ) )["z"].number() );
+ client().ensureIndex( ns() , BSON( "x" << 1 ) );
+ ASSERT_EQUALS( 17 , client().findOne( ns() , BSON( "x" << "eliot" ) )["z"].number() );
+ }
+ };
+
+ class TailableCappedRaceCondition : public CollectionBase {
+ public:
+
+ TailableCappedRaceCondition() : CollectionBase( "tailablecappedrace" ) {
+ client().dropCollection( ns() );
+ _n = 0;
+ }
+ void run() {
+ string err;
+
+ writelock lk("");
+ Client::Context ctx( "unittests" );
+
+ // note that extents are always at least 4KB now - so this will get rounded up a bit.
+ ASSERT( userCreateNS( ns() , fromjson( "{ capped : true , size : 2000 }" ) , err , false ) );
+ for ( int i=0; i<200; i++ ) {
+ insertNext();
+// cout << count() << endl;
+ ASSERT( count() < 90 );
+ }
+
+ int a = count();
+
+ auto_ptr< DBClientCursor > c = client().query( ns() , QUERY( "i" << GT << 0 ).hint( BSON( "$natural" << 1 ) ), 0, 0, 0, QueryOption_CursorTailable );
+ int n=0;
+ while ( c->more() ) {
+ BSONObj z = c->next();
+ n++;
+ }
+
+ ASSERT_EQUALS( a , n );
+
+ insertNext();
+ ASSERT( c->more() );
+
+ for ( int i=0; i<90; i++ ) {
+ insertNext();
+ }
+
+ while ( c->more() ) { c->next(); }
+ ASSERT( c->isDead() );
+ }
+
+ void insertNext() {
+ BSONObjBuilder b;
+ b.appendOID("_id", 0, true);
+ b.append("i", _n++);
+ insert( ns() , b.obj() );
+ }
+
+ int _n;
+ };
+
+ class HelperTest : public CollectionBase {
+ public:
+
+ HelperTest() : CollectionBase( "helpertest" ) {
+ }
+
+ void run() {
+ writelock lk("");
+ Client::Context ctx( "unittests" );
+
+ for ( int i=0; i<50; i++ ) {
+ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) );
+ }
+
+ ASSERT_EQUALS( 50 , count() );
+
+ BSONObj res;
+ ASSERT( Helpers::findOne( ns() , BSON( "_id" << 20 ) , res , true ) );
+ ASSERT_EQUALS( 40 , res["x"].numberInt() );
+
+ ASSERT( Helpers::findById( cc(), ns() , BSON( "_id" << 20 ) , res ) );
+ ASSERT_EQUALS( 40 , res["x"].numberInt() );
+
+ ASSERT( ! Helpers::findById( cc(), ns() , BSON( "_id" << 200 ) , res ) );
+
+ unsigned long long slow , fast;
+
+ int n = 10000;
+ DEV n = 1000;
+ {
+ Timer t;
+ for ( int i=0; i<n; i++ ) {
+ ASSERT( Helpers::findOne( ns() , BSON( "_id" << 20 ) , res , true ) );
+ }
+ slow = t.micros();
+ }
+ {
+ Timer t;
+ for ( int i=0; i<n; i++ ) {
+ ASSERT( Helpers::findById( cc(), ns() , BSON( "_id" << 20 ) , res ) );
+ }
+ fast = t.micros();
+ }
+
+ cout << "HelperTest slow:" << slow << " fast:" << fast << endl;
+
+ }
+ };
+
+ class HelperByIdTest : public CollectionBase {
+ public:
+
+ HelperByIdTest() : CollectionBase( "helpertestbyid" ) {
+ }
+
+ void run() {
+ writelock lk("");
+ Client::Context ctx( "unittests" );
+
+ for ( int i=0; i<1000; i++ ) {
+ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) );
+ }
+ for ( int i=0; i<1000; i+=2 ) {
+ client_.remove( ns() , BSON( "_id" << i ) );
+ }
+
+ BSONObj res;
+ for ( int i=0; i<1000; i++ ) {
+ bool found = Helpers::findById( cc(), ns() , BSON( "_id" << i ) , res );
+ ASSERT_EQUALS( i % 2 , int(found) );
+ }
+
+ }
+ };
+
+ class ClientCursorTest : public CollectionBase {
+ ClientCursorTest() : CollectionBase( "clientcursortest" ) {
+ }
+
+ void run() {
+ writelock lk("");
+ Client::Context ctx( "unittests" );
+
+ for ( int i=0; i<1000; i++ ) {
+ insert( ns() , BSON( "_id" << i << "x" << i * 2 ) );
+ }
+
+
+ }
+ };
+
+ class FindingStart : public CollectionBase {
+ public:
+ FindingStart() : CollectionBase( "findingstart" ), _old( __findingStartInitialTimeout ) {
+ __findingStartInitialTimeout = 0;
+ }
+ ~FindingStart() {
+ __findingStartInitialTimeout = _old;
+ }
+
+ void run() {
+ BSONObj info;
+ ASSERT( client().runCommand( "unittests", BSON( "create" << "querytests.findingstart" << "capped" << true << "$nExtents" << 5 << "autoIndexId" << false ), info ) );
+
+ int i = 0;
+ for( int oldCount = -1;
+ count() != oldCount;
+ oldCount = count(), client().insert( ns(), BSON( "ts" << i++ ) ) );
+
+ for( int k = 0; k < 5; ++k ) {
+ client().insert( ns(), BSON( "ts" << i++ ) );
+ int min = client().query( ns(), Query().sort( BSON( "$natural" << 1 ) ) )->next()[ "ts" ].numberInt();
+ for( int j = -1; j < i; ++j ) {
+ auto_ptr< DBClientCursor > c = client().query( ns(), QUERY( "ts" << GTE << j ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ BSONObj next = c->next();
+ ASSERT( !next[ "ts" ].eoo() );
+ ASSERT_EQUALS( ( j > min ? j : min ), next[ "ts" ].numberInt() );
+ }
+ //cout << k << endl;
+ }
+ }
+
+ private:
+ int _old;
+ };
+
+ class FindingStartPartiallyFull : public CollectionBase {
+ public:
+ FindingStartPartiallyFull() : CollectionBase( "findingstart" ), _old( __findingStartInitialTimeout ) {
+ __findingStartInitialTimeout = 0;
+ }
+ ~FindingStartPartiallyFull() {
+ __findingStartInitialTimeout = _old;
+ }
+
+ void run() {
+ unsigned startNumCursors = ClientCursor::numCursors();
+
+ BSONObj info;
+ ASSERT( client().runCommand( "unittests", BSON( "create" << "querytests.findingstart" << "capped" << true << "$nExtents" << 5 << "autoIndexId" << false ), info ) );
+
+ int i = 0;
+ for( ; i < 150; client().insert( ns(), BSON( "ts" << i++ ) ) );
+
+ for( int k = 0; k < 5; ++k ) {
+ client().insert( ns(), BSON( "ts" << i++ ) );
+ int min = client().query( ns(), Query().sort( BSON( "$natural" << 1 ) ) )->next()[ "ts" ].numberInt();
+ for( int j = -1; j < i; ++j ) {
+ auto_ptr< DBClientCursor > c = client().query( ns(), QUERY( "ts" << GTE << j ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ BSONObj next = c->next();
+ ASSERT( !next[ "ts" ].eoo() );
+ ASSERT_EQUALS( ( j > min ? j : min ), next[ "ts" ].numberInt() );
+ }
+ }
+
+ ASSERT_EQUALS( startNumCursors, ClientCursor::numCursors() );
+ }
+
+ private:
+ int _old;
+ };
+
+ /**
+ * Check OplogReplay mode where query timestamp is earlier than the earliest
+ * entry in the collection.
+ */
+ class FindingStartStale : public CollectionBase {
+ public:
+ FindingStartStale() : CollectionBase( "findingstart" ) {}
+
+ void run() {
+ unsigned startNumCursors = ClientCursor::numCursors();
+
+ BSONObj info;
+ ASSERT( client().runCommand( "unittests", BSON( "create" << "querytests.findingstart" << "capped" << true << "$nExtents" << 5 << "autoIndexId" << false ), info ) );
+
+ // Check OplogReplay mode with empty collection.
+ auto_ptr< DBClientCursor > c = client().query( ns(), QUERY( "ts" << GTE << 50 ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( !c->more() );
+
+ // Check with some docs in the collection.
+ for( int i = 100; i < 150; client().insert( ns(), BSON( "ts" << i++ ) ) );
+ c = client().query( ns(), QUERY( "ts" << GTE << 50 ), 0, 0, 0, QueryOption_OplogReplay );
+ ASSERT( c->more() );
+ ASSERT_EQUALS( 100, c->next()[ "ts" ].numberInt() );
+
+ // Check that no persistent cursors outlast our queries above.
+ ASSERT_EQUALS( startNumCursors, ClientCursor::numCursors() );
+ }
+ };
+
+ class WhatsMyUri : public CollectionBase {
+ public:
+ WhatsMyUri() : CollectionBase( "whatsmyuri" ) {}
+ void run() {
+ BSONObj result;
+ client().runCommand( "admin", BSON( "whatsmyuri" << 1 ), result );
+ ASSERT_EQUALS( unknownAddress.toString(), result[ "you" ].str() );
+ }
+ };
+
+ namespace parsedtests {
+ class basic1 {
+ public:
+ void _test( const BSONObj& in ) {
+ ParsedQuery q( "a.b" , 5 , 6 , 9 , in , BSONObj() );
+ ASSERT_EQUALS( BSON( "x" << 5 ) , q.getFilter() );
+ }
+ void run() {
+ _test( BSON( "x" << 5 ) );
+ _test( BSON( "query" << BSON( "x" << 5 ) ) );
+ _test( BSON( "$query" << BSON( "x" << 5 ) ) );
+
+ {
+ ParsedQuery q( "a.b" , 5 , 6 , 9 , BSON( "x" << 5 ) , BSONObj() );
+ ASSERT_EQUALS( 6 , q.getNumToReturn() );
+ ASSERT( q.wantMore() );
+ }
+ {
+ ParsedQuery q( "a.b" , 5 , -6 , 9 , BSON( "x" << 5 ) , BSONObj() );
+ ASSERT_EQUALS( 6 , q.getNumToReturn() );
+ ASSERT( ! q.wantMore() );
+ }
+ }
+ };
+ };
+
+ namespace queryobjecttests {
+ class names1 {
+ public:
+ void run() {
+ ASSERT_EQUALS( BSON( "x" << 1 ) , QUERY( "query" << BSON( "x" << 1 ) ).getFilter() );
+ ASSERT_EQUALS( BSON( "x" << 1 ) , QUERY( "$query" << BSON( "x" << 1 ) ).getFilter() );
+ }
+
+ };
+ }
+
+ class OrderingTest {
+ public:
+ void run() {
+ {
+ Ordering o = Ordering::make( BSON( "a" << 1 << "b" << -1 << "c" << 1 ) );
+ ASSERT_EQUALS( 1 , o.get(0) );
+ ASSERT_EQUALS( -1 , o.get(1) );
+ ASSERT_EQUALS( 1 , o.get(2) );
+
+ ASSERT( ! o.descending( 1 ) );
+ ASSERT( o.descending( 1 << 1 ) );
+ ASSERT( ! o.descending( 1 << 2 ) );
+ }
+
+ {
+ Ordering o = Ordering::make( BSON( "a.d" << 1 << "a" << 1 << "e" << -1 ) );
+ ASSERT_EQUALS( 1 , o.get(0) );
+ ASSERT_EQUALS( 1 , o.get(1) );
+ ASSERT_EQUALS( -1 , o.get(2) );
+
+ ASSERT( ! o.descending( 1 ) );
+ ASSERT( ! o.descending( 1 << 1 ) );
+ ASSERT( o.descending( 1 << 2 ) );
+ }
+
+ }
+ };
+
+ namespace proj { // Projection tests
+
+ class T1 {
+ public:
+ void run() {
+
+ Projection m;
+ m.init( BSON( "a" << 1 ) );
+ ASSERT_EQUALS( BSON( "a" << 5 ) , m.transform( BSON( "x" << 1 << "a" << 5 ) ) );
+ }
+ };
+
+ class K1 {
+ public:
+ void run() {
+
+ Projection m;
+ m.init( BSON( "a" << 1 ) );
+
+ scoped_ptr<Projection::KeyOnly> x( m.checkKey( BSON( "a" << 1 ) ) );
+ ASSERT( ! x );
+
+ x.reset( m.checkKey( BSON( "a" << 1 << "_id" << 1 ) ) );
+ ASSERT( x );
+
+ ASSERT_EQUALS( BSON( "a" << 5 << "_id" << 17 ) ,
+ x->hydrate( BSON( "" << 5 << "" << 17 ) ) );
+
+ x.reset( m.checkKey( BSON( "a" << 1 << "x" << 1 << "_id" << 1 ) ) );
+ ASSERT( x );
+
+ ASSERT_EQUALS( BSON( "a" << 5 << "_id" << 17 ) ,
+ x->hydrate( BSON( "" << 5 << "" << 123 << "" << 17 ) ) );
+
+ }
+ };
+
+ class K2 {
+ public:
+ void run() {
+
+ Projection m;
+ m.init( BSON( "a" << 1 << "_id" << 0 ) );
+
+ scoped_ptr<Projection::KeyOnly> x( m.checkKey( BSON( "a" << 1 ) ) );
+ ASSERT( x );
+
+ ASSERT_EQUALS( BSON( "a" << 17 ) ,
+ x->hydrate( BSON( "" << 17 ) ) );
+
+ x.reset( m.checkKey( BSON( "x" << 1 << "a" << 1 << "_id" << 1 ) ) );
+ ASSERT( x );
+
+ ASSERT_EQUALS( BSON( "a" << 123 ) ,
+ x->hydrate( BSON( "" << 5 << "" << 123 << "" << 17 ) ) );
+
+ }
+ };
+
+
+ class K3 {
+ public:
+ void run() {
+
+ {
+ Projection m;
+ m.init( BSON( "a" << 1 << "_id" << 0 ) );
+
+ scoped_ptr<Projection::KeyOnly> x( m.checkKey( BSON( "a" << 1 << "x.a" << 1 ) ) );
+ ASSERT( x );
+ }
+
+
+ {
+ // TODO: this is temporary SERVER-2104
+ Projection m;
+ m.init( BSON( "x.a" << 1 << "_id" << 0 ) );
+
+ scoped_ptr<Projection::KeyOnly> x( m.checkKey( BSON( "a" << 1 << "x.a" << 1 ) ) );
+ ASSERT( ! x );
+ }
+
+ }
+ };
+
+
+ }
+
+ class All : public Suite {
+ public:
+ All() : Suite( "query" ) {
+ }
+
+ void setupTests() {
+ add< FindingStart >();
+ add< FindOne >();
+ add< FindOneRequireIndex >();
+ add< FindOneEmptyObj >();
+ add< BoundedKey >();
+ add< GetMore >();
+ add< PositiveLimit >();
+ add< ReturnOneOfManyAndTail >();
+ add< TailNotAtEnd >();
+ add< EmptyTail >();
+ add< TailableDelete >();
+ add< TailableInsertDelete >();
+ add< TailCappedOnly >();
+ add< TailableQueryOnId >();
+ add< OplogReplayMode >();
+ add< ArrayId >();
+ add< UnderscoreNs >();
+ add< EmptyFieldSpec >();
+ add< MultiNe >();
+ add< EmbeddedNe >();
+ add< EmbeddedNumericTypes >();
+ add< AutoResetIndexCache >();
+ add< UniqueIndex >();
+ add< UniqueIndexPreexistingData >();
+ add< SubobjectInArray >();
+ add< Size >();
+ add< FullArray >();
+ add< InsideArray >();
+ add< IndexInsideArrayCorrect >();
+ add< SubobjArr >();
+ add< MinMax >();
+ add< MatchCodeCodeWScope >();
+ add< MatchDBRefType >();
+ add< DirectLocking >();
+ add< FastCountIn >();
+ add< EmbeddedArray >();
+ add< DifferentNumbers >();
+ add< SymbolStringSame >();
+ add< TailableCappedRaceCondition >();
+ add< HelperTest >();
+ add< HelperByIdTest >();
+ add< FindingStartPartiallyFull >();
+ add< FindingStartStale >();
+ add< WhatsMyUri >();
+
+ add< parsedtests::basic1 >();
+
+ add< queryobjecttests::names1 >();
+
+ add< OrderingTest >();
+
+ add< proj::T1 >();
+ add< proj::K1 >();
+ add< proj::K2 >();
+ add< proj::K3 >();
+ }
+ } myall;
+
+} // namespace QueryTests
+