void DBClientCursor::_assembleInit(Message& toSend) { // If we haven't gotten a cursorId yet, we need to issue a new query or command. if (!cursorId) { // HACK: // Unfortunately, this code is used by the shell to run commands, // so we need to allow the shell to send invalid options so that we can // test that the server rejects them. Thus, to allow generating commands with // invalid options, we validate them here, and fall back to generating an OP_QUERY // through assembleQueryRequest if the options are invalid. bool hasValidNToReturnForCommand = (nToReturn == 1 || nToReturn == -1); bool hasValidFlagsForCommand = !(opts & mongo::QueryOption_Exhaust); if (_isCommand && hasValidNToReturnForCommand && hasValidFlagsForCommand) { toSend = assembleCommandRequest(_client, nsToDatabaseSubstring(ns), opts, query); return; } assembleQueryRequest(ns, query, nextBatchSize(), nToSkip, fieldsToReturn, opts, toSend); return; } // Assemble a legacy getMore request. BufBuilder b; b.appendNum(opts); b.appendStr(ns); b.appendNum(nToReturn); b.appendNum(cursorId); toSend.setData(dbGetMore, b.buf(), b.len()); }
void DBClientCursor::requestMore() { assert( cursorId && pos == nReturned ); if (haveLimit){ nToReturn -= nReturned; assert(nToReturn > 0); } BufBuilder b; b.appendNum(opts); b.appendStr(ns); b.appendNum(nextBatchSize()); b.appendNum(cursorId); Message toSend; toSend.setData(dbGetMore, b.buf(), b.len()); auto_ptr<Message> response(new Message()); if ( connector ){ connector->call( toSend, *response ); m = response; dataReceived(); } else { assert( _scopedHost.size() ); ScopedDbConnection conn( _scopedHost ); conn->call( toSend , *response ); connector = conn.get(); m = response; dataReceived(); connector = 0; conn.done(); } }
void Timestamp::append(BufBuilder& builder, const StringData& fieldName) const { // No endian conversions needed, since we store in-memory representation // in little endian format, regardless of target endian. builder.appendNum(static_cast<char>(bsonTimestamp)); builder.appendStr(fieldName); builder.appendNum(asULL()); }
void DBClientMultiCommand::sendAll() { for ( deque<PendingCommand*>::iterator it = _pendingCommands.begin(); it != _pendingCommands.end(); ++it ) { PendingCommand* command = *it; dassert( NULL == command->conn ); try { // TODO: Figure out how to handle repl sets, configs dassert( command->endpoint.type() == ConnectionString::MASTER || command->endpoint.type() == ConnectionString::CUSTOM ); command->conn = shardConnectionPool.get( command->endpoint, 0 /*timeout*/); Message toSend; // see query.h for the protocol we are using here. BufBuilder bufB; bufB.appendNum( 0 ); // command/query options bufB.appendStr( command->dbName + ".$cmd" ); // write command ns bufB.appendNum( 0 ); // ntoskip (0 for command) bufB.appendNum( 1 ); // ntoreturn (1 for command) command->cmdObj.appendSelfToBufBuilder( bufB ); toSend.setData( dbQuery, bufB.buf(), bufB.len() ); // Send our command command->conn->say( toSend ); } catch ( const DBException& ex ) { command->status = ex.toStatus(); if ( NULL != command->conn ) delete command->conn; command->conn = NULL; } } }
DBClientCursor::~DBClientCursor() { if (!this) return; DESTRUCTOR_GUARD ( if ( cursorId && _ownCursor ) { BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendNum( (int)1 ); // number b.appendNum( cursorId ); Message m; m.setData( dbKillCursors , b.buf() , b.len() ); if ( connector ){ connector->sayPiggyBack( m ); } else { assert( _scopedHost.size() ); ScopedDbConnection conn( _scopedHost ); conn->sayPiggyBack( m ); conn.done(); } } );
void DBClientCursor::requestMore() { verify( cursorId && batch.pos == batch.nReturned ); if (haveLimit) { nToReturn -= batch.nReturned; verify(nToReturn > 0); } BufBuilder b; b.appendNum(opts); b.appendStr(ns); b.appendNum(nextBatchSize()); b.appendNum(cursorId); Message toSend; toSend.setData(dbGetMore, b.buf(), b.len()); auto_ptr<Message> response(new Message()); if ( _client ) { _client->call( toSend, *response ); this->batch.m = response; dataReceived(); } else { verify( _scopedHost.size() ); ScopedDbConnection conn(_scopedHost); conn->call( toSend , *response ); _client = conn.get(); this->batch.m = response; dataReceived(); _client = 0; conn.done(); } }
void assembleRequest( const string &ns, BSONObj query, int nToReturn, int nToSkip, const BSONObj *fieldsToReturn, int queryOptions, Message &toSend ) { CHECK_OBJECT( query , "assembleRequest query" ); // see query.h for the protocol we are using here. BufBuilder b; int opts = queryOptions; b.appendNum(opts); b.appendStr(ns); b.appendNum(nToSkip); b.appendNum(nToReturn); query.appendSelfToBufBuilder(b); if ( fieldsToReturn ) fieldsToReturn->appendSelfToBufBuilder(b); toSend.setData(dbQuery, b.buf(), b.len()); }
// THROWS static void sayAsCmd( DBClientBase* conn, const StringData& dbName, const BSONObj& cmdObj ) { Message toSend; // see query.h for the protocol we are using here. BufBuilder bufB; bufB.appendNum( 0 ); // command/query options bufB.appendStr( dbName.toString() + ".$cmd" ); // write command ns bufB.appendNum( 0 ); // ntoskip (0 for command) bufB.appendNum( 1 ); // ntoreturn (1 for command) cmdObj.appendSelfToBufBuilder( bufB ); toSend.setData( dbQuery, bufB.buf(), bufB.len() ); // Send our command conn->say( toSend ); }
BSONObj ShardKeyPattern::moveToFront(const BSONObj& obj) const { vector<const char*> keysToMove; keysToMove.push_back("_id"); BSONForEach(e, pattern) { if (strchr(e.fieldName(), '.') == NULL) keysToMove.push_back(e.fieldName()); } if (keysToMove.size() == 1) { return obj; } else { BufBuilder buf (obj.objsize()); buf.appendNum(obj.objsize()); vector<pair<const char*, size_t> > copies; pair<const char*, size_t> toCopy ((const char*)NULL, 0); // C++ NULL isn't a pointer type yet BSONForEach(e, obj) { bool moveToFront = false; for (vector<const char*>::const_iterator it(keysToMove.begin()), end(keysToMove.end()); it!=end; ++it) { if (strcmp(e.fieldName(), *it) == 0) { moveToFront = true; break; } } if (moveToFront) { buf.appendBuf(e.fieldName()-1, e.size()); if (toCopy.first) { copies.push_back(toCopy); toCopy.first = NULL; } } else { if (!toCopy.first) { toCopy.first = e.fieldName()-1; toCopy.second = e.size(); } else { toCopy.second += e.size(); } } } for (vector<pair<const char*, size_t> >::const_iterator it(copies.begin()), end(copies.end()); it!=end; ++it) { buf.appendBuf(it->first, it->second); } if (toCopy.first) { buf.appendBuf(toCopy.first, toCopy.second); } buf.appendChar('\0'); BSONObj out (buf.buf(), true); buf.decouple(); return out; }
// Test a basic good insert, and an extra read TEST(DBMessage1, GoodInsert) { BufBuilder b; string ns("test"); b.appendNum(static_cast<int>(1)); b.appendStr(ns); b.appendNum(static_cast<int>(3)); b.appendNum(static_cast<int>(39)); Message toSend; toSend.setData(dbInsert, b.buf(), b.len()); DbMessage d1(toSend); ASSERT_EQUALS(3, d1.pullInt()); ASSERT_EQUALS(39, d1.pullInt()); ASSERT_THROWS(d1.pullInt(), UserException); }
void Document::serializeForSorter(BufBuilder& buf) const { const int numElems = size(); buf.appendNum(numElems); for (DocumentStorageIterator it = storage().iterator(); !it.atEnd(); it.advance()) { buf.appendStr(it->nameSD(), /*NUL byte*/ true); it->val.serializeForSorter(buf); } if (hasTextScore()) { buf.appendNum(char(1)); buf.appendNum(getTextScore()); } else { buf.appendNum(char(0)); } }
StatusWith<Message> downconvertGetMoreCommandRequest(const RemoteCommandRequest& request) { auto swGetMoreRequest = GetMoreRequest::parseFromBSON(request.dbname, request.cmdObj); if (!swGetMoreRequest.isOK()) { return swGetMoreRequest.getStatus(); } auto getMoreRequest = std::move(swGetMoreRequest.getValue()); BufBuilder b; b.appendNum(std::int32_t{0}); // reserved bits b.appendStr(getMoreRequest.nss.ns()); // Without this static cast, we will append batchSize as an int64 and get an invalid message. b.appendNum(static_cast<std::int32_t>(getMoreRequest.batchSize.value_or(0))); b.appendNum(getMoreRequest.cursorid); Message m; m.setData(dbGetMore, b.buf(), b.len()); return {std::move(m)}; }
void DBClientBase::remove( const string & ns , Query obj , bool justOne ) { Message toSend; BufBuilder b; int opts = 0; b.appendNum( opts ); b.appendStr( ns ); int flags = 0; if ( justOne ) flags |= RemoveOption_JustOne; b.appendNum( flags ); obj.obj.appendSelfToBufBuilder( b ); toSend.setData( dbDelete , b.buf() , b.len() ); say( toSend ); }
// Try a bad read of a type too large TEST(DBMessage1, GoodKill2) { BufBuilder b; b.appendNum(static_cast<int>(1)); b.appendNum(static_cast<int>(3)); Message toSend; toSend.setData(dbKillCursors, b.buf(), b.len()); DbMessage d1(toSend); ASSERT_THROWS(d1.pullInt64(), UserException); }
void DBClientBase::insert( const string & ns , BSONObj obj , int flags) { Message toSend; BufBuilder b; b.appendNum( flags ); b.appendStr( ns ); obj.appendSelfToBufBuilder( b ); toSend.setData( dbInsert , b.buf() , b.len() ); say( toSend ); }
void DBClientBase::update( const string & ns , Query query , BSONObj obj , bool upsert , bool multi ) { BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendStr( ns ); int flags = 0; if ( upsert ) flags |= UpdateOption_Upsert; if ( multi ) flags |= UpdateOption_Multi; b.appendNum( flags ); query.obj.appendSelfToBufBuilder( b ); obj.appendSelfToBufBuilder( b ); Message toSend; toSend.setData( dbUpdate , b.buf() , b.len() ); say( toSend ); }
void DBClientBase::insert( const string & ns , const vector< BSONObj > &v , int flags) { Message toSend; BufBuilder b; b.appendNum( flags ); b.appendStr( ns ); for( vector< BSONObj >::const_iterator i = v.begin(); i != v.end(); ++i ) i->appendSelfToBufBuilder( b ); toSend.setData( dbInsert, b.buf(), b.len() ); say( toSend ); }
DBClientCursor::~DBClientCursor() { if (!this) return; DESTRUCTOR_GUARD ( if ( cursorId && _ownCursor && ! inShutdown() ) { BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendNum( (int)1 ); // number b.appendNum( cursorId ); Message m; m.setData( dbKillCursors , b.buf() , b.len() ); if ( _client ) { // Kill the cursor the same way the connection itself would. Usually, non-lazily if( DBClientConnection::getLazyKillCursor() ) _client->sayPiggyBack( m ); else _client->say( m ); } else { verify( _scopedHost.size() ); ScopedDbConnection conn(_scopedHost); if( DBClientConnection::getLazyKillCursor() ) conn->sayPiggyBack( m ); else conn->say( m ); conn.done(); } } );
// Test a short NS missing a trailing null TEST(DBMessage1, BadNS) { BufBuilder b; b.appendNum(static_cast<int>(1)); b.appendChar('b'); b.appendChar('a'); b.appendChar('d'); // Forget to append \0 Message toSend; toSend.setData(dbDelete, b.buf(), b.len()); ASSERT_THROWS(DbMessage d1(toSend), UserException); }
// Test a basic good insert, and an extra read TEST(DBMessage1, GoodInsert2) { BufBuilder b; string ns("test"); b.appendNum(static_cast<int>(1)); b.appendStr(ns); b.appendNum(static_cast<int>(3)); b.appendNum(static_cast<int>(39)); BSONObj bo = BSON("ts" << 0); bo.appendSelfToBufBuilder(b); Message toSend; toSend.setData(dbInsert, b.buf(), b.len()); DbMessage d1(toSend); ASSERT_EQUALS(3, d1.pullInt()); ASSERT_EQUALS(39, d1.pullInt()); BSONObj bo2 = d1.nextJsObj(); ASSERT_THROWS(d1.nextJsObj(), MsgAssertionException); }
void assembleQueryRequest(const string& ns, BSONObj query, int nToReturn, int nToSkip, const BSONObj* fieldsToReturn, int queryOptions, Message& toSend) { if (kDebugBuild) { massert(10337, (string) "object not valid assembleRequest query", query.isValid()); } // see query.h for the protocol we are using here. BufBuilder b; int opts = queryOptions; b.appendNum(opts); b.appendStr(ns); b.appendNum(nToSkip); b.appendNum(nToReturn); query.appendSelfToBufBuilder(b); if (fieldsToReturn) fieldsToReturn->appendSelfToBufBuilder(b); toSend.setData(dbQuery, b.buf(), b.len()); }
void NetworkInterfaceASIO::_messageFromRequest(const RemoteCommandRequest& request, Message* toSend, bool useOpCommand) { BSONObj query = request.cmdObj; invariant(query.isValid()); // TODO: Once OP_COMMAND work is complete, // look at client to see if it supports OP_COMMAND. // TODO: Investigate whether we can use CommandRequestBuilder here. BufBuilder b; b.appendNum(0); // opts b.appendStr(request.dbname + ".$cmd"); b.appendNum(0); // toSkip b.appendNum(1); // toReturn, don't care about responses query.appendSelfToBufBuilder(b); // TODO: If AsyncOp can own this buffer, we can avoid copying it in setData(). toSend->setData(dbQuery, b.buf(), b.len()); toSend->header().setId(nextMessageId()); toSend->header().setResponseTo(0); }
void Document::serializeForSorter(BufBuilder& buf) const { const int numElems = size(); buf.appendNum(numElems); for (DocumentStorageIterator it = storage().iterator(); !it.atEnd(); it.advance()) { buf.appendStr(it->nameSD(), /*NUL byte*/ true); it->val.serializeForSorter(buf); } if (hasTextScore()) { buf.appendNum(char(DocumentStorage::MetaType::TEXT_SCORE + 1)); buf.appendNum(getTextScore()); } if (hasRandMetaField()) { buf.appendNum(char(DocumentStorage::MetaType::RAND_VAL + 1)); buf.appendNum(getRandMetaField()); } buf.appendNum(char(0)); }
int main( int argc, const char **argv ) { const char *port = "27017"; if ( argc != 1 ) { if ( argc != 3 ) throw -12; port = argv[ 2 ]; } DBClientConnection conn; string errmsg; if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) { cout << "couldn't connect : " << errmsg << endl; throw -11; } const char * ns = "test.test1"; conn.dropCollection(ns); // clean up old data from any previous tests conn.remove( ns, BSONObj() ); assert( conn.findOne( ns , BSONObj() ).isEmpty() ); // test insert conn.insert( ns ,BSON( "name" << "eliot" << "num" << 1 ) ); assert( ! conn.findOne( ns , BSONObj() ).isEmpty() ); // test remove conn.remove( ns, BSONObj() ); assert( conn.findOne( ns , BSONObj() ).isEmpty() ); // insert, findOne testing conn.insert( ns , BSON( "name" << "eliot" << "num" << 1 ) ); { BSONObj res = conn.findOne( ns , BSONObj() ); assert( strstr( res.getStringField( "name" ) , "eliot" ) ); assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); assert( 1 == res.getIntField( "num" ) ); } // cursor conn.insert( ns ,BSON( "name" << "sara" << "num" << 2 ) ); { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } assert( count == 2 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 1 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } assert( count == 1 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 3 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } assert( count == 0 ); } // update { BSONObj res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); BSONObj after = BSONObjBuilder().appendElements( res ).append( "name2" , "h" ).obj(); conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); assert( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); assert( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); conn.update( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); assert( strstr( res.getStringField( "name" ) , "eliot" ) ); assert( strstr( res.getStringField( "name2" ) , "h" ) ); assert( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); // upsert conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after , 1 ); assert( ! conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ).isEmpty() ); } { // ensure index assert( conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); assert( ! conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); } { // hint related tests assert( conn.findOne(ns, "{}")["name"].str() == "sara" ); assert( conn.findOne(ns, "{ name : 'eliot' }")["name"].str() == "eliot" ); assert( conn.getLastError() == "" ); // nonexistent index test bool asserted = false; try { conn.findOne(ns, Query("{name:\"eliot\"}").hint("{foo:1}")); } catch ( ... ){ asserted = true; } assert( asserted ); //existing index assert( conn.findOne(ns, Query("{name:'eliot'}").hint("{name:1}")).hasElement("name") ); // run validate assert( conn.validate( ns ) ); } { // timestamp test const char * tsns = "test.tstest1"; conn.dropCollection( tsns ); { mongo::BSONObjBuilder b; b.appendTimestamp( "ts" ); conn.insert( tsns , b.obj() ); } mongo::BSONObj out = conn.findOne( tsns , mongo::BSONObj() ); Date_t oldTime = out["ts"].timestampTime(); unsigned int oldInc = out["ts"].timestampInc(); { mongo::BSONObjBuilder b1; b1.append( out["_id"] ); mongo::BSONObjBuilder b2; b2.append( out["_id"] ); b2.appendTimestamp( "ts" ); conn.update( tsns , b1.obj() , b2.obj() ); } BSONObj found = conn.findOne( tsns , mongo::BSONObj() ); cout << "old: " << out << "\nnew: " << found << endl; assert( ( oldTime < found["ts"].timestampTime() ) || ( oldTime == found["ts"].timestampTime() && oldInc < found["ts"].timestampInc() ) ); } { // check that killcursors doesn't affect last error assert( conn.getLastError().empty() ); BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendNum( (int)-1 ); // invalid # of cursors triggers exception b.appendNum( (int)-1 ); // bogus cursor id Message m; m.setData( dbKillCursors, b.buf(), b.len() ); // say() is protected in DBClientConnection, so get superclass static_cast< DBConnector* >( &conn )->say( m ); assert( conn.getLastError().empty() ); } { list<string> l = conn.getDatabaseNames(); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ){ cout << "db name : " << *i << endl; } l = conn.getCollectionNames( "test" ); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ){ cout << "coll name : " << *i << endl; } } cout << "client test finished!" << endl; }
BSONObj KeyV1::toBson(BufBuilder &bb) const { verify( _keyData != 0 ); if( !isCompactFormat() ) return bson(); BSONObjBuilder b(bb); const unsigned char *p = _keyData; while( 1 ) { unsigned bits = *p++; switch( bits & cFULLTYPEMASK ) { case cminkey: b.appendMinKey(""); break; case cnull: b.appendNull(""); break; case cfalse: b.appendBool("", false); break; case ctrue: b.appendBool("", true); break; case cmaxkey: b.appendMaxKey(""); break; case cstring: { unsigned sz = *p++; // we build the element ourself as we have to null terminate it BufBuilder &bb = b.bb(); bb.appendNum((char) String); bb.appendUChar(0); // fieldname "" bb.appendNum(sz+1); bb.appendBuf(p, sz); bb.appendUChar(0); // null char at end of string p += sz; break; } case coid: b.appendOID("", (OID *) p); p += sizeof(OID); break; case cbindata: { int len = binDataCodeToLength(*p); int subtype = (*p) & BinDataTypeMask; if( subtype & 0x8 ) { subtype = (subtype & 0x7) | 0x80; } b.appendBinData("", len, (BinDataType) subtype, ++p); p += len; break; } case cdate: b.appendDate("", (Date_t&) *p); p += 8; break; case cdouble: b.append("", (double&) *p); p += sizeof(double); break; case cint: b.append("", static_cast< int >((reinterpret_cast< const PackedDouble& >(*p)).d)); p += sizeof(double); break; case clong: b.append("", static_cast< long long>((reinterpret_cast< const PackedDouble& >(*p)).d)); p += sizeof(double); break; case cint64: b.append("", *reinterpret_cast<const long long *>(p)); p += sizeof(long long); break; default: verify(false); } if( (bits & cHASMORE) == 0 ) break; } return b.done(); }
int main( int argc, const char **argv ) { const char *port = "27017"; if ( argc != 1 ) { if ( argc != 3 ) { std::cout << "need to pass port as second param" << endl; return EXIT_FAILURE; } port = argv[ 2 ]; } DBClientConnection conn; string errmsg; if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) { cout << "couldn't connect : " << errmsg << endl; return EXIT_FAILURE; } const char * ns = "test.test1"; conn.dropCollection(ns); // clean up old data from any previous tests conn.remove( ns, BSONObj() ); verify( conn.findOne( ns , BSONObj() ).isEmpty() ); // test insert conn.insert( ns ,BSON( "name" << "eliot" << "num" << 1 ) ); verify( ! conn.findOne( ns , BSONObj() ).isEmpty() ); // test remove conn.remove( ns, BSONObj() ); verify( conn.findOne( ns , BSONObj() ).isEmpty() ); // insert, findOne testing conn.insert( ns , BSON( "name" << "eliot" << "num" << 1 ) ); { BSONObj res = conn.findOne( ns , BSONObj() ); verify( strstr( res.getStringField( "name" ) , "eliot" ) ); verify( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); verify( 1 == res.getIntField( "num" ) ); } // cursor conn.insert( ns ,BSON( "name" << "sara" << "num" << 2 ) ); { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSONObj() ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } verify( count == 2 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 1 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } verify( count == 1 ); } { auto_ptr<DBClientCursor> cursor = conn.query( ns , BSON( "num" << 3 ) ); int count = 0; while ( cursor->more() ) { count++; BSONObj obj = cursor->next(); } verify( count == 0 ); } // update { BSONObj res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); verify( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); BSONObj after = BSONObjBuilder().appendElements( res ).append( "name2" , "h" ).obj(); conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); verify( ! strstr( res.getStringField( "name2" ) , "eliot" ) ); verify( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); conn.update( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() , after ); res = conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ); verify( strstr( res.getStringField( "name" ) , "eliot" ) ); verify( strstr( res.getStringField( "name2" ) , "h" ) ); verify( conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() ).isEmpty() ); // upsert conn.update( ns , BSONObjBuilder().append( "name" , "eliot2" ).obj() , after , 1 ); verify( ! conn.findOne( ns , BSONObjBuilder().append( "name" , "eliot" ).obj() ).isEmpty() ); } { // ensure index verify( conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); verify( ! conn.ensureIndex( ns , BSON( "name" << 1 ) ) ); } { // 5 second TTL index const char * ttlns = "test.ttltest1"; conn.dropCollection( ttlns ); { mongo::BSONObjBuilder b; b.appendTimeT("ttltime", time(0)); b.append("name", "foo"); conn.insert(ttlns, b.obj()); } conn.ensureIndex(ttlns, BSON("ttltime" << 1), false, false, "", true, false, -1, 5); verify(!conn.findOne(ttlns, BSONObjBuilder().append("name", "foo").obj()).isEmpty()); // Sleep 66 seconds, 60 seconds for the TTL loop, 5 seconds for the TTL and 1 to ensure sleepsecs(66); verify(conn.findOne(ttlns, BSONObjBuilder().append("name", "foo").obj()).isEmpty()); } { // hint related tests // tokumx doesn't reorder documents just because you updated one, what even is that verify( conn.findOne(ns, "{}")["name"].str() == "eliot" ); verify( conn.findOne(ns, "{ name : 'sara' }")["name"].str() == "sara" ); verify( conn.getLastError() == "" ); // nonexistent index test bool asserted = false; try { conn.findOne(ns, Query("{name:\"eliot\"}").hint("{foo:1}")); } catch ( ... ) { asserted = true; } verify( asserted ); //existing index verify( conn.findOne(ns, Query("{name:'eliot'}").hint("{name:1}")).hasElement("name") ); // run validate verify( conn.validate( ns ) ); } { // timestamp test const char * tsns = "test.tstest1"; conn.dropCollection( tsns ); { mongo::BSONObjBuilder b; b.appendTimestamp( "ts" ); conn.insert( tsns , b.obj() ); } mongo::BSONObj out = conn.findOne( tsns , mongo::BSONObj() ); Date_t oldTime = out["ts"].timestampTime(); unsigned int oldInc = out["ts"].timestampInc(); { mongo::BSONObjBuilder b1; b1.append( out["_id"] ); mongo::BSONObjBuilder b2; b2.append( out["_id"] ); b2.appendTimestamp( "ts" ); conn.update( tsns , b1.obj() , b2.obj() ); } BSONObj found = conn.findOne( tsns , mongo::BSONObj() ); cout << "old: " << out << "\nnew: " << found << endl; verify( ( oldTime < found["ts"].timestampTime() ) || ( oldTime == found["ts"].timestampTime() && oldInc < found["ts"].timestampInc() ) ); } { // check that killcursors doesn't affect last error verify( conn.getLastError().empty() ); BufBuilder b; b.appendNum( (int)0 ); // reserved b.appendNum( (int)-1 ); // invalid # of cursors triggers exception b.appendNum( (int)-1 ); // bogus cursor id Message m; m.setData( dbKillCursors, b.buf(), b.len() ); // say() is protected in DBClientConnection, so get superclass static_cast< DBConnector* >( &conn )->say( m ); verify( conn.getLastError().empty() ); } { list<string> l = conn.getDatabaseNames(); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ) { cout << "db name : " << *i << endl; } l = conn.getCollectionNames( "test" ); for ( list<string>::iterator i = l.begin(); i != l.end(); i++ ) { cout << "coll name : " << *i << endl; } } { //Map Reduce (this mostly just tests that it compiles with all output types) const string ns = "test.mr"; conn.insert(ns, BSON("a" << 1)); conn.insert(ns, BSON("a" << 1)); const char* map = "function() { emit(this.a, 1); }"; const char* reduce = "function(key, values) { return Array.sum(values); }"; const string outcoll = ns + ".out"; BSONObj out; out = conn.mapreduce(ns, map, reduce, BSONObj()); // default to inline //MONGO_PRINT(out); out = conn.mapreduce(ns, map, reduce, BSONObj(), outcoll); //MONGO_PRINT(out); out = conn.mapreduce(ns, map, reduce, BSONObj(), outcoll.c_str()); //MONGO_PRINT(out); out = conn.mapreduce(ns, map, reduce, BSONObj(), BSON("reduce" << outcoll)); //MONGO_PRINT(out); } { // test timeouts DBClientConnection conn( true , 0 , 2 ); if ( ! conn.connect( string( "127.0.0.1:" ) + port , errmsg ) ) { cout << "couldn't connect : " << errmsg << endl; throw -11; } conn.insert( "test.totest" , BSON( "x" << 1 ) ); BSONObj res; bool gotError = false; verify( conn.eval( "test" , "return db.totest.findOne().x" , res ) ); try { conn.eval( "test" , "sleep(5000); return db.totest.findOne().x" , res ); } catch ( std::exception& e ) { gotError = true; log() << e.what() << endl; } verify( gotError ); // sleep so the server isn't locked anymore sleepsecs( 4 ); verify( conn.eval( "test" , "return db.totest.findOne().x" , res ) ); } cout << "client test finished!" << endl; return EXIT_SUCCESS; }