| btree.h | | btree.h | |
| | | | |
| skipping to change at line 23 | | skipping to change at line 23 | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
|
| #include "storage.h" | | #include "diskloc.h" | |
| #include "pdfile.h" | | #include "pdfile.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| #pragma pack(1) | | #pragma pack(1) | |
| | | | |
| struct _KeyNode { | | struct _KeyNode { | |
|
| DiskLoc prevChildBucket; | | DiskLoc prevChildBucket; // the lchild | |
| DiskLoc recordLoc; | | DiskLoc recordLoc; // location of the record associated with the ke | |
| | | y | |
| short keyDataOfs() const { | | short keyDataOfs() const { | |
| return (short) _kdo; | | return (short) _kdo; | |
| } | | } | |
| unsigned short _kdo; | | unsigned short _kdo; | |
| void setKeyDataOfs(short s) { | | void setKeyDataOfs(short s) { | |
| _kdo = s; | | _kdo = s; | |
| assert(s>=0); | | assert(s>=0); | |
| } | | } | |
| void setKeyDataOfsSavingUse(short s) { | | void setKeyDataOfsSavingUse(short s) { | |
| _kdo = s; | | _kdo = s; | |
| | | | |
| skipping to change at line 91 | | skipping to change at line 91 | |
| friend class BtreeBuilder; | | friend class BtreeBuilder; | |
| friend class KeyNode; | | friend class KeyNode; | |
| public: | | public: | |
| void dumpTree(DiskLoc thisLoc, const BSONObj &order); | | void dumpTree(DiskLoc thisLoc, const BSONObj &order); | |
| bool isHead() { return parent.isNull(); } | | bool isHead() { return parent.isNull(); } | |
| void assertValid(const BSONObj &order, bool force = false); | | void assertValid(const BSONObj &order, bool force = false); | |
| int fullValidate(const DiskLoc& thisLoc, const BSONObj &order); /*
traverses everything */ | | int fullValidate(const DiskLoc& thisLoc, const BSONObj &order); /*
traverses everything */ | |
| protected: | | protected: | |
| void modified(const DiskLoc& thisLoc); | | void modified(const DiskLoc& thisLoc); | |
| KeyNode keyNode(int i) const { | | KeyNode keyNode(int i) const { | |
|
| assert( i < n ); | | if ( i >= n ){ | |
| | | massert( 13000 , (string)"invalid keyNode: " + BSON( "i" < | |
| | | < i << "n" << n ).jsonString() , i < n ); | |
| | | } | |
| return KeyNode(*this, k(i)); | | return KeyNode(*this, k(i)); | |
| } | | } | |
| | | | |
| char * dataAt(short ofs) { | | char * dataAt(short ofs) { | |
| return data + ofs; | | return data + ofs; | |
| } | | } | |
| | | | |
| void init(); // initialize a new node | | void init(); // initialize a new node | |
| | | | |
| /* returns false if node is full and must be split | | /* returns false if node is full and must be split | |
| | | | |
| skipping to change at line 187 | | skipping to change at line 189 | |
| void dump(); | | void dump(); | |
| | | | |
| /* @return true if key exists in index | | /* @return true if key exists in index | |
| | | | |
| order - indicates order of keys in the index. this is basically
the index's key pattern, e.g.: | | order - indicates order of keys in the index. this is basically
the index's key pattern, e.g.: | |
| BSONObj order = ((IndexDetails&)idx).keyPattern(); | | BSONObj order = ((IndexDetails&)idx).keyPattern(); | |
| likewise below in bt_insert() etc. | | likewise below in bt_insert() etc. | |
| */ | | */ | |
| bool exists(const IndexDetails& idx, DiskLoc thisLoc, const BSONObj
& key, BSONObj order); | | bool exists(const IndexDetails& idx, DiskLoc thisLoc, const BSONObj
& key, BSONObj order); | |
| | | | |
|
| | | bool wouldCreateDup( | |
| | | const IndexDetails& idx, DiskLoc thisLoc, | |
| | | const BSONObj& key, BSONObj order, | |
| | | DiskLoc self); | |
| | | | |
| static DiskLoc addBucket(IndexDetails&); /* start a new index off,
empty */ | | static DiskLoc addBucket(IndexDetails&); /* start a new index off,
empty */ | |
|
| | | void deallocBucket(const DiskLoc &thisLoc); // clear bucket memory,
placeholder for deallocation | |
| | | | |
| static void renameIndexNamespace(const char *oldNs, const char *new
Ns); | | static void renameIndexNamespace(const char *oldNs, const char *new
Ns); | |
| | | | |
| int bt_insert(DiskLoc thisLoc, DiskLoc recordLoc, | | int bt_insert(DiskLoc thisLoc, DiskLoc recordLoc, | |
| const BSONObj& key, const BSONObj &order, bool dupsAllow
ed, | | const BSONObj& key, const BSONObj &order, bool dupsAllow
ed, | |
| IndexDetails& idx, bool toplevel = true); | | IndexDetails& idx, bool toplevel = true); | |
| | | | |
| bool unindex(const DiskLoc& thisLoc, IndexDetails& id, BSONObj& key
, const DiskLoc& recordLoc); | | bool unindex(const DiskLoc& thisLoc, IndexDetails& id, BSONObj& key
, const DiskLoc& recordLoc); | |
| | | | |
| /* locate may return an "unused" key that is just a marker. so be
careful. | | /* locate may return an "unused" key that is just a marker. so be
careful. | |
| | | | |
| skipping to change at line 372 | | skipping to change at line 380 | |
| DiskLoc locAtKeyOfs; | | DiskLoc locAtKeyOfs; | |
| BoundList bounds_; | | BoundList bounds_; | |
| unsigned boundIndex_; | | unsigned boundIndex_; | |
| }; | | }; | |
| | | | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| inline bool IndexDetails::hasKey(const BSONObj& key) { | | inline bool IndexDetails::hasKey(const BSONObj& key) { | |
| return head.btree()->exists(*this, head, key, keyPattern()); | | return head.btree()->exists(*this, head, key, keyPattern()); | |
| } | | } | |
|
| | | inline bool IndexDetails::wouldCreateDup(const BSONObj& key, DiskLoc se | |
| | | lf) { | |
| | | return head.btree()->wouldCreateDup(*this, head, key, keyPattern(), | |
| | | self); | |
| | | } | |
| | | | |
| /* build btree from the bottom up */ | | /* build btree from the bottom up */ | |
| /* _ TODO dropDups */ | | /* _ TODO dropDups */ | |
| class BtreeBuilder { | | class BtreeBuilder { | |
| bool dupsAllowed; | | bool dupsAllowed; | |
| IndexDetails& idx; | | IndexDetails& idx; | |
| unsigned long long n; | | unsigned long long n; | |
| BSONObj keyLast; | | BSONObj keyLast; | |
| BSONObj order; | | BSONObj order; | |
| bool committed; | | bool committed; | |
| | | | |
End of changes. 6 change blocks. |
| 4 lines changed or deleted | | 19 lines changed or added | |
|
| chunk.h | | chunk.h | |
| | | | |
| skipping to change at line 64 | | skipping to change at line 64 | |
| const BSONObj& getMin() const { return _min; } | | const BSONObj& getMin() const { return _min; } | |
| const BSONObj& getMax() const { return _max; } | | const BSONObj& getMax() const { return _max; } | |
| | | | |
| void setMin(const BSONObj& o){ | | void setMin(const BSONObj& o){ | |
| _min = o; | | _min = o; | |
| } | | } | |
| void setMax(const BSONObj& o){ | | void setMax(const BSONObj& o){ | |
| _max = o; | | _max = o; | |
| } | | } | |
| | | | |
|
| string getShard(){ | | string getShard() const{ | |
| return _shard; | | return _shard; | |
| } | | } | |
| void setShard( string shard ); | | void setShard( string shard ); | |
| | | | |
|
| bool contains( const BSONObj& obj ); | | bool contains( const BSONObj& obj ) const; | |
| | | | |
| string toString() const; | | string toString() const; | |
| operator string() const { return toString(); } | | operator string() const { return toString(); } | |
| | | | |
|
| bool operator==(const Chunk& s); | | bool operator==(const Chunk& s) const; | |
| | | | |
|
| bool operator!=(const Chunk& s){ | | bool operator!=(const Chunk& s) const{ | |
| return ! ( *this == s ); | | return ! ( *this == s ); | |
| } | | } | |
| | | | |
|
| void getFilter( BSONObjBuilder& b ); | | void getFilter( BSONObjBuilder& b ) const; | |
| BSONObj getFilter(){ BSONObjBuilder b; getFilter( b ); return b.obj | | BSONObj getFilter() const{ BSONObjBuilder b; getFilter( b ); return | |
| (); } | | b.obj(); } | |
| | | | |
|
| BSONObj pickSplitPoint(); | | BSONObj pickSplitPoint() const; | |
| Chunk * split(); | | Chunk * split(); | |
| Chunk * split( const BSONObj& middle ); | | Chunk * split( const BSONObj& middle ); | |
| | | | |
| /** | | /** | |
| * @return size of shard in bytes | | * @return size of shard in bytes | |
| * talks to mongod to do this | | * talks to mongod to do this | |
| */ | | */ | |
|
| long getPhysicalSize(); | | long getPhysicalSize() const; | |
| | | | |
|
| long countObjects( const BSONObj& filter = BSONObj() ); | | long countObjects( const BSONObj& filter = BSONObj() ) const; | |
| | | | |
| /** | | /** | |
| * if the amount of data written nears the max size of a shard | | * if the amount of data written nears the max size of a shard | |
| * then we check the real size, and if its too big, we split | | * then we check the real size, and if its too big, we split | |
| */ | | */ | |
| bool splitIfShould( long dataWritten ); | | bool splitIfShould( long dataWritten ); | |
| | | | |
| /* | | /* | |
| * moves either this shard or newShard if it makes sense too | | * moves either this shard or newShard if it makes sense too | |
| * @return whether or not a shard was moved | | * @return whether or not a shard was moved | |
| | | | |
| skipping to change at line 127 | | skipping to change at line 127 | |
| | | | |
| void _markModified(); | | void _markModified(); | |
| | | | |
| static long MaxChunkSize; | | static long MaxChunkSize; | |
| | | | |
| private: | | private: | |
| | | | |
| // main shard info | | // main shard info | |
| | | | |
| ChunkManager * _manager; | | ChunkManager * _manager; | |
|
| ShardKeyPattern skey(); | | ShardKeyPattern skey() const; | |
| | | | |
| string _ns; | | string _ns; | |
| BSONObj _min; | | BSONObj _min; | |
| BSONObj _max; | | BSONObj _max; | |
| string _shard; | | string _shard; | |
| ShardChunkVersion _lastmod; | | ShardChunkVersion _lastmod; | |
| | | | |
| bool _modified; | | bool _modified; | |
| | | | |
| // transient stuff | | // transient stuff | |
| | | | |
| skipping to change at line 219 | | skipping to change at line 219 | |
| | | | |
| vector<Chunk*> _chunks; | | vector<Chunk*> _chunks; | |
| map<string,unsigned long long> _maxMarkers; | | map<string,unsigned long long> _maxMarkers; | |
| | | | |
| unsigned long long _sequenceNumber; | | unsigned long long _sequenceNumber; | |
| | | | |
| friend class Chunk; | | friend class Chunk; | |
| static unsigned long long NextSequenceNumber; | | static unsigned long long NextSequenceNumber; | |
| }; | | }; | |
| | | | |
|
| | | // like BSONObjCmp. for use as an STL comparison functor | |
| | | // key-order in "order" argument must match key-order in shardkey | |
| | | class ChunkCmp { | |
| | | public: | |
| | | ChunkCmp( const BSONObj &order = BSONObj() ) : _cmp( order ) {} | |
| | | bool operator()( const Chunk &l, const Chunk &r ) const { | |
| | | return _cmp(l.getMin(), r.getMin()); | |
| | | } | |
| | | | |
| | | bool operator()( const Chunk *l, const Chunk *r ) const { | |
| | | return operator()(*l, *r); | |
| | | } | |
| | | private: | |
| | | BSONObjCmp _cmp; | |
| | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 10 change blocks. |
| 11 lines changed or deleted | | 27 lines changed or added | |
|
| client.h | | client.h | |
| | | | |
| skipping to change at line 28 | | skipping to change at line 28 | |
| | | | |
| /* Client represents a connection to the database (the server-side) and cor
responds | | /* Client represents a connection to the database (the server-side) and cor
responds | |
| to an open socket (or logical connection if pooling on sockets) from a c
lient. | | to an open socket (or logical connection if pooling on sockets) from a c
lient. | |
| | | | |
| todo: switch to asio...this will fit nicely with that. | | todo: switch to asio...this will fit nicely with that. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
|
| | | #include "security.h" | |
| #include "namespace.h" | | #include "namespace.h" | |
| #include "lasterror.h" | | #include "lasterror.h" | |
|
| #include "../util/top.h" | | #include "stats/top.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class AuthenticationInfo; | | class AuthenticationInfo; | |
| class Database; | | class Database; | |
| class CurOp; | | class CurOp; | |
| class Command; | | class Command; | |
| class Client; | | class Client; | |
| | | | |
| extern boost::thread_specific_ptr<Client> currentClient; | | extern boost::thread_specific_ptr<Client> currentClient; | |
| | | | |
|
| bool setClient(const char *ns, const string& path=dbpath, mongolock *lo | | | |
| ck = 0); | | | |
| | | | |
| class Client : boost::noncopyable { | | class Client : boost::noncopyable { | |
| public: | | public: | |
| static boost::mutex clientsMutex; | | static boost::mutex clientsMutex; | |
| static set<Client*> clients; // always be in clientsMutex when mani
pulating this | | static set<Client*> clients; // always be in clientsMutex when mani
pulating this | |
| | | | |
| class GodScope { | | class GodScope { | |
| bool _prev; | | bool _prev; | |
| public: | | public: | |
| GodScope(); | | GodScope(); | |
| ~GodScope(); | | ~GodScope(); | |
| }; | | }; | |
| | | | |
| /* Set database we want to use, then, restores when we finish (are
out of scope) | | /* Set database we want to use, then, restores when we finish (are
out of scope) | |
| Note this is also helpful if an exception happens as the state i
f fixed up. | | Note this is also helpful if an exception happens as the state i
f fixed up. | |
| */ | | */ | |
|
| class Context { | | class Context : boost::noncopyable{ | |
| Client * _client; | | Client * _client; | |
|
| Database * _olddb; | | Context * _oldContext; | |
| string _oldns; | | | |
| | | string _path; | |
| | | mongolock * _lock; | |
| | | bool _justCreated; | |
| | | | |
| | | string _ns; | |
| | | Database * _db; | |
| | | | |
| | | /** | |
| | | * at this point _client, _oldContext and _ns have to be set | |
| | | * _db should not have been touched | |
| | | * this will set _db and create if needed | |
| | | * will also set _client->_context to this | |
| | | */ | |
| | | void _finishInit( bool doauth=true); | |
| | | | |
| | | void _auth( int lockState = dbMutex.getState() ); | |
| public: | | public: | |
|
| Context(const char *ns) | | Context(const string& ns, string path=dbpath, mongolock * lock | |
| : _client( currentClient.get() ) { | | = 0 , bool doauth=true ) | |
| _olddb = _client->_database; | | : _client( currentClient.get() ) , _oldContext( _client->_c | |
| _oldns = _client->_ns; | | ontext ) , | |
| setClient(ns); | | _path( path ) , _lock( lock ) , | |
| } | | _ns( ns ){ | |
| Context(string ns) | | _finishInit( doauth ); | |
| : _client( currentClient.get() ){ | | | |
| _olddb = _client->_database; | | | |
| _oldns = _client->_ns; | | | |
| setClient(ns.c_str()); | | | |
| } | | } | |
| | | | |
| /* this version saves the context but doesn't yet set the new o
ne: */ | | /* this version saves the context but doesn't yet set the new o
ne: */ | |
|
| Context() | | | |
| : _client( currentClient.get() ) { | | | |
| _olddb = _client->database(); | | | |
| _oldns = _client->ns(); | | | |
| | | | |
|
| | | Context() | |
| | | : _client( currentClient.get() ) , _oldContext( _client->_c | |
| | | ontext ), | |
| | | _path( dbpath ) , _lock(0) , _justCreated(false){ | |
| | | _client->_context = this; | |
| | | clear(); | |
| } | | } | |
| | | | |
| /** | | /** | |
| * if you are doing this after allowing a write there could be
a race condition | | * if you are doing this after allowing a write there could be
a race condition | |
| * if someone closes that db. this checks that the DB is still
valid | | * if someone closes that db. this checks that the DB is still
valid | |
| */ | | */ | |
| Context( string ns , Database * db ); | | Context( string ns , Database * db ); | |
| | | | |
|
| ~Context() { | | ~Context(); | |
| DEV assert( _client == currentClient.get() ); | | | |
| _client->setns( _oldns.c_str(), _olddb ); | | Client* getClient() const { return _client; } | |
| | | | |
| | | Database* db() const { | |
| | | return _db; | |
| | | } | |
| | | | |
| | | const char * ns() const { | |
| | | return _ns.c_str(); | |
| | | } | |
| | | | |
| | | bool justCreated() const { | |
| | | return _justCreated; | |
| | | } | |
| | | | |
| | | bool equals( const string& ns , const string& path=dbpath ) con | |
| | | st { | |
| | | return _ns == ns && _path == path; | |
| } | | } | |
| | | | |
|
| | | bool inDB( const string& db , const string& path=dbpath ) const | |
| | | { | |
| | | if ( _path != path ) | |
| | | return false; | |
| | | | |
| | | if ( db == _ns ) | |
| | | return true; | |
| | | | |
| | | string::size_type idx = _ns.find( db ); | |
| | | if ( idx != 0 ) | |
| | | return false; | |
| | | | |
| | | return _ns[db.size()] == '.'; | |
| | | } | |
| | | | |
| | | void clear(){ | |
| | | _ns = ""; | |
| | | _db = 0; | |
| | | } | |
| | | | |
| | | /** | |
| | | * call before unlocking, so clear any non-thread safe state | |
| | | */ | |
| | | void unlocked(){ | |
| | | _db = 0; | |
| | | } | |
| | | | |
| | | /** | |
| | | * call after going back into the lock, will re-establish non-t | |
| | | hread safe stuff | |
| | | */ | |
| | | void relocked(){ | |
| | | _finishInit(); | |
| | | } | |
| | | | |
| | | friend class CurOp; | |
| }; | | }; | |
| | | | |
| private: | | private: | |
|
| CurOp * const _curOp; | | CurOp * _curOp; | |
| Database *_database; | | Context * _context; | |
| Namespace _ns; | | | |
| //NamespaceString _nsstr; | | | |
| bool _shutdown; | | bool _shutdown; | |
| list<string> _tempCollections; | | list<string> _tempCollections; | |
| const char *_desc; | | const char *_desc; | |
| bool _god; | | bool _god; | |
|
| | | AuthenticationInfo _ai; | |
| | | | |
| public: | | public: | |
|
| AuthenticationInfo *ai; | | | |
| Top top; | | AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } | |
| | | bool isAdmin() { return _ai.isAuthorized( "admin" ); } | |
| | | | |
| CurOp* curop() { return _curOp; } | | CurOp* curop() { return _curOp; } | |
|
| Database* database() { | | | |
| return _database; | | | |
| } | | | |
| const char *ns() { return _ns.buf; } | | | |
| | | | |
|
| void setns(const char *ns, Database *db) { | | Context* getContext(){ return _context; } | |
| _database = db; | | Database* database() { return _context ? _context->db() : 0; } | |
| _ns = ns; | | const char *ns() { return _context->ns(); } | |
| //_nsstr = ns; | | | |
| } | | | |
| void clearns() { setns("", 0); } | | | |
| | | | |
| Client(const char *desc); | | Client(const char *desc); | |
| ~Client(); | | ~Client(); | |
| | | | |
| const char *desc() const { return _desc; } | | const char *desc() const { return _desc; } | |
| | | | |
| void addTempCollection( const string& ns ){ | | void addTempCollection( const string& ns ){ | |
| _tempCollections.push_back( ns ); | | _tempCollections.push_back( ns ); | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 145 | | skipping to change at line 198 | |
| */ | | */ | |
| static void initThread(const char *desc); | | static void initThread(const char *desc); | |
| | | | |
| /* | | /* | |
| this has to be called as the client goes away, but before thread
termination | | this has to be called as the client goes away, but before thread
termination | |
| @return true if anything was done | | @return true if anything was done | |
| */ | | */ | |
| bool shutdown(); | | bool shutdown(); | |
| | | | |
| bool isGod() const { return _god; } | | bool isGod() const { return _god; } | |
|
| | | | |
| | | friend class CurOp; | |
| | | | |
| | | string toString() const; | |
| }; | | }; | |
| | | | |
| inline Client& cc() { | | inline Client& cc() { | |
| return *currentClient.get(); | | return *currentClient.get(); | |
| } | | } | |
| | | | |
| /* each thread which does db operations has a Client object in TLS. | | /* each thread which does db operations has a Client object in TLS. | |
| call this when your thread starts. | | call this when your thread starts. | |
| */ | | */ | |
| inline void Client::initThread(const char *desc) { | | inline void Client::initThread(const char *desc) { | |
| | | | |
| skipping to change at line 184 | | skipping to change at line 241 | |
| if( s != -1 ) { | | if( s != -1 ) { | |
| log() << "error: releaseAndWriteLock() s == " << s << endl; | | log() << "error: releaseAndWriteLock() s == " << s << endl; | |
| msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai
led, probably recursive" ); | | msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai
led, probably recursive" ); | |
| } | | } | |
| #endif | | #endif | |
| | | | |
| _writelock = true; | | _writelock = true; | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| dbMutex.lock(); | | dbMutex.lock(); | |
| | | | |
|
| /* this is defensive; as we were unlocked for a moment above, | | if ( cc().getContext() ) | |
| the Database object we reference could have been deleted: | | cc().getContext()->unlocked(); | |
| */ | | | |
| cc().clearns(); | | | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | string sayClientState(); | |
| | | | |
| }; | | }; | |
| | | | |
End of changes. 18 change blocks. |
| 45 lines changed or deleted | | 107 lines changed or added | |
|
| clientcursor.h | | clientcursor.h | |
| | | | |
| skipping to change at line 31 | | skipping to change at line 31 | |
| ClientCursor is a wrapper that represents a cursorid from our database | | ClientCursor is a wrapper that represents a cursorid from our database | |
| application's perspective. | | application's perspective. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "cursor.h" | | #include "cursor.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include "../util/message.h" | | #include "../util/message.h" | |
|
| #include "storage.h" | | #include "diskloc.h" | |
| #include "dbhelpers.h" | | #include "dbhelpers.h" | |
| #include "matcher.h" | | #include "matcher.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| typedef long long CursorId; /* passed to the client so it can send back
on getMore */ | | typedef long long CursorId; /* passed to the client so it can send back
on getMore */ | |
| class Cursor; /* internal server cursor base class */ | | class Cursor; /* internal server cursor base class */ | |
| class ClientCursor; | | class ClientCursor; | |
| | | | |
| /* todo: make this map be per connection. this will prevent cursor hij
acking security attacks perhaps. | | /* todo: make this map be per connection. this will prevent cursor hij
acking security attacks perhaps. | |
| | | | |
| skipping to change at line 108 | | skipping to change at line 108 | |
| } | | } | |
| }; | | }; | |
| | | | |
| /*const*/ CursorId cursorid; | | /*const*/ CursorId cursorid; | |
| string ns; | | string ns; | |
| auto_ptr<CoveredIndexMatcher> matcher; | | auto_ptr<CoveredIndexMatcher> matcher; | |
| auto_ptr<Cursor> c; | | auto_ptr<Cursor> c; | |
| int pos; // # objects into the curs
or so far | | int pos; // # objects into the curs
or so far | |
| BSONObj query; | | BSONObj query; | |
| | | | |
|
| ClientCursor() : _idleAgeMillis(0), _pinValue(0), _doingDeletes(fal | | ClientCursor(auto_ptr<Cursor>& _c, const char *_ns, bool okToTimeou | |
| se), pos(0) { | | t) : | |
| | | _idleAgeMillis(0), _pinValue(0), | |
| | | _doingDeletes(false), | |
| | | ns(_ns), c(_c), | |
| | | pos(0) | |
| | | { | |
| | | if( !okToTimeout ) | |
| | | noTimeout(); | |
| recursive_boostlock lock(ccmutex); | | recursive_boostlock lock(ccmutex); | |
| cursorid = allocCursorId_inlock(); | | cursorid = allocCursorId_inlock(); | |
| clientCursorsById.insert( make_pair(cursorid, this) ); | | clientCursorsById.insert( make_pair(cursorid, this) ); | |
| } | | } | |
| ~ClientCursor(); | | ~ClientCursor(); | |
| | | | |
| DiskLoc lastLoc() const { | | DiskLoc lastLoc() const { | |
| return _lastLoc; | | return _lastLoc; | |
| } | | } | |
| | | | |
| auto_ptr< FieldMatcher > filter; // which fields query wants return
ed | | auto_ptr< FieldMatcher > filter; // which fields query wants return
ed | |
| Message originalMessage; // this is effectively an auto ptr for dat
a the matcher points to | | Message originalMessage; // this is effectively an auto ptr for dat
a the matcher points to | |
| | | | |
| /* Get rid of cursors for namespaces that begin with nsprefix. | | /* Get rid of cursors for namespaces that begin with nsprefix. | |
|
| Used by drop, deleteIndexes, dropDatabase. | | Used by drop, dropIndexes, dropDatabase. | |
| */ | | */ | |
| static void invalidate(const char *nsPrefix); | | static void invalidate(const char *nsPrefix); | |
| | | | |
| /** | | /** | |
| * do a dbtemprelease | | * do a dbtemprelease | |
| * note: caller should check matcher.docMatcher().atomic() first an
d not yield if atomic - | | * note: caller should check matcher.docMatcher().atomic() first an
d not yield if atomic - | |
| * we don't do herein as this->matcher (above) is only initia
lized for true queries/getmore. | | * we don't do herein as this->matcher (above) is only initia
lized for true queries/getmore. | |
| * (ie not set for remote/update) | | * (ie not set for remote/update) | |
| * @return if the cursor is still valid. | | * @return if the cursor is still valid. | |
|
| * if false is returned, then this ClientCursor should be c | | * if false is returned, then this ClientCursor should be c | |
| onsidered deleted | | onsidered deleted - | |
| | | * in fact, the whole database could be gone. | |
| */ | | */ | |
| bool yield(); | | bool yield(); | |
| private: | | private: | |
| void setLastLoc_inlock(DiskLoc); | | void setLastLoc_inlock(DiskLoc); | |
| | | | |
| static ClientCursor* find_inlock(CursorId id, bool warn = true) { | | static ClientCursor* find_inlock(CursorId id, bool warn = true) { | |
| CCById::iterator it = clientCursorsById.find(id); | | CCById::iterator it = clientCursorsById.find(id); | |
| if ( it == clientCursorsById.end() ) { | | if ( it == clientCursorsById.end() ) { | |
| if ( warn ) | | if ( warn ) | |
| OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | | OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | |
| return 0; | | return 0; | |
| } | | } | |
| return it->second; | | return it->second; | |
| } | | } | |
| public: | | public: | |
| static ClientCursor* find(CursorId id, bool warn = true) { | | static ClientCursor* find(CursorId id, bool warn = true) { | |
| recursive_boostlock lock(ccmutex); | | recursive_boostlock lock(ccmutex); | |
| ClientCursor *c = find_inlock(id, warn); | | ClientCursor *c = find_inlock(id, warn); | |
| // if this asserts, your code was not thread safe -
you either need to set no timeout | | // if this asserts, your code was not thread safe -
you either need to set no timeout | |
| // for the cursor or keep a ClientCursor::Pointer in
scope for it. | | // for the cursor or keep a ClientCursor::Pointer in
scope for it. | |
|
| massert( 12521, "internal error: use of an unlocked ClientCurso
r", c->_pinValue ); | | massert( 12521, "internal error: use of an unlocked ClientCurso
r", c == 0 || c->_pinValue ); | |
| return c; | | return c; | |
| } | | } | |
| | | | |
| static bool erase(CursorId id) { | | static bool erase(CursorId id) { | |
| recursive_boostlock lock(ccmutex); | | recursive_boostlock lock(ccmutex); | |
| ClientCursor *cc = find_inlock(id); | | ClientCursor *cc = find_inlock(id); | |
| if ( cc ) { | | if ( cc ) { | |
| assert( cc->_pinValue < 100 ); // you can't still have an a
ctive ClientCursor::Pointer | | assert( cc->_pinValue < 100 ); // you can't still have an a
ctive ClientCursor::Pointer | |
| delete cc; | | delete cc; | |
| return true; | | return true; | |
| | | | |
| skipping to change at line 198 | | skipping to change at line 206 | |
| bool shouldTimeout( unsigned millis ){ | | bool shouldTimeout( unsigned millis ){ | |
| _idleAgeMillis += millis; | | _idleAgeMillis += millis; | |
| return _idleAgeMillis > 600000 && _pinValue == 0; | | return _idleAgeMillis > 600000 && _pinValue == 0; | |
| } | | } | |
| | | | |
| unsigned idleTime(){ | | unsigned idleTime(){ | |
| return _idleAgeMillis; | | return _idleAgeMillis; | |
| } | | } | |
| | | | |
| static void idleTimeReport(unsigned millis); | | static void idleTimeReport(unsigned millis); | |
|
| | | private: | |
| // cursors normally timeout after an inactivy period to prevent exc
ess memory use | | // cursors normally timeout after an inactivy period to prevent exc
ess memory use | |
| // setting this prevents timeout of the cursor in question. | | // setting this prevents timeout of the cursor in question. | |
| void noTimeout() { | | void noTimeout() { | |
| _pinValue++; | | _pinValue++; | |
| } | | } | |
|
| | | public: | |
| void setDoingDeletes( bool doingDeletes ){ | | void setDoingDeletes( bool doingDeletes ){ | |
| _doingDeletes = doingDeletes; | | _doingDeletes = doingDeletes; | |
| } | | } | |
| | | | |
| static unsigned byLocSize(); // just for diagnostics | | static unsigned byLocSize(); // just for diagnostics | |
| | | | |
| static void informAboutToDeleteBucket(const DiskLoc& b); | | static void informAboutToDeleteBucket(const DiskLoc& b); | |
| static void aboutToDelete(const DiskLoc& dl); | | static void aboutToDelete(const DiskLoc& dl); | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 7 change blocks. |
| 9 lines changed or deleted | | 17 lines changed or added | |
|
| concurrency.h | | concurrency.h | |
|
| | | /* | |
| | | * Copyright (C) 2010 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 Lice | |
| | | nse | |
| | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| | | */ | |
| | | | |
| /* concurrency.h | | /* concurrency.h | |
| | | | |
| mongod concurrency rules & notes will be placed here. | | mongod concurrency rules & notes will be placed here. | |
| | | | |
| Mutex heirarchy (1 = "leaf") | | Mutex heirarchy (1 = "leaf") | |
| name level | | name level | |
| Logstream::mutex 1 | | Logstream::mutex 1 | |
| ClientCursor::ccmutex 2 | | ClientCursor::ccmutex 2 | |
| dblock 3 | | dblock 3 | |
| | | | |
| End func name with _inlock to indicate "caller must lock before callin
g". | | End func name with _inlock to indicate "caller must lock before callin
g". | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #if BOOST_VERSION >= 103500 | | #if BOOST_VERSION >= 103500 | |
| #include <boost/thread/shared_mutex.hpp> | | #include <boost/thread/shared_mutex.hpp> | |
| #undef assert | | #undef assert | |
| #define assert xassert | | #define assert xassert | |
|
| | | #define HAVE_READLOCK | |
| #else | | #else | |
| #warning built with boost version 1.34 or older limited concurrency | | #warning built with boost version 1.34 or older limited concurrency | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | inline bool readLockSupported(){ | |
| | | #ifdef HAVE_READLOCK | |
| | | return true; | |
| | | #else | |
| | | return false; | |
| | | #endif | |
| | | } | |
| | | | |
| | | string sayClientState(); | |
| | | | |
| | | void curopWaitingForLock( int type ); | |
| | | void curopGotLock(); | |
| | | | |
| /* mutex time stats */ | | /* mutex time stats */ | |
| class MutexInfo { | | class MutexInfo { | |
| unsigned long long start, enter, timeLocked; // all in microseconds | | unsigned long long start, enter, timeLocked; // all in microseconds | |
| int locked; | | int locked; | |
| | | | |
| public: | | public: | |
|
| MutexInfo() : locked(0) { | | MutexInfo() : timeLocked(0) , locked(0) { | |
| start = curTimeMicros64(); | | start = curTimeMicros64(); | |
| } | | } | |
| void entered() { | | void entered() { | |
| if ( locked == 0 ) | | if ( locked == 0 ) | |
| enter = curTimeMicros64(); | | enter = curTimeMicros64(); | |
| locked++; | | locked++; | |
| assert( locked >= 1 ); | | assert( locked >= 1 ); | |
| } | | } | |
| void leaving() { | | void leaving() { | |
| locked--; | | locked--; | |
| | | | |
| skipping to change at line 54 | | skipping to change at line 84 | |
| if ( locked == 0 ) | | if ( locked == 0 ) | |
| timeLocked += curTimeMicros64() - enter; | | timeLocked += curTimeMicros64() - enter; | |
| } | | } | |
| int isLocked() const { | | int isLocked() const { | |
| return locked; | | return locked; | |
| } | | } | |
| void getTimingInfo(unsigned long long &s, unsigned long long &tl) c
onst { | | void getTimingInfo(unsigned long long &s, unsigned long long &tl) c
onst { | |
| s = start; | | s = start; | |
| tl = timeLocked; | | tl = timeLocked; | |
| } | | } | |
|
| | | unsigned long long getTimeLocked() const { | |
| | | return timeLocked; | |
| | | } | |
| }; | | }; | |
| | | | |
|
| #if BOOST_VERSION >= 103500 | | #ifdef HAVE_READLOCK | |
| //#if 0 | | //#if 0 | |
| class MongoMutex { | | class MongoMutex { | |
| MutexInfo _minfo; | | MutexInfo _minfo; | |
| boost::shared_mutex _m; | | boost::shared_mutex _m; | |
| ThreadLocalValue<int> _state; | | ThreadLocalValue<int> _state; | |
| | | | |
| /* we use a separate TLS value for releasedEarly - that is ok as | | /* we use a separate TLS value for releasedEarly - that is ok as | |
| our normal/common code path, we never even touch it. | | our normal/common code path, we never even touch it. | |
| */ | | */ | |
| ThreadLocalValue<bool> _releasedEarly; | | ThreadLocalValue<bool> _releasedEarly; | |
| | | | |
| skipping to change at line 83 | | skipping to change at line 116 | |
| */ | | */ | |
| int getState(){ return _state.get(); } | | int getState(){ return _state.get(); } | |
| void assertWriteLocked() { | | void assertWriteLocked() { | |
| assert( getState() > 0 ); | | assert( getState() > 0 ); | |
| DEV assert( !_releasedEarly.get() ); | | DEV assert( !_releasedEarly.get() ); | |
| } | | } | |
| bool atLeastReadLocked() { return _state.get() != 0; } | | bool atLeastReadLocked() { return _state.get() != 0; } | |
| void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | | void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | |
| | | | |
| void lock() { | | void lock() { | |
|
| DEV cout << "LOCK" << endl; | | //DEV cout << "LOCK" << endl; | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s > 0 ) { | | if( s > 0 ) { | |
| _state.set(s+1); | | _state.set(s+1); | |
| return; | | return; | |
| } | | } | |
|
| massert( 10293 , "internal error: locks are not upgradeable", s
== 0 ); | | massert( 10293 , (string)"internal error: locks are not upgrade
able: " + sayClientState() , s == 0 ); | |
| _state.set(1); | | _state.set(1); | |
|
| | | | |
| | | curopWaitingForLock( 1 ); | |
| _m.lock(); | | _m.lock(); | |
|
| | | curopGotLock(); | |
| | | | |
| _minfo.entered(); | | _minfo.entered(); | |
| } | | } | |
| void unlock() { | | void unlock() { | |
|
| DEV cout << "UNLOCK" << endl; | | //DEV cout << "UNLOCK" << endl; | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s > 1 ) { | | if( s > 1 ) { | |
| _state.set(s-1); | | _state.set(s-1); | |
| return; | | return; | |
| } | | } | |
| if( s != 1 ) { | | if( s != 1 ) { | |
| if( _releasedEarly.get() ) { | | if( _releasedEarly.get() ) { | |
| _releasedEarly.set(false); | | _releasedEarly.set(false); | |
| return; | | return; | |
| } | | } | |
|
| assert(false); // attempt to unlock when wasn't in a write
lock | | massert( 12599, "internal error: attempt to unlock when was
n't in a write lock", false); | |
| } | | } | |
| _state.set(0); | | _state.set(0); | |
| _minfo.leaving(); | | _minfo.leaving(); | |
| _m.unlock(); | | _m.unlock(); | |
| } | | } | |
| | | | |
| /* unlock (write lock), and when unlock() is called later, | | /* unlock (write lock), and when unlock() is called later, | |
| be smart then and don't unlock it again. | | be smart then and don't unlock it again. | |
| */ | | */ | |
| void releaseEarly() { | | void releaseEarly() { | |
| assert( getState() == 1 ); // must not be recursive | | assert( getState() == 1 ); // must not be recursive | |
| assert( !_releasedEarly.get() ); | | assert( !_releasedEarly.get() ); | |
| _releasedEarly.set(true); | | _releasedEarly.set(true); | |
| unlock(); | | unlock(); | |
| } | | } | |
| | | | |
| void lock_shared() { | | void lock_shared() { | |
|
| DEV cout << " LOCKSHARED" << endl; | | //DEV cout << " LOCKSHARED" << endl; | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s ) { | | if( s ) { | |
| if( s > 0 ) { | | if( s > 0 ) { | |
| // already in write lock - just be recursive and stay w
rite locked | | // already in write lock - just be recursive and stay w
rite locked | |
| _state.set(s+1); | | _state.set(s+1); | |
| return; | | return; | |
| } | | } | |
| else { | | else { | |
| // already in read lock - recurse | | // already in read lock - recurse | |
| _state.set(s-1); | | _state.set(s-1); | |
| return; | | return; | |
| } | | } | |
| } | | } | |
| _state.set(-1); | | _state.set(-1); | |
|
| | | curopWaitingForLock( -1 ); | |
| _m.lock_shared(); | | _m.lock_shared(); | |
|
| | | curopGotLock(); | |
| | | } | |
| | | | |
| | | bool lock_shared_try( int millis ) { | |
| | | int s = _state.get(); | |
| | | if ( s ){ | |
| | | // we already have a lock, so no need to try | |
| | | lock_shared(); | |
| | | return true; | |
| | | } | |
| | | | |
| | | boost::system_time until = get_system_time(); | |
| | | until += boost::posix_time::milliseconds(2); | |
| | | bool got = _m.timed_lock_shared( until ); | |
| | | if ( got ) | |
| | | _state.set(-1); | |
| | | return got; | |
| } | | } | |
|
| | | | |
| void unlock_shared() { | | void unlock_shared() { | |
|
| DEV cout << " UNLOCKSHARED" << endl; | | //DEV cout << " UNLOCKSHARED" << endl; | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s > 0 ) { | | if( s > 0 ) { | |
| assert( s > 1 ); /* we must have done a lock write first to
have s > 1 */ | | assert( s > 1 ); /* we must have done a lock write first to
have s > 1 */ | |
| _state.set(s-1); | | _state.set(s-1); | |
| return; | | return; | |
| } | | } | |
| if( s < -1 ) { | | if( s < -1 ) { | |
| _state.set(s+1); | | _state.set(s+1); | |
| return; | | return; | |
| } | | } | |
| | | | |
| skipping to change at line 168 | | skipping to change at line 225 | |
| }; | | }; | |
| #else | | #else | |
| /* this will be for old versions of boost */ | | /* this will be for old versions of boost */ | |
| class MongoMutex { | | class MongoMutex { | |
| MutexInfo _minfo; | | MutexInfo _minfo; | |
| boost::recursive_mutex m; | | boost::recursive_mutex m; | |
| ThreadLocalValue<bool> _releasedEarly; | | ThreadLocalValue<bool> _releasedEarly; | |
| public: | | public: | |
| MongoMutex() { } | | MongoMutex() { } | |
| void lock() { | | void lock() { | |
|
| #if BOOST_VERSION >= 103500 | | #ifdef HAVE_READLOCK | |
| m.lock(); | | m.lock(); | |
| #else | | #else | |
| boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m
); | | boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m
); | |
| #endif | | #endif | |
| _minfo.entered(); | | _minfo.entered(); | |
| } | | } | |
| | | | |
| void releaseEarly() { | | void releaseEarly() { | |
| assertWriteLocked(); // aso must not be recursive, although we
don't verify that in the old boost version | | assertWriteLocked(); // aso must not be recursive, although we
don't verify that in the old boost version | |
| assert( !_releasedEarly.get() ); | | assert( !_releasedEarly.get() ); | |
| _releasedEarly.set(true); | | _releasedEarly.set(true); | |
| _unlock(); | | _unlock(); | |
| } | | } | |
| | | | |
| void _unlock() { | | void _unlock() { | |
| _minfo.leaving(); | | _minfo.leaving(); | |
|
| #if BOOST_VERSION >= 103500 | | #ifdef HAVE_READLOCK | |
| m.unlock(); | | m.unlock(); | |
| #else | | #else | |
| boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock
(m); | | boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock
(m); | |
| #endif | | #endif | |
| } | | } | |
| void unlock() { | | void unlock() { | |
| if( _releasedEarly.get() ) { | | if( _releasedEarly.get() ) { | |
| _releasedEarly.set(false); | | _releasedEarly.set(false); | |
| return; | | return; | |
| } | | } | |
| _unlock(); | | _unlock(); | |
| } | | } | |
| | | | |
| void lock_shared() { lock(); } | | void lock_shared() { lock(); } | |
|
| | | bool lock_shared_try( int millis ) { | |
| | | while ( millis-- ){ | |
| | | if ( getState() ){ | |
| | | sleepmillis(1); | |
| | | continue; | |
| | | } | |
| | | lock_shared(); | |
| | | return true; | |
| | | } | |
| | | return false; | |
| | | } | |
| | | | |
| void unlock_shared() { unlock(); } | | void unlock_shared() { unlock(); } | |
| MutexInfo& info() { return _minfo; } | | MutexInfo& info() { return _minfo; } | |
| void assertWriteLocked() { | | void assertWriteLocked() { | |
| assert( info().isLocked() ); | | assert( info().isLocked() ); | |
| } | | } | |
| void assertAtLeastReadLocked() { | | void assertAtLeastReadLocked() { | |
| assert( info().isLocked() ); | | assert( info().isLocked() ); | |
| } | | } | |
| bool atLeastReadLocked() { return info().isLocked(); } | | bool atLeastReadLocked() { return info().isLocked(); } | |
| int getState(){ return info().isLocked() ? 1 : 0; } | | int getState(){ return info().isLocked() ? 1 : 0; } | |
| | | | |
| skipping to change at line 223 | | skipping to change at line 292 | |
| extern MongoMutex &dbMutex; | | extern MongoMutex &dbMutex; | |
| | | | |
| void dbunlocking_write(); | | void dbunlocking_write(); | |
| void dbunlocking_read(); | | void dbunlocking_read(); | |
| | | | |
| struct writelock { | | struct writelock { | |
| writelock(const string& ns) { | | writelock(const string& ns) { | |
| dbMutex.lock(); | | dbMutex.lock(); | |
| } | | } | |
| ~writelock() { | | ~writelock() { | |
|
| dbunlocking_write(); | | DESTRUCTOR_GUARD( | |
| dbMutex.unlock(); | | dbunlocking_write(); | |
| | | dbMutex.unlock(); | |
| | | ); | |
| } | | } | |
| }; | | }; | |
| | | | |
| struct readlock { | | struct readlock { | |
| readlock(const string& ns) { | | readlock(const string& ns) { | |
| dbMutex.lock_shared(); | | dbMutex.lock_shared(); | |
| } | | } | |
| ~readlock() { | | ~readlock() { | |
|
| dbunlocking_read(); | | DESTRUCTOR_GUARD( | |
| dbMutex.unlock_shared(); | | dbunlocking_read(); | |
| | | dbMutex.unlock_shared(); | |
| | | ); | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | struct readlocktry { | |
| | | readlocktry( const string&ns , int tryms ){ | |
| | | _got = dbMutex.lock_shared_try( tryms ); | |
| | | } | |
| | | ~readlocktry() { | |
| | | if ( _got ){ | |
| | | dbunlocking_read(); | |
| | | dbMutex.unlock_shared(); | |
| | | } | |
| | | } | |
| | | bool got(){ | |
| | | return _got; | |
| | | } | |
| | | bool _got; | |
| | | }; | |
| | | | |
| class mongolock { | | class mongolock { | |
| bool _writelock; | | bool _writelock; | |
| public: | | public: | |
| mongolock(bool write) : _writelock(write) { | | mongolock(bool write) : _writelock(write) { | |
| if( _writelock ) { | | if( _writelock ) { | |
| dbMutex.lock(); | | dbMutex.lock(); | |
| } | | } | |
| else | | else | |
| dbMutex.lock_shared(); | | dbMutex.lock_shared(); | |
| } | | } | |
| ~mongolock() { | | ~mongolock() { | |
|
| if( _writelock ) { | | DESTRUCTOR_GUARD( | |
| dbunlocking_write(); | | if( _writelock ) { | |
| dbMutex.unlock(); | | dbunlocking_write(); | |
| } | | dbMutex.unlock(); | |
| else { | | } else { | |
| dbunlocking_read(); | | dbunlocking_read(); | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| } | | } | |
| | | ); | |
| } | | } | |
| /* this unlocks, does NOT upgrade. that works for our current usage
*/ | | /* this unlocks, does NOT upgrade. that works for our current usage
*/ | |
| void releaseAndWriteLock(); | | void releaseAndWriteLock(); | |
| }; | | }; | |
| | | | |
| /* use writelock and readlock instead */ | | /* use writelock and readlock instead */ | |
| struct dblock : public writelock { | | struct dblock : public writelock { | |
| dblock() : writelock("") { } | | dblock() : writelock("") { } | |
| ~dblock() { | | ~dblock() { | |
| } | | } | |
| | | | |
End of changes. 24 change blocks. |
| 22 lines changed or deleted | | 113 lines changed or added | |
|
| curop.h | | curop.h | |
| // curop.h | | // curop.h | |
|
| | | /* | |
| | | * Copyright (C) 2010 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 Lice | |
| | | nse | |
| | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "namespace.h" | | #include "namespace.h" | |
|
| #include "security.h" | | | |
| #include "client.h" | | #include "client.h" | |
|
| | | #include "../util/atomic_int.h" | |
| | | #include "db.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class OpDebug { | | class OpDebug { | |
| public: | | public: | |
| StringBuilder str; | | StringBuilder str; | |
| | | | |
| void reset(){ | | void reset(){ | |
| str.reset(); | | str.reset(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| /* Current operation (for the current Client). | | /* Current operation (for the current Client). | |
| an embedded member of Client class, and typically used from within t
he mutex there. */ | | an embedded member of Client class, and typically used from within t
he mutex there. */ | |
| class CurOp : boost::noncopyable { | | class CurOp : boost::noncopyable { | |
|
| static WrappingInt _nextOpNum; | | static AtomicUInt _nextOpNum; | |
| static BSONObj _tooBig; // { $msg : "query not recording (too large
)" } | | static BSONObj _tooBig; // { $msg : "query not recording (too large
)" } | |
| | | | |
|
| | | Client * _client; | |
| | | CurOp * _wrapped; | |
| | | | |
| | | unsigned long long _start; | |
| | | unsigned long long _checkpoint; | |
| | | unsigned long long _end; | |
| | | | |
| bool _active; | | bool _active; | |
|
| Timer _timer; | | | |
| int _op; | | int _op; | |
|
| WrappingInt _opNum; | | bool _command; | |
| | | int _lockType; // see concurrency.h for values | |
| | | bool _waitingForLock; | |
| | | int _dbprofile; // 0=off, 1=slow, 2=all | |
| | | AtomicUInt _opNum; | |
| char _ns[Namespace::MaxNsLen+2]; | | char _ns[Namespace::MaxNsLen+2]; | |
|
| struct sockaddr_in client; | | struct sockaddr_in _remote; | |
| | | | |
| char _queryBuf[256]; | | char _queryBuf[256]; | |
|
| bool haveQuery() const { return *((int *) _queryBuf) != 0; } | | | |
| void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | | void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | |
|
| | | | |
| | | OpDebug _debug; | |
| | | | |
| | | void _reset(){ | |
| | | _command = false; | |
| | | _lockType = 0; | |
| | | _dbprofile = 0; | |
| | | _end = 0; | |
| | | _waitingForLock = false; | |
| | | } | |
| | | | |
| | | void setNS(const char *ns) { | |
| | | strncpy(_ns, ns, Namespace::MaxNsLen); | |
| | | } | |
| | | | |
| | | public: | |
| | | | |
| | | bool haveQuery() const { return *((int *) _queryBuf) != 0; } | |
| | | | |
| BSONObj query() { | | BSONObj query() { | |
| if( *((int *) _queryBuf) == 1 ) { | | if( *((int *) _queryBuf) == 1 ) { | |
| return _tooBig; | | return _tooBig; | |
| } | | } | |
| BSONObj o(_queryBuf); | | BSONObj o(_queryBuf); | |
| return o; | | return o; | |
| } | | } | |
| | | | |
|
| OpDebug _debug; | | void ensureStarted(){ | |
| public: | | if ( _start == 0 ) | |
| void reset( const sockaddr_in &_client) { | | _start = _checkpoint = curTimeMicros64(); | |
| | | } | |
| | | void enter( Client::Context * context ){ | |
| | | ensureStarted(); | |
| | | setNS( context->ns() ); | |
| | | if ( context->_db && context->_db->profile > _dbprofile ) | |
| | | _dbprofile = context->_db->profile; | |
| | | } | |
| | | | |
| | | void leave( Client::Context * context ){ | |
| | | unsigned long long now = curTimeMicros64(); | |
| | | Top::global.record( _ns , _op , _lockType , now - _checkpoint , | |
| | | _command ); | |
| | | _checkpoint = now; | |
| | | } | |
| | | | |
| | | void reset( const sockaddr_in & remote, int op ) { | |
| | | _reset(); | |
| | | _start = _checkpoint = 0; | |
| _active = true; | | _active = true; | |
|
| _opNum = _nextOpNum.atomicIncrement(); | | _opNum = _nextOpNum++; | |
| _timer.reset(); | | | |
| _ns[0] = '?'; // just in case not set later | | _ns[0] = '?'; // just in case not set later | |
| _debug.reset(); | | _debug.reset(); | |
| resetQuery(); | | resetQuery(); | |
|
| client = _client; | | _remote = remote; | |
| | | _op = op; | |
| | | } | |
| | | | |
| | | void markCommand(){ | |
| | | _command = true; | |
| | | } | |
| | | | |
| | | void waitingForLock( int type ){ | |
| | | _waitingForLock = true; | |
| | | if ( type > 0 ) | |
| | | _lockType = 1; | |
| | | else | |
| | | _lockType = -1; | |
| | | } | |
| | | void gotLock(){ | |
| | | _waitingForLock = false; | |
| } | | } | |
| | | | |
| OpDebug& debug(){ | | OpDebug& debug(){ | |
| return _debug; | | return _debug; | |
| } | | } | |
| | | | |
|
| WrappingInt opNum() const { return _opNum; } | | int profileLevel() const { | |
| | | return _dbprofile; | |
| | | } | |
| | | | |
| | | const char * getNS() const { | |
| | | return _ns; | |
| | | } | |
| | | | |
| | | bool shouldDBProfile( int ms ) const { | |
| | | if ( _dbprofile <= 0 ) | |
| | | return false; | |
| | | | |
| | | return _dbprofile >= 2 || ms >= cmdLine.slowMS; | |
| | | } | |
| | | | |
| | | AtomicUInt opNum() const { return _opNum; } | |
| | | | |
| | | /** if this op is running */ | |
| bool active() const { return _active; } | | bool active() const { return _active; } | |
| | | | |
|
| int elapsedMillis(){ return _timer.millis(); } | | int getLockType() const { return _lockType; } | |
| | | bool isWaitingForLock() const { return _waitingForLock; } | |
| | | int getOp() const { return _op; } | |
| | | | |
| /** micros */ | | /** micros */ | |
|
| unsigned long long startTime(){ | | unsigned long long startTime() { | |
| return _timer.startTime(); | | ensureStarted(); | |
| | | return _start; | |
| } | | } | |
| | | | |
|
| void setActive(bool active) { _active = active; } | | void done() { | |
| void setNS(const char *ns) { | | _active = false; | |
| strncpy(_ns, ns, Namespace::MaxNsLen); | | _end = curTimeMicros64(); | |
| } | | } | |
|
| void setOp(int op) { _op = op; } | | | |
| | | unsigned long long totalTimeMicros() { | |
| | | massert( 12601 , "CurOp not marked done yet" , ! _active ); | |
| | | return _end - startTime(); | |
| | | } | |
| | | | |
| | | int totalTimeMillis() { | |
| | | return (int) (totalTimeMicros() / 1000); | |
| | | } | |
| | | | |
| | | int elapsedMillis() { | |
| | | unsigned long long total = curTimeMicros64() - startTime(); | |
| | | return (int) (total / 1000); | |
| | | } | |
| | | | |
| | | int elapsedSeconds() { | |
| | | return elapsedMillis() / 1000; | |
| | | } | |
| | | | |
| void setQuery(const BSONObj& query) { | | void setQuery(const BSONObj& query) { | |
| if( query.objsize() > (int) sizeof(_queryBuf) ) { | | if( query.objsize() > (int) sizeof(_queryBuf) ) { | |
| resetQuery(1); // flag as too big and return | | resetQuery(1); // flag as too big and return | |
| return; | | return; | |
| } | | } | |
| memcpy(_queryBuf, query.objdata(), query.objsize()); | | memcpy(_queryBuf, query.objdata(), query.objsize()); | |
| } | | } | |
| | | | |
|
| CurOp() { | | CurOp( Client * client , CurOp * wrapped = 0 ) { | |
| | | _client = client; | |
| | | _wrapped = wrapped; | |
| | | if ( _wrapped ){ | |
| | | _client->_curOp = this; | |
| | | } | |
| | | _start = _checkpoint = 0; | |
| _active = false; | | _active = false; | |
|
| // opNum = 0; | | _reset(); | |
| _op = 0; | | _op = 0; | |
| // These addresses should never be written to again. The zeroe
s are | | // These addresses should never be written to again. The zeroe
s are | |
| // placed here as a precaution because currentOp may be accesse
d | | // placed here as a precaution because currentOp may be accesse
d | |
| // without the db mutex. | | // without the db mutex. | |
| memset(_ns, 0, sizeof(_ns)); | | memset(_ns, 0, sizeof(_ns)); | |
| memset(_queryBuf, 0, sizeof(_queryBuf)); | | memset(_queryBuf, 0, sizeof(_queryBuf)); | |
| } | | } | |
| | | | |
|
| | | ~CurOp(){ | |
| | | if ( _wrapped ) | |
| | | _client->_curOp = _wrapped; | |
| | | } | |
| | | | |
| BSONObj info() { | | BSONObj info() { | |
|
| AuthenticationInfo *ai = currentClient.get()->ai; | | if( ! cc().getAuthenticationInfo()->isAuthorized("admin") ) { | |
| if( !ai->isAuthorized("admin") ) { | | | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
| b.append("err", "unauthorized"); | | b.append("err", "unauthorized"); | |
| return b.obj(); | | return b.obj(); | |
| } | | } | |
| return infoNoauth(); | | return infoNoauth(); | |
| } | | } | |
| | | | |
|
| BSONObj infoNoauth() { | | BSONObj infoNoauth(); | |
| BSONObjBuilder b; | | | |
| b.append("opid", _opNum); | | | |
| b.append("active", _active); | | | |
| if( _active ) | | | |
| b.append("secs_running", _timer.seconds() ); | | | |
| if( _op == 2004 ) | | | |
| b.append("op", "query"); | | | |
| else if( _op == 2005 ) | | | |
| b.append("op", "getMore"); | | | |
| else if( _op == 2001 ) | | | |
| b.append("op", "update"); | | | |
| else if( _op == 2002 ) | | | |
| b.append("op", "insert"); | | | |
| else if( _op == 2006 ) | | | |
| b.append("op", "delete"); | | | |
| else | | | |
| b.append("op", _op); | | | |
| b.append("ns", _ns); | | | |
| | | | |
|
| if( haveQuery() ) { | | string getRemoteString(){ | |
| b.append("query", query()); | | stringstream ss; | |
| } | | ss << inet_ntoa( _remote.sin_addr ) << ":" << ntohs( _remote.si | |
| // b.append("inLock", ?? | | n_port ); | |
| stringstream clientStr; | | return ss.str(); | |
| clientStr << inet_ntoa( client.sin_addr ) << ":" << ntohs( clie | | | |
| nt.sin_port ); | | | |
| b.append("client", clientStr.str()); | | | |
| return b.obj(); | | | |
| } | | } | |
|
| | | | |
| | | friend class Client; | |
| }; | | }; | |
| | | | |
| /* 0 = ok | | /* 0 = ok | |
| 1 = kill current operation and reset this to 0 | | 1 = kill current operation and reset this to 0 | |
| future: maybe use this as a "going away" thing on process terminatio
n with a higher flag value | | future: maybe use this as a "going away" thing on process terminatio
n with a higher flag value | |
| */ | | */ | |
| extern class KillCurrentOp { | | extern class KillCurrentOp { | |
| enum { Off, On, All } state; | | enum { Off, On, All } state; | |
|
| WrappingInt toKill; | | AtomicUInt toKill; | |
| public: | | public: | |
| void killAll() { state = All; } | | void killAll() { state = All; } | |
|
| void kill(WrappingInt i) { toKill = i; state = On; } | | void kill(AtomicUInt i) { toKill = i; state = On; } | |
| | | | |
| void checkForInterrupt() { | | void checkForInterrupt() { | |
| if( state != Off ) { | | if( state != Off ) { | |
| if( state == All ) | | if( state == All ) | |
| uasserted(11600,"interrupted at shutdown"); | | uasserted(11600,"interrupted at shutdown"); | |
| if( cc().curop()->opNum() == toKill ) { | | if( cc().curop()->opNum() == toKill ) { | |
| state = Off; | | state = Off; | |
| uasserted(11601,"interrupted"); | | uasserted(11601,"interrupted"); | |
| } | | } | |
| } | | } | |
| | | | |
End of changes. 27 change blocks. |
| 54 lines changed or deleted | | 162 lines changed or added | |
|
| database.h | | database.h | |
| | | | |
| skipping to change at line 38 | | skipping to change at line 38 | |
| * NOT memory mapped | | * NOT memory mapped | |
| */ | | */ | |
| class Database { | | class Database { | |
| public: | | public: | |
| static bool _openAllFiles; | | static bool _openAllFiles; | |
| | | | |
| Database(const char *nm, bool& newDb, const string& _path = dbpath) | | Database(const char *nm, bool& newDb, const string& _path = dbpath) | |
| : name(nm), path(_path), namespaceIndex( path, name ) { | | : name(nm), path(_path), namespaceIndex( path, name ) { | |
| | | | |
| { // check db name is valid | | { // check db name is valid | |
|
| int L = strlen(nm); | | size_t L = strlen(nm); | |
| uassert( 10028 , "db name is empty", L > 0 ); | | uassert( 10028 , "db name is empty", L > 0 ); | |
| uassert( 10029 , "bad db name [1]", *nm != '.' ); | | uassert( 10029 , "bad db name [1]", *nm != '.' ); | |
| uassert( 10030 , "bad db name [2]", nm[L-1] != '.' ); | | uassert( 10030 , "bad db name [2]", nm[L-1] != '.' ); | |
| uassert( 10031 , "bad char(s) in db name", strchr(nm, ' ')
== 0 ); | | uassert( 10031 , "bad char(s) in db name", strchr(nm, ' ')
== 0 ); | |
| uassert( 10032 , "db name too long", L < 64 ); | | uassert( 10032 , "db name too long", L < 64 ); | |
| } | | } | |
| | | | |
| newDb = namespaceIndex.exists(); | | newDb = namespaceIndex.exists(); | |
| profile = 0; | | profile = 0; | |
| profileName = name + ".system.profile"; | | profileName = name + ".system.profile"; | |
| | | | |
| skipping to change at line 65 | | skipping to change at line 65 | |
| openAllFiles(); | | openAllFiles(); | |
| | | | |
| } | | } | |
| | | | |
| magic = 781231; | | magic = 781231; | |
| } | | } | |
| | | | |
| ~Database() { | | ~Database() { | |
| magic = 0; | | magic = 0; | |
| btreeStore->closeFiles(name, path); | | btreeStore->closeFiles(name, path); | |
|
| int n = files.size(); | | size_t n = files.size(); | |
| for ( int i = 0; i < n; i++ ) | | for ( size_t i = 0; i < n; i++ ) | |
| delete files[i]; | | delete files[i]; | |
| } | | } | |
| | | | |
| /** | | /** | |
| * tries to make sure that this hasn't been deleted | | * tries to make sure that this hasn't been deleted | |
| */ | | */ | |
| bool isOk(){ | | bool isOk(){ | |
| return magic == 781231; | | return magic == 781231; | |
| } | | } | |
| | | | |
| bool isEmpty(){ | | bool isEmpty(){ | |
| return ! namespaceIndex.allocated(); | | return ! namespaceIndex.allocated(); | |
| } | | } | |
| | | | |
|
| bool exists(int n) { | | boost::filesystem::path fileName( int n ) { | |
| stringstream ss; | | stringstream ss; | |
| ss << name << '.' << n; | | ss << name << '.' << n; | |
| boost::filesystem::path fullName; | | boost::filesystem::path fullName; | |
|
| fullName = boost::filesystem::path(path) / ss.str(); | | fullName = boost::filesystem::path(path); | |
| return boost::filesystem::exists(fullName); | | if ( directoryperdb ) | |
| | | fullName /= name; | |
| | | fullName /= ss.str(); | |
| | | return fullName; | |
| | | } | |
| | | | |
| | | bool exists(int n) { | |
| | | return boost::filesystem::exists( fileName( n ) ); | |
| } | | } | |
| | | | |
| void openAllFiles() { | | void openAllFiles() { | |
| int n = 0; | | int n = 0; | |
| while( exists(n) ) { | | while( exists(n) ) { | |
| getFile(n); | | getFile(n); | |
| n++; | | n++; | |
| } | | } | |
| // If last file is empty, consider it preallocated and make sur
e it's not mapped | | // If last file is empty, consider it preallocated and make sur
e it's not mapped | |
| // until a write is requested | | // until a write is requested | |
| | | | |
| skipping to change at line 126 | | skipping to change at line 133 | |
| if ( n > 100 ) | | if ( n > 100 ) | |
| out() << "getFile(): n=" << n << "?" << endl; | | out() << "getFile(): n=" << n << "?" << endl; | |
| } | | } | |
| MongoDataFile* p = 0; | | MongoDataFile* p = 0; | |
| if ( !preallocateOnly ) { | | if ( !preallocateOnly ) { | |
| while ( n >= (int) files.size() ) | | while ( n >= (int) files.size() ) | |
| files.push_back(0); | | files.push_back(0); | |
| p = files[n]; | | p = files[n]; | |
| } | | } | |
| if ( p == 0 ) { | | if ( p == 0 ) { | |
|
| stringstream ss; | | boost::filesystem::path fullName = fileName( n ); | |
| ss << name << '.' << n; | | | |
| boost::filesystem::path fullName; | | | |
| fullName = boost::filesystem::path(path) / ss.str(); | | | |
| string fullNameString = fullName.string(); | | string fullNameString = fullName.string(); | |
| p = new MongoDataFile(n); | | p = new MongoDataFile(n); | |
| int minSize = 0; | | int minSize = 0; | |
| if ( n != 0 && files[ n - 1 ] ) | | if ( n != 0 && files[ n - 1 ] ) | |
| minSize = files[ n - 1 ]->getHeader()->fileLength; | | minSize = files[ n - 1 ]->getHeader()->fileLength; | |
| if ( sizeNeeded + MDFHeader::headerSize() > minSize ) | | if ( sizeNeeded + MDFHeader::headerSize() > minSize ) | |
| minSize = sizeNeeded + MDFHeader::headerSize(); | | minSize = sizeNeeded + MDFHeader::headerSize(); | |
| try { | | try { | |
| p->open( fullNameString.c_str(), minSize, preallocateOn
ly ); | | p->open( fullNameString.c_str(), minSize, preallocateOn
ly ); | |
| } | | } | |
| | | | |
End of changes. 5 change blocks. |
| 10 lines changed or deleted | | 14 lines changed or added | |
|
| db.h | | db.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "../util/message.h" | | #include "../util/message.h" | |
|
| #include "../util/top.h" | | | |
| #include "boost/version.hpp" | | #include "boost/version.hpp" | |
| #include "concurrency.h" | | #include "concurrency.h" | |
| #include "pdfile.h" | | #include "pdfile.h" | |
| #include "client.h" | | #include "client.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| // void jniCallback(Message& m, Message& out); | | // void jniCallback(Message& m, Message& out); | |
| | | | |
| /* Note the limit here is rather arbitrary and is simply a standard. ge
nerally the code works | | /* Note the limit here is rather arbitrary and is simply a standard. ge
nerally the code works | |
| | | | |
| skipping to change at line 53 | | skipping to change at line 52 | |
| | | | |
| /** | | /** | |
| * class to hold path + dbname -> Database | | * class to hold path + dbname -> Database | |
| * might be able to optimizer further | | * might be able to optimizer further | |
| */ | | */ | |
| class DatabaseHolder { | | class DatabaseHolder { | |
| public: | | public: | |
| DatabaseHolder() : _size(0){ | | DatabaseHolder() : _size(0){ | |
| } | | } | |
| | | | |
|
| | | bool isLoaded( const string& ns , const string& path ){ | |
| | | dbMutex.assertAtLeastReadLocked(); | |
| | | map<string,Database*>& m = _paths[path]; | |
| | | | |
| | | string db = _todb( ns ); | |
| | | | |
| | | map<string,Database*>::iterator it = m.find(db); | |
| | | return it != m.end(); | |
| | | } | |
| | | | |
| Database * get( const string& ns , const string& path ){ | | Database * get( const string& ns , const string& path ){ | |
| dbMutex.assertAtLeastReadLocked(); | | dbMutex.assertAtLeastReadLocked(); | |
| map<string,Database*>& m = _paths[path]; | | map<string,Database*>& m = _paths[path]; | |
| | | | |
| string db = _todb( ns ); | | string db = _todb( ns ); | |
| | | | |
| map<string,Database*>::iterator it = m.find(db); | | map<string,Database*>::iterator it = m.find(db); | |
| if ( it != m.end() ) | | if ( it != m.end() ) | |
| return it->second; | | return it->second; | |
| return 0; | | return 0; | |
| | | | |
| skipping to change at line 74 | | skipping to change at line 83 | |
| | | | |
| void put( const string& ns , const string& path , Database * db ){ | | void put( const string& ns , const string& path , Database * db ){ | |
| dbMutex.assertWriteLocked(); | | dbMutex.assertWriteLocked(); | |
| map<string,Database*>& m = _paths[path]; | | map<string,Database*>& m = _paths[path]; | |
| Database*& d = m[_todb(ns)]; | | Database*& d = m[_todb(ns)]; | |
| if ( ! d ) | | if ( ! d ) | |
| _size++; | | _size++; | |
| d = db; | | d = db; | |
| } | | } | |
| | | | |
|
| | | Database* getOrCreate( const string& ns , const string& path , bool | |
| | | & justCreated ){ | |
| | | dbMutex.assertWriteLocked(); | |
| | | map<string,Database*>& m = _paths[path]; | |
| | | | |
| | | string dbname = _todb( ns ); | |
| | | | |
| | | Database* & db = m[dbname]; | |
| | | if ( db ){ | |
| | | justCreated = false; | |
| | | return db; | |
| | | } | |
| | | | |
| | | log(1) << "Accessing: " << dbname << " for the first time" << e | |
| | | ndl; | |
| | | db = new Database( dbname.c_str() , justCreated , path ); | |
| | | _size++; | |
| | | return db; | |
| | | } | |
| | | | |
| void erase( const string& ns , const string& path ){ | | void erase( const string& ns , const string& path ){ | |
| dbMutex.assertWriteLocked(); | | dbMutex.assertWriteLocked(); | |
| map<string,Database*>& m = _paths[path]; | | map<string,Database*>& m = _paths[path]; | |
|
| _size -= m.erase( _todb( ns ) ); | | _size -= (int)m.erase( _todb( ns ) ); | |
| } | | } | |
| | | | |
|
| bool closeAll( const string& path , BSONObjBuilder& result ); | | /* force - force close even if something underway - use at shutdown | |
| | | */ | |
| | | bool closeAll( const string& path , BSONObjBuilder& result, bool fo | |
| | | rce ); | |
| | | | |
| int size(){ | | int size(){ | |
| return _size; | | return _size; | |
| } | | } | |
| | | | |
| /** | | /** | |
| * gets all unique db names, ignoring paths | | * gets all unique db names, ignoring paths | |
| */ | | */ | |
| void getAllShortNames( set<string>& all ) const{ | | void getAllShortNames( set<string>& all ) const{ | |
| dbMutex.assertAtLeastReadLocked(); | | dbMutex.assertAtLeastReadLocked(); | |
| | | | |
| skipping to change at line 115 | | skipping to change at line 143 | |
| return ns.substr( 0 , i ); | | return ns.substr( 0 , i ); | |
| } | | } | |
| | | | |
| map<string, map<string,Database*> > _paths; | | map<string, map<string,Database*> > _paths; | |
| int _size; | | int _size; | |
| | | | |
| }; | | }; | |
| | | | |
| extern DatabaseHolder dbHolder; | | extern DatabaseHolder dbHolder; | |
| | | | |
|
| /* returns true if the database ("database") did not exist, and it was | | | |
| created on this call | | | |
| path - datafiles directory, if not the default, so we can differenti | | | |
| ate between db's of the same | | | |
| name in different places (for example temp ones on repair). | | | |
| */ | | | |
| inline bool setClient(const char *ns, const string& path , mongolock *l | | | |
| ock ) { | | | |
| if( logLevel > 5 ) | | | |
| log() << "setClient: " << ns << endl; | | | |
| | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| | | | |
| Client& c = cc(); | | | |
| c.top.clientStart( ns ); | | | |
| | | | |
| Database * db = dbHolder.get( ns , path ); | | | |
| if ( db ){ | | | |
| c.setns(ns, db ); | | | |
| return false; | | | |
| } | | | |
| | | | |
| if( lock ) | | | |
| lock->releaseAndWriteLock(); | | | |
| | | | |
| assertInWriteLock(); | | | |
| | | | |
| char cl[256]; | | | |
| nsToDatabase(ns, cl); | | | |
| bool justCreated; | | | |
| Database *newdb = new Database(cl, justCreated, path); | | | |
| dbHolder.put(ns,path,newdb); | | | |
| c.setns(ns, newdb); | | | |
| | | | |
| newdb->finishInit(); | | | |
| | | | |
| return justCreated; | | | |
| } | | | |
| | | | |
| // shared functionality for removing references to a database from this
program instance | | // shared functionality for removing references to a database from this
program instance | |
| // does not delete the files on disk | | // does not delete the files on disk | |
| void closeDatabase( const char *cl, const string& path = dbpath ); | | void closeDatabase( const char *cl, const string& path = dbpath ); | |
| | | | |
| struct dbtemprelease { | | struct dbtemprelease { | |
|
| string clientname; | | Client::Context * _context; | |
| string clientpath; | | int _locktype; | |
| int locktype; | | | |
| dbtemprelease() { | | dbtemprelease() { | |
|
| Client& client = cc(); | | _context = cc().getContext(); | |
| Database *database = client.database(); | | _locktype = dbMutex.getState(); | |
| if ( database ) { | | assert( _locktype ); | |
| clientname = database->name; | | | |
| clientpath = database->path; | | if ( _locktype > 0 ) { | |
| } | | massert( 10298 , "can't temprelease nested w | |
| client.top.clientStop(); | | rite lock", _locktype == 1); | |
| locktype = dbMutex.getState(); | | if ( _context ) _context->unlocked(); | |
| assert( locktype ); | | | |
| if ( locktype > 0 ) { | | | |
| massert( 10298 , "can't temprelease nested w | | | |
| rite lock", locktype == 1); | | | |
| dbMutex.unlock(); | | dbMutex.unlock(); | |
| } | | } | |
| else { | | else { | |
|
| massert( 10299 , "can't temprelease nested r | | massert( 10299 , "can't temprelease nested r | |
| ead lock", locktype == -1); | | ead lock", _locktype == -1); | |
| | | if ( _context ) _context->unlocked(); | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| } | | } | |
|
| | | | |
| } | | } | |
| ~dbtemprelease() { | | ~dbtemprelease() { | |
|
| if ( locktype > 0 ) | | if ( _locktype > 0 ) | |
| dbMutex.lock(); | | dbMutex.lock(); | |
| else | | else | |
| dbMutex.lock_shared(); | | dbMutex.lock_shared(); | |
|
| if ( clientname.empty() ) | | | |
| cc().setns("", 0); | | if ( _context ) _context->relocked(); | |
| else | | | |
| setClient(clientname.c_str(), clientpath.c_str()); | | | |
| } | | } | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| only does a temp release if we're not nested and have a lock | | only does a temp release if we're not nested and have a lock | |
| */ | | */ | |
| struct dbtempreleasecond { | | struct dbtempreleasecond { | |
| dbtemprelease * real; | | dbtemprelease * real; | |
| int locktype; | | int locktype; | |
| | | | |
| | | | |
End of changes. 12 change blocks. |
| 64 lines changed or deleted | | 53 lines changed or added | |
|
| file_allocator.h | | file_allocator.h | |
| | | | |
| skipping to change at line 160 | | skipping to change at line 160 | |
| string name; | | string name; | |
| long size; | | long size; | |
| { | | { | |
| boostlock lk( a_.pendingMutex_ ); | | boostlock lk( a_.pendingMutex_ ); | |
| if ( a_.pending_.size() == 0 ) | | if ( a_.pending_.size() == 0 ) | |
| break; | | break; | |
| name = a_.pending_.front(); | | name = a_.pending_.front(); | |
| size = a_.pendingSize_[ name ]; | | size = a_.pendingSize_[ name ]; | |
| } | | } | |
| try { | | try { | |
|
| | | log() << "allocating new datafile " << name <<
", filling with zeroes..." << endl; | |
| long fd = open(name.c_str(), O_CREAT | O_RDWR |
O_NOATIME, S_IRUSR | S_IWUSR); | | long fd = open(name.c_str(), O_CREAT | O_RDWR |
O_NOATIME, S_IRUSR | S_IWUSR); | |
| if ( fd <= 0 ) { | | if ( fd <= 0 ) { | |
| stringstream ss; | | stringstream ss; | |
| ss << "couldn't open " << name << ' ' << OU
TPUT_ERRNO; | | ss << "couldn't open " << name << ' ' << OU
TPUT_ERRNO; | |
| massert( 10439 , ss.str(), fd <= 0 ); | | massert( 10439 , ss.str(), fd <= 0 ); | |
| } | | } | |
| | | | |
| #if defined(POSIX_FADV_DONTNEED) | | #if defined(POSIX_FADV_DONTNEED) | |
| if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN
EED) ) { | | if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN
EED) ) { | |
| log() << "warning: posix_fadvise fails " <<
name << ' ' << OUTPUT_ERRNO << endl; | | log() << "warning: posix_fadvise fails " <<
name << ' ' << OUTPUT_ERRNO << endl; | |
| | | | |
| skipping to change at line 183 | | skipping to change at line 184 | |
| /* make sure the file is the full desired lengt
h */ | | /* make sure the file is the full desired lengt
h */ | |
| off_t filelen = lseek(fd, 0, SEEK_END); | | off_t filelen = lseek(fd, 0, SEEK_END); | |
| if ( filelen < size ) { | | if ( filelen < size ) { | |
| massert( 10440 , "failure creating new dat
afile", filelen == 0 ); | | massert( 10440 , "failure creating new dat
afile", filelen == 0 ); | |
| // Check for end of disk. | | // Check for end of disk. | |
| massert( 10441 , "Unable to allocate file
of desired size", | | massert( 10441 , "Unable to allocate file
of desired size", | |
| size - 1 == lseek(fd, size - 1, SEE
K_SET) ); | | size - 1 == lseek(fd, size - 1, SEE
K_SET) ); | |
| massert( 10442 , "Unable to allocate file
of desired size", | | massert( 10442 , "Unable to allocate file
of desired size", | |
| 1 == write(fd, "", 1) ); | | 1 == write(fd, "", 1) ); | |
| lseek(fd, 0, SEEK_SET); | | lseek(fd, 0, SEEK_SET); | |
|
| log() << "allocating new datafile " << name
<< ", filling with zeroes..." << endl; | | | |
| Timer t; | | Timer t; | |
| long z = 256 * 1024; | | long z = 256 * 1024; | |
| char buf[z]; | | char buf[z]; | |
| memset(buf, 0, z); | | memset(buf, 0, z); | |
| long left = size; | | long left = size; | |
|
| while ( 1 ) { | | while ( left > 0 ) { | |
| if ( left <= z ) { | | long towrite = left; | |
| massert( 10443 , "write failed", l | | if ( towrite > z ) | |
| eft == write(fd, buf, left) ); | | towrite = z; | |
| break; | | | |
| } | | int written = write( fd , buf , towrite | |
| massert( 10444 , "write failed", z == | | ); | |
| write(fd, buf, z) ); | | massert( 10443 , errnostring("write fai | |
| left -= z; | | led" ), written > 0 ); | |
| | | left -= written; | |
| } | | } | |
| log() << "done allocating datafile " << nam
e << ", size: " << size/1024/1024 << "MB, took " << ((double)t.millis())/10
00.0 << " secs" << endl; | | log() << "done allocating datafile " << nam
e << ", size: " << size/1024/1024 << "MB, took " << ((double)t.millis())/10
00.0 << " secs" << endl; | |
| } | | } | |
| close( fd ); | | close( fd ); | |
| | | | |
| } catch ( ... ) { | | } catch ( ... ) { | |
| problem() << "Failed to allocate new file: " <<
name | | problem() << "Failed to allocate new file: " <<
name | |
| << ", size: " << size << ", aborting.
" << endl; | | << ", size: " << size << ", aborting.
" << endl; | |
| try { | | try { | |
| BOOST_CHECK_EXCEPTION( boost::filesystem::r
emove( name ) ); | | BOOST_CHECK_EXCEPTION( boost::filesystem::r
emove( name ) ); | |
| | | | |
End of changes. 3 change blocks. |
| 10 lines changed or deleted | | 11 lines changed or added | |
|
| index.h | | index.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | /* precomputed details about an index, used for inserting keys on updat | |
| | | es | |
| | | stored/cached in NamespaceDetailsTransient, or can be used standalon | |
| | | e | |
| | | */ | |
| class IndexSpec { | | class IndexSpec { | |
| public: | | public: | |
|
| BSONObj keys; | | BSONObj keyPattern; // e.g., { name : 1 } | |
| BSONObj meta; | | BSONObj info; // this is the same as IndexDetails::info.obj() | |
| | | | |
| IndexSpec(){ | | IndexSpec(){ | |
| } | | } | |
| | | | |
| IndexSpec( const BSONObj& k , const BSONObj& m = BSONObj() ) | | IndexSpec( const BSONObj& k , const BSONObj& m = BSONObj() ) | |
|
| : keys(k) , meta(m){ | | : keyPattern(k) , info(m){ | |
| _init(); | | _init(); | |
| } | | } | |
| | | | |
| /** | | /** | |
|
| this is a DickLock of an IndexDetails info | | this is a DiscLoc of an IndexDetails info | |
| should have a key field | | should have a key field | |
| */ | | */ | |
| IndexSpec( const DiskLoc& loc ){ | | IndexSpec( const DiskLoc& loc ){ | |
| reset( loc ); | | reset( loc ); | |
| } | | } | |
| | | | |
| void reset( const DiskLoc& loc ){ | | void reset( const DiskLoc& loc ){ | |
|
| meta = loc.obj(); | | info = loc.obj(); | |
| keys = meta["key"].embeddedObjectUserCheck(); | | keyPattern = info["key"].embeddedObjectUserCheck(); | |
| if ( keys.objsize() == 0 ) { | | if ( keyPattern.objsize() == 0 ) { | |
| out() << meta.toString() << endl; | | out() << info.toString() << endl; | |
| assert(false); | | assert(false); | |
|
| | | | |
| } | | } | |
| _init(); | | _init(); | |
| } | | } | |
| | | | |
| void getKeys( const BSONObj &obj, BSONObjSetDefaultOrder &keys ) co
nst; | | void getKeys( const BSONObj &obj, BSONObjSetDefaultOrder &keys ) co
nst; | |
| | | | |
| private: | | private: | |
|
| | | | |
| void _getKeys( vector<const char*> fieldNames , vector<BSONElement>
fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const; | | void _getKeys( vector<const char*> fieldNames , vector<BSONElement>
fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const; | |
| | | | |
| vector<const char*> _fieldNames; | | vector<const char*> _fieldNames; | |
| vector<BSONElement> _fixed; | | vector<BSONElement> _fixed; | |
| BSONObj _nullKey; | | BSONObj _nullKey; | |
| | | | |
| BSONObj _nullObj; | | BSONObj _nullObj; | |
| BSONElement _nullElt; | | BSONElement _nullElt; | |
| | | | |
| void _init(); | | void _init(); | |
| | | | |
| skipping to change at line 77 | | skipping to change at line 78 | |
| BSONObj _nullObj; | | BSONObj _nullObj; | |
| BSONElement _nullElt; | | BSONElement _nullElt; | |
| | | | |
| void _init(); | | void _init(); | |
| }; | | }; | |
| | | | |
| /* Details about a particular index. There is one of these effective
ly for each object in | | /* Details about a particular index. There is one of these effective
ly for each object in | |
| system.namespaces (although this also includes the head pointer,
which is not in that | | system.namespaces (although this also includes the head pointer,
which is not in that | |
| collection). | | collection). | |
| | | | |
|
| ** MemoryMapped Record ** | | ** MemoryMapped Record ** (i.e., this is on disk data) | |
| */ | | */ | |
| class IndexDetails { | | class IndexDetails { | |
| public: | | public: | |
| DiskLoc head; /* btree head disk location */ | | DiskLoc head; /* btree head disk location */ | |
| | | | |
| /* Location of index info object. Format: | | /* Location of index info object. Format: | |
| | | | |
| { name:"nameofindex", ns:"parentnsname", key: {keypattobject} | | { name:"nameofindex", ns:"parentnsname", key: {keypattobject} | |
| [, unique: <bool>, background: <bool>] | | [, unique: <bool>, background: <bool>] | |
| } | | } | |
| | | | |
| skipping to change at line 120 | | skipping to change at line 121 | |
| | | | |
| /* get the key pattern for this object. | | /* get the key pattern for this object. | |
| e.g., { lastname:1, firstname:1 } | | e.g., { lastname:1, firstname:1 } | |
| */ | | */ | |
| BSONObj keyPattern() const { | | BSONObj keyPattern() const { | |
| return info.obj().getObjectField("key"); | | return info.obj().getObjectField("key"); | |
| } | | } | |
| | | | |
| /* true if the specified key is in the index */ | | /* true if the specified key is in the index */ | |
| bool hasKey(const BSONObj& key); | | bool hasKey(const BSONObj& key); | |
|
| | | bool wouldCreateDup(const BSONObj& key, DiskLoc self); | |
| | | | |
| // returns name of this index's storage area | | // returns name of this index's storage area | |
| // database.table.$index | | // database.table.$index | |
| string indexNamespace() const { | | string indexNamespace() const { | |
| BSONObj io = info.obj(); | | BSONObj io = info.obj(); | |
| string s; | | string s; | |
| s.reserve(Namespace::MaxNsLen); | | s.reserve(Namespace::MaxNsLen); | |
| s = io.getStringField("ns"); | | s = io.getStringField("ns"); | |
| assert( !s.empty() ); | | assert( !s.empty() ); | |
| s += ".$"; | | s += ".$"; | |
| | | | |
| skipping to change at line 187 | | skipping to change at line 189 | |
| return info.obj().toString(); | | return info.obj().toString(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| struct IndexChanges/*on an update*/ { | | struct IndexChanges/*on an update*/ { | |
| BSONObjSetDefaultOrder oldkeys; | | BSONObjSetDefaultOrder oldkeys; | |
| BSONObjSetDefaultOrder newkeys; | | BSONObjSetDefaultOrder newkeys; | |
| vector<BSONObj*> removed; // these keys were removed as part of the
change | | vector<BSONObj*> removed; // these keys were removed as part of the
change | |
| vector<BSONObj*> added; // these keys were added as part of the c
hange | | vector<BSONObj*> added; // these keys were added as part of the c
hange | |
| | | | |
|
| void dupCheck(IndexDetails& idx) { | | /** @curObjLoc - the object we want to add's location. if it is al | |
| | | ready in the | |
| | | index, that is allowed here (for bg indexing case) | |
| | | . | |
| | | */ | |
| | | void dupCheck(IndexDetails& idx, DiskLoc curObjLoc) { | |
| if( added.empty() || !idx.unique() ) | | if( added.empty() || !idx.unique() ) | |
| return; | | return; | |
|
| for( vector<BSONObj*>::iterator i = added.begin(); i != added.e | | for( vector<BSONObj*>::iterator i = added.begin(); i != added.e | |
| nd(); i++ ) | | nd(); i++ ) { | |
| uassert( 11001 , "E11001 duplicate key on update", !idx.has | | bool dup = idx.wouldCreateDup(**i, curObjLoc); | |
| Key(**i)); | | uassert( 11001 , "E11001 duplicate key on update", !dup); | |
| | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
| class NamespaceDetails; | | class NamespaceDetails; | |
| void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON
Obj newObj, BSONObj oldObj); | | void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON
Obj newObj, BSONObj oldObj); | |
|
| void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d); | | void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d, DiskLoc cur
ObjLoc); | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 12 change blocks. |
| 17 lines changed or deleted | | 27 lines changed or added | |
|
| instance.h | | instance.h | |
| | | | |
| skipping to change at line 41 | | skipping to change at line 41 | |
| | | | |
| #define OPWRITE if( _diaglog.level & 1 ) _diaglog.write((char *) m.data, m.
data->len); | | #define OPWRITE if( _diaglog.level & 1 ) _diaglog.write((char *) m.data, m.
data->len); | |
| #define OPREAD if( _diaglog.level & 2 ) _diaglog.readop((char *) m.data, m.
data->len); | | #define OPREAD if( _diaglog.level & 2 ) _diaglog.readop((char *) m.data, m.
data->len); | |
| | | | |
| struct DiagLog { | | struct DiagLog { | |
| ofstream *f; | | ofstream *f; | |
| /* 0 = off; 1 = writes, 2 = reads, 3 = both | | /* 0 = off; 1 = writes, 2 = reads, 3 = both | |
| 7 = log a few reads, and all writes. | | 7 = log a few reads, and all writes. | |
| */ | | */ | |
| int level; | | int level; | |
|
| | | boost::mutex mutex; | |
| | | | |
| DiagLog() : f(0) , level(0) { } | | DiagLog() : f(0) , level(0) { } | |
| void init() { | | void init() { | |
| if ( ! f && level ){ | | if ( ! f && level ){ | |
| log() << "diagLogging = " << level << endl; | | log() << "diagLogging = " << level << endl; | |
| stringstream ss; | | stringstream ss; | |
|
| ss << "diaglog." << hex << time(0); | | ss << dbpath << "/diaglog." << hex << time(0); | |
| string name = ss.str(); | | string name = ss.str(); | |
| f = new ofstream(name.c_str(), ios::out | ios::binary); | | f = new ofstream(name.c_str(), ios::out | ios::binary); | |
| if ( ! f->good() ) { | | if ( ! f->good() ) { | |
| problem() << "couldn't open log stream" << endl; | | problem() << "couldn't open log stream" << endl; | |
| throw 1717; | | throw 1717; | |
| } | | } | |
| } | | } | |
| } | | } | |
| /** | | /** | |
| * @return old | | * @return old | |
| */ | | */ | |
| int setLevel( int newLevel ){ | | int setLevel( int newLevel ){ | |
| int old = level; | | int old = level; | |
| level = newLevel; | | level = newLevel; | |
| init(); | | init(); | |
| return old; | | return old; | |
| } | | } | |
| void flush() { | | void flush() { | |
|
| if ( level ) f->flush(); | | if ( level ){ | |
| | | boostlock lk(mutex); | |
| | | f->flush(); | |
| | | } | |
| } | | } | |
| void write(char *data,int len) { | | void write(char *data,int len) { | |
|
| if ( level & 1 ) f->write(data,len); | | if ( level & 1 ){ | |
| | | boostlock lk(mutex); | |
| | | f->write(data,len); | |
| | | } | |
| } | | } | |
| void readop(char *data, int len) { | | void readop(char *data, int len) { | |
| if ( level & 2 ) { | | if ( level & 2 ) { | |
| bool log = (level & 4) == 0; | | bool log = (level & 4) == 0; | |
| OCCASIONALLY log = true; | | OCCASIONALLY log = true; | |
|
| if ( log ) | | if ( log ){ | |
| | | boostlock lk(mutex); | |
| f->write(data,len); | | f->write(data,len); | |
|
| | | } | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
| extern DiagLog _diaglog; | | extern DiagLog _diaglog; | |
| | | | |
| /* we defer response until we unlock. don't want a blocked socket to | | /* we defer response until we unlock. don't want a blocked socket to | |
| keep things locked. | | keep things locked. | |
| */ | | */ | |
| struct DbResponse { | | struct DbResponse { | |
| | | | |
| skipping to change at line 127 | | skipping to change at line 137 | |
| } | | } | |
| virtual string getServerAddress() const{ | | virtual string getServerAddress() const{ | |
| return "localhost"; // TODO: should this have the port? | | return "localhost"; // TODO: should this have the port? | |
| } | | } | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k=true ); | | virtual bool call( Message &toSend, Message &response, bool assertO
k=true ); | |
| virtual void say( Message &toSend ); | | virtual void say( Message &toSend ); | |
| virtual void sayPiggyBack( Message &toSend ) { | | virtual void sayPiggyBack( Message &toSend ) { | |
| // don't need to piggy back when connected locally | | // don't need to piggy back when connected locally | |
| return say( toSend ); | | return say( toSend ); | |
| } | | } | |
|
| class AlwaysAuthorized : public AuthenticationInfo { | | | |
| virtual bool isAuthorized( const char *dbname ) { | | | |
| return true; | | | |
| } | | | |
| }; | | | |
| | | | |
| /* TODO: this looks bad that auth is set to always. is that really | | | |
| always safe? */ | | | |
| class SavedContext { | | | |
| public: | | | |
| SavedContext() { | | | |
| _save = dbMutex.atLeastReadLocked(); | | | |
| | | | |
| Client *c = currentClient.get(); | | | |
| oldAuth = c->ai; | | | |
| // careful, don't want to free this: | | | |
| c->ai = &always; | | | |
| | | | |
| /* it only makes sense to manipulate a pointer - c->databas | | | |
| e() - if locked. | | | |
| thus the _saved flag. | | | |
| */ | | | |
| if( _save ) { | | | |
| if ( c->database() ) { | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| _oldName = c->database()->name; | | | |
| } | | | |
| } | | | |
| } | | | |
| ~SavedContext() { | | | |
| Client *c = currentClient.get(); | | | |
| c->ai = oldAuth; | | | |
| if( _save ) { | | | |
| if ( !_oldName.empty() ) { | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| setClient( _oldName.c_str() ); | | | |
| } | | | |
| } | | | |
| else { | | | |
| // defensive | | | |
| cc().clearns(); | | | |
| } | | | |
| } | | | |
| private: | | | |
| bool _save; | | | |
| static AlwaysAuthorized always; | | | |
| AuthenticationInfo *oldAuth; | | | |
| string _oldName; | | | |
| }; | | | |
| }; | | }; | |
| | | | |
| extern int lockFile; | | extern int lockFile; | |
| void acquirePathLock(); | | void acquirePathLock(); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 7 change blocks. |
| 53 lines changed or deleted | | 14 lines changed or added | |
|
| jsobj.h | | jsobj.h | |
| | | | |
| skipping to change at line 512 | | skipping to change at line 512 | |
| const char *binData(int& len) const { | | const char *binData(int& len) const { | |
| // BinData: <int len> <byte subtype> <byte[len] data> | | // BinData: <int len> <byte subtype> <byte[len] data> | |
| assert( type() == BinData ); | | assert( type() == BinData ); | |
| len = valuestrsize(); | | len = valuestrsize(); | |
| return value() + 5; | | return value() + 5; | |
| } | | } | |
| | | | |
| BinDataType binDataType() const { | | BinDataType binDataType() const { | |
| // BinData: <int len> <byte subtype> <byte[len] data> | | // BinData: <int len> <byte subtype> <byte[len] data> | |
| assert( type() == BinData ); | | assert( type() == BinData ); | |
|
| char c = (value() + 4)[0]; | | unsigned char c = (value() + 4)[0]; | |
| return (BinDataType)c; | | return (BinDataType)c; | |
| } | | } | |
| | | | |
| /** Retrieve the regex string for a Regex element */ | | /** Retrieve the regex string for a Regex element */ | |
| const char *regex() const { | | const char *regex() const { | |
| assert(type() == RegEx); | | assert(type() == RegEx); | |
| return value(); | | return value(); | |
| } | | } | |
| | | | |
| /** Retrieve the regex flags (options) for a Regex element */ | | /** Retrieve the regex flags (options) for a Regex element */ | |
| | | | |
| skipping to change at line 576 | | skipping to change at line 576 | |
| int getGtLtOp( int def = 0 ) const; | | int getGtLtOp( int def = 0 ) const; | |
| | | | |
| /** Constructs an empty element */ | | /** Constructs an empty element */ | |
| BSONElement(); | | BSONElement(); | |
| | | | |
| /** Check that data is internally consistent. */ | | /** Check that data is internally consistent. */ | |
| void validate() const; | | void validate() const; | |
| | | | |
| /** True if this element may contain subobjects. */ | | /** True if this element may contain subobjects. */ | |
| bool mayEncapsulate() const { | | bool mayEncapsulate() const { | |
|
| return type() == Object || | | switch ( type() ){ | |
| type() == Array || | | case Object: | |
| type() == CodeWScope; | | case Array: | |
| | | case CodeWScope: | |
| | | return true; | |
| | | default: | |
| | | return false; | |
| | | } | |
| | | } | |
| | | | |
| | | /** True if this element can be a BSONObj */ | |
| | | bool isABSONObj() const { | |
| | | switch( type() ){ | |
| | | case Object: | |
| | | case Array: | |
| | | return true; | |
| | | default: | |
| | | return false; | |
| | | } | |
| } | | } | |
| | | | |
| Date_t timestampTime() const{ | | Date_t timestampTime() const{ | |
| unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | | unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | |
| return t * 1000; | | return t * 1000; | |
| } | | } | |
| unsigned int timestampInc() const{ | | unsigned int timestampInc() const{ | |
| return ((unsigned int*)(value() ))[0]; | | return ((unsigned int*)(value() ))[0]; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 627 | | skipping to change at line 643 | |
| fieldNameSize_ = size + 1; | | fieldNameSize_ = size + 1; | |
| } | | } | |
| } | | } | |
| totalSize = -1; | | totalSize = -1; | |
| } | | } | |
| private: | | private: | |
| const char *data; | | const char *data; | |
| mutable int fieldNameSize_; // cached value | | mutable int fieldNameSize_; // cached value | |
| int fieldNameSize() const { | | int fieldNameSize() const { | |
| if ( fieldNameSize_ == -1 ) | | if ( fieldNameSize_ == -1 ) | |
|
| fieldNameSize_ = strlen( fieldName() ) + 1; | | fieldNameSize_ = (int)strlen( fieldName() ) + 1; | |
| return fieldNameSize_; | | return fieldNameSize_; | |
| } | | } | |
| mutable int totalSize; /* caches the computed size */ | | mutable int totalSize; /* caches the computed size */ | |
| }; | | }; | |
| | | | |
| int getGtLtOp(const BSONElement& e); | | int getGtLtOp(const BSONElement& e); | |
| | | | |
| struct BSONElementCmpWithoutField { | | struct BSONElementCmpWithoutField { | |
| bool operator()( const BSONElement &l, const BSONElement &r ) const
{ | | bool operator()( const BSONElement &l, const BSONElement &r ) const
{ | |
| return l.woCompare( r, false ); | | return l.woCompare( r, false ); | |
| | | | |
| skipping to change at line 702 | | skipping to change at line 718 | |
| }; | | }; | |
| const char *_objdata; | | const char *_objdata; | |
| boost::shared_ptr< Holder > _holder; | | boost::shared_ptr< Holder > _holder; | |
| void init(const char *data, bool ifree) { | | void init(const char *data, bool ifree) { | |
| if ( ifree ) | | if ( ifree ) | |
| _holder.reset( new Holder( data ) ); | | _holder.reset( new Holder( data ) ); | |
| _objdata = data; | | _objdata = data; | |
| if ( ! isValid() ){ | | if ( ! isValid() ){ | |
| stringstream ss; | | stringstream ss; | |
| ss << "Invalid BSONObj spec size: " << objsize(); | | ss << "Invalid BSONObj spec size: " << objsize(); | |
|
| | | try { | |
| | | BSONElement e = firstElement(); | |
| | | ss << " first element:" << e.toString() << " "; | |
| | | } | |
| | | catch ( ... ){} | |
| string s = ss.str(); | | string s = ss.str(); | |
| massert( 10334 , s , 0 ); | | massert( 10334 , s , 0 ); | |
| } | | } | |
| } | | } | |
| #pragma pack(1) | | #pragma pack(1) | |
| static struct EmptyObject { | | static struct EmptyObject { | |
| EmptyObject() { | | EmptyObject() { | |
| len = 5; | | len = 5; | |
| jstype = EOO; | | jstype = EOO; | |
| } | | } | |
| | | | |
| skipping to change at line 770 | | skipping to change at line 791 | |
| */ | | */ | |
| void getFieldsDotted(const char *name, BSONElementSet &ret, bool *d
eep = 0) const; | | void getFieldsDotted(const char *name, BSONElementSet &ret, bool *d
eep = 0) const; | |
| /** Like getFieldDotted(), but returns first array encountered whil
e traversing the | | /** Like getFieldDotted(), but returns first array encountered whil
e traversing the | |
| dotted fields of name. The name variable is updated to represe
nt field | | dotted fields of name. The name variable is updated to represe
nt field | |
| names with respect to the returned element. */ | | names with respect to the returned element. */ | |
| BSONElement getFieldDottedOrArray(const char *&name) const; | | BSONElement getFieldDottedOrArray(const char *&name) const; | |
| | | | |
| /** Get the field of the specified name. eoo() is true on the retur
ned | | /** Get the field of the specified name. eoo() is true on the retur
ned | |
| element if not found. | | element if not found. | |
| */ | | */ | |
|
| BSONElement getField(const string name) const { | | BSONElement getField(const char *name) const; | |
| return getField( name.c_str() ); | | | |
| }; | | | |
| | | | |
| /** Get the field of the specified name. eoo() is true on the retur
ned | | /** Get the field of the specified name. eoo() is true on the retur
ned | |
| element if not found. | | element if not found. | |
| */ | | */ | |
|
| BSONElement getField(const char *name) const; /* return has eoo() t | | BSONElement getField(const string name) const { | |
| rue if no match */ | | return getField( name.c_str() ); | |
| | | }; | |
| | | | |
| /** Get the field of the specified name. eoo() is true on the retur
ned | | /** Get the field of the specified name. eoo() is true on the retur
ned | |
| element if not found. | | element if not found. | |
| */ | | */ | |
| BSONElement operator[] (const char *field) const { | | BSONElement operator[] (const char *field) const { | |
| return getField(field); | | return getField(field); | |
| } | | } | |
| | | | |
| BSONElement operator[] (const string& field) const { | | BSONElement operator[] (const string& field) const { | |
| return getField(field); | | return getField(field); | |
| | | | |
| skipping to change at line 904 | | skipping to change at line 925 | |
| return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); | | return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| /** @return first field of the object */ | | /** @return first field of the object */ | |
| BSONElement firstElement() const { | | BSONElement firstElement() const { | |
| return BSONElement(objdata() + 4); | | return BSONElement(objdata() + 4); | |
| } | | } | |
| | | | |
|
| /** @return element with fieldname "name". returnvalue.eoo( | | /** use getField() instead. */ | |
| ) is true if not found */ | | //BSONElement getField(const char *name) const; | |
| BSONElement findElement(const char *name) const; | | //BSONElement getField(string name) const { | |
| | | | |
| /** @return element with fieldname "name". returnvalue.eoo( | | | |
| ) is true if not found */ | | | |
| BSONElement findElement(string name) const { | | | |
| return findElement(name.c_str()); | | | |
| } | | | |
| | | | |
| /** @return true if field exists in the object */ | | /** @return true if field exists in the object */ | |
| bool hasElement(const char *name) const; | | bool hasElement(const char *name) const; | |
| | | | |
| /** Get the _id field from the object. For good performance
drivers should | | /** Get the _id field from the object. For good performance
drivers should | |
| assure that _id is the first element of the object; however, co
rrect operation | | assure that _id is the first element of the object; however, co
rrect operation | |
| is assured regardless. | | is assured regardless. | |
| @return true if found | | @return true if found | |
| */ | | */ | |
| bool getObjectID(BSONElement& e) const; | | bool getObjectID(BSONElement& e) const; | |
| | | | |
| skipping to change at line 1188 | | skipping to change at line 1205 | |
| return b; | | return b; | |
| } | | } | |
| | | | |
| /** Append a boolean element */ | | /** Append a boolean element */ | |
| void appendBool(const char *fieldName, int val) { | | void appendBool(const char *fieldName, int val) { | |
| b.append((char) Bool); | | b.append((char) Bool); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append((char) (val?1:0)); | | b.append((char) (val?1:0)); | |
| } | | } | |
| | | | |
|
| | | /** Append a boolean element */ | |
| | | void append(const char *fieldName, bool val) { | |
| | | b.append((char) Bool); | |
| | | b.append(fieldName); | |
| | | b.append((char) (val?1:0)); | |
| | | } | |
| | | | |
| /** Append a 32 bit integer element */ | | /** Append a 32 bit integer element */ | |
| void append(const char *fieldName, int n) { | | void append(const char *fieldName, int n) { | |
| b.append((char) NumberInt); | | b.append((char) NumberInt); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(n); | | b.append(n); | |
| } | | } | |
| /** Append a 32 bit integer element */ | | /** Append a 32 bit integer element */ | |
| void append(const string &fieldName, int n) { | | void append(const string &fieldName, int n) { | |
| append( fieldName.c_str(), n ); | | append( fieldName.c_str(), n ); | |
| } | | } | |
| | | | |
| skipping to change at line 1214 | | skipping to change at line 1238 | |
| b.append((char) NumberLong); | | b.append((char) NumberLong); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(n); | | b.append(n); | |
| } | | } | |
| | | | |
| /** Append a NumberLong */ | | /** Append a NumberLong */ | |
| void append(const string& fieldName, long long n) { | | void append(const string& fieldName, long long n) { | |
| append( fieldName.c_str() , n ); | | append( fieldName.c_str() , n ); | |
| } | | } | |
| | | | |
|
| | | /** appends a number. if n < max(int)/2 then uses int, otherwise l | |
| | | ong long */ | |
| | | void appendIntOrLL( const string& fieldName , long long n ){ | |
| | | long long x = n; | |
| | | if ( x < 0 ) | |
| | | x = x * -1; | |
| | | if ( x < ( numeric_limits<int>::max() / 2 ) ) | |
| | | append( fieldName.c_str() , (int)n ); | |
| | | else | |
| | | append( fieldName.c_str() , n ); | |
| | | } | |
| | | | |
| /** Append a double element */ | | /** Append a double element */ | |
| BSONObjBuilder& append(const char *fieldName, double n) { | | BSONObjBuilder& append(const char *fieldName, double n) { | |
| b.append((char) NumberDouble); | | b.append((char) NumberDouble); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(n); | | b.append(n); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /** tries to append the data as a number | | /** tries to append the data as a number | |
| * @return true if the data was able to be converted to a number | | * @return true if the data was able to be converted to a number | |
| | | | |
| skipping to change at line 1450 | | skipping to change at line 1485 | |
| | | | |
| /** Fetch the object we have built. | | /** Fetch the object we have built. | |
| BSONObjBuilder still frees the object when the build
er goes out of | | BSONObjBuilder still frees the object when the build
er goes out of | |
| scope -- very important to keep in mind. Use obj()
if you | | scope -- very important to keep in mind. Use obj()
if you | |
| would like the BSONObj to last longer than the build
er. | | would like the BSONObj to last longer than the build
er. | |
| */ | | */ | |
| BSONObj done() { | | BSONObj done() { | |
| return BSONObj(_done()); | | return BSONObj(_done()); | |
| } | | } | |
| | | | |
|
| | | /** Peek at what is in the builder, but leave the builder ready for | |
| | | more appends. | |
| | | The returned object is only valid until the next modification o | |
| | | r destruction of the builder. | |
| | | Intended use case: append a field if not already there. | |
| | | */ | |
| | | BSONObj asTempObj() { | |
| | | BSONObj temp(_done()); | |
| | | b.setlen(b.len()-1); //next append should overwrite the EOO | |
| | | return temp; | |
| | | } | |
| | | | |
| /* assume ownership of the buffer - you must then free it (with fre
e()) */ | | /* assume ownership of the buffer - you must then free it (with fre
e()) */ | |
| char* decouple(int& l) { | | char* decouple(int& l) { | |
| char *x = _done(); | | char *x = _done(); | |
| assert( x ); | | assert( x ); | |
| l = b.len(); | | l = b.len(); | |
| b.decouple(); | | b.decouple(); | |
| return x; | | return x; | |
| } | | } | |
| void decouple() { | | void decouple() { | |
| b.decouple(); // post done() call version. be sure jsobj fr
ees... | | b.decouple(); // post done() call version. be sure jsobj fr
ees... | |
| | | | |
| skipping to change at line 1581 | | skipping to change at line 1626 | |
| /** @return true if more elements exist to be enumerated. */ | | /** @return true if more elements exist to be enumerated. */ | |
| bool moreWithEOO() { | | bool moreWithEOO() { | |
| return pos < theend; | | return pos < theend; | |
| } | | } | |
| bool more(){ | | bool more(){ | |
| return pos < theend && pos[0]; | | return pos < theend && pos[0]; | |
| } | | } | |
| /** @return the next element in the object. For the final element,
element.eoo() will be true. */ | | /** @return the next element in the object. For the final element,
element.eoo() will be true. */ | |
| BSONElement next( bool checkEnd = false ) { | | BSONElement next( bool checkEnd = false ) { | |
| assert( pos < theend ); | | assert( pos < theend ); | |
|
| BSONElement e( pos, checkEnd ? theend - pos : -1 ); | | BSONElement e( pos, checkEnd ? (int)(theend - pos) : -1 ); | |
| pos += e.size( checkEnd ? theend - pos : -1 ); | | pos += e.size( checkEnd ? (int)(theend - pos) : -1 ); | |
| return e; | | return e; | |
| } | | } | |
| private: | | private: | |
| const char *pos; | | const char *pos; | |
| const char *theend; | | const char *theend; | |
| }; | | }; | |
| | | | |
| /* iterator a BSONObj which is an array, in array order. | | /* iterator a BSONObj which is an array, in array order. | |
| class JSArrayIter { | | class JSArrayIter { | |
| public: | | public: | |
| | | | |
| skipping to change at line 1651 | | skipping to change at line 1696 | |
| #pragma pack() | | #pragma pack() | |
| extern JSObj1 js1; | | extern JSObj1 js1; | |
| | | | |
| #ifdef _DEBUG | | #ifdef _DEBUG | |
| #define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid
" + (msg) , (o).isValid() ) | | #define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid
" + (msg) , (o).isValid() ) | |
| #else | | #else | |
| #define CHECK_OBJECT( o , msg ) | | #define CHECK_OBJECT( o , msg ) | |
| #endif | | #endif | |
| | | | |
| inline BSONObj BSONElement::embeddedObjectUserCheck() { | | inline BSONObj BSONElement::embeddedObjectUserCheck() { | |
|
| uassert( 10065 , "invalid parameter: expected an object", type()==
Object || type()==Array ); | | uassert( 10065 , "invalid parameter: expected an object", isABSONO
bj() ); | |
| return BSONObj(value()); | | return BSONObj(value()); | |
| } | | } | |
| | | | |
| inline BSONObj BSONElement::embeddedObject() const { | | inline BSONObj BSONElement::embeddedObject() const { | |
|
| assert( type()==Object || type()==Array ); | | assert( isABSONObj() ); | |
| return BSONObj(value()); | | return BSONObj(value()); | |
| } | | } | |
| | | | |
| inline BSONObj BSONElement::codeWScopeObject() const { | | inline BSONObj BSONElement::codeWScopeObject() const { | |
| assert( type() == CodeWScope ); | | assert( type() == CodeWScope ); | |
| int strSizeWNull = *(int *)( value() + 4 ); | | int strSizeWNull = *(int *)( value() + 4 ); | |
| return BSONObj( value() + 4 + 4 + strSizeWNull ); | | return BSONObj( value() + 4 + 4 + strSizeWNull ); | |
| } | | } | |
| | | | |
| inline BSONObj BSONObj::copy() const { | | inline BSONObj BSONObj::copy() const { | |
| | | | |
| skipping to change at line 1697 | | skipping to change at line 1742 | |
| BSONObjIterator it(*this); | | BSONObjIterator it(*this); | |
| while ( it.moreWithEOO() ) { | | while ( it.moreWithEOO() ) { | |
| BSONElement e = it.next(); | | BSONElement e = it.next(); | |
| if ( strcmp(name, e.fieldName()) == 0 ) | | if ( strcmp(name, e.fieldName()) == 0 ) | |
| return true; | | return true; | |
| } | | } | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| inline BSONElement BSONObj::findElement(const char *name) const { | | inline BSONElement BSONObj::getField(const char *name) const { | |
| if ( !isEmpty() ) { | | BSONObjIterator i(*this); | |
| BSONObjIterator it(*this); | | while ( i.more() ) { | |
| while ( it.moreWithEOO() ) { | | BSONElement e = i.next(); | |
| BSONElement e = it.next(); | | if ( strcmp(e.fieldName(), name) == 0 ) | |
| if ( strcmp(name, e.fieldName()) == 0 ) | | return e; | |
| return e; | | | |
| } | | | |
| } | | } | |
| return BSONElement(); | | return BSONElement(); | |
| } | | } | |
| | | | |
| /* add all the fields from the object specified to this object */ | | /* add all the fields from the object specified to this object */ | |
| inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { | | inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { | |
| BSONObjIterator it(x); | | BSONObjIterator it(x); | |
| while ( it.moreWithEOO() ) { | | while ( it.moreWithEOO() ) { | |
| BSONElement e = it.next(); | | BSONElement e = it.next(); | |
| if ( e.eoo() ) break; | | if ( e.eoo() ) break; | |
| append(e); | | append(e); | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| inline bool BSONObj::isValid(){ | | inline bool BSONObj::isValid(){ | |
| return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | | return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | |
| } | | } | |
| | | | |
| inline bool BSONObj::getObjectID(BSONElement& e) const { | | inline bool BSONObj::getObjectID(BSONElement& e) const { | |
|
| BSONElement f = findElement("_id"); | | BSONElement f = getField("_id"); | |
| if( !f.eoo() ) { | | if( !f.eoo() ) { | |
| e = f; | | e = f; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBui
lder * builder ) { | | inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBui
lder * builder ) { | |
| _fieldName = 0; | | _fieldName = 0; | |
| _builder = builder; | | _builder = builder; | |
| | | | |
End of changes. 15 change blocks. |
| 32 lines changed or deleted | | 75 lines changed or added | |
|
| jsobjmanipulator.h | | jsobjmanipulator.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * You should have received a copy of the GNU Affero General Public Lice
nse | | * You should have received a copy of the GNU Affero General Public Lice
nse | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| /** Manipulate the binary representation of a BSONElement in-place. | | /** Manipulate the binary representation of a BSONElement in-place. | |
| Careful, this casts away const. | | Careful, this casts away const. | |
| */ | | */ | |
| class BSONElementManipulator { | | class BSONElementManipulator { | |
| public: | | public: | |
| BSONElementManipulator( const BSONElement &element ) : | | BSONElementManipulator( const BSONElement &element ) : | |
| element_( element ) { | | _element( element ) { | |
| assert( !element_.eoo() ); | | assert( !_element.eoo() ); | |
| } | | } | |
| /** Replace a Timestamp type with a Date type initialized to | | /** Replace a Timestamp type with a Date type initialized to | |
| OpTime::now().asDate() | | OpTime::now().asDate() | |
| */ | | */ | |
| void initTimestamp(); | | void initTimestamp(); | |
| | | | |
|
| /** Change the value, in place, of the number. */ | | /** Change the value, in place, of the number. */ | |
| void setNumber(double d) { | | void setNumber(double d) { | |
| if ( element_.type() == NumberDouble ) *reinterpret_cast< double * | | if ( _element.type() == NumberDouble ) *reinterpret_cast< doubl | |
| >( value() ) = d; | | e * >( value() ) = d; | |
| else if ( element_.type() == NumberInt ) *reinterpret_cast< int * > | | else if ( _element.type() == NumberInt ) *reinterpret_cast< int | |
| ( value() ) = (int) d; | | * >( value() ) = (int) d; | |
| } | | } | |
| void setLong(long long n) { | | void setLong(long long n) { | |
| if( element_.type() == NumberLong ) *reinterpret_cast< long long * | | if( _element.type() == NumberLong ) *reinterpret_cast< long lon | |
| >( value() ) = n; | | g * >( value() ) = n; | |
| } | | } | |
| | | void setInt(int n) { | |
| | | assert( _element.type() == NumberInt ); | |
| | | *reinterpret_cast< int * >( value() ) = n; | |
| | | } | |
| | | | |
|
| /** Replace the type and value of the element with the type and value o | | /** Replace the type and value of the element with the type and val | |
| f e, | | ue of e, | |
| preserving the original fieldName */ | | preserving the original fieldName */ | |
| void replaceTypeAndValue( const BSONElement &e ) { | | void replaceTypeAndValue( const BSONElement &e ) { | |
| *data() = e.type(); | | *data() = e.type(); | |
| memcpy( value(), e.value(), e.valuesize() ); | | memcpy( value(), e.value(), e.valuesize() ); | |
| } | | } | |
| | | | |
|
| static void lookForTimestamps( const BSONObj& obj ){ | | static void lookForTimestamps( const BSONObj& obj ){ | |
| // If have a Timestamp field as the first or second element, | | // If have a Timestamp field as the first or second element, | |
| // update it to a Date field set to OpTime::now().asDate(). The | | // update it to a Date field set to OpTime::now().asDate(). Th | |
| // replacement policy is a work in progress. | | e | |
| | | // replacement policy is a work in progress. | |
| | | | |
|
| BSONObjIterator i( obj ); | | BSONObjIterator i( obj ); | |
| for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { | | for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { | |
| BSONElement e = i.next(); | | BSONElement e = i.next(); | |
| if ( e.eoo() ) | | if ( e.eoo() ) | |
| break; | | break; | |
| if ( e.type() == Timestamp ){ | | if ( e.type() == Timestamp ){ | |
| BSONElementManipulator( e ).initTimestamp(); | | BSONElementManipulator( e ).initTimestamp(); | |
| break; | | break; | |
| | | } | |
| } | | } | |
| } | | } | |
|
| } | | private: | |
| private: | | char *data() { return nonConst( _element.rawdata() ); } | |
| char *data() { return nonConst( element_.rawdata() ); } | | char *value() { return nonConst( _element.value() ); } | |
| char *value() { return nonConst( element_.value() ); } | | static char *nonConst( const char *s ) { return const_cast< char * | |
| static char *nonConst( const char *s ) { return const_cast< char * >( s | | >( s ); } | |
| ); } | | | |
| const BSONElement element_; | | const BSONElement _element; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 6 change blocks. |
| 51 lines changed or deleted | | 57 lines changed or added | |
|
| message.h | | message.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../util/sock.h" | | #include "../util/sock.h" | |
|
| | | #include "../util/atomic_int.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class Message; | | class Message; | |
| class MessagingPort; | | class MessagingPort; | |
| class PiggyBackData; | | class PiggyBackData; | |
|
| typedef WrappingInt MSGID; | | typedef AtomicUInt MSGID; | |
| | | | |
| class Listener { | | class Listener { | |
| public: | | public: | |
| Listener(const string &_ip, int p) : ip(_ip), port(p) { } | | Listener(const string &_ip, int p) : ip(_ip), port(p) { } | |
| virtual ~Listener() {} | | virtual ~Listener() {} | |
| bool init(); // set up socket | | bool init(); // set up socket | |
| int socket() const { return sock; } | | int socket() const { return sock; } | |
| void listen(); // never returns (start a thread) | | void listen(); // never returns (start a thread) | |
| | | | |
| /* spawn a thread, etc., then return */ | | /* spawn a thread, etc., then return */ | |
| | | | |
| skipping to change at line 102 | | skipping to change at line 103 | |
| dbInsert = 2002, | | dbInsert = 2002, | |
| //dbGetByOID = 2003, | | //dbGetByOID = 2003, | |
| dbQuery = 2004, | | dbQuery = 2004, | |
| dbGetMore = 2005, | | dbGetMore = 2005, | |
| dbDelete = 2006, | | dbDelete = 2006, | |
| dbKillCursors = 2007 | | dbKillCursors = 2007 | |
| }; | | }; | |
| | | | |
| bool doesOpGetAResponse( int op ); | | bool doesOpGetAResponse( int op ); | |
| | | | |
|
| | | inline const char * opToString( int op ){ | |
| | | switch ( op ){ | |
| | | case 0: return "none"; | |
| | | case opReply: return "reply"; | |
| | | case dbMsg: return "msg"; | |
| | | case dbUpdate: return "update"; | |
| | | case dbInsert: return "insert"; | |
| | | case dbQuery: return "query"; | |
| | | case dbGetMore: return "getmore"; | |
| | | case dbDelete: return "remove"; | |
| | | case dbKillCursors: return "killcursors"; | |
| | | default: | |
| | | PRINT(op); | |
| | | assert(0); | |
| | | return ""; | |
| | | } | |
| | | } | |
| | | | |
| struct MsgData { | | struct MsgData { | |
| int len; /* len of the msg, including this field */ | | int len; /* len of the msg, including this field */ | |
| MSGID id; /* request/reply id's match... */ | | MSGID id; /* request/reply id's match... */ | |
| MSGID responseTo; /* id of the message we are responding to */ | | MSGID responseTo; /* id of the message we are responding to */ | |
| int _operation; | | int _operation; | |
| int operation() const { | | int operation() const { | |
| return _operation; | | return _operation; | |
| } | | } | |
| void setOperation(int o) { | | void setOperation(int o) { | |
| _operation = o; | | _operation = o; | |
| | | | |
| skipping to change at line 178 | | skipping to change at line 197 | |
| } | | } | |
| | | | |
| void setData(MsgData *d, bool _freeIt) { | | void setData(MsgData *d, bool _freeIt) { | |
| assert( data == 0 ); | | assert( data == 0 ); | |
| freeIt = _freeIt; | | freeIt = _freeIt; | |
| data = d; | | data = d; | |
| } | | } | |
| void setData(int operation, const char *msgtxt) { | | void setData(int operation, const char *msgtxt) { | |
| setData(operation, msgtxt, strlen(msgtxt)+1); | | setData(operation, msgtxt, strlen(msgtxt)+1); | |
| } | | } | |
|
| void setData(int operation, const char *msgdata, int len) { | | void setData(int operation, const char *msgdata, size_t len) { | |
| assert(data == 0); | | assert(data == 0); | |
|
| int dataLen = len + sizeof(MsgData) - 4; | | size_t dataLen = len + sizeof(MsgData) - 4; | |
| MsgData *d = (MsgData *) malloc(dataLen); | | MsgData *d = (MsgData *) malloc(dataLen); | |
| memcpy(d->_data, msgdata, len); | | memcpy(d->_data, msgdata, len); | |
| d->len = fixEndian(dataLen); | | d->len = fixEndian(dataLen); | |
| d->setOperation(operation); | | d->setOperation(operation); | |
| freeIt= true; | | freeIt= true; | |
| data = d; | | data = d; | |
| } | | } | |
| | | | |
| bool doIFreeIt() { | | bool doIFreeIt() { | |
| return freeIt; | | return freeIt; | |
| | | | |
End of changes. 5 change blocks. |
| 3 lines changed or deleted | | 22 lines changed or added | |
|
| namespace.h | | namespace.h | |
| | | | |
| skipping to change at line 24 | | skipping to change at line 24 | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include "queryutil.h" | | #include "queryutil.h" | |
|
| #include "storage.h" | | #include "diskloc.h" | |
| #include "../util/hashtab.h" | | #include "../util/hashtab.h" | |
| #include "../util/mmap.h" | | #include "../util/mmap.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class Cursor; | | class Cursor; | |
| | | | |
| #pragma pack(1) | | #pragma pack(1) | |
| | | | |
| /* in the mongo source code, "client" means "database". */ | | /* in the mongo source code, "client" means "database". */ | |
| | | | |
| skipping to change at line 78 | | skipping to change at line 78 | |
| void init(const char *ns) { | | void init(const char *ns) { | |
| const char *p = strchr(ns, '.'); | | const char *p = strchr(ns, '.'); | |
| if( p == 0 ) return; | | if( p == 0 ) return; | |
| db = string(ns, p - ns); | | db = string(ns, p - ns); | |
| coll = p + 1; | | coll = p + 1; | |
| } | | } | |
| public: | | public: | |
| NamespaceString( const char * ns ) { init(ns); } | | NamespaceString( const char * ns ) { init(ns); } | |
| NamespaceString( const string& ns ) { init(ns.c_str()); } | | NamespaceString( const string& ns ) { init(ns.c_str()); } | |
| | | | |
|
| | | string ns() const { | |
| | | return db + '.' + coll; | |
| | | } | |
| | | | |
| bool isSystem() { | | bool isSystem() { | |
| return strncmp(coll.c_str(), "system.", 7) == 0; | | return strncmp(coll.c_str(), "system.", 7) == 0; | |
| } | | } | |
| }; | | }; | |
| | | | |
| /* This helper class is used to make the HashMap below in NamespaceD
etails */ | | /* This helper class is used to make the HashMap below in NamespaceD
etails */ | |
| class Namespace { | | class Namespace { | |
| public: | | public: | |
| enum MaxNsLenValue { MaxNsLen = 128 }; | | enum MaxNsLenValue { MaxNsLen = 128 }; | |
| Namespace(const char *ns) { | | Namespace(const char *ns) { | |
| | | | |
| skipping to change at line 103 | | skipping to change at line 107 | |
| strcpy_s(buf, MaxNsLen, ns); | | strcpy_s(buf, MaxNsLen, ns); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /* for more than 10 indexes -- see NamespaceDetails::Extra */ | | /* for more than 10 indexes -- see NamespaceDetails::Extra */ | |
| string extraName() { | | string extraName() { | |
| string s = string(buf) + "$extra"; | | string s = string(buf) + "$extra"; | |
| massert( 10348 , "ns name too long", s.size() < MaxNsLen); | | massert( 10348 , "ns name too long", s.size() < MaxNsLen); | |
| return s; | | return s; | |
| } | | } | |
|
| | | bool isExtra() const { | |
| | | const char *p = strstr(buf, "$extra"); | |
| | | return p && p[6] == 0; //==0 important in case an index uses na | |
| | | me "$extra_1" for example | |
| | | } | |
| | | | |
| void kill() { | | void kill() { | |
| buf[0] = 0x7f; | | buf[0] = 0x7f; | |
| } | | } | |
| | | | |
| bool operator==(const char *r) { | | bool operator==(const char *r) { | |
| return strcmp(buf, r) == 0; | | return strcmp(buf, r) == 0; | |
| } | | } | |
| bool operator==(const Namespace& r) { | | bool operator==(const Namespace& r) { | |
| return strcmp(buf, r.buf) == 0; | | return strcmp(buf, r.buf) == 0; | |
| | | | |
| skipping to change at line 188 | | skipping to change at line 196 | |
| assert( extraOffset ); | | assert( extraOffset ); | |
| return (Extra *) (((char *) this) + extraOffset); | | return (Extra *) (((char *) this) + extraOffset); | |
| } | | } | |
| public: | | public: | |
| void copyingFrom(const char *thisns, NamespaceDetails *src); // mus
t be called when renaming a NS to fix up extra | | void copyingFrom(const char *thisns, NamespaceDetails *src); // mus
t be called when renaming a NS to fix up extra | |
| | | | |
| enum { NIndexesMax = 40 }; | | enum { NIndexesMax = 40 }; | |
| | | | |
| BOOST_STATIC_ASSERT( NIndexesMax == NIndexesBase + NIndexesExtra ); | | BOOST_STATIC_ASSERT( NIndexesMax == NIndexesBase + NIndexesExtra ); | |
| | | | |
|
| | | /* called when loaded from disk */ | |
| | | void onLoad(const Namespace& k); | |
| | | | |
| NamespaceDetails( const DiskLoc &loc, bool _capped ) { | | NamespaceDetails( const DiskLoc &loc, bool _capped ) { | |
| /* be sure to initialize new fields here -- doesn't default to
zeroes the way we use it */ | | /* be sure to initialize new fields here -- doesn't default to
zeroes the way we use it */ | |
| firstExtent = lastExtent = capExtent = loc; | | firstExtent = lastExtent = capExtent = loc; | |
| datasize = nrecords = 0; | | datasize = nrecords = 0; | |
| lastExtentSize = 0; | | lastExtentSize = 0; | |
| nIndexes = 0; | | nIndexes = 0; | |
| capped = _capped; | | capped = _capped; | |
| max = 0x7fffffff; | | max = 0x7fffffff; | |
| paddingFactor = 1.0; | | paddingFactor = 1.0; | |
| flags = 0; | | flags = 0; | |
| | | | |
| skipping to change at line 253 | | skipping to change at line 264 | |
| unsigned short indexFileVersion; | | unsigned short indexFileVersion; | |
| | | | |
| unsigned long long multiKeyIndexBits; | | unsigned long long multiKeyIndexBits; | |
| private: | | private: | |
| unsigned long long reservedA; | | unsigned long long reservedA; | |
| long long extraOffset; // where the $extra info is located (bytes r
elative to this) | | long long extraOffset; // where the $extra info is located (bytes r
elative to this) | |
| public: | | public: | |
| int backgroundIndexBuildInProgress; // 1 if in prog | | int backgroundIndexBuildInProgress; // 1 if in prog | |
| char reserved[76]; | | char reserved[76]; | |
| | | | |
|
| | | /* when a background index build is in progress, we don't count the | |
| | | index in nIndexes until | |
| | | complete, yet need to still use it in _indexRecord() - thus we u | |
| | | se this function for that. | |
| | | */ | |
| | | int nIndexesBeingBuilt() const { | |
| | | return nIndexes + backgroundIndexBuildInProgress; | |
| | | } | |
| | | | |
| /* NOTE: be careful with flags. are we manipulating them in read l
ocks? if so, | | /* NOTE: be careful with flags. are we manipulating them in read l
ocks? if so, | |
| this isn't thread safe. TODO | | this isn't thread safe. TODO | |
| */ | | */ | |
| enum NamespaceFlags { | | 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_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 allo
wed during capped table allocation. | | Flag_CappedDisallowDelete = 1 << 1 // set when deletes not allo
wed during capped table allocation. | |
| }; | | }; | |
| | | | |
| IndexDetails& idx(int idxNo) { | | IndexDetails& idx(int idxNo) { | |
| if( idxNo < NIndexesBase ) | | if( idxNo < NIndexesBase ) | |
| return _indexes[idxNo]; | | return _indexes[idxNo]; | |
| return extra()->details[idxNo-NIndexesBase]; | | return extra()->details[idxNo-NIndexesBase]; | |
| } | | } | |
|
| | | IndexDetails& backgroundIdx() { | |
| | | DEV assert(backgroundIndexBuildInProgress); | |
| | | return idx(nIndexes); | |
| | | } | |
| | | | |
| class IndexIterator { | | class IndexIterator { | |
| friend class NamespaceDetails; | | friend class NamespaceDetails; | |
| int i; | | int i; | |
| int n; | | int n; | |
| NamespaceDetails *d; | | NamespaceDetails *d; | |
| Extra *e; | | Extra *e; | |
| IndexIterator(NamespaceDetails *_d) { | | IndexIterator(NamespaceDetails *_d) { | |
| d = _d; | | d = _d; | |
| i = 0; | | i = 0; | |
| | | | |
| skipping to change at line 412 | | skipping to change at line 434 | |
| // Start from firstExtent by default. | | // Start from firstExtent by default. | |
| DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const
; | | DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const
; | |
| | | | |
| // Start from lastExtent by default. | | // Start from lastExtent by default. | |
| DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | | DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | |
| | | | |
| bool inCapExtent( const DiskLoc &dl ) const; | | bool inCapExtent( const DiskLoc &dl ) const; | |
| | | | |
| void checkMigrate(); | | void checkMigrate(); | |
| | | | |
|
| long long storageSize(); | | long long storageSize( int * numExtents = 0 ); | |
| | | | |
| private: | | private: | |
| bool cappedMayDelete() const { | | bool cappedMayDelete() const { | |
| return !( flags & Flag_CappedDisallowDelete ); | | return !( flags & Flag_CappedDisallowDelete ); | |
| } | | } | |
| Extent *theCapExtent() const { | | Extent *theCapExtent() const { | |
| return capExtent.ext(); | | return capExtent.ext(); | |
| } | | } | |
| void advanceCapExtent( const char *ns ); | | void advanceCapExtent( const char *ns ); | |
| void maybeComplain( const char *ns, int len ) const; | | void maybeComplain( const char *ns, int len ) const; | |
| | | | |
| skipping to change at line 490 | | skipping to change at line 512 | |
| return _indexKeys; | | return _indexKeys; | |
| } | | } | |
| | | | |
| /* IndexSpec caching */ | | /* IndexSpec caching */ | |
| private: | | private: | |
| map<const IndexDetails*,IndexSpec> _indexSpecs; | | map<const IndexDetails*,IndexSpec> _indexSpecs; | |
| public: | | public: | |
| const IndexSpec& getIndexSpec( const IndexDetails * details ){ | | const IndexSpec& getIndexSpec( const IndexDetails * details ){ | |
| DEV assertInWriteLock(); | | DEV assertInWriteLock(); | |
| IndexSpec& spec = _indexSpecs[details]; | | IndexSpec& spec = _indexSpecs[details]; | |
|
| if ( spec.meta.isEmpty() ){ | | if ( spec.info.isEmpty() ){ | |
| spec.reset( details->info ); | | spec.reset( details->info ); | |
| } | | } | |
| return spec; | | return spec; | |
| } | | } | |
| | | | |
| /* query cache (for query optimizer) ------------------------------
------- */ | | /* query cache (for query optimizer) ------------------------------
------- */ | |
| private: | | private: | |
| int _qcWriteCount; | | int _qcWriteCount; | |
| map< QueryPattern, pair< BSONObj, long long > > _qcCache; | | map< QueryPattern, pair< BSONObj, long long > > _qcCache; | |
| public: | | public: | |
| | | | |
| skipping to change at line 557 | | skipping to change at line 579 | |
| } | | } | |
| | | | |
| /* NamespaceIndex is the ".ns" file you see in the data directory. It
is the "system catalog" | | /* NamespaceIndex is the ".ns" file you see in the data directory. It
is the "system catalog" | |
| if you will: at least the core parts. (Additional info in system.*
collections.) | | if you will: at least the core parts. (Additional info in system.*
collections.) | |
| */ | | */ | |
| class NamespaceIndex { | | class NamespaceIndex { | |
| friend class NamespaceCursor; | | friend class NamespaceCursor; | |
| BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name
spaceDetails) ); | | BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name
spaceDetails) ); | |
| public: | | public: | |
| NamespaceIndex(const string &dir, const string &database) : | | NamespaceIndex(const string &dir, const string &database) : | |
|
| ht( 0 ), | | ht( 0 ), | |
| dir_( dir ), | | dir_( dir ), | |
| database_( database ) {} | | database_( database ) {} | |
| | | | |
| /* returns true if new db will be created if we init lazily */ | | /* returns true if new db will be created if we init lazily */ | |
| bool exists() const; | | bool exists() const; | |
| | | | |
| void init(); | | void init(); | |
| | | | |
| void add_ns(const char *ns, DiskLoc& loc, bool capped) { | | void add_ns(const char *ns, DiskLoc& loc, bool capped) { | |
| NamespaceDetails details( loc, capped ); | | NamespaceDetails details( loc, capped ); | |
| add_ns( ns, details ); | | add_ns( ns, details ); | |
| } | | } | |
| | | | |
| skipping to change at line 639 | | skipping to change at line 661 | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| bool allocated() const { | | bool allocated() const { | |
| return ht != 0; | | return ht != 0; | |
| } | | } | |
| | | | |
| private: | | private: | |
| boost::filesystem::path path() const; | | boost::filesystem::path path() const; | |
|
| | | void maybeMkdir() const; | |
| | | | |
| MemoryMappedFile f; | | MemoryMappedFile f; | |
| HashTable<Namespace,NamespaceDetails> *ht; | | HashTable<Namespace,NamespaceDetails> *ht; | |
| string dir_; | | string dir_; | |
| string database_; | | string database_; | |
| }; | | }; | |
| | | | |
| extern string dbpath; // --dbpath parm | | extern string dbpath; // --dbpath parm | |
|
| | | extern bool directoryperdb; | |
| | | | |
| // Rename a namespace within current 'client' db. | | // Rename a namespace within current 'client' db. | |
| // (Arguments should include db name) | | // (Arguments should include db name) | |
| void renameNamespace( const char *from, const char *to ); | | void renameNamespace( const char *from, const char *to ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 11 change blocks. |
| 6 lines changed or deleted | | 33 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 30 | | skipping to change at line 30 | |
| database.ns - namespace index | | database.ns - namespace index | |
| database.1 - data files | | database.1 - data files | |
| database.2 | | database.2 | |
| ... | | ... | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "../util/mmap.h" | | #include "../util/mmap.h" | |
|
| #include "storage.h" | | #include "diskloc.h" | |
| #include "jsobjmanipulator.h" | | #include "jsobjmanipulator.h" | |
| #include "namespace.h" | | #include "namespace.h" | |
| #include "client.h" | | #include "client.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class MDFHeader; | | class MDFHeader; | |
| class Extent; | | class Extent; | |
| class Record; | | class Record; | |
| class Cursor; | | class Cursor; | |
| | | | |
| skipping to change at line 101 | | skipping to change at line 101 | |
| | | | |
| class DataFileMgr { | | class DataFileMgr { | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| public: | | public: | |
| void init(const string& path ); | | void init(const string& path ); | |
| | | | |
| /* see if we can find an extent of the right size in the freelist.
*/ | | /* see if we can find an extent of the right size in the freelist.
*/ | |
| static Extent* allocFromFreeList(const char *ns, int approxSize, bo
ol capped = false); | | static Extent* allocFromFreeList(const char *ns, int approxSize, bo
ol capped = false); | |
| | | | |
| /** @return DiskLoc where item ends up */ | | /** @return DiskLoc where item ends up */ | |
|
| const DiskLoc update( | | const DiskLoc updateRecord( | |
| const char *ns, | | const char *ns, | |
|
| | | NamespaceDetails *d, | |
| | | NamespaceDetailsTransient *nsdt, | |
| Record *toupdate, const DiskLoc& dl, | | Record *toupdate, const DiskLoc& dl, | |
| const char *buf, int len, OpDebug& debug); | | const char *buf, int len, OpDebug& debug); | |
| // The object o may be updated if modified on insert. | | // The object o may be updated if modified on insert. | |
| void insertAndLog( const char *ns, const BSONObj &o, bool god = fal
se ); | | void insertAndLog( const char *ns, const BSONObj &o, bool god = fal
se ); | |
| DiskLoc insert(const char *ns, BSONObj &o, bool god = false); | | DiskLoc insert(const char *ns, BSONObj &o, bool god = false); | |
| DiskLoc insert(const char *ns, const void *buf, int len, bool god =
false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true
); | | DiskLoc insert(const char *ns, const void *buf, int len, bool god =
false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true
); | |
| void deleteRecord(const char *ns, Record *todelete, const DiskLoc&
dl, bool cappedOK = false, bool noWarn = false); | | void deleteRecord(const char *ns, Record *todelete, const DiskLoc&
dl, bool cappedOK = false, bool noWarn = false); | |
| static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star
tLoc = DiskLoc()); | | static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star
tLoc = DiskLoc()); | |
| | | | |
| /* special version of insert for transaction logging -- streamlined
a bit. | | /* special version of insert for transaction logging -- streamlined
a bit. | |
| | | | |
| skipping to change at line 395 | | skipping to change at line 397 | |
| public: | | public: | |
| virtual ~FileOp() {} | | virtual ~FileOp() {} | |
| // Return true if file exists and operation successful | | // Return true if file exists and operation successful | |
| virtual bool apply( const boost::filesystem::path &p ) = 0; | | virtual bool apply( const boost::filesystem::path &p ) = 0; | |
| virtual const char * op() const = 0; | | virtual const char * op() const = 0; | |
| }; | | }; | |
| | | | |
| void _applyOpToDataFiles( const char *database, FileOp &fo, bool afterA
llocator = false, const string& path = dbpath ); | | void _applyOpToDataFiles( const char *database, FileOp &fo, bool afterA
llocator = false, const string& path = dbpath ); | |
| | | | |
| inline void _deleteDataFiles(const char *database) { | | inline void _deleteDataFiles(const char *database) { | |
|
| | | if ( directoryperdb ) { | |
| | | BOOST_CHECK_EXCEPTION( boost::filesystem::remove_all( boost::fi | |
| | | lesystem::path( dbpath ) / database ) ); | |
| | | return; | |
| | | } | |
| class : public FileOp { | | class : public FileOp { | |
| virtual bool apply( const boost::filesystem::path &p ) { | | virtual bool apply( const boost::filesystem::path &p ) { | |
| return boost::filesystem::remove( p ); | | return boost::filesystem::remove( p ); | |
| } | | } | |
| virtual const char * op() const { | | virtual const char * op() const { | |
| return "remove"; | | return "remove"; | |
| } | | } | |
| } deleter; | | } deleter; | |
| _applyOpToDataFiles( database, deleter, true ); | | _applyOpToDataFiles( database, deleter, true ); | |
| } | | } | |
| | | | |
| skipping to change at line 446 | | skipping to change at line 452 | |
| return cc().database()->getFile(dl.a())->getExtent(dl); | | return cc().database()->getFile(dl.a())->getExtent(dl); | |
| } | | } | |
| | | | |
| inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | | inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | |
| assert( dl.a() != -1 ); | | assert( dl.a() != -1 ); | |
| return cc().database()->getFile(dl.a())->recordAt(dl); | | return cc().database()->getFile(dl.a())->recordAt(dl); | |
| } | | } | |
| | | | |
| void ensureHaveIdIndex(const char *ns); | | void ensureHaveIdIndex(const char *ns); | |
| | | | |
|
| bool deleteIndexes( NamespaceDetails *d, const char *ns, const char *na
me, string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | | bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name
, string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 5 change blocks. |
| 3 lines changed or deleted | | 10 lines changed or added | |
|
| update.h | | update.h | |
| | | | |
| skipping to change at line 26 | | skipping to change at line 26 | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include "../util/embedded_builder.h" | | #include "../util/embedded_builder.h" | |
| #include "matcher.h" | | #include "matcher.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| /* Used for modifiers such as $inc, $set, $push, ... */ | | class ModState; | |
| | | class ModSetState; | |
| | | | |
| | | /* Used for modifiers such as $inc, $set, $push, ... | |
| | | * stores the info about a single operation | |
| | | * once created should never be modified | |
| | | */ | |
| struct Mod { | | struct Mod { | |
| // See opFromStr below | | // See opFromStr below | |
|
| // 0 1 2 3 4 5 6 7 8 | | // 0 1 2 3 4 5 6 7 8 | |
| 9 10 | | 9 10 11 | |
| enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI | | enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI | |
| TAND, BITOR , BIT } op; | | TAND, BITOR , BIT , ADDTOSET } op; | |
| | | | |
| static const char* modNames[]; | | static const char* modNames[]; | |
| static unsigned modNamesNum; | | static unsigned modNamesNum; | |
| | | | |
| const char *fieldName; | | const char *fieldName; | |
| const char *shortFieldName; | | const char *shortFieldName; | |
| | | | |
|
| // kind of lame; fix one day? | | | |
| double *ndouble; | | | |
| int *nint; | | | |
| long long *nlong; | | | |
| | | | |
| BSONElement elt; // x:5 note: this is the actual element from the u
pdateobj | | BSONElement elt; // x:5 note: this is the actual element from the u
pdateobj | |
|
| int pushStartSize; | | | |
| boost::shared_ptr<Matcher> matcher; | | boost::shared_ptr<Matcher> matcher; | |
| | | | |
| void init( Op o , BSONElement& e ){ | | void init( Op o , BSONElement& e ){ | |
| op = o; | | op = o; | |
| elt = e; | | elt = e; | |
| if ( op == PULL && e.type() == Object ) | | if ( op == PULL && e.type() == Object ) | |
| matcher.reset( new Matcher( e.embeddedObject() ) ); | | matcher.reset( new Matcher( e.embeddedObject() ) ); | |
| } | | } | |
| | | | |
| void setFieldName( const char * s ){ | | void setFieldName( const char * s ){ | |
| fieldName = s; | | fieldName = s; | |
| shortFieldName = strrchr( fieldName , '.' ); | | shortFieldName = strrchr( fieldName , '.' ); | |
| if ( shortFieldName ) | | if ( shortFieldName ) | |
| shortFieldName++; | | shortFieldName++; | |
| else | | else | |
| shortFieldName = fieldName; | | shortFieldName = fieldName; | |
| } | | } | |
| | | | |
|
| /* [dm] why is this const? (or rather, why was setn const?) i see | | /** | |
| why but think maybe clearer if were not. */ | | * @param in incrememnts the actual value inside in | |
| void inc(BSONElement& n) const { | | */ | |
| uassert( 10160 , "$inc value is not a number", n.isNumber() ); | | void incrementMe( BSONElement& in ) const { | |
| if( ndouble ) | | BSONElementManipulator manip( in ); | |
| *ndouble += n.numberDouble(); | | | |
| else if( nint ) | | | |
| *nint += n.numberInt(); | | | |
| else | | | |
| *nlong += n.numberLong(); | | | |
| } | | | |
| | | | |
|
| void setElementToOurNumericValue(BSONElement& e) const { | | switch ( in.type() ){ | |
| BSONElementManipulator manip(e); | | case NumberDouble: | |
| if( e.type() == NumberLong ) | | manip.setNumber( elt.numberDouble() + in.numberDouble() ); | |
| manip.setLong(_getlong()); | | break; | |
| else | | case NumberLong: | |
| manip.setNumber(_getn()); | | manip.setLong( elt.numberLong() + in.numberLong() ); | |
| } | | break; | |
| | | case NumberInt: | |
| | | manip.setInt( elt.numberInt() + in.numberInt() ); | |
| | | break; | |
| | | default: | |
| | | assert(0); | |
| | | } | |
| | | | |
|
| double _getn() const { | | | |
| if( ndouble ) return *ndouble; | | | |
| if( nint ) return *nint; | | | |
| return (double) *nlong; | | | |
| } | | | |
| long long _getlong() const { | | | |
| if( nlong ) return *nlong; | | | |
| if( ndouble ) return (long long) *ndouble; | | | |
| return *nint; | | | |
| } | | } | |
|
| | | | |
| | | void appendIncremented( BSONObjBuilder& bb , const BSONElement& in, | |
| | | ModState& ms ) const; | |
| | | | |
| bool operator<( const Mod &other ) const { | | bool operator<( const Mod &other ) const { | |
| return strcmp( fieldName, other.fieldName ) < 0; | | return strcmp( fieldName, other.fieldName ) < 0; | |
| } | | } | |
| | | | |
| bool arrayDep() const { | | bool arrayDep() const { | |
| switch (op){ | | switch (op){ | |
| case PUSH: | | case PUSH: | |
| case PUSH_ALL: | | case PUSH_ALL: | |
| case POP: | | case POP: | |
| return true; | | return true; | |
| | | | |
| skipping to change at line 123 | | skipping to change at line 118 | |
| // check if there is an index key equal to mod | | // check if there is an index key equal to mod | |
| if ( idxKeys.count(fullName) ) | | if ( idxKeys.count(fullName) ) | |
| return true; | | return true; | |
| // check if there is an index key that is a child of mod | | // check if there is an index key that is a child of mod | |
| set< string >::const_iterator j = idxKeys.upper_bound( fullName
); | | set< string >::const_iterator j = idxKeys.upper_bound( fullName
); | |
| if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful
lName.size()] == '.' ) | | if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful
lName.size()] == '.' ) | |
| return true; | | return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| void apply( BSONObjBuilder& b , BSONElement in ); | | void apply( BSONObjBuilder& b , BSONElement in , ModState& ms ) con
st; | |
| | | | |
| /** | | /** | |
| * @return true iff toMatch should be removed from the array | | * @return true iff toMatch should be removed from the array | |
| */ | | */ | |
| bool _pullElementMatch( BSONElement& toMatch ) const; | | bool _pullElementMatch( BSONElement& toMatch ) const; | |
| | | | |
|
| bool needOpLogRewrite() const { | | void _checkForAppending( const BSONElement& e ) const { | |
| switch( op ){ | | | |
| case BIT: | | | |
| case BITAND: | | | |
| case BITOR: | | | |
| // TODO: should we convert this to $set? | | | |
| return false; | | | |
| default: | | | |
| return false; | | | |
| } | | | |
| } | | | |
| | | | |
| void appendForOpLog( BSONObjBuilder& b ) const { | | | |
| const char * name = modNames[op]; | | | |
| | | | |
| BSONObjBuilder bb( b.subobjStart( name ) ); | | | |
| bb.append( elt ); | | | |
| bb.done(); | | | |
| } | | | |
| | | | |
| void _checkForAppending( BSONElement& e ){ | | | |
| if ( e.type() == Object ){ | | if ( e.type() == Object ){ | |
| // this is a tiny bit slow, but rare and important | | // this is a tiny bit slow, but rare and important | |
| // only when setting something TO an object, not setting so
mething in an object | | // only when setting something TO an object, not setting so
mething in an object | |
| // and it checks for { $set : { x : { 'a.b' : 1 } } } | | // and it checks for { $set : { x : { 'a.b' : 1 } } } | |
| // which is feel has been common | | // which is feel has been common | |
| uassert( 12527 , "not okForStorage" , e.embeddedObject().ok
ForStorage() ); | | uassert( 12527 , "not okForStorage" , e.embeddedObject().ok
ForStorage() ); | |
| } | | } | |
| } | | } | |
| | | | |
| }; | | }; | |
| | | | |
|
| class ModSet { | | /** | |
| | | * stores a set of Mods | |
| | | * once created, should never be changed | |
| | | */ | |
| | | class ModSet : boost::noncopyable { | |
| typedef map<string,Mod> ModHolder; | | typedef map<string,Mod> ModHolder; | |
| ModHolder _mods; | | ModHolder _mods; | |
|
| | | int _isIndexed; | |
| | | | |
| static void extractFields( map< string, BSONElement > &fields, cons
t BSONElement &top, const string &base ); | | static void extractFields( map< string, BSONElement > &fields, cons
t BSONElement &top, const string &base ); | |
| | | | |
| FieldCompareResult compare( const ModHolder::iterator &m, map< stri
ng, BSONElement >::iterator &p, const map< string, BSONElement >::iterator
&pEnd ) const { | | FieldCompareResult compare( const ModHolder::iterator &m, map< stri
ng, BSONElement >::iterator &p, const map< string, BSONElement >::iterator
&pEnd ) const { | |
| bool mDone = ( m == _mods.end() ); | | bool mDone = ( m == _mods.end() ); | |
| bool pDone = ( p == pEnd ); | | bool pDone = ( p == pEnd ); | |
| assert( ! mDone ); | | assert( ! mDone ); | |
| assert( ! pDone ); | | assert( ! pDone ); | |
| if ( mDone && pDone ) | | if ( mDone && pDone ) | |
| return SAME; | | return SAME; | |
| // If one iterator is done we want to read from the other one,
so say the other one is lower. | | // If one iterator is done we want to read from the other one,
so say the other one is lower. | |
| if ( mDone ) | | if ( mDone ) | |
| return RIGHT_BEFORE; | | return RIGHT_BEFORE; | |
| if ( pDone ) | | if ( pDone ) | |
| return LEFT_BEFORE; | | return LEFT_BEFORE; | |
| | | | |
| return compareDottedFieldNames( m->first, p->first.c_str() ); | | return compareDottedFieldNames( m->first, p->first.c_str() ); | |
| } | | } | |
| | | | |
|
| void _appendNewFromMods( const string& root , Mod& m , BSONObjBuild | | | |
| er& b , set<string>& onedownseen ); | | | |
| | | | |
| void appendNewFromMod( Mod& m , BSONObjBuilder& b ){ | | | |
| switch ( m.op ){ | | | |
| | | | |
| case Mod::PUSH: { | | | |
| BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) ); | | | |
| arr.appendAs( m.elt, "0" ); | | | |
| arr.done(); | | | |
| m.pushStartSize = -1; | | | |
| break; | | | |
| } | | | |
| | | | |
| case Mod::PUSH_ALL: { | | | |
| b.appendAs( m.elt, m.shortFieldName ); | | | |
| m.pushStartSize = -1; | | | |
| break; | | | |
| } | | | |
| | | | |
| case Mod::UNSET: | | | |
| case Mod::PULL: | | | |
| case Mod::PULL_ALL: | | | |
| // no-op b/c unset/pull of nothing does nothing | | | |
| break; | | | |
| | | | |
| case Mod::INC: | | | |
| case Mod::SET: { | | | |
| m._checkForAppending( m.elt ); | | | |
| b.appendAs( m.elt, m.shortFieldName ); | | | |
| break; | | | |
| } | | | |
| default: | | | |
| stringstream ss; | | | |
| ss << "unknown mod in appendNewFromMod: " << m.op; | | | |
| throw UserException( 9015, ss.str() ); | | | |
| } | | | |
| | | | |
| } | | | |
| | | | |
| bool mayAddEmbedded( map< string, BSONElement > &existing, string r
ight ) { | | bool mayAddEmbedded( map< string, BSONElement > &existing, string r
ight ) { | |
| for( string left = EmbeddedBuilder::splitDot( right ); | | for( string left = EmbeddedBuilder::splitDot( right ); | |
| left.length() > 0 && left[ left.length() - 1 ] != '.'; | | left.length() > 0 && left[ left.length() - 1 ] != '.'; | |
| left += "." + EmbeddedBuilder::splitDot( right ) ) { | | left += "." + EmbeddedBuilder::splitDot( right ) ) { | |
| if ( existing.count( left ) > 0 && existing[ left ].type()
!= Object ) | | if ( existing.count( left ) > 0 && existing[ left ].type()
!= Object ) | |
| return false; | | return false; | |
| if ( haveModForField( left.c_str() ) ) | | if ( haveModForField( left.c_str() ) ) | |
| return false; | | return false; | |
| } | | } | |
| return true; | | return true; | |
| | | | |
| skipping to change at line 282 | | skipping to change at line 223 | |
| if ( fn[2] == 'i' && fn[3] == 't' ){ | | if ( fn[2] == 'i' && fn[3] == 't' ){ | |
| if ( fn[4] == 0 ) | | if ( fn[4] == 0 ) | |
| return Mod::BIT; | | return Mod::BIT; | |
| if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn
[7] == 0 ) | | if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn
[7] == 0 ) | |
| return Mod::BITAND; | | return Mod::BITAND; | |
| if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 ) | | if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 ) | |
| return Mod::BITOR; | | return Mod::BITOR; | |
| } | | } | |
| break; | | break; | |
| } | | } | |
|
| | | case 'a': { | |
| | | if ( fn[2] == 'd' && fn[3] == 'd' ){ | |
| | | // add | |
| | | if ( fn[4] == 'T' && fn[5] == 'o' && fn[6] == 'S' && fn | |
| | | [7] == 'e' && fn[8] == 't' && fn[9] == 0 ) | |
| | | return Mod::ADDTOSET; | |
| | | | |
| | | } | |
| | | } | |
| default: break; | | default: break; | |
| } | | } | |
| uassert( 10161 , "Invalid modifier specified " + string( fn ),
false ); | | uassert( 10161 , "Invalid modifier specified " + string( fn ),
false ); | |
| return Mod::INC; | | return Mod::INC; | |
| } | | } | |
| | | | |
| public: | | public: | |
| | | | |
|
| void getMods( const BSONObj &from ); | | ModSet( const BSONObj &from , | |
| | | const set<string>& idxKeys = set<string>(), | |
| | | const set<string>* backgroundKeys = 0 | |
| | | ); | |
| | | | |
| /** | | /** | |
|
| will return if can be done in place, or uassert if there is an e | | * creates a ModSetState suitable for operation on obj | |
| rror | | * doesn't change or modify this ModSet or any underying Mod | |
| @return whether or not the mods can be done in place | | | |
| */ | | */ | |
|
| bool canApplyInPlaceAndVerify( const BSONObj &obj ) const; | | auto_ptr<ModSetState> prepare( const BSONObj& obj ) const; | |
| void applyModsInPlace( const BSONObj &obj ) const; | | | |
| | | | |
| // new recursive version, will replace at some point | | | |
| void createNewFromMods( const string& root , BSONObjBuilder& b , co | | | |
| nst BSONObj &obj ); | | | |
| | | | |
| BSONObj createNewFromMods( const BSONObj &obj ); | | | |
| | | | |
|
| | | /** | |
| | | * given a query pattern, builds an object suitable for an upsert | |
| | | * will take the query spec and combine all $ operators | |
| | | */ | |
| BSONObj createNewFromQuery( const BSONObj& query ); | | BSONObj createNewFromQuery( const BSONObj& query ); | |
| | | | |
| /** | | /** | |
| * | | * | |
| */ | | */ | |
|
| int isIndexed( const set<string>& idxKeys ) const { | | int isIndexed() const { | |
| int numIndexes = 0; | | return _isIndexed; | |
| for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e | | | |
| nd(); i++ ){ | | | |
| if ( i->second.isIndexed( idxKeys ) ) | | | |
| numIndexes++; | | | |
| } | | | |
| return numIndexes; | | | |
| } | | } | |
| | | | |
| unsigned size() const { return _mods.size(); } | | unsigned size() const { return _mods.size(); } | |
| | | | |
| bool haveModForField( const char *fieldName ) const { | | bool haveModForField( const char *fieldName ) const { | |
| return _mods.find( fieldName ) != _mods.end(); | | return _mods.find( fieldName ) != _mods.end(); | |
| } | | } | |
| | | | |
| bool haveConflictingMod( const string& fieldName ){ | | bool haveConflictingMod( const string& fieldName ){ | |
| size_t idx = fieldName.find( '.' ); | | size_t idx = fieldName.find( '.' ); | |
| | | | |
| skipping to change at line 343 | | skipping to change at line 289 | |
| case LEFT_BEFORE: return false; | | case LEFT_BEFORE: return false; | |
| case SAME: return true; | | case SAME: return true; | |
| case RIGHT_BEFORE: return false; | | case RIGHT_BEFORE: return false; | |
| case RIGHT_SUBFIELD: return true; | | case RIGHT_SUBFIELD: return true; | |
| } | | } | |
| } | | } | |
| return false; | | return false; | |
| | | | |
| } | | } | |
| | | | |
|
| | | }; | |
| | | | |
| | | /** | |
| | | * stores any information about a single Mod operating on a single Obje | |
| | | ct | |
| | | */ | |
| | | class ModState { | |
| | | public: | |
| | | const Mod * m; | |
| | | BSONElement old; | |
| | | | |
| | | const char * fixedName; | |
| | | BSONElement * fixed; | |
| | | int pushStartSize; | |
| | | | |
| | | BSONType incType; | |
| | | int incint; | |
| | | double incdouble; | |
| | | long long inclong; | |
| | | | |
| | | ModState(){ | |
| | | fixedName = 0; | |
| | | fixed = 0; | |
| | | pushStartSize = -1; | |
| | | incType = EOO; | |
| | | } | |
| | | | |
| | | Mod::Op op() const { | |
| | | return m->op; | |
| | | } | |
| | | | |
| | | const char * fieldName() const { | |
| | | return m->fieldName; | |
| | | } | |
| | | | |
| | | bool needOpLogRewrite() const { | |
| | | if ( fixed || incType ) | |
| | | return true; | |
| | | | |
| | | switch( op() ){ | |
| | | case Mod::BIT: | |
| | | case Mod::BITAND: | |
| | | case Mod::BITOR: | |
| | | // TODO: should we convert this to $set? | |
| | | return false; | |
| | | default: | |
| | | return false; | |
| | | } | |
| | | } | |
| | | | |
| | | void appendForOpLog( BSONObjBuilder& b ) const { | |
| | | if ( incType ){ | |
| | | BSONObjBuilder bb( b.subobjStart( "$set" ) ); | |
| | | appendIncValue( bb ); | |
| | | bb.done(); | |
| | | return; | |
| | | } | |
| | | | |
| | | const char * name = fixedName ? fixedName : Mod::modNames[op()] | |
| | | ; | |
| | | | |
| | | BSONObjBuilder bb( b.subobjStart( name ) ); | |
| | | if ( fixed ) | |
| | | bb.append( *fixed ); | |
| | | else | |
| | | bb.append( m->elt ); | |
| | | bb.done(); | |
| | | } | |
| | | | |
| | | void apply( BSONObjBuilder& b , BSONElement in ){ | |
| | | m->apply( b , in , *this ); | |
| | | } | |
| | | | |
| | | void appendIncValue( BSONObjBuilder& b ) const { | |
| | | switch ( incType ){ | |
| | | case NumberDouble: | |
| | | b.append( m->shortFieldName , incdouble ); break; | |
| | | case NumberLong: | |
| | | b.append( m->shortFieldName , inclong ); break; | |
| | | case NumberInt: | |
| | | b.append( m->shortFieldName , incint ); break; | |
| | | default: | |
| | | assert(0); | |
| | | } | |
| | | } | |
| | | }; | |
| | | | |
| | | /** | |
| | | * this is used to hold state, meta data while applying a ModSet to a B | |
| | | SONObj | |
| | | * the goal is to make ModSet const so its re-usable | |
| | | */ | |
| | | class ModSetState : boost::noncopyable { | |
| | | typedef map<string,ModState> ModStateHolder; | |
| | | const BSONObj& _obj; | |
| | | ModStateHolder _mods; | |
| | | bool _inPlacePossible; | |
| | | | |
| | | ModSetState( const BSONObj& obj ) | |
| | | : _obj( obj ) , _inPlacePossible(true){ | |
| | | } | |
| | | | |
| | | /** | |
| | | * @return if in place is still possible | |
| | | */ | |
| | | bool amIInPlacePossible( bool inPlacePossible ){ | |
| | | if ( ! inPlacePossible ) | |
| | | _inPlacePossible = false; | |
| | | return _inPlacePossible; | |
| | | } | |
| | | | |
| | | void createNewFromMods( const string& root , BSONObjBuilder& b , co | |
| | | nst BSONObj &obj ); | |
| | | | |
| | | void _appendNewFromMods( const string& root , ModState& m , BSONObj | |
| | | Builder& b , set<string>& onedownseen ); | |
| | | | |
| | | void appendNewFromMod( ModState& ms , BSONObjBuilder& b ){ | |
| | | //const Mod& m = *(ms.m); // HACK | |
| | | Mod& m = *((Mod*)(ms.m)); // HACK | |
| | | | |
| | | switch ( m.op ){ | |
| | | | |
| | | case Mod::PUSH: { | |
| | | BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) ); | |
| | | arr.appendAs( m.elt, "0" ); | |
| | | arr.done(); | |
| | | break; | |
| | | } | |
| | | | |
| | | case Mod::PUSH_ALL: { | |
| | | b.appendAs( m.elt, m.shortFieldName ); | |
| | | break; | |
| | | } | |
| | | | |
| | | case Mod::UNSET: | |
| | | case Mod::PULL: | |
| | | case Mod::PULL_ALL: | |
| | | // no-op b/c unset/pull of nothing does nothing | |
| | | break; | |
| | | | |
| | | case Mod::INC: | |
| | | case Mod::SET: { | |
| | | m._checkForAppending( m.elt ); | |
| | | b.appendAs( m.elt, m.shortFieldName ); | |
| | | break; | |
| | | } | |
| | | default: | |
| | | stringstream ss; | |
| | | ss << "unknown mod in appendNewFromMod: " << m.op; | |
| | | throw UserException( 9015, ss.str() ); | |
| | | } | |
| | | | |
| | | } | |
| | | | |
| | | public: | |
| | | | |
| | | bool canApplyInPlace() const { | |
| | | return _inPlacePossible; | |
| | | } | |
| | | | |
| | | /** | |
| | | * modified underlying _obj | |
| | | */ | |
| | | void applyModsInPlace(); | |
| | | | |
| | | BSONObj createNewFromMods(); | |
| | | | |
| // re-writing for oplog | | // re-writing for oplog | |
| | | | |
| bool needOpLogRewrite() const { | | bool needOpLogRewrite() const { | |
|
| for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e
nd(); i++ ) | | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m
ods.end(); i++ ) | |
| if ( i->second.needOpLogRewrite() ) | | if ( i->second.needOpLogRewrite() ) | |
| return true; | | return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| BSONObj getOpLogRewrite() const { | | BSONObj getOpLogRewrite() const { | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
|
| for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e
nd(); i++ ) | | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m
ods.end(); i++ ) | |
| i->second.appendForOpLog( b ); | | i->second.appendForOpLog( b ); | |
| return b.obj(); | | return b.obj(); | |
| } | | } | |
| | | | |
| bool haveArrayDepMod() const { | | bool haveArrayDepMod() const { | |
|
| for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e | | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | |
| nd(); i++ ) | | ods.end(); i++ ) | |
| if ( i->second.arrayDep() ) | | if ( i->second.m->arrayDep() ) | |
| return true; | | return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | | void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { | |
|
| for ( ModHolder::const_iterator i = _mods.begin(); i != _mods.e | | for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m | |
| nd(); i++ ) { | | ods.end(); i++ ) { | |
| const Mod& m = i->second; | | const ModState& m = i->second; | |
| if ( m.arrayDep() ){ | | if ( m.m->arrayDep() ){ | |
| if ( m.pushStartSize == -1 ) | | if ( m.pushStartSize == -1 ) | |
|
| b.appendNull( m.fieldName ); | | b.appendNull( m.fieldName() ); | |
| else | | else | |
|
| b << m.fieldName << BSON( "$size" << m.pushStartSiz
e ); | | b << m.fieldName() << BSON( "$size" << m.pushStartS
ize ); | |
| } | | } | |
| } | | } | |
| } | | } | |
|
| | | | |
| | | friend class ModSet; | |
| }; | | }; | |
| | | | |
| } | | } | |
| | | | |
End of changes. 27 change blocks. |
| 132 lines changed or deleted | | 245 lines changed or added | |
|