diff --git a/db/cloner.cpp b/db/cloner.cpp index 4c109ca0b14..c7daf5c8037 100644 --- a/db/cloner.cpp +++ b/db/cloner.cpp @@ -111,7 +111,7 @@ namespace mongo { js = fixindex(tmp); } - theDataFileMgr.insert(to_collection, (void*) js.objdata(), js.objsize()); + theDataFileMgr.insert(to_collection, js); if ( logForRepl ) logOp("i", to_collection, js); } diff --git a/db/instance.cpp b/db/instance.cpp index 77ebb102478..f1f52bb65ff 100644 --- a/db/instance.cpp +++ b/db/instance.cpp @@ -438,7 +438,7 @@ namespace mongo { uassert("insert: bad object from client", false); } - theDataFileMgr.insert(ns, (void*) js.objdata(), js.objsize()); + theDataFileMgr.insert(ns, js); logOp("i", ns, js); } } diff --git a/db/pdfile.cpp b/db/pdfile.cpp index 226e37375f3..feb72c1b691 100644 --- a/db/pdfile.cpp +++ b/db/pdfile.cpp @@ -708,7 +708,7 @@ namespace mongo { } } - if ( toupdate->netLength() < len ) { + if ( toupdate->netLength() < len ) { // doesn't fit. must reallocate. if ( d && d->capped ) { @@ -918,6 +918,13 @@ namespace mongo { } idToInsert; #pragma pack() + DiskLoc DataFileMgr::insert(const char *ns, BSONObj &o) { + DiskLoc loc = insert( ns, o.objdata(), o.objsize() ); + if ( !loc.isNull() ) + o = BSONObj( loc.rec() ); + return loc; + } + DiskLoc DataFileMgr::insert(const char *ns, const void *obuf, int len, bool god, const BSONElement &writeId) { bool addIndex = false; const char *sys = strstr(ns, "system."); diff --git a/db/pdfile.h b/db/pdfile.h index 7dc870adcfb..f83523a5b85 100644 --- a/db/pdfile.h +++ b/db/pdfile.h @@ -87,6 +87,8 @@ namespace mongo { const char *ns, Record *toupdate, const DiskLoc& dl, const char *buf, int len, stringstream& profiling); + // The object o may be updated if modified on insert. + DiskLoc insert(const char *ns, BSONObj &o); DiskLoc insert(const char *ns, const void *buf, int len, bool god = false, const BSONElement &writeId = BSONElement()); void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false); static auto_ptr findAll(const char *ns); diff --git a/db/query.cpp b/db/query.cpp index 717a67ecc22..014c29644a2 100644 --- a/db/query.cpp +++ b/db/query.cpp @@ -322,14 +322,7 @@ namespace mongo { } } - /* todo: - _ smart requery find record immediately - returns: - 2: we did applyMods() but didn't logOp() - 5: we did applyMods() and did logOp() (so don't do it again) - (clean these up later...) - */ - int _updateObjects(const char *ns, BSONObj updateobj, BSONObj pattern, bool upsert, stringstream& ss, bool logop=false) { + int __updateObjects(const char *ns, BSONObj updateobj, BSONObj &pattern, bool upsert, stringstream& ss, bool logop=false) { int profile = database->profile; if ( strstr(ns, ".system.") ) { @@ -358,6 +351,12 @@ namespace mongo { if ( !matcher.matches(js) ) { } else { + BSONObjBuilder idPattern; + BSONElement id; + if ( js.getObjectID( id ) ) + idPattern.append( id ); + pattern = idPattern.doneAndDecouple(); + /* note: we only update one row and quit. if you do multiple later, be careful or multikeys in arrays could break things badly. best to only allow updating a single row with a multikey lookup. @@ -425,10 +424,22 @@ namespace mongo { } return 0; } + + /* todo: + _ smart requery find record immediately + returns: + 2: we did applyMods() but didn't logOp() + 5: we did applyMods() and did logOp() (so don't do it again) + (clean these up later...) + */ + int _updateObjects(const char *ns, BSONObj updateobj, BSONObj pattern, bool upsert, stringstream& ss, bool logop=false) { + return __updateObjects( ns, updateobj, pattern, upsert, ss, logop ); + } + /* todo: we can optimize replication by just doing insert when an upsert triggers. */ void updateObjects(const char *ns, BSONObj updateobj, BSONObj pattern, bool upsert, stringstream& ss) { - int rc = _updateObjects(ns, updateobj, pattern, upsert, ss, true); + int rc = __updateObjects(ns, updateobj, pattern, upsert, ss, true); if ( rc != 5 && rc != 0 ) logOp("u", ns, updateobj, &pattern, &upsert); } diff --git a/dbtests/repltests.cpp b/dbtests/repltests.cpp index a9f8bbcdbb7..42daf0db5e4 100644 --- a/dbtests/repltests.cpp +++ b/dbtests/repltests.cpp @@ -176,15 +176,14 @@ namespace ReplTests { virtual void reset() const = 0; }; - class Insert : public Base { + class InsertAutoId : public Base { public: - Insert() : o_( fromjson( "{\"a\":\"b\"}" ) ) {} + InsertAutoId() : o_( fromjson( "{\"a\":\"b\"}" ) ) {} void doIt() const { client()->insert( ns(), o_ ); } void check() const { ASSERT_EQUALS( 1, count() ); - checkOne( o_ ); } void reset() const { deleteAll( ns() ); @@ -193,18 +192,22 @@ namespace ReplTests { BSONObj o_; }; - class InsertWithId : public Insert { + class InsertWithId : public InsertAutoId { public: InsertWithId() { o_ = fromjson( "{\"_id\":ObjectId(\"0f0f0f0f0f0f0f0f0f0f0f0f\"),\"a\":\"b\"}" ); } + void check() const { + ASSERT_EQUALS( 1, count() ); + checkOne( o_ ); + } }; class InsertTwo : public Base { public: InsertTwo() : - o_( fromjson( "{\"a\":\"b\"}" ) ), - t_( fromjson( "{\"c\":\"d\"}" ) ) {} + o_( fromjson( "{'_id':1,a:'b'}" ) ), + t_( fromjson( "{'_id':2,c:'d'}" ) ) {} void doIt() const { vector< BSONObj > v; v.push_back( o_ ); @@ -233,7 +236,6 @@ namespace ReplTests { } void check() const { ASSERT_EQUALS( 2, count() ); - checkAll( o_ ); } void reset() const { deleteAll( ns() ); @@ -317,20 +319,114 @@ namespace ReplTests { UpdateSpec *s_; }; - class UpdateSameField : public UpdateBase { + class UpdateSameField : public Base { public: - class Spec : public UpdateSpec { - virtual BSONObj o() const { return f( "{\"a\":\"b\",\"m\":\"n\"}" ); } - virtual BSONObj q() const { return f( "{\"a\":\"b\"}" ); } - virtual BSONObj u() const { return f( "{\"a\":\"c\"}" ); } - virtual BSONObj ou() const { return u(); } - }; - static Spec spec; UpdateSameField() : - UpdateBase( &spec ) {} - }; - UpdateSameField::Spec UpdateSameField::spec; + o_( fromjson( "{a:'b'}" ) ), + u_( fromjson( "{a:'c'}" ) ){} + void doIt() const { + client()->update( ns(), o_, u_ ); + } + void check() const { + ASSERT_EQUALS( 2, count() ); + ASSERT( !client()->findOne( ns(), o_ ).isEmpty() ); + ASSERT( !client()->findOne( ns(), u_ ).isEmpty() ); + } + void reset() const { + deleteAll( ns() ); + insert( o_ ); + insert( o_ ); + } + private: + BSONObj o_, u_; + }; + + class UpdateSameFieldWithId : public Base { + public: + UpdateSameFieldWithId() : + o_( fromjson( "{'_id':1,a:'b'}" ) ), + q_( fromjson( "{a:'b'}" ) ), + u_( fromjson( "{'_id':1,a:'c'}" ) ){} + void doIt() const { + client()->update( ns(), q_, u_ ); + } + void check() const { + ASSERT_EQUALS( 2, count() ); + ASSERT( !client()->findOne( ns(), q_ ).isEmpty() ); + ASSERT( !client()->findOne( ns(), u_ ).isEmpty() ); + } + void reset() const { + deleteAll( ns() ); + insert( o_ ); + insert( fromjson( "{'_id':2,a:'b'}" ) ); + } + private: + BSONObj o_, q_, u_; + }; + class UpdateSameFieldExplicitId : public Base { + public: + UpdateSameFieldExplicitId() : + o_( fromjson( "{'_id':1,a:'b'}" ) ), + u_( fromjson( "{'_id':1,a:'c'}" ) ){} + void doIt() const { + client()->update( ns(), o_, u_ ); + } + void check() const { + ASSERT_EQUALS( 1, count() ); + checkOne( u_ ); + } + void reset() const { + deleteAll( ns() ); + insert( o_ ); + } + protected: + BSONObj o_, u_; + }; + + class UpdateId : public UpdateSameFieldExplicitId { + public: + UpdateId() { + o_ = fromjson( "{'_id':1}" ); + u_ = fromjson( "{'_id':2}" ); + } + }; + + // class UpdateSameField : public UpdateBase { +// public: +// class Spec : public UpdateSpec { +// virtual BSONObj o() const { return f( "{\"a\":\"b\",\"m\":\"n\"}" ); } +// virtual BSONObj q() const { return f( "{\"a\":\"b\"}" ); } +// virtual BSONObj u() const { return f( "{\"a\":\"c\"}" ); } +// virtual BSONObj ou() const { return u(); } +// }; +// static Spec spec; +// UpdateSameField() : +// UpdateBase( &spec ) {} +// }; +// UpdateSameField::Spec UpdateSameField::spec; + + class UpdateDifferentFieldExplicitId : public Base { + public: + UpdateDifferentFieldExplicitId() : + o_( fromjson( "{'_id':1,a:'b'}" ) ), + q_( fromjson( "{'_id':1}" ) ), + u_( fromjson( "{'_id':1,a:'c'}" ) ){} + void doIt() const { + client()->update( ns(), q_, u_ ); + } + void check() const { + ASSERT_EQUALS( 1, count() ); + checkOne( u_ ); + } + void reset() const { + deleteAll( ns() ); + insert( o_ ); + } + protected: + BSONObj o_, q_, u_; + }; + class UpdateDifferentField : public UpdateBase { public: class Spec : public UpdateSpec { @@ -434,8 +530,8 @@ namespace ReplTests { class FailingUpdate : public Base { public: FailingUpdate() : - o_( fromjson( "{\"a\":\"b\"}" ) ), - u_( fromjson( "{\"c\":\"d\"}" ) ) {} + o_( fromjson( "{'_id':1,a:'b'}" ) ), + u_( fromjson( "{'_id':1,c:'d'}" ) ) {} void doIt() const { client()->update( ns(), o_, u_ ); client()->insert( ns(), o_ ); @@ -457,14 +553,16 @@ namespace ReplTests { public: All() { add< LogBasic >(); -// add< Idempotence::Insert >(); + add< Idempotence::InsertAutoId >(); add< Idempotence::InsertWithId >(); -// add< Idempotence::InsertTwo >(); - // FIXME Decide what is correct & uncomment -// add< Idempotence::InsertTwoIdentical >(); - // FIXME Decide what is correct & uncomment + add< Idempotence::InsertTwo >(); + add< Idempotence::InsertTwoIdentical >(); // add< Idempotence::UpdateSameField >(); + add< Idempotence::UpdateSameFieldWithId >(); + add< Idempotence::UpdateSameFieldExplicitId >(); + add< Idempotence::UpdateId >(); // add< Idempotence::UpdateDifferentField >(); + add< Idempotence::UpdateDifferentFieldExplicitId >(); // add< Idempotence::Set >(); // FIXME Decide what is correct & uncomment // add< Idempotence::SetSame >(); @@ -473,7 +571,7 @@ namespace ReplTests { // add< Idempotence::IncSame >(); add< Idempotence::Remove >(); add< Idempotence::RemoveOne >(); -// add< Idempotence::FailingUpdate >(); + add< Idempotence::FailingUpdate >(); } };