| clientcursor.h | | clientcursor.h | |
| | | | |
| skipping to change at line 53 | | skipping to change at line 53 | |
| typedef map<CursorId, ClientCursor*> CCById; | | typedef map<CursorId, ClientCursor*> CCById; | |
| | | | |
| typedef multimap<DiskLoc, ClientCursor*> CCByLoc; | | typedef multimap<DiskLoc, ClientCursor*> CCByLoc; | |
| | | | |
| extern BSONObj id_obj; | | extern BSONObj id_obj; | |
| | | | |
| class ClientCursor { | | class ClientCursor { | |
| friend class CmdCursorInfo; | | friend class CmdCursorInfo; | |
| DiskLoc _lastLoc; // use getter and setter n
ot this (important) | | DiskLoc _lastLoc; // use getter and setter n
ot this (important) | |
| unsigned _idleAgeMillis; // how long has the cursor
been around, relative to server idle time | | unsigned _idleAgeMillis; // how long has the cursor
been around, relative to server idle time | |
|
| bool _noTimeout; // if true, never time out c | | | |
| ursor | | /* 0 = normal | |
| | | 1 = no timeout allowed | |
| | | 100 = in use (pinned) -- see Pointer class | |
| | | */ | |
| | | unsigned _pinValue; | |
| | | | |
| bool _doingDeletes; | | bool _doingDeletes; | |
| | | | |
| static CCById clientCursorsById; | | static CCById clientCursorsById; | |
| static CCByLoc byLoc; | | static CCByLoc byLoc; | |
| static boost::recursive_mutex ccmutex; // must use this for all s
tatics above! | | static boost::recursive_mutex ccmutex; // must use this for all s
tatics above! | |
| | | | |
| static CursorId allocCursorId_inlock(); | | static CursorId allocCursorId_inlock(); | |
| | | | |
| public: | | public: | |
|
| | | /* use this to assure we don't in the background time out cursor wh | |
| | | ile it is under use. | |
| | | if you are using noTimeout() already, there is no risk anyway. | |
| | | Further, this mechanism guards against two getMore requests on t | |
| | | he same cursor executing | |
| | | at the same time - which might be bad. That should never happen | |
| | | , but if a client driver | |
| | | had a bug, it could (or perhaps some sort of attack situation). | |
| | | */ | |
| | | class Pointer : boost::noncopyable { | |
| | | public: | |
| | | ClientCursor *_c; | |
| | | void release() { | |
| | | if( _c ) { | |
| | | assert( _c->_pinValue >= 100 ); | |
| | | _c->_pinValue -= 100; | |
| | | } | |
| | | _c = 0; | |
| | | } | |
| | | Pointer(long long cursorid) { | |
| | | recursive_boostlock lock(ccmutex); | |
| | | _c = ClientCursor::find_inlock(cursorid, true); | |
| | | if( _c ) { | |
| | | if( _c->_pinValue >= 100 ) { | |
| | | _c = 0; | |
| | | uassert(12051, "clientcursor already in use? driver | |
| | | problem?", false); | |
| | | } | |
| | | _c->_pinValue += 100; | |
| | | } | |
| | | } | |
| | | ~Pointer() { | |
| | | release(); | |
| | | } | |
| | | }; | |
| | | | |
| /*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), _noTimeout(false), _doingDelete
s(false), pos(0) { | | ClientCursor() : _idleAgeMillis(0), _pinValue(0), _doingDeletes(fal
se), pos(0) { | |
| 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; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 113 | | skipping to change at line 151 | |
| 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); | |
|
| return 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 | |
| | | // for the cursor or keep a ClientCursor::Pointer in | |
| | | scope for it. | |
| | | massert( 12521, "internal error: use of an unlocked ClientCurso | |
| | | r", c->_pinValue ); | |
| | | 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 | |
| delete cc; | | delete cc; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| /* call when cursor's location changes so that we can update the | | /* call when cursor's location changes so that we can update the | |
| cursorsbylocation map. if you are locked and internally iterati
ng, only | | cursorsbylocation map. if you are locked and internally iterati
ng, only | |
| need to call when you are ready to "unlock". | | need to call when you are ready to "unlock". | |
| */ | | */ | |
| | | | |
| skipping to change at line 147 | | skipping to change at line 190 | |
| stringstream ss; | | stringstream ss; | |
| ss << ns << "." << cursorid; | | ss << ns << "." << cursorid; | |
| ids_->mayUpgradeStorage( ss.str() );*/ | | ids_->mayUpgradeStorage( ss.str() );*/ | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @param millis amount of idle passed time since last call | | * @param millis amount of idle passed time since last call | |
| */ | | */ | |
| bool shouldTimeout( unsigned millis ){ | | bool shouldTimeout( unsigned millis ){ | |
| _idleAgeMillis += millis; | | _idleAgeMillis += millis; | |
|
| return ! _noTimeout && _idleAgeMillis > 600000; | | return _idleAgeMillis > 600000 && _pinValue == 0; | |
| } | | } | |
| | | | |
| unsigned idleTime(){ | | unsigned idleTime(){ | |
| return _idleAgeMillis; | | return _idleAgeMillis; | |
| } | | } | |
| | | | |
| static void idleTimeReport(unsigned millis); | | static void idleTimeReport(unsigned millis); | |
| | | | |
|
| | | // cursors normally timeout after an inactivy period to prevent exc | |
| | | ess memory use | |
| | | // setting this prevents timeout of the cursor in question. | |
| void noTimeout() { | | void noTimeout() { | |
|
| _noTimeout = true; | | _pinValue++; | |
| } | | } | |
| | | | |
| 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 idleTimeReport(unsigned millis); | | | |
| | | | |
| static void informAboutToDeleteBucket(const DiskLoc& b); | | static void informAboutToDeleteBucket(const DiskLoc& b); | |
| static void aboutToDelete(const DiskLoc& dl); | | static void aboutToDelete(const DiskLoc& dl); | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 9 change blocks. |
| 7 lines changed or deleted | | 58 lines changed or added | |
|
| concurrency.h | | concurrency.h | |
| | | | |
| skipping to change at line 62 | | skipping to change at line 62 | |
| tl = timeLocked; | | tl = timeLocked; | |
| } | | } | |
| }; | | }; | |
| | | | |
| #if BOOST_VERSION >= 103500 | | #if BOOST_VERSION >= 103500 | |
| //#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 | |
| | | our normal/common code path, we never even touch it. | |
| | | */ | |
| | | ThreadLocalValue<bool> _releasedEarly; | |
| public: | | public: | |
| /** | | /** | |
| * @return | | * @return | |
| * > 0 write lock | | * > 0 write lock | |
| * = 0 no lock | | * = 0 no lock | |
| * < 0 read lock | | * < 0 read lock | |
| */ | | */ | |
| int getState(){ return _state.get(); } | | int getState(){ return _state.get(); } | |
|
| void assertWriteLocked() { assert( _state.get() > 0 ); } | | void assertWriteLocked() { | |
| | | assert( getState() > 0 ); | |
| | | 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 , "internal error: locks are not upgradeable", s
== 0 ); | |
| _state.set(1); | | _state.set(1); | |
| _m.lock(); | | _m.lock(); | |
| | | | |
| skipping to change at line 92 | | skipping to change at line 101 | |
| _m.lock(); | | _m.lock(); | |
| _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; | |
| } | | } | |
|
| assert( s == 1 ); | | if( s != 1 ) { | |
| | | if( _releasedEarly.get() ) { | |
| | | _releasedEarly.set(false); | |
| | | return; | |
| | | } | |
| | | assert(false); // attempt to unlock when wasn't in a write | |
| | | lock | |
| | | } | |
| _state.set(0); | | _state.set(0); | |
| _minfo.leaving(); | | _minfo.leaving(); | |
| _m.unlock(); | | _m.unlock(); | |
| } | | } | |
|
| | | | |
| | | /* unlock (write lock), and when unlock() is called later, | |
| | | be smart then and don't unlock it again. | |
| | | */ | |
| | | void releaseEarly() { | |
| | | assert( getState() == 1 ); // must not be recursive | |
| | | assert( !_releasedEarly.get() ); | |
| | | _releasedEarly.set(true); | |
| | | 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 { | |
| | | | |
| skipping to change at line 138 | | skipping to change at line 164 | |
| _state.set(0); | | _state.set(0); | |
| _m.unlock_shared(); | | _m.unlock_shared(); | |
| } | | } | |
| MutexInfo& info() { return _minfo; } | | MutexInfo& info() { return _minfo; } | |
| }; | | }; | |
| #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; | |
| public: | | public: | |
| MongoMutex() { } | | MongoMutex() { } | |
| void lock() { | | void lock() { | |
| #if BOOST_VERSION >= 103500 | | #if BOOST_VERSION >= 103500 | |
| 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 unlock() { | | void releaseEarly() { | |
| | | assertWriteLocked(); // aso must not be recursive, although we | |
| | | don't verify that in the old boost version | |
| | | assert( !_releasedEarly.get() ); | |
| | | _releasedEarly.set(true); | |
| | | _unlock(); | |
| | | } | |
| | | | |
| | | void _unlock() { | |
| _minfo.leaving(); | | _minfo.leaving(); | |
| #if BOOST_VERSION >= 103500 | | #if BOOST_VERSION >= 103500 | |
| 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() { | |
| | | if( _releasedEarly.get() ) { | |
| | | _releasedEarly.set(false); | |
| | | return; | |
| | | } | |
| | | _unlock(); | |
| | | } | |
| | | | |
| void lock_shared() { lock(); } | | void lock_shared() { lock(); } | |
| 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() ); | |
| } | | } | |
| | | | |
End of changes. 8 change blocks. |
| 3 lines changed or deleted | | 46 lines changed or added | |
|
| database.h | | database.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 "cmdline.h" | | #include "cmdline.h" | |
| | | | |
|
| /* Database represents a database database | | | |
| Each database database has its own set of files -- dbname.ns, dbname.0, | | | |
| dbname.1, ... | | | |
| */ | | | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | /** | |
| | | * Database represents a database database | |
| | | * Each database database has its own set of files -- dbname.ns, dbname | |
| | | .0, dbname.1, ... | |
| | | * 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), | | { // check db name is valid | |
| namespaceIndex( path, name ) | | | |
| { | | | |
| { | | | |
| int L = strlen(nm); | | int 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; | |
| | | | |
| skipping to change at line 60 | | skipping to change at line 59 | |
| | | | |
| // If already exists, open. Otherwise behave as if empty until | | // If already exists, open. Otherwise behave as if empty until | |
| // there's a write, then open. | | // there's a write, then open. | |
| if ( ! newDb || cmdLine.defaultProfile ) { | | if ( ! newDb || cmdLine.defaultProfile ) { | |
| namespaceIndex.init(); | | namespaceIndex.init(); | |
| if( _openAllFiles ) | | if( _openAllFiles ) | |
| openAllFiles(); | | openAllFiles(); | |
| | | | |
| } | | } | |
| | | | |
|
| | | magic = 781231; | |
| } | | } | |
| | | | |
| ~Database() { | | ~Database() { | |
|
| | | magic = 0; | |
| btreeStore->closeFiles(name, path); | | btreeStore->closeFiles(name, path); | |
| int n = files.size(); | | int n = files.size(); | |
| for ( int i = 0; i < n; i++ ) | | for ( int i = 0; i < n; i++ ) | |
| delete files[i]; | | delete files[i]; | |
| } | | } | |
| | | | |
|
| | | /** | |
| | | * tries to make sure that this hasn't been deleted | |
| | | */ | |
| | | bool isOk(){ | |
| | | return magic == 781231; | |
| | | } | |
| | | | |
| | | bool isEmpty(){ | |
| | | return ! namespaceIndex.allocated(); | |
| | | } | |
| | | | |
| bool exists(int n) { | | bool exists(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) / ss.str(); | |
| return boost::filesystem::exists(fullName); | | return boost::filesystem::exists(fullName); | |
| } | | } | |
| | | | |
| 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 | |
| | | // until a write is requested | |
| | | if ( n > 1 && getFile( n - 1 )->getHeader()->isEmpty() ) { | |
| | | delete files[ n - 1 ]; | |
| | | files.pop_back(); | |
| | | } | |
| } | | } | |
| | | | |
| MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate
Only = false ) { | | MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate
Only = false ) { | |
| assert(this); | | assert(this); | |
| | | | |
| namespaceIndex.init(); | | namespaceIndex.init(); | |
| if ( n < 0 || n >= DiskLoc::MaxFiles ) { | | if ( n < 0 || n >= DiskLoc::MaxFiles ) { | |
| out() << "getFile(): n=" << n << endl; | | out() << "getFile(): n=" << n << endl; | |
| #if !defined(_RECSTORE) | | #if !defined(_RECSTORE) | |
| if( n >= RecCache::Base && n <= RecCache::Base+1000 ) | | if( n >= RecCache::Base && n <= RecCache::Base+1000 ) | |
| | | | |
| skipping to change at line 185 | | skipping to change at line 203 | |
| bool setProfilingLevel( int newLevel , string& errmsg ); | | bool setProfilingLevel( int newLevel , string& errmsg ); | |
| | | | |
| void finishInit(); | | void finishInit(); | |
| | | | |
| vector<MongoDataFile*> files; | | vector<MongoDataFile*> files; | |
| string name; // "alleyinsider" | | string name; // "alleyinsider" | |
| string path; | | string path; | |
| NamespaceIndex namespaceIndex; | | NamespaceIndex namespaceIndex; | |
| int profile; // 0=off. | | int profile; // 0=off. | |
| string profileName; // "alleyinsider.system.profile" | | string profileName; // "alleyinsider.system.profile" | |
|
| | | int magic; // used for making sure the object is still loaded in me
mory | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 8 change blocks. |
| 13 lines changed or deleted | | 31 lines changed or added | |
|
| db.h | | db.h | |
| | | | |
| skipping to change at line 44 | | skipping to change at line 44 | |
| Also note that the server has some basic checks to enforce this limi
t but those checks are not exhaustive | | Also note that the server has some basic checks to enforce this limi
t but those checks are not exhaustive | |
| for example need to check for size too big after | | for example need to check for size too big after | |
| update $push (append) operation | | update $push (append) operation | |
| various db.eval() type operations | | various db.eval() type operations | |
| | | | |
| Note also we sometimes do work with objects slightly larger - an obj
ect in the replication local.oplog | | Note also we sometimes do work with objects slightly larger - an obj
ect in the replication local.oplog | |
| could be slightly larger. | | could be slightly larger. | |
| */ | | */ | |
| const int MaxBSONObjectSize = 4 * 1024 * 1024; | | const int MaxBSONObjectSize = 4 * 1024 * 1024; | |
| | | | |
|
| // tempish...move to TLS or pass all the way down as a parm | | /** | |
| extern map<string,Database*> databases; | | * class to hold path + dbname -> Database | |
| extern bool master; | | * might be able to optimizer further | |
| | | */ | |
| | | class DatabaseHolder { | |
| | | public: | |
| | | DatabaseHolder() : _size(0){ | |
| | | } | |
| | | | |
|
| /* sometimes we deal with databases with the same name in different dir | | Database * get( const string& ns , const string& path ){ | |
| ectories - thus this */ | | dbMutex.assertAtLeastReadLocked(); | |
| inline string makeDbKeyStr( const char *ns, const string& path ) { | | map<string,Database*>& m = _paths[path]; | |
| char cl[256]; | | | |
| nsToClient(ns, cl); | | | |
| return string( cl ) + ":" + path; | | | |
| } | | | |
| | | | |
|
| inline void resetClient(const char *ns, const string& path=dbpath) { | | string db = _todb( ns ); | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| string key = makeDbKeyStr( ns, path ); | | map<string,Database*>::iterator it = m.find(db); | |
| map<string,Database*>::iterator it = databases.find(key); | | if ( it != m.end() ) | |
| if ( it != databases.end() ) { | | return it->second; | |
| cc().setns(ns, it->second); | | return 0; | |
| return; | | | |
| } | | } | |
|
| assert(false); | | | |
| } | | void put( const string& ns , const string& path , Database * db ){ | |
| | | dbMutex.assertWriteLocked(); | |
| | | map<string,Database*>& m = _paths[path]; | |
| | | Database*& d = m[_todb(ns)]; | |
| | | if ( ! d ) | |
| | | _size++; | |
| | | d = db; | |
| | | } | |
| | | | |
| | | void erase( const string& ns , const string& path ){ | |
| | | dbMutex.assertWriteLocked(); | |
| | | map<string,Database*>& m = _paths[path]; | |
| | | _size -= m.erase( _todb( ns ) ); | |
| | | } | |
| | | | |
| | | bool closeAll( const string& path , BSONObjBuilder& result ); | |
| | | | |
| | | int size(){ | |
| | | return _size; | |
| | | } | |
| | | | |
| | | /** | |
| | | * gets all unique db names, ignoring paths | |
| | | */ | |
| | | void getAllShortNames( set<string>& all ) const{ | |
| | | dbMutex.assertAtLeastReadLocked(); | |
| | | for ( map<string, map<string,Database*> >::const_iterator i=_pa | |
| | | ths.begin(); i!=_paths.end(); i++ ){ | |
| | | map<string,Database*> m = i->second; | |
| | | for( map<string,Database*>::const_iterator j=m.begin(); j!= | |
| | | m.end(); j++ ){ | |
| | | all.insert( j->first ); | |
| | | } | |
| | | } | |
| | | } | |
| | | | |
| | | private: | |
| | | | |
| | | string _todb( const string& ns ){ | |
| | | size_t i = ns.find( '.' ); | |
| | | if ( i == string::npos ) | |
| | | return ns; | |
| | | return ns.substr( 0 , i ); | |
| | | } | |
| | | | |
| | | map<string, map<string,Database*> > _paths; | |
| | | int _size; | |
| | | | |
| | | }; | |
| | | | |
| | | extern DatabaseHolder dbHolder; | |
| | | | |
| /* returns true if the database ("database") did not exist, and it was
created on this call | | /* 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 | | 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). | | name in different places (for example temp ones on repair). | |
| */ | | */ | |
|
| inline bool setClient(const char *ns, const string& path=dbpath, mongol
ock *lock = 0) { | | inline bool setClient(const char *ns, const string& path , mongolock *l
ock ) { | |
| if( logLevel > 5 ) | | if( logLevel > 5 ) | |
| log() << "setClient: " << ns << endl; | | log() << "setClient: " << ns << endl; | |
| | | | |
| dbMutex.assertAtLeastReadLocked(); | | dbMutex.assertAtLeastReadLocked(); | |
| | | | |
|
| cc().top.clientStart( ns ); | | Client& c = cc(); | |
| | | c.top.clientStart( ns ); | |
| | | | |
|
| string key = makeDbKeyStr( ns, path ); | | Database * db = dbHolder.get( ns , path ); | |
| map<string,Database*>::iterator it = databases.find(key); | | if ( db ){ | |
| if ( it != databases.end() ) { | | c.setns(ns, db ); | |
| cc().setns(ns, it->second); | | | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| if( lock ) | | if( lock ) | |
| lock->releaseAndWriteLock(); | | lock->releaseAndWriteLock(); | |
| | | | |
|
| // when master for replication, we advertise all the db's, and that | | | |
| // looks like a 'first operation'. so that breaks this log message' | | | |
| s | | | |
| // meaningfulness. instead of fixing (which would be better), we j | | | |
| ust | | | |
| // stop showing for now. | | | |
| // 2008-12-22 We now open every database on startup, so this log is | | | |
| // no longer helpful. Commenting. | | | |
| // if( !master ) | | | |
| // log() << "first operation for database " << key << endl; | | | |
| | | | |
| assertInWriteLock(); | | assertInWriteLock(); | |
| | | | |
| char cl[256]; | | char cl[256]; | |
|
| nsToClient(ns, cl); | | nsToDatabase(ns, cl); | |
| bool justCreated; | | bool justCreated; | |
| Database *newdb = new Database(cl, justCreated, path); | | Database *newdb = new Database(cl, justCreated, path); | |
|
| databases[key] = newdb; | | dbHolder.put(ns,path,newdb); | |
| cc().setns(ns, newdb); | | c.setns(ns, newdb); | |
| | | | |
| newdb->finishInit(); | | newdb->finishInit(); | |
| | | | |
| return justCreated; | | return justCreated; | |
| } | | } | |
| | | | |
|
| // shared functionality for removing references to a database from this pro | | // shared functionality for removing references to a database from this | |
| gram instance | | 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 ); | |
| | | | |
|
| /* remove database from the databases map */ | | | |
| inline void eraseDatabase( const char *ns, const string& path=dbpath ) | | | |
| { | | | |
| string key = makeDbKeyStr( ns, path ); | | | |
| databases.erase( key ); | | | |
| } | | | |
| | | | |
| inline bool clientIsEmpty() { | | | |
| return !cc().database()->namespaceIndex.allocated(); | | | |
| } | | | |
| | | | |
| struct dbtemprelease { | | struct dbtemprelease { | |
| string clientname; | | string clientname; | |
| string clientpath; | | string clientpath; | |
| int locktype; | | int locktype; | |
| dbtemprelease() { | | dbtemprelease() { | |
| Client& client = cc(); | | Client& client = cc(); | |
| Database *database = client.database(); | | Database *database = client.database(); | |
| if ( database ) { | | if ( database ) { | |
| clientname = database->name; | | clientname = database->name; | |
| clientpath = database->path; | | clientpath = database->path; | |
| | | | |
End of changes. 12 change blocks. |
| 53 lines changed or deleted | | 81 lines changed or added | |
|
| dbclient.h | | dbclient.h | |
| | | | |
| skipping to change at line 37 | | skipping to change at line 37 | |
| /** the query field 'options' can have these bits set: */ | | /** the query field 'options' can have these bits set: */ | |
| enum QueryOptions { | | enum QueryOptions { | |
| /** Tailable means cursor is not closed when the last data is retri
eved. rather, the cursor marks | | /** Tailable means cursor is not closed when the last data is retri
eved. rather, the cursor marks | |
| the final object's position. you can resume using the cursor la
ter, from where it was located, | | the final object's position. you can resume using the cursor la
ter, from where it was located, | |
| if more data were received. Set on dbQuery and dbGetMore. | | if more data were received. Set on dbQuery and dbGetMore. | |
| | | | |
| like any "latent cursor", the cursor may become invalid at some
point -- for example if that | | like any "latent cursor", the cursor may become invalid at some
point -- for example if that | |
| final object it references were deleted. Thus, you should be pr
epared to requery if you get back | | final object it references were deleted. Thus, you should be pr
epared to requery if you get back | |
| ResultFlag_CursorNotFound. | | ResultFlag_CursorNotFound. | |
| */ | | */ | |
|
| Option_CursorTailable = 1 << 1, | | QueryOption_CursorTailable = 1 << 1, | |
| | | | |
| /** allow query of replica slave. normally these return an error e
xcept for namespace "local". | | /** allow query of replica slave. normally these return an error e
xcept for namespace "local". | |
| */ | | */ | |
|
| Option_SlaveOk = 1 << 2, | | QueryOption_SlaveOk = 1 << 2, | |
| | | | |
|
| Option_OplogReplay = 1 << 3, | | // findingStart mode is used to find the first operation of interes | |
| | | t when | |
| | | // we are scanning through a repl log. For efficiency in the commo | |
| | | n case, | |
| | | // where the first operation of interest is closer to the tail than | |
| | | the head, | |
| | | // we start from the tail of the log and work backwards until we fi | |
| | | nd the | |
| | | // first operation of interest. Then we scan forward from that fir | |
| | | st operation, | |
| | | // actually returning results to the client. During the findingSta | |
| | | rt phase, | |
| | | // we release the db mutex occasionally to avoid blocking the db pr | |
| | | ocess for | |
| | | // an extended period of time. | |
| | | QueryOption_OplogReplay = 1 << 3, | |
| | | | |
| | | /** The server normally times out idle cursors after an inactivy pe | |
| | | riod to prevent excess memory use | |
| | | Set this option to prevent that. | |
| | | */ | |
| | | QueryOption_NoCursorTimeout = 1 << 4, | |
| | | | |
| | | /** Use with QueryOption_CursorTailable. If we are at the end of t | |
| | | he data, block for a while rather | |
| | | than returning no data. After a timeout period, we do return as | |
| | | normal. | |
| | | */ | |
| | | QueryOption_AwaitData = 1 << 5 | |
| | | | |
|
| /** if there is a cursor, ignore the normal cursor timeout behavior | | | |
| and never time it out | | | |
| */ | | | |
| Option_NoCursorTimeout = 1 << 4 | | | |
| }; | | }; | |
| | | | |
| enum UpdateOptions { | | enum UpdateOptions { | |
|
| Option_Upsert = 1 << 0, | | /** Upsert - that is, insert the item if no matching item is found. | |
| Option_Multi = 1 << 1 | | */ | |
| | | UpdateOption_Upsert = 1 << 0, | |
| | | | |
| | | /** Update multiple documents (if multiple documents match query ex | |
| | | pression). | |
| | | (Default is update a single document and stop.) */ | |
| | | UpdateOption_Multi = 1 << 1 | |
| }; | | }; | |
| | | | |
| class BSONObj; | | class BSONObj; | |
| | | | |
| /** Represents a Mongo query expression. Typically one uses the QUERY(
...) macro to construct a Query object. | | /** Represents a Mongo query expression. Typically one uses the QUERY(
...) macro to construct a Query object. | |
| Examples: | | Examples: | |
| QUERY( "age" << 33 << "school" << "UCLA" ).sort("name") | | QUERY( "age" << 33 << "school" << "UCLA" ).sort("name") | |
| QUERY( "age" << GT << 30 << LT << 50 ) | | QUERY( "age" << GT << 30 << LT << 50 ) | |
| */ | | */ | |
| class Query { | | class Query { | |
| | | | |
| skipping to change at line 184 | | skipping to change at line 203 | |
| virtual ~DBConnector() {} | | virtual ~DBConnector() {} | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k=true ) = 0; | | virtual bool call( Message &toSend, Message &response, bool assertO
k=true ) = 0; | |
| virtual void say( Message &toSend ) = 0; | | virtual void say( Message &toSend ) = 0; | |
| virtual void sayPiggyBack( Message &toSend ) = 0; | | virtual void sayPiggyBack( Message &toSend ) = 0; | |
| virtual void checkResponse( const string &data, int nReturned ) {} | | virtual void checkResponse( const string &data, int nReturned ) {} | |
| }; | | }; | |
| | | | |
| /** Queries return a cursor object */ | | /** Queries return a cursor object */ | |
| class DBClientCursor : boost::noncopyable { | | class DBClientCursor : boost::noncopyable { | |
| friend class DBClientBase; | | friend class DBClientBase; | |
|
| | | bool init(); | |
| public: | | public: | |
|
| /** if true, safe to call next() */ | | /** If true, safe to call next(). Requests more from server
if necessary. */ | |
| bool more(); | | bool more(); | |
| | | | |
| /** next | | /** next | |
| @return next object in the result cursor. | | @return next object in the result cursor. | |
| on an error at the remote server, you will get back: | | on an error at the remote server, you will get back: | |
| { $err: <string> } | | { $err: <string> } | |
| if you do not want to handle that yourself, call nextSafe(). | | if you do not want to handle that yourself, call nextSafe(). | |
| */ | | */ | |
| BSONObj next(); | | BSONObj next(); | |
| | | | |
| | | | |
| skipping to change at line 226 | | skipping to change at line 246 | |
| /** cursor no longer valid -- use with tailable cursors. | | /** cursor no longer valid -- use with tailable cursors. | |
| note you should only rely on this once more() returns false; | | note you should only rely on this once more() returns false; | |
| 'dead' may be preset yet some data still queued and locally | | 'dead' may be preset yet some data still queued and locally | |
| available from the dbclientcursor. | | available from the dbclientcursor. | |
| */ | | */ | |
| bool isDead() const { | | bool isDead() const { | |
| return cursorId == 0; | | return cursorId == 0; | |
| } | | } | |
| | | | |
| bool tailable() const { | | bool tailable() const { | |
|
| return (opts & Option_CursorTailable) != 0; | | return (opts & QueryOption_CursorTailable) != 0; | |
| } | | } | |
| | | | |
| bool hasResultFlag( int flag ){ | | bool hasResultFlag( int flag ){ | |
| return (resultFlags & flag) != 0; | | return (resultFlags & flag) != 0; | |
| } | | } | |
|
| private: | | | |
| bool init(); | | | |
| public: | | public: | |
| DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj
_query, int _nToReturn, | | DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj
_query, int _nToReturn, | |
| int _nToSkip, const BSONObj *_fieldsToReturn, int q
ueryOptions ) : | | int _nToSkip, const BSONObj *_fieldsToReturn, int q
ueryOptions ) : | |
| connector(_connector), | | connector(_connector), | |
| ns(_ns), | | ns(_ns), | |
| query(_query), | | query(_query), | |
| nToReturn(_nToReturn), | | nToReturn(_nToReturn), | |
| nToSkip(_nToSkip), | | nToSkip(_nToSkip), | |
| fieldsToReturn(_fieldsToReturn), | | fieldsToReturn(_fieldsToReturn), | |
| opts(queryOptions), | | opts(queryOptions), | |
| m(new Message()), | | m(new Message()), | |
| cursorId(), | | cursorId(), | |
| nReturned(), | | nReturned(), | |
| pos(), | | pos(), | |
| data(), | | data(), | |
|
| ownCursor_( true ) { | | _ownCursor( true ) { | |
| } | | } | |
| | | | |
| DBClientCursor( DBConnector *_connector, const string &_ns, long lo
ng _cursorId, int _nToReturn, int options ) : | | DBClientCursor( DBConnector *_connector, const string &_ns, long lo
ng _cursorId, int _nToReturn, int options ) : | |
| connector(_connector), | | connector(_connector), | |
| ns(_ns), | | ns(_ns), | |
| nToReturn( _nToReturn ), | | nToReturn( _nToReturn ), | |
| opts( options ), | | opts( options ), | |
| m(new Message()), | | m(new Message()), | |
| cursorId( _cursorId ), | | cursorId( _cursorId ), | |
| nReturned(), | | nReturned(), | |
| pos(), | | pos(), | |
| data(), | | data(), | |
|
| ownCursor_( true ) { | | _ownCursor( true ) { | |
| } | | } | |
| | | | |
| virtual ~DBClientCursor(); | | virtual ~DBClientCursor(); | |
| | | | |
| long long getCursorId() const { return cursorId; } | | long long getCursorId() const { return cursorId; } | |
|
| void decouple() { ownCursor_ = false; } | | | |
| | | /** by default we "own" the cursor and will send the server a KillC | |
| | | ursor | |
| | | message when ~DBClientCursor() is called. This function overrid | |
| | | es that. | |
| | | */ | |
| | | void decouple() { _ownCursor = false; } | |
| | | | |
| private: | | private: | |
| DBConnector *connector; | | DBConnector *connector; | |
| string ns; | | string ns; | |
| BSONObj query; | | BSONObj query; | |
| int nToReturn; | | int nToReturn; | |
| int nToSkip; | | int nToSkip; | |
| const BSONObj *fieldsToReturn; | | const BSONObj *fieldsToReturn; | |
| int opts; | | int opts; | |
| auto_ptr<Message> m; | | auto_ptr<Message> m; | |
| | | | |
| int resultFlags; | | int resultFlags; | |
| long long cursorId; | | long long cursorId; | |
| int nReturned; | | int nReturned; | |
| int pos; | | int pos; | |
| const char *data; | | const char *data; | |
| void dataReceived(); | | void dataReceived(); | |
| void requestMore(); | | void requestMore(); | |
|
| bool ownCursor_; | | bool _ownCursor; // see decouple() | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| The interface that any db connection should implement | | The interface that any db connection should implement | |
| */ | | */ | |
| class DBClientInterface : boost::noncopyable { | | class DBClientInterface : boost::noncopyable { | |
| public: | | public: | |
| virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn = 0, int nToSkip = 0, | | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn = 0, int nToSkip = 0, | |
| const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0) = 0; | | const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0) = 0; | |
| | | | |
| | | | |
End of changes. 13 change blocks. |
| 17 lines changed or deleted | | 52 lines changed or added | |
|
| dbhelpers.h | | dbhelpers.h | |
| | | | |
| skipping to change at line 76 | | skipping to change at line 76 | |
| /* fetch a single object from collection ns that matches query. | | /* fetch a single object from collection ns that matches query. | |
| set your db SavedContext first. | | set your db SavedContext first. | |
| | | | |
| @param requireIndex if true, complain if no index for the query.
a way to guard against | | @param requireIndex if true, complain if no index for the query.
a way to guard against | |
| writing a slow query. | | writing a slow query. | |
| | | | |
| @return true if object found | | @return true if object found | |
| */ | | */ | |
| static bool findOne(const char *ns, BSONObj query, BSONObj& result,
bool requireIndex = false); | | static bool findOne(const char *ns, BSONObj query, BSONObj& result,
bool requireIndex = false); | |
| | | | |
|
| static bool findById(const char *ns, BSONObj query, BSONObj& result | | /** | |
| ); | | * @param foundIndex if passed in will be set to 1 if ns and index | |
| | | found | |
| | | * @return true if object found | |
| | | */ | |
| | | static bool findById(Client&, const char *ns, BSONObj query, BSONOb | |
| | | j& result , | |
| | | bool * nsFound = 0 , bool * indexFound = 0 ); | |
| | | | |
| static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer
y = BSONObj() , bool requireIndex = false ); | | static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer
y = BSONObj() , bool requireIndex = false ); | |
| | | | |
| /* Get/put the first object from a collection. Generally only usef
ul if the collection | | /* Get/put the first object from a collection. Generally only usef
ul if the collection | |
| only ever has a single object -- which is a "singleton collectio
n". | | only ever has a single object -- which is a "singleton collectio
n". | |
| | | | |
| You do not need to set the database before calling. | | You do not need to set the database before calling. | |
| | | | |
| Returns: true if object exists. | | Returns: true if object exists. | |
| */ | | */ | |
| | | | |
| skipping to change at line 99 | | skipping to change at line 104 | |
| | | | |
| /* Remove all objects from a collection. | | /* Remove all objects from a collection. | |
| You do not need to set the database before calling. | | You do not need to set the database before calling. | |
| */ | | */ | |
| static void emptyCollection(const char *ns); | | static void emptyCollection(const char *ns); | |
| | | | |
| }; | | }; | |
| | | | |
| class Database; | | class Database; | |
| | | | |
|
| /* 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 if fi | | | |
| xed up. | | | |
| */ | | | |
| class DBContext { | | | |
| Database *olddb; | | | |
| string oldns; | | | |
| public: | | | |
| DBContext(const char *ns) { | | | |
| olddb = cc().database(); | | | |
| oldns = cc().ns(); | | | |
| setClient(ns); | | | |
| } | | | |
| DBContext(string ns) { | | | |
| olddb = cc().database(); | | | |
| oldns = cc().ns(); | | | |
| setClient(ns.c_str()); | | | |
| } | | | |
| | | | |
| /* this version saves the context but doesn't yet set the new one: | | | |
| */ | | | |
| DBContext() { | | | |
| olddb = cc().database(); | | | |
| oldns = cc().ns(); } | | | |
| | | | |
| ~DBContext() { | | | |
| cc().setns(oldns.c_str(), olddb); | | | |
| } | | | |
| }; | | | |
| | | | |
| // manage a set using collection backed storage | | // manage a set using collection backed storage | |
| class DbSet { | | class DbSet { | |
| public: | | public: | |
| DbSet( const string &name = "", const BSONObj &key = BSONObj() ) : | | DbSet( const string &name = "", const BSONObj &key = BSONObj() ) : | |
| name_( name ), | | name_( name ), | |
| key_( key.getOwned() ) { | | key_( key.getOwned() ) { | |
| } | | } | |
| ~DbSet(); | | ~DbSet(); | |
| void reset( const string &name = "", const BSONObj &key = BSONObj()
); | | void reset( const string &name = "", const BSONObj &key = BSONObj()
); | |
| bool get( const BSONObj &obj ) const; | | bool get( const BSONObj &obj ) const; | |
| | | | |
End of changes. 2 change blocks. |
| 33 lines changed or deleted | | 8 lines changed or added | |
|
| dbmessage.h | | dbmessage.h | |
| | | | |
| skipping to change at line 41 | | skipping to change at line 41 | |
| int startingFrom; | | int startingFrom; | |
| int nReturned; | | int nReturned; | |
| list of marshalled JSObjects; | | list of marshalled JSObjects; | |
| */ | | */ | |
| | | | |
| extern bool objcheck; | | extern bool objcheck; | |
| | | | |
| #pragma pack(1) | | #pragma pack(1) | |
| struct QueryResult : public MsgData { | | struct QueryResult : public MsgData { | |
| enum ResultFlagType { | | enum ResultFlagType { | |
|
| ResultFlag_CursorNotFound = 1, /* returned, with zero results | | /* returned, with zero results, when getMore is called but the | |
| , when getMore is called but the cursor id is not valid at the server. */ | | cursor id | |
| ResultFlag_ErrSet = 2, /* { $err : ... } is being ret | | is not valid at the server. */ | |
| urned */ | | ResultFlag_CursorNotFound = 1, | |
| ResultFlag_ShardConfigStale = 4 /* have to update config from | | | |
| the server, usually $err is also set */ | | /* { $err : ... } is being returned */ | |
| | | ResultFlag_ErrSet = 2, | |
| | | | |
| | | /* Have to update config from the server, usually $err is also | |
| | | set */ | |
| | | ResultFlag_ShardConfigStale = 4, | |
| | | | |
| | | /* for backward compatability: this let's us know the server su | |
| | | pports | |
| | | the QueryOption_AwaitData option. if it doesn't, a repl slav | |
| | | e client should sleep | |
| | | a little between getMore's. | |
| | | */ | |
| | | ResultFlag_AwaitCapable = 8 | |
| }; | | }; | |
| | | | |
| long long cursorId; | | long long cursorId; | |
| int startingFrom; | | int startingFrom; | |
| int nReturned; | | int nReturned; | |
| const char *data() { | | const char *data() { | |
| return (char *) (((int *)&nReturned)+1); | | return (char *) (((int *)&nReturned)+1); | |
| } | | } | |
|
| int& resultFlags() { | | int resultFlags() { | |
| | | return dataAsInt(); | |
| | | } | |
| | | int& _resultFlags() { | |
| return dataAsInt(); | | return dataAsInt(); | |
| } | | } | |
|
| | | void setResultFlagsToOk() { | |
| | | _resultFlags() = 0; // ResultFlag_AwaitCapable | |
| | | } | |
| }; | | }; | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| /* For the database/server protocol, these objects and functions encaps
ulate | | /* For the database/server protocol, these objects and functions encaps
ulate | |
| the various messages transmitted over the connection. | | the various messages transmitted over the connection. | |
| */ | | */ | |
| | | | |
| class DbMessage { | | class DbMessage { | |
| public: | | public: | |
| DbMessage(const Message& _m) : m(_m) { | | DbMessage(const Message& _m) : m(_m) { | |
| | | | |
| skipping to change at line 198 | | skipping to change at line 216 | |
| inline void replyToQuery(int queryResultFlags, | | inline void replyToQuery(int queryResultFlags, | |
| AbstractMessagingPort* p, Message& requestMsg, | | AbstractMessagingPort* p, Message& requestMsg, | |
| void *data, int size, | | void *data, int size, | |
| int nReturned, int startingFrom = 0, | | int nReturned, int startingFrom = 0, | |
| long long cursorId = 0 | | long long cursorId = 0 | |
| ) { | | ) { | |
| BufBuilder b(32768); | | BufBuilder b(32768); | |
| b.skip(sizeof(QueryResult)); | | b.skip(sizeof(QueryResult)); | |
| b.append(data, size); | | b.append(data, size); | |
| QueryResult *qr = (QueryResult *) b.buf(); | | QueryResult *qr = (QueryResult *) b.buf(); | |
|
| qr->resultFlags() = queryResultFlags; | | qr->_resultFlags() = queryResultFlags; | |
| qr->len = b.len(); | | qr->len = b.len(); | |
| qr->setOperation(opReply); | | qr->setOperation(opReply); | |
| qr->cursorId = cursorId; | | qr->cursorId = cursorId; | |
| qr->startingFrom = startingFrom; | | qr->startingFrom = startingFrom; | |
| qr->nReturned = nReturned; | | qr->nReturned = nReturned; | |
| b.decouple(); | | b.decouple(); | |
| Message *resp = new Message(); | | Message *resp = new Message(); | |
| resp->setData(qr, true); // transport will free | | resp->setData(qr, true); // transport will free | |
| p->reply(requestMsg, *resp, requestMsg.data->id); | | p->reply(requestMsg, *resp, requestMsg.data->id); | |
| } | | } | |
| | | | |
| skipping to change at line 235 | | skipping to change at line 253 | |
| } | | } | |
| | | | |
| /* helper to do a reply using a DbResponse object */ | | /* helper to do a reply using a DbResponse object */ | |
| inline void replyToQuery(int queryResultFlags, Message &m, DbResponse &
dbresponse, BSONObj obj) { | | inline void replyToQuery(int queryResultFlags, Message &m, DbResponse &
dbresponse, BSONObj obj) { | |
| BufBuilder b; | | BufBuilder b; | |
| b.skip(sizeof(QueryResult)); | | b.skip(sizeof(QueryResult)); | |
| b.append((void*) obj.objdata(), obj.objsize()); | | b.append((void*) obj.objdata(), obj.objsize()); | |
| QueryResult* msgdata = (QueryResult *) b.buf(); | | QueryResult* msgdata = (QueryResult *) b.buf(); | |
| b.decouple(); | | b.decouple(); | |
| QueryResult *qr = msgdata; | | QueryResult *qr = msgdata; | |
|
| qr->resultFlags() = queryResultFlags; | | qr->_resultFlags() = queryResultFlags; | |
| qr->len = b.len(); | | qr->len = b.len(); | |
| qr->setOperation(opReply); | | qr->setOperation(opReply); | |
| qr->cursorId = 0; | | qr->cursorId = 0; | |
| qr->startingFrom = 0; | | qr->startingFrom = 0; | |
| qr->nReturned = 1; | | qr->nReturned = 1; | |
| Message *resp = new Message(); | | Message *resp = new Message(); | |
| resp->setData(msgdata, true); // transport will free | | resp->setData(msgdata, true); // transport will free | |
| dbresponse.response = resp; | | dbresponse.response = resp; | |
| dbresponse.responseTo = m.data->id; | | dbresponse.responseTo = m.data->id; | |
| } | | } | |
| | | | |
End of changes. 5 change blocks. |
| 9 lines changed or deleted | | 28 lines changed or added | |
|
| file_allocator.h | | file_allocator.h | |
| | | | |
| skipping to change at line 58 | | skipping to change at line 58 | |
| } | | } | |
| // May be called if file exists. If file exists, or its allocation
has | | // May be called if file exists. If file exists, or its allocation
has | |
| // been requested, size is updated to match existing file size. | | // been requested, size is updated to match existing file size. | |
| void requestAllocation( const string &name, long &size ) { | | void requestAllocation( const string &name, long &size ) { | |
| /* Some of the system calls in the file allocator don't work in
win, | | /* Some of the system calls in the file allocator don't work in
win, | |
| so no win support - 32 or 64 bit. Plus we don't seem to nee
d preallocation | | so no win support - 32 or 64 bit. Plus we don't seem to nee
d preallocation | |
| on windows anyway as we don't have to pre-zero the file ther
e. | | on windows anyway as we don't have to pre-zero the file ther
e. | |
| */ | | */ | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| boostlock lk( pendingMutex_ ); | | boostlock lk( pendingMutex_ ); | |
|
| | | if ( failed_ ) | |
| | | return; | |
| long oldSize = prevSize( name ); | | long oldSize = prevSize( name ); | |
| if ( oldSize != -1 ) { | | if ( oldSize != -1 ) { | |
| size = oldSize; | | size = oldSize; | |
| return; | | return; | |
| } | | } | |
| pending_.push_back( name ); | | pending_.push_back( name ); | |
| pendingSize_[ name ] = size; | | pendingSize_[ name ] = size; | |
| pendingUpdated_.notify_all(); | | pendingUpdated_.notify_all(); | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| skipping to change at line 79 | | skipping to change at line 81 | |
| // updated to match existing file size. | | // updated to match existing file size. | |
| void allocateAsap( const string &name, long &size ) { | | void allocateAsap( const string &name, long &size ) { | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| boostlock lk( pendingMutex_ ); | | boostlock lk( pendingMutex_ ); | |
| long oldSize = prevSize( name ); | | long oldSize = prevSize( name ); | |
| if ( oldSize != -1 ) { | | if ( oldSize != -1 ) { | |
| size = oldSize; | | size = oldSize; | |
| if ( !inProgress( name ) ) | | if ( !inProgress( name ) ) | |
| return; | | return; | |
| } | | } | |
|
| | | checkFailure(); | |
| pendingSize_[ name ] = size; | | pendingSize_[ name ] = size; | |
| if ( pending_.size() == 0 ) | | if ( pending_.size() == 0 ) | |
| pending_.push_back( name ); | | pending_.push_back( name ); | |
| else if ( pending_.front() != name ) { | | else if ( pending_.front() != name ) { | |
| pending_.remove( name ); | | pending_.remove( name ); | |
| list< string >::iterator i = pending_.begin(); | | list< string >::iterator i = pending_.begin(); | |
| ++i; | | ++i; | |
| pending_.insert( i, name ); | | pending_.insert( i, name ); | |
| } | | } | |
| pendingUpdated_.notify_all(); | | pendingUpdated_.notify_all(); | |
|
| while( inProgress( name ) ) | | while( inProgress( name ) ) { | |
| | | checkFailure(); | |
| pendingUpdated_.wait( lk ); | | pendingUpdated_.wait( lk ); | |
|
| | | } | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| void waitUntilFinished() const { | | void waitUntilFinished() const { | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| if ( failed_ ) | | if ( failed_ ) | |
| return; | | return; | |
| boostlock lk( pendingMutex_ ); | | boostlock lk( pendingMutex_ ); | |
| while( pending_.size() != 0 ) | | while( pending_.size() != 0 ) | |
| pendingUpdated_.wait( lk ); | | pendingUpdated_.wait( lk ); | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| private: | | private: | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
|
| | | void checkFailure() { | |
| | | massert( 12520, "file allocation failure", !failed_ ); | |
| | | } | |
| | | | |
| // caller must hold pendingMutex_ lock. Returns size if allocated
or | | // caller must hold pendingMutex_ lock. Returns size if allocated
or | |
| // allocation requested, -1 otherwise. | | // allocation requested, -1 otherwise. | |
| long prevSize( const string &name ) const { | | long prevSize( const string &name ) const { | |
| if ( pendingSize_.count( name ) > 0 ) | | if ( pendingSize_.count( name ) > 0 ) | |
| return pendingSize_[ name ]; | | return pendingSize_[ name ]; | |
| if ( boost::filesystem::exists( name ) ) | | if ( boost::filesystem::exists( name ) ) | |
| return boost::filesystem::file_size( name ); | | return boost::filesystem::file_size( name ); | |
| return -1; | | return -1; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 199 | | skipping to change at line 208 | |
| } | | } | |
| 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 ) ); | |
| } catch ( ... ) { | | } catch ( ... ) { | |
| } | | } | |
|
| | | boostlock lk( a_.pendingMutex_ ); | |
| a_.failed_ = true; | | a_.failed_ = true; | |
|
| dbexit( EXIT_FS ); | | // not erasing from pending | |
| | | a_.pendingUpdated_.notify_all(); | |
| | | return; // no more allocation | |
| } | | } | |
| | | | |
| { | | { | |
| boostlock lk( a_.pendingMutex_ ); | | boostlock lk( a_.pendingMutex_ ); | |
| a_.pendingSize_.erase( name ); | | a_.pendingSize_.erase( name ); | |
| a_.pending_.pop_front(); | | a_.pending_.pop_front(); | |
| a_.pendingUpdated_.notify_all(); | | a_.pendingUpdated_.notify_all(); | |
| } | | } | |
| } | | } | |
| } | | } | |
| | | | |
End of changes. 7 change blocks. |
| 2 lines changed or deleted | | 14 lines changed or added | |
|
| goodies.h | | goodies.h | |
| | | | |
| skipping to change at line 187 | | skipping to change at line 187 | |
| ctime_r(&t, buf); | | ctime_r(&t, buf); | |
| #endif | | #endif | |
| buf[24] = 0; // don't want the \n | | buf[24] = 0; // don't want the \n | |
| } | | } | |
| | | | |
| #define asctime _asctime_not_threadsafe_ | | #define asctime _asctime_not_threadsafe_ | |
| #define gmtime _gmtime_not_threadsafe_ | | #define gmtime _gmtime_not_threadsafe_ | |
| #define localtime _localtime_not_threadsafe_ | | #define localtime _localtime_not_threadsafe_ | |
| #define ctime _ctime_is_not_threadsafe_ | | #define ctime _ctime_is_not_threadsafe_ | |
| | | | |
|
| | | #if defined(_WIN32) || defined(__sunos__) | |
| inline void sleepsecs(int s) { | | inline void sleepsecs(int s) { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| xt.sec += s; | | xt.sec += s; | |
| boost::thread::sleep(xt); | | boost::thread::sleep(xt); | |
| } | | } | |
| inline void sleepmillis(int s) { | | inline void sleepmillis(int s) { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| xt.sec += ( s / 1000 ); | | xt.sec += ( s / 1000 ); | |
| | | | |
| skipping to change at line 215 | | skipping to change at line 216 | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| xt.sec += ( s / 1000000 ); | | xt.sec += ( s / 1000000 ); | |
| xt.nsec += ( s % 1000000 ) * 1000; | | xt.nsec += ( s % 1000000 ) * 1000; | |
| if ( xt.nsec >= 1000000000 ) { | | if ( xt.nsec >= 1000000000 ) { | |
| xt.nsec -= 1000000000; | | xt.nsec -= 1000000000; | |
| xt.sec++; | | xt.sec++; | |
| } | | } | |
| boost::thread::sleep(xt); | | boost::thread::sleep(xt); | |
| } | | } | |
|
| | | #else | |
| | | inline void sleepsecs(int s) { | |
| | | struct timespec t; | |
| | | t.tv_sec = s; | |
| | | t.tv_nsec = 0; | |
| | | if ( nanosleep( &t , 0 ) ){ | |
| | | cout << "nanosleep failed" << endl; | |
| | | } | |
| | | } | |
| | | inline void sleepmicros(int s) { | |
| | | struct timespec t; | |
| | | t.tv_sec = (int)(s / 1000000); | |
| | | t.tv_nsec = s % 1000000; | |
| | | if ( nanosleep( &t , 0 ) ){ | |
| | | cout << "nanosleep failed" << endl; | |
| | | } | |
| | | } | |
| | | inline void sleepmillis(int s) { | |
| | | sleepmicros( s * 1000 ); | |
| | | } | |
| | | #endif | |
| | | | |
| // note this wraps | | // note this wraps | |
| inline int tdiff(unsigned told, unsigned tnew) { | | inline int tdiff(unsigned told, unsigned tnew) { | |
| return WrappingInt::diff(tnew, told); | | return WrappingInt::diff(tnew, told); | |
| } | | } | |
| inline unsigned curTimeMillis() { | | inline unsigned curTimeMillis() { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| unsigned t = xt.nsec / 1000000; | | unsigned t = xt.nsec / 1000000; | |
| return (xt.sec & 0xfffff) * 1000 + t; | | return (xt.sec & 0xfffff) * 1000 + t; | |
| } | | } | |
| | | | |
End of changes. 2 change blocks. |
| 0 lines changed or deleted | | 23 lines changed or added | |
|
| jsobj.h | | jsobj.h | |
| | | | |
| skipping to change at line 522 | | skipping to change at line 522 | |
| char c = (value() + 4)[0]; | | 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(); | |
| } | | } | |
| | | | |
|
| /** returns a string that when used as a matcher, would match a sup | | | |
| er set of regex() | | | |
| returns "" for complex regular expressions | | | |
| used to optimize queries in some simple regex cases | | | |
| that start with '^' | | | |
| */ | | | |
| string simpleRegex() const; | | | |
| | | | |
| /** Retrieve the regex flags (options) for a Regex element */ | | /** Retrieve the regex flags (options) for a Regex element */ | |
| const char *regexFlags() const { | | const char *regexFlags() const { | |
| const char *p = regex(); | | const char *p = regex(); | |
| return p + strlen(p) + 1; | | return p + strlen(p) + 1; | |
| } | | } | |
| | | | |
| /** like operator== but doesn't check the fieldname, | | /** like operator== but doesn't check the fieldname, | |
| just the value. | | just the value. | |
| */ | | */ | |
| bool valuesEqual(const BSONElement& r) const { | | bool valuesEqual(const BSONElement& r) const { | |
| | | | |
| skipping to change at line 860 | | skipping to change at line 854 | |
| const char *objdata() const { | | const char *objdata() const { | |
| return _objdata; | | return _objdata; | |
| } | | } | |
| /** @return total size of the BSON object in bytes */ | | /** @return total size of the BSON object in bytes */ | |
| int objsize() const { | | int objsize() const { | |
| return *(reinterpret_cast<const int*>(objdata())); | | return *(reinterpret_cast<const int*>(objdata())); | |
| } | | } | |
| | | | |
| bool isValid(); | | bool isValid(); | |
| | | | |
|
| | | /** @return if the user is a valid user doc | |
| | | criter: isValid() no . or $ field names | |
| | | */ | |
| | | bool okForStorage() const; | |
| | | | |
| /** @return true if object is empty -- i.e., {} */ | | /** @return true if object is empty -- i.e., {} */ | |
| bool isEmpty() const { | | bool isEmpty() const { | |
| return objsize() <= 5; | | return objsize() <= 5; | |
| } | | } | |
| | | | |
| void dump() const { | | void dump() const { | |
| out() << hex; | | out() << hex; | |
| const char *p = objdata(); | | const char *p = objdata(); | |
| for ( int i = 0; i < objsize(); i++ ) { | | for ( int i = 0; i < objsize(); i++ ) { | |
| out() << i << '\t' << ( 0xff & ( (unsigned) *p ) ); | | out() << i << '\t' << ( 0xff & ( (unsigned) *p ) ); | |
| | | | |
| skipping to change at line 978 | | skipping to change at line 977 | |
| GT = 0x4, | | GT = 0x4, | |
| opIN = 0x8, // { x : { $in : [1,2,3] } } | | opIN = 0x8, // { x : { $in : [1,2,3] } } | |
| NE = 0x9, | | NE = 0x9, | |
| opSIZE = 0x0A, | | opSIZE = 0x0A, | |
| opALL = 0x0B, | | opALL = 0x0B, | |
| NIN = 0x0C, | | NIN = 0x0C, | |
| opEXISTS = 0x0D, | | opEXISTS = 0x0D, | |
| opMOD = 0x0E, | | opMOD = 0x0E, | |
| opTYPE = 0x0F, | | opTYPE = 0x0F, | |
| opREGEX = 0x10, | | opREGEX = 0x10, | |
|
| opOPTIONS = 0x11 | | opOPTIONS = 0x11, | |
| | | opELEM_MATCH = 0x12 | |
| }; | | }; | |
| }; | | }; | |
| ostream& operator<<( ostream &s, const BSONObj &o ); | | ostream& operator<<( ostream &s, const BSONObj &o ); | |
| ostream& operator<<( ostream &s, const BSONElement &e ); | | ostream& operator<<( ostream &s, const BSONElement &e ); | |
| | | | |
| struct BSONArray: BSONObj { | | struct BSONArray: BSONObj { | |
| // Don't add anything other than forwarding constructors!!! | | // Don't add anything other than forwarding constructors!!! | |
| BSONArray(): BSONObj() {} | | BSONArray(): BSONObj() {} | |
| explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | | explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | |
| }; | | }; | |
| | | | |
| skipping to change at line 1339 | | skipping to change at line 1339 | |
| b.append( fieldName ); | | b.append( fieldName ); | |
| b.append( (unsigned long long) 0 ); | | b.append( (unsigned long long) 0 ); | |
| } | | } | |
| | | | |
| void appendTimestamp( const char *fieldName , unsigned long long va
l ) { | | void appendTimestamp( const char *fieldName , unsigned long long va
l ) { | |
| b.append( (char) Timestamp ); | | b.append( (char) Timestamp ); | |
| b.append( fieldName ); | | b.append( fieldName ); | |
| b.append( val ); | | b.append( val ); | |
| } | | } | |
| | | | |
|
| | | /** | |
| | | * @param time - in millis (but stored in seconds) | |
| | | */ | |
| void appendTimestamp( const char *fieldName , unsigned long long ti
me , unsigned int inc ){ | | void appendTimestamp( const char *fieldName , unsigned long long ti
me , unsigned int inc ){ | |
| OpTime t( (unsigned) (time / 1000) , inc ); | | OpTime t( (unsigned) (time / 1000) , inc ); | |
| appendTimestamp( fieldName , t.asDate() ); | | appendTimestamp( fieldName , t.asDate() ); | |
| } | | } | |
| | | | |
| /* Deprecated (but supported) */ | | /* Deprecated (but supported) */ | |
| void appendDBRef( const char *fieldName, const char *ns, const OID
&oid ) { | | void appendDBRef( const char *fieldName, const char *ns, const OID
&oid ) { | |
| b.append( (char) DBRef ); | | b.append( (char) DBRef ); | |
| b.append( fieldName ); | | b.append( fieldName ); | |
| b.append( (int) strlen( ns ) + 1 ); | | b.append( (int) strlen( ns ) + 1 ); | |
| | | | |
End of changes. 4 change blocks. |
| 9 lines changed or deleted | | 10 lines changed or added | |
|
| lasterror.h | | lasterror.h | |
| | | | |
| skipping to change at line 37 | | skipping to change at line 37 | |
| | | | |
| struct LastError { | | struct LastError { | |
| int code; | | int code; | |
| string msg; | | string msg; | |
| enum UpdatedExistingType { NotUpdate, True, False } updatedExisting
; | | enum UpdatedExistingType { NotUpdate, True, False } updatedExisting
; | |
| /* todo: nObjects should be 64 bit */ | | /* todo: nObjects should be 64 bit */ | |
| int nObjects; | | int nObjects; | |
| int nPrev; | | int nPrev; | |
| bool valid; | | bool valid; | |
| bool overridenById; | | bool overridenById; | |
|
| | | bool disabled; | |
| void raiseError(int _code , const char *_msg) { | | void raiseError(int _code , const char *_msg) { | |
| reset( true ); | | reset( true ); | |
| code = _code; | | code = _code; | |
| msg = _msg; | | msg = _msg; | |
| } | | } | |
| void recordUpdate( bool _updatedExisting, int nChanged ) { | | void recordUpdate( bool _updatedExisting, int nChanged ) { | |
| reset( true ); | | reset( true ); | |
| nObjects = nChanged; | | nObjects = nChanged; | |
| updatedExisting = _updatedExisting ? True : False; | | updatedExisting = _updatedExisting ? True : False; | |
| } | | } | |
| | | | |
| skipping to change at line 62 | | skipping to change at line 63 | |
| overridenById = false; | | overridenById = false; | |
| reset(); | | reset(); | |
| } | | } | |
| void reset( bool _valid = false ) { | | void reset( bool _valid = false ) { | |
| code = 0; | | code = 0; | |
| msg.clear(); | | msg.clear(); | |
| updatedExisting = NotUpdate; | | updatedExisting = NotUpdate; | |
| nObjects = 0; | | nObjects = 0; | |
| nPrev = 1; | | nPrev = 1; | |
| valid = _valid; | | valid = _valid; | |
|
| | | disabled = false; | |
| } | | } | |
| void appendSelf( BSONObjBuilder &b ); | | void appendSelf( BSONObjBuilder &b ); | |
| static LastError noError; | | static LastError noError; | |
| }; | | }; | |
| | | | |
| extern class LastErrorHolder { | | extern class LastErrorHolder { | |
| public: | | public: | |
|
| LastErrorHolder() : _id( 0 ){} | | LastErrorHolder() : _id( 0 ) {} | |
| | | | |
| LastError * get( bool create = false ); | | LastError * get( bool create = false ); | |
| | | | |
|
| | | LastError * _get( bool create = false ); // may return a disabled L | |
| | | astError | |
| | | | |
| void reset( LastError * le ); | | void reset( LastError * le ); | |
| | | | |
| /** | | /** | |
| * id of 0 means should use thread local management | | * id of 0 means should use thread local management | |
| */ | | */ | |
| void setID( int id ); | | void setID( int id ); | |
| int getID(); | | int getID(); | |
| | | | |
| void remove( int id ); | | void remove( int id ); | |
| void release(); | | void release(); | |
| | | | |
| /** when db receives a message/request, call this */ | | /** when db receives a message/request, call this */ | |
| void startRequest( Message& m , LastError * connectionOwned ); | | void startRequest( Message& m , LastError * connectionOwned ); | |
| void startRequest( Message& m ); | | void startRequest( Message& m ); | |
|
| | | | |
| | | // used to disable lastError reporting while processing a killCurso | |
| | | rs message | |
| | | // disable causes get() to return 0. | |
| | | LastError *disableForCommand(); // only call once per command invoc | |
| | | ation! | |
| private: | | private: | |
| ThreadLocalValue<int> _id; | | ThreadLocalValue<int> _id; | |
| boost::thread_specific_ptr<LastError> _tl; | | boost::thread_specific_ptr<LastError> _tl; | |
| | | | |
| struct Status { | | struct Status { | |
| time_t time; | | time_t time; | |
| LastError *lerr; | | LastError *lerr; | |
| }; | | }; | |
| static boost::mutex _idsmutex; | | static boost::mutex _idsmutex; | |
| map<int,Status> _ids; | | map<int,Status> _ids; | |
| } lastError; | | } lastError; | |
| | | | |
| inline void raiseError(int code , const char *msg) { | | inline void raiseError(int code , const char *msg) { | |
| LastError *le = lastError.get(); | | LastError *le = lastError.get(); | |
| if ( le == 0 ) { | | if ( le == 0 ) { | |
| DEV log() << "warning: lastError==0 can't report:" << msg << '\
n'; | | DEV log() << "warning: lastError==0 can't report:" << msg << '\
n'; | |
|
| return; | | } else if ( le->disabled ) { | |
| | | log() << "lastError disabled, can't report: " << msg << endl; | |
| | | } else { | |
| | | le->raiseError(code, msg); | |
| } | | } | |
|
| le->raiseError(code, msg); | | | |
| } | | } | |
| | | | |
| inline void recordUpdate( bool updatedExisting, int nChanged ) { | | inline void recordUpdate( bool updatedExisting, int nChanged ) { | |
| LastError *le = lastError.get(); | | LastError *le = lastError.get(); | |
| if ( le ) | | if ( le ) | |
| le->recordUpdate( updatedExisting, nChanged ); | | le->recordUpdate( updatedExisting, nChanged ); | |
| } | | } | |
| | | | |
| inline void recordDelete( int nDeleted ) { | | inline void recordDelete( int nDeleted ) { | |
| LastError *le = lastError.get(); | | LastError *le = lastError.get(); | |
| | | | |
End of changes. 7 change blocks. |
| 3 lines changed or deleted | | 16 lines changed or added | |
|
| matcher.h | | matcher.h | |
| // matcher.h | | // matcher.h | |
| | | | |
|
| /* JSMatcher is our boolean expression evaluator for "where" clauses */ | | /* Matcher is our boolean expression evaluator for "where" clauses */ | |
| | | | |
| /** | | /** | |
| * Copyright (C) 2008 10gen Inc. | | * Copyright (C) 2008 10gen Inc. | |
| * | | * | |
| * This program is free software: you can redistribute it and/or modify | | * 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
, | | * it under the terms of the GNU Affero General Public License, version 3
, | |
| * as published by the Free Software Foundation. | | * as published by the Free Software Foundation. | |
| * | | * | |
| * This program is distributed in the hope that it will be useful, | | * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| | | | |
| skipping to change at line 29 | | skipping to change at line 29 | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include <pcrecpp.h> | | #include <pcrecpp.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class CoveredIndexMatcher; | | class CoveredIndexMatcher; | |
|
| | | class Matcher; | |
| | | | |
| class RegexMatcher { | | class RegexMatcher { | |
| public: | | public: | |
| const char *fieldName; | | const char *fieldName; | |
| pcrecpp::RE *re; | | pcrecpp::RE *re; | |
| RegexMatcher() { | | RegexMatcher() { | |
| re = 0; | | re = 0; | |
| } | | } | |
| ~RegexMatcher() { | | ~RegexMatcher() { | |
| delete re; | | delete re; | |
| | | | |
| skipping to change at line 53 | | skipping to change at line 54 | |
| { | | { | |
| bool operator()(const BSONElement& l, const BSONElement& r) const | | bool operator()(const BSONElement& l, const BSONElement& r) const | |
| { | | { | |
| int x = (int) l.canonicalType() - (int) r.canonicalType(); | | int x = (int) l.canonicalType() - (int) r.canonicalType(); | |
| if ( x < 0 ) return true; | | if ( x < 0 ) return true; | |
| else if ( x > 0 ) return false; | | else if ( x > 0 ) return false; | |
| return compareElementValues(l,r) < 0; | | return compareElementValues(l,r) < 0; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| class BasicMatcher { | | class ElementMatcher { | |
| public: | | public: | |
| | | | |
|
| BasicMatcher(){ | | ElementMatcher() { | |
| } | | } | |
| | | | |
|
| BasicMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareO | | ElementMatcher( BSONElement _e , int _op ); | |
| p( _op ){ | | | |
| if ( _op == BSONObj::opMOD ){ | | | |
| BSONObj o = _e.embeddedObject().firstElement().embeddedObje | | | |
| ct(); | | | |
| mod = o["0"].numberInt(); | | | |
| modm = o["1"].numberInt(); | | | |
| | | | |
| uassert( 10073 , "mod can't be 0" , mod ); | | | |
| } | | | |
| else if ( _op == BSONObj::opTYPE ){ | | | |
| type = (BSONType)(_e.embeddedObject().firstElement().number | | | |
| Int()); | | | |
| } | | | |
| } | | | |
| | | | |
|
| BasicMatcher( BSONElement _e , int _op , const BSONObj& array ) : t
oMatch( _e ) , compareOp( _op ){ | | ElementMatcher( BSONElement _e , int _op , const BSONObj& array ) :
toMatch( _e ) , compareOp( _op ) { | |
| | | | |
| myset.reset( new set<BSONElement,element_lt>() ); | | myset.reset( new set<BSONElement,element_lt>() ); | |
| | | | |
| BSONObjIterator i( array ); | | BSONObjIterator i( array ); | |
| while ( i.more() ) { | | while ( i.more() ) { | |
| BSONElement ie = i.next(); | | BSONElement ie = i.next(); | |
| myset->insert(ie); | | myset->insert(ie); | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | ~ElementMatcher(); | |
| | | | |
| BSONElement toMatch; | | BSONElement toMatch; | |
| int compareOp; | | int compareOp; | |
| shared_ptr< set<BSONElement,element_lt> > myset; | | shared_ptr< set<BSONElement,element_lt> > myset; | |
| | | | |
| // these are for specific operators | | // these are for specific operators | |
| int mod; | | int mod; | |
| int modm; | | int modm; | |
| BSONType type; | | BSONType type; | |
|
| | | | |
| | | shared_ptr<Matcher> subMatcher; | |
| }; | | }; | |
| | | | |
| // SQL where clause equivalent | | // SQL where clause equivalent | |
| class Where; | | class Where; | |
| class DiskLoc; | | class DiskLoc; | |
| | | | |
| /* Match BSON objects against a query pattern. | | /* Match BSON objects against a query pattern. | |
| | | | |
| e.g. | | e.g. | |
| db.foo.find( { a : 3 } ); | | db.foo.find( { a : 3 } ); | |
| | | | |
| { a : 3 } is the pattern object. See wiki documentation for full in
fo. | | { a : 3 } is the pattern object. See wiki documentation for full in
fo. | |
| | | | |
| GT/LT: | | GT/LT: | |
| { a : { $gt : 3 } } | | { a : { $gt : 3 } } | |
| Not equal: | | Not equal: | |
| { a : { $ne : 3 } } | | { a : { $ne : 3 } } | |
| | | | |
| TODO: we should rewrite the matcher to be more an AST style. | | TODO: we should rewrite the matcher to be more an AST style. | |
| */ | | */ | |
|
| class JSMatcher : boost::noncopyable { | | class Matcher : boost::noncopyable { | |
| int matchesDotted( | | int matchesDotted( | |
| const char *fieldName, | | const char *fieldName, | |
| const BSONElement& toMatch, const BSONObj& obj, | | const BSONElement& toMatch, const BSONObj& obj, | |
|
| int compareOp, const BasicMatcher& bm, bool isArr = false); | | int compareOp, const ElementMatcher& bm, bool isArr = false); | |
| | | | |
| int matchesNe( | | int matchesNe( | |
| const char *fieldName, | | const char *fieldName, | |
| const BSONElement &toMatch, const BSONObj &obj, | | const BSONElement &toMatch, const BSONObj &obj, | |
|
| const BasicMatcher&bm); | | const ElementMatcher&bm); | |
| | | | |
| public: | | public: | |
| static int opDirection(int op) { | | static int opDirection(int op) { | |
| return op <= BSONObj::LTE ? -1 : 1; | | return op <= BSONObj::LTE ? -1 : 1; | |
| } | | } | |
| | | | |
| // Only specify constrainIndexKey if matches() will be called with | | // Only specify constrainIndexKey if matches() will be called with | |
| // index keys having empty string field names. | | // index keys having empty string field names. | |
|
| JSMatcher(const BSONObj &pattern, const BSONObj &constrainIndexKey
= BSONObj()); | | Matcher(const BSONObj &pattern, const BSONObj &constrainIndexKey =
BSONObj()); | |
| | | | |
|
| ~JSMatcher(); | | ~Matcher(); | |
| | | | |
| bool matches(const BSONObj& j); | | bool matches(const BSONObj& j); | |
| | | | |
| bool keyMatch() const { return !all && !haveSize && !hasArray; } | | bool keyMatch() const { return !all && !haveSize && !hasArray; } | |
| | | | |
| bool atomic() const { return _atomic; } | | bool atomic() const { return _atomic; } | |
| | | | |
| private: | | private: | |
| void addBasic(const BSONElement &e, int c) { | | void addBasic(const BSONElement &e, int c) { | |
| // TODO May want to selectively ignore these element types base
d on op type. | | // TODO May want to selectively ignore these element types base
d on op type. | |
| if ( e.type() == MinKey || e.type() == MaxKey ) | | if ( e.type() == MinKey || e.type() == MaxKey ) | |
| return; | | return; | |
|
| basics.push_back( BasicMatcher( e , c ) ); | | basics.push_back( ElementMatcher( e , c ) ); | |
| } | | } | |
| | | | |
|
| int valuesMatch(const BSONElement& l, const BSONElement& r, int op,
const BasicMatcher& bm); | | int valuesMatch(const BSONElement& l, const BSONElement& r, int op,
const ElementMatcher& bm); | |
| | | | |
| Where *where; // set if query uses $where | | Where *where; // set if query uses $where | |
| BSONObj jsobj; // the query pattern. e.g., { name
: "joe" } | | BSONObj jsobj; // the query pattern. e.g., { name
: "joe" } | |
| BSONObj constrainIndexKey_; | | BSONObj constrainIndexKey_; | |
|
| vector<BasicMatcher> basics; | | vector<ElementMatcher> basics; | |
| // int n; // # of basicmatcher items | | // int n; // # of basicmatcher items | |
| bool haveSize; | | bool haveSize; | |
| bool all; | | bool all; | |
| bool hasArray; | | bool hasArray; | |
| | | | |
| /* $atomic - if true, a multi document operation (some removes, upd
ates) | | /* $atomic - if true, a multi document operation (some removes, upd
ates) | |
| should be done atomically. in that case, we do not yi
eld - | | should be done atomically. in that case, we do not yi
eld - | |
| i.e. we stay locked the whole time. | | i.e. we stay locked the whole time. | |
|
| | | http://www.mongodb.org/display/DOCS/Removing[ | |
| */ | | */ | |
| bool _atomic; | | bool _atomic; | |
| | | | |
| RegexMatcher regexs[4]; | | RegexMatcher regexs[4]; | |
| int nRegex; | | int nRegex; | |
| | | | |
| // so we delete the mem when we're done: | | // so we delete the mem when we're done: | |
| vector< shared_ptr< BSONObjBuilder > > _builders; | | vector< shared_ptr< BSONObjBuilder > > _builders; | |
| | | | |
| friend class CoveredIndexMatcher; | | friend class CoveredIndexMatcher; | |
| }; | | }; | |
| | | | |
| // If match succeeds on index key, then attempt to match full document. | | // If match succeeds on index key, then attempt to match full document. | |
| class CoveredIndexMatcher : boost::noncopyable { | | class CoveredIndexMatcher : boost::noncopyable { | |
| public: | | public: | |
| CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey
Pattern); | | CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey
Pattern); | |
| bool matches(const BSONObj &o){ return _docMatcher.matches( o ); } | | bool matches(const BSONObj &o){ return _docMatcher.matches( o ); } | |
| bool matches(const BSONObj &key, const DiskLoc &recLoc); | | bool matches(const BSONObj &key, const DiskLoc &recLoc); | |
| bool needRecord(){ return _needRecord; } | | bool needRecord(){ return _needRecord; } | |
| | | | |
|
| JSMatcher& docMatcher() { return _docMatcher; } | | Matcher& docMatcher() { return _docMatcher; } | |
| private: | | private: | |
|
| JSMatcher _keyMatcher; | | Matcher _keyMatcher; | |
| JSMatcher _docMatcher; | | Matcher _docMatcher; | |
| bool _needRecord; | | bool _needRecord; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 19 change blocks. |
| 30 lines changed or deleted | | 22 lines changed or added | |
|
| namespace.h | | namespace.h | |
| | | | |
| skipping to change at line 36 | | skipping to change at line 36 | |
| #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". */ | |
| | | | |
|
| const int MaxClientLen = 256; // max str len for the db name, including
null char | | const int MaxDatabaseLen = 256; // max str len for the db name, includi
ng null char | |
| | | | |
| // "database.a.b.c" -> "database" | | // "database.a.b.c" -> "database" | |
|
| inline void nsToClient(const char *ns, char *database) { | | inline void nsToDatabase(const char *ns, char *database) { | |
| const char *p = ns; | | const char *p = ns; | |
| char *q = database; | | char *q = database; | |
| while ( *p != '.' ) { | | while ( *p != '.' ) { | |
| if ( *p == 0 ) | | if ( *p == 0 ) | |
| break; | | break; | |
| *q++ = *p++; | | *q++ = *p++; | |
| } | | } | |
| *q = 0; | | *q = 0; | |
|
| if (q-database>=MaxClientLen) { | | if (q-database>=MaxDatabaseLen) { | |
| log() << "nsToClient: ns too long. terminating, buf overrun con | | log() << "nsToDatabase: ns too long. terminating, buf overrun c | |
| dition" << endl; | | ondition" << endl; | |
| dbexit( EXIT_POSSIBLE_CORRUPTION ); | | dbexit( EXIT_POSSIBLE_CORRUPTION ); | |
| } | | } | |
| } | | } | |
|
| inline string nsToClient(const char *ns) { | | inline string nsToDatabase(const char *ns) { | |
| char buf[MaxClientLen]; | | char buf[MaxDatabaseLen]; | |
| nsToClient(ns, buf); | | nsToDatabase(ns, buf); | |
| return buf; | | return buf; | |
| } | | } | |
| | | | |
| /* e.g. | | /* e.g. | |
| NamespaceString ns("acme.orders"); | | NamespaceString ns("acme.orders"); | |
| cout << ns.coll; // "orders" | | cout << ns.coll; // "orders" | |
| */ | | */ | |
| class NamespaceString { | | class NamespaceString { | |
| public: | | public: | |
| string db; | | string db; | |
| | | | |
| skipping to change at line 136 | | skipping to change at line 136 | |
| perhaps this should move to the NamespaceString helper? | | perhaps this should move to the NamespaceString helper? | |
| */ | | */ | |
| string getSisterNS( const char * local ) { | | string getSisterNS( const char * local ) { | |
| assert( local && local[0] != '.' ); | | assert( local && local[0] != '.' ); | |
| string old(buf); | | string old(buf); | |
| if ( old.find( "." ) != string::npos ) | | if ( old.find( "." ) != string::npos ) | |
| old = old.substr( 0 , old.find( "." ) ); | | old = old.substr( 0 , old.find( "." ) ); | |
| return old + "." + local; | | return old + "." + local; | |
| } | | } | |
| | | | |
|
| | | operator string() const { | |
| | | return (string)buf; | |
| | | } | |
| | | | |
| char buf[MaxNsLen]; | | char buf[MaxNsLen]; | |
| }; | | }; | |
| | | | |
|
| | | } | |
| | | | |
| | | #include "index.h" | |
| | | | |
| | | namespace mongo { | |
| | | | |
| /** | | /** | |
| @return true if a client can modify this namespace | | @return true if a client can modify this namespace | |
| things like *.system.users | | things like *.system.users | |
| */ | | */ | |
| bool legalClientSystemNS( const string& ns , bool write ); | | bool legalClientSystemNS( const string& ns , bool write ); | |
| | | | |
| /* deleted lists -- linked lists of deleted records -- are placed in 'b
uckets' of various sizes | | /* deleted lists -- linked lists of deleted records -- are placed in 'b
uckets' of various sizes | |
| so you can look for a deleterecord about the right size. | | so you can look for a deleterecord about the right size. | |
| */ | | */ | |
| const int Buckets = 19; | | const int Buckets = 19; | |
| const int MaxBucket = 18; | | const int MaxBucket = 18; | |
| | | | |
|
| /* Maximum # of indexes per collection. We need to raise this limit | | | |
| at some point. | | | |
| (Backward datafile compatibility is main issue with changing.) | | | |
| */ | | | |
| // const int MaxIndexes = 10; | | | |
| | | | |
| /* 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 | | | |
| collection). | | | |
| */ | | | |
| class IndexDetails { | | | |
| public: | | | |
| DiskLoc head; /* btree head disk location */ | | | |
| | | | |
| /* Location of index info object. Format: | | | |
| | | | |
| { name:"nameofindex", ns:"parentnsname", key: {keypattobject}[ | | | |
| , unique: <bool>] } | | | |
| | | | |
| This object is in the system.indexes collection. Note that sinc | | | |
| e we | | | |
| have a pointer to the object here, the object in system.indexes | | | |
| MUST NEVER MOVE. | | | |
| */ | | | |
| DiskLoc info; | | | |
| | | | |
| /* extract key value from the query object | | | |
| e.g., if key() == { x : 1 }, | | | |
| { x : 70, y : 3 } -> { x : 70 } | | | |
| */ | | | |
| BSONObj getKeyFromQuery(const BSONObj& query) const { | | | |
| BSONObj k = keyPattern(); | | | |
| BSONObj res = query.extractFieldsUnDotted(k); | | | |
| return res; | | | |
| } | | | |
| | | | |
| /* pull out the relevant key objects from obj, so we | | | |
| can index them. Note that the set is multiple elements | | | |
| only when it's a "multikey" array. | | | |
| keys will be left empty if key not found in the object. | | | |
| */ | | | |
| void getKeysFromObject( const BSONObj& obj, BSONObjSetDefaultOrder& | | | |
| keys) const; | | | |
| | | | |
| /* get the key pattern for this object. | | | |
| e.g., { lastname:1, firstname:1 } | | | |
| */ | | | |
| BSONObj keyPattern() const { | | | |
| return info.obj().getObjectField("key"); | | | |
| } | | | |
| | | | |
| /* true if the specified key is in the index */ | | | |
| bool hasKey(const BSONObj& key); | | | |
| | | | |
| // returns name of this index's storage area | | | |
| // database.table.$index | | | |
| string indexNamespace() const { | | | |
| BSONObj io = info.obj(); | | | |
| string s; | | | |
| s.reserve(Namespace::MaxNsLen); | | | |
| s = io.getStringField("ns"); | | | |
| assert( !s.empty() ); | | | |
| s += ".$"; | | | |
| s += io.getStringField("name"); | | | |
| return s; | | | |
| } | | | |
| | | | |
| string indexName() const { // e.g. "ts_1" | | | |
| BSONObj io = info.obj(); | | | |
| return io.getStringField("name"); | | | |
| } | | | |
| | | | |
| static bool isIdIndexPattern( const BSONObj &pattern ) { | | | |
| BSONObjIterator i(pattern); | | | |
| BSONElement e = i.next(); | | | |
| if( strcmp(e.fieldName(), "_id") != 0 ) return false; | | | |
| return i.next().eoo(); | | | |
| } | | | |
| | | | |
| /* returns true if this is the _id index. */ | | | |
| bool isIdIndex() const { | | | |
| return isIdIndexPattern( keyPattern() ); | | | |
| } | | | |
| | | | |
| /* gets not our namespace name (indexNamespace for that), | | | |
| but the collection we index, its name. | | | |
| */ | | | |
| string parentNS() const { | | | |
| BSONObj io = info.obj(); | | | |
| return io.getStringField("ns"); | | | |
| } | | | |
| | | | |
| bool unique() const { | | | |
| BSONObj io = info.obj(); | | | |
| return io["unique"].trueValue() || | | | |
| /* temp: can we juse make unique:true always be there for _ | | | |
| id and get rid of this? */ | | | |
| isIdIndex(); | | | |
| } | | | |
| | | | |
| /* if set, when building index, if any duplicates, drop the duplica | | | |
| ting object */ | | | |
| bool dropDups() const { | | | |
| return info.obj().getBoolField( "dropDups" ); | | | |
| } | | | |
| | | | |
| /* delete this index. does NOT clean up the system catalog | | | |
| (system.indexes or system.namespaces) -- only NamespaceIndex. | | | |
| */ | | | |
| void kill_idx(); | | | |
| | | | |
| operator string() const { | | | |
| return info.obj().toString(); | | | |
| } | | | |
| }; | | | |
| | | | |
| extern int bucketSizes[]; | | extern int bucketSizes[]; | |
| | | | |
| /* this is the "header" for a collection that has all its details. in
the .ns file. | | /* this is the "header" for a collection that has all its details. in
the .ns file. | |
| */ | | */ | |
| class NamespaceDetails { | | class NamespaceDetails { | |
| friend class NamespaceIndex; | | friend class NamespaceIndex; | |
| enum { NIndexesExtra = 30, | | enum { NIndexesExtra = 30, | |
| NIndexesBase = 10 | | NIndexesBase = 10 | |
| }; | | }; | |
| struct Extra { | | struct Extra { | |
| | | | |
| skipping to change at line 309 | | skipping to change at line 210 | |
| capFirstNewRecord.setInvalid(); | | capFirstNewRecord.setInvalid(); | |
| // For capped case, signal that we are doing initial extent all
ocation. | | // For capped case, signal that we are doing initial extent all
ocation. | |
| if ( capped ) | | if ( capped ) | |
| deletedList[ 1 ].setInvalid(); | | deletedList[ 1 ].setInvalid(); | |
| assert( sizeof(dataFileVersion) == 2 ); | | assert( sizeof(dataFileVersion) == 2 ); | |
| dataFileVersion = 0; | | dataFileVersion = 0; | |
| indexFileVersion = 0; | | indexFileVersion = 0; | |
| multiKeyIndexBits = 0; | | multiKeyIndexBits = 0; | |
| reservedA = 0; | | reservedA = 0; | |
| extraOffset = 0; | | extraOffset = 0; | |
|
| | | backgroundIndexBuildInProgress = 0; | |
| memset(reserved, 0, sizeof(reserved)); | | memset(reserved, 0, sizeof(reserved)); | |
| } | | } | |
| DiskLoc firstExtent; | | DiskLoc firstExtent; | |
| DiskLoc lastExtent; | | DiskLoc lastExtent; | |
| | | | |
| /* NOTE: capped collections override the meaning of deleted list. | | /* NOTE: capped collections override the meaning of deleted list. | |
| deletedList[0] points to a list of free records (DeletedRe
cord's) for all extents in | | deletedList[0] points to a list of free records (DeletedRe
cord's) for all extents in | |
| the namespace. | | the namespace. | |
| deletedList[1] points to the last record in the prev exten
t. When the "current extent" | | deletedList[1] points to the last record in the prev exten
t. When the "current extent" | |
| changes, this value is updated. !deletedList[1].isValid()
when this value is not | | changes, this value is updated. !deletedList[1].isValid()
when this value is not | |
| | | | |
| skipping to change at line 348 | | skipping to change at line 250 | |
| See filever.h | | See filever.h | |
| */ | | */ | |
| unsigned short dataFileVersion; | | unsigned short dataFileVersion; | |
| 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: | |
|
| char reserved[80]; | | int backgroundIndexBuildInProgress; // 1 if in prog | |
| | | char reserved[76]; | |
| | | | |
|
| | | /* NOTE: be careful with flags. are we manipulating them in read l | |
| | | ocks? if so, | |
| | | 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]; | |
| } | | } | |
| | | | |
| skipping to change at line 403 | | skipping to change at line 309 | |
| while( i.more() ) { | | while( i.more() ) { | |
| if( &i.next() == &idx ) | | if( &i.next() == &idx ) | |
| return i.pos()-1; | | return i.pos()-1; | |
| } | | } | |
| massert( 10349 , "E12000 idxNo fails", false); | | massert( 10349 , "E12000 idxNo fails", false); | |
| return -1; | | return -1; | |
| } | | } | |
| | | | |
| /* multikey indexes are indexes where there are more than one key i
n the index | | /* multikey indexes are indexes where there are more than one key i
n the index | |
| for a single document. see multikey in wiki. | | for a single document. see multikey in wiki. | |
|
| for these, we have to do some dedup object on queries. | | for these, we have to do some dedup work on queries. | |
| */ | | */ | |
| bool isMultikey(int i) { | | bool isMultikey(int i) { | |
| return (multiKeyIndexBits & (((unsigned long long) 1) << i)) !=
0; | | return (multiKeyIndexBits & (((unsigned long long) 1) << i)) !=
0; | |
| } | | } | |
| void setIndexIsMultikey(int i) { | | void setIndexIsMultikey(int i) { | |
| dassert( i < NIndexesMax ); | | dassert( i < NIndexesMax ); | |
| multiKeyIndexBits |= (((unsigned long long) 1) << i); | | multiKeyIndexBits |= (((unsigned long long) 1) << i); | |
| } | | } | |
| void clearIndexIsMultikey(int i) { | | void clearIndexIsMultikey(int i) { | |
| dassert( i < NIndexesMax ); | | dassert( i < NIndexesMax ); | |
| | | | |
| skipping to change at line 454 | | skipping to change at line 360 | |
| //returns offset in indexes[] | | //returns offset in indexes[] | |
| int findIndexByName(const char *name) { | | int findIndexByName(const char *name) { | |
| IndexIterator i = ii(); | | IndexIterator i = ii(); | |
| while( i.more() ) { | | while( i.more() ) { | |
| if ( strcmp(i.next().info.obj().getStringField("name"),name
) == 0 ) | | if ( strcmp(i.next().info.obj().getStringField("name"),name
) == 0 ) | |
| return i.pos()-1; | | return i.pos()-1; | |
| } | | } | |
| return -1; | | return -1; | |
| } | | } | |
| | | | |
|
| | | //returns offset in indexes[] | |
| | | int findIndexByKeyPattern(const BSONObj& keyPattern) { | |
| | | IndexIterator i = ii(); | |
| | | while( i.more() ) { | |
| | | if( i.next().keyPattern() == keyPattern ) | |
| | | return i.pos()-1; | |
| | | } | |
| | | return -1; | |
| | | } | |
| | | | |
| /* @return -1 = not found | | /* @return -1 = not found | |
| generally id is first index, so not that expensive an operation
(assuming present). | | generally id is first index, so not that expensive an operation
(assuming present). | |
| */ | | */ | |
| int findIdIndex() { | | int findIdIndex() { | |
| IndexIterator i = ii(); | | IndexIterator i = ii(); | |
| while( i.more() ) { | | while( i.more() ) { | |
| if( i.next().isIdIndex() ) | | if( i.next().isIdIndex() ) | |
| return i.pos()-1; | | return i.pos()-1; | |
| } | | } | |
| return -1; | | return -1; | |
| | | | |
| skipping to change at line 510 | | skipping to change at line 426 | |
| 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; | |
| DiskLoc __stdAlloc(int len); | | DiskLoc __stdAlloc(int len); | |
| DiskLoc __capAlloc(int len); | | DiskLoc __capAlloc(int len); | |
| DiskLoc _alloc(const char *ns, int len); | | DiskLoc _alloc(const char *ns, int len); | |
|
| void compact(); | | void compact(); // combine adjacent deleted records | |
| | | | |
| DiskLoc &firstDeletedInCapExtent(); | | DiskLoc &firstDeletedInCapExtent(); | |
| bool nextIsInCapExtent( const DiskLoc &dl ) const; | | bool nextIsInCapExtent( const DiskLoc &dl ) const; | |
| }; | | }; | |
| | | | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| /* these are things we know / compute about a namespace that are transi
ent -- things | | /* these are things we know / compute about a namespace that are transi
ent -- things | |
| we don't actually store in the .ns file. so mainly caching of frequ
ently used | | we don't actually store in the .ns file. so mainly caching of frequ
ently used | |
| information. | | information. | |
| | | | |
| skipping to change at line 567 | | skipping to change at line 483 | |
| /* get set of index keys for this namespace. handy to quickly chec
k if a given | | /* get set of index keys for this namespace. handy to quickly chec
k if a given | |
| field is indexed (Note it might be a secondary component of a co
mpound index.) | | field is indexed (Note it might be a secondary component of a co
mpound index.) | |
| */ | | */ | |
| set<string>& indexKeys() { | | set<string>& indexKeys() { | |
| DEV assertInWriteLock(); | | DEV assertInWriteLock(); | |
| if ( !_keysComputed ) | | if ( !_keysComputed ) | |
| computeIndexKeys(); | | computeIndexKeys(); | |
| return _indexKeys; | | return _indexKeys; | |
| } | | } | |
| | | | |
|
| | | /* IndexSpec caching */ | |
| | | private: | |
| | | map<const IndexDetails*,IndexSpec> _indexSpecs; | |
| | | public: | |
| | | const IndexSpec& getIndexSpec( const IndexDetails * details ){ | |
| | | DEV assertInWriteLock(); | |
| | | IndexSpec& spec = _indexSpecs[details]; | |
| | | if ( spec.meta.isEmpty() ){ | |
| | | spec.reset( details->info ); | |
| | | } | |
| | | 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: | |
| static boost::mutex _qcMutex; | | static boost::mutex _qcMutex; | |
| /* you must be in the qcMutex when calling this (and using the retu
rned val): */ | | /* you must be in the qcMutex when calling this (and using the retu
rned val): */ | |
| static NamespaceDetailsTransient& get_inlock(const char *ns) { | | static NamespaceDetailsTransient& get_inlock(const char *ns) { | |
| return _get(ns); | | return _get(ns); | |
| } | | } | |
| | | | |
End of changes. 14 change blocks. |
| 129 lines changed or deleted | | 50 lines changed or added | |
|
| parallel.h | | parallel.h | |
| | | | |
| skipping to change at line 43 | | skipping to change at line 43 | |
| public: | | public: | |
| ClusteredCursor( QueryMessage& q ); | | ClusteredCursor( QueryMessage& q ); | |
| ClusteredCursor( const string& ns , const BSONObj& q , int options=
0 , const BSONObj& fields=BSONObj() ); | | ClusteredCursor( const string& ns , const BSONObj& q , int options=
0 , const BSONObj& fields=BSONObj() ); | |
| virtual ~ClusteredCursor(); | | virtual ~ClusteredCursor(); | |
| | | | |
| virtual bool more() = 0; | | virtual bool more() = 0; | |
| virtual BSONObj next() = 0; | | virtual BSONObj next() = 0; | |
| | | | |
| static BSONObj concatQuery( const BSONObj& query , const BSONObj& e
xtraFilter ); | | static BSONObj concatQuery( const BSONObj& query , const BSONObj& e
xtraFilter ); | |
| | | | |
|
| | | virtual string type() const = 0; | |
| | | | |
| protected: | | protected: | |
| auto_ptr<DBClientCursor> query( const string& server , int num = 0
, BSONObj extraFilter = BSONObj() ); | | auto_ptr<DBClientCursor> query( const string& server , int num = 0
, BSONObj extraFilter = BSONObj() ); | |
| | | | |
| static BSONObj _concatFilter( const BSONObj& filter , const BSONObj
& extraFilter ); | | static BSONObj _concatFilter( const BSONObj& filter , const BSONObj
& extraFilter ); | |
| | | | |
| string _ns; | | string _ns; | |
| BSONObj _query; | | BSONObj _query; | |
| int _options; | | int _options; | |
| BSONObj _fields; | | BSONObj _fields; | |
| | | | |
| | | | |
| skipping to change at line 76 | | skipping to change at line 78 | |
| if ( ! _orderObject.isEmpty() ) | | if ( ! _orderObject.isEmpty() ) | |
| return _orderObject.woCompare( other._orderObject ) < 0; | | return _orderObject.woCompare( other._orderObject ) < 0; | |
| | | | |
| if ( _server < other._server ) | | if ( _server < other._server ) | |
| return true; | | return true; | |
| if ( other._server > _server ) | | if ( other._server > _server ) | |
| return false; | | return false; | |
| return _extra.woCompare( other._extra ) < 0; | | return _extra.woCompare( other._extra ) < 0; | |
| } | | } | |
| | | | |
|
| | | string toString() const { | |
| | | StringBuilder ss; | |
| | | ss << "server:" << _server << " _extra:" << _extra << " _orderO | |
| | | bject:" << _orderObject; | |
| | | return ss.str(); | |
| | | } | |
| | | | |
| | | operator string() const { | |
| | | return toString(); | |
| | | } | |
| | | | |
| string _server; | | string _server; | |
| BSONObj _extra; | | BSONObj _extra; | |
| BSONObj _orderObject; | | BSONObj _orderObject; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * runs a query in serial across any number of servers | | * runs a query in serial across any number of servers | |
| * returns all results from 1 server, then the next, etc... | | * returns all results from 1 server, then the next, etc... | |
| */ | | */ | |
| class SerialServerClusteredCursor : public ClusteredCursor { | | class SerialServerClusteredCursor : public ClusteredCursor { | |
| public: | | public: | |
| SerialServerClusteredCursor( set<ServerAndQuery> servers , QueryMes
sage& q , int sortOrder=0); | | SerialServerClusteredCursor( set<ServerAndQuery> servers , QueryMes
sage& q , int sortOrder=0); | |
| virtual bool more(); | | virtual bool more(); | |
| virtual BSONObj next(); | | virtual BSONObj next(); | |
|
| | | virtual string type() const { return "SerialServer"; } | |
| private: | | private: | |
| vector<ServerAndQuery> _servers; | | vector<ServerAndQuery> _servers; | |
| unsigned _serverIndex; | | unsigned _serverIndex; | |
| | | | |
| auto_ptr<DBClientCursor> _current; | | auto_ptr<DBClientCursor> _current; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * runs a query in parellel across N servers | | * runs a query in parellel across N servers | |
| * sots | | * sots | |
| */ | | */ | |
| class ParallelSortClusteredCursor : public ClusteredCursor { | | class ParallelSortClusteredCursor : public ClusteredCursor { | |
| public: | | public: | |
| ParallelSortClusteredCursor( set<ServerAndQuery> servers , QueryMes
sage& q , const BSONObj& sortKey ); | | ParallelSortClusteredCursor( set<ServerAndQuery> servers , QueryMes
sage& q , const BSONObj& sortKey ); | |
| ParallelSortClusteredCursor( set<ServerAndQuery> servers , const st
ring& ns , | | ParallelSortClusteredCursor( set<ServerAndQuery> servers , const st
ring& ns , | |
| const Query& q , int options=0, const
BSONObj& fields=BSONObj() ); | | const Query& q , int options=0, const
BSONObj& fields=BSONObj() ); | |
| virtual ~ParallelSortClusteredCursor(); | | virtual ~ParallelSortClusteredCursor(); | |
| virtual bool more(); | | virtual bool more(); | |
| virtual BSONObj next(); | | virtual BSONObj next(); | |
|
| | | virtual string type() const { return "ParallelSort"; } | |
| private: | | private: | |
| void _init(); | | void _init(); | |
| | | | |
| void advance(); | | void advance(); | |
| | | | |
| int _numServers; | | int _numServers; | |
| set<ServerAndQuery> _servers; | | set<ServerAndQuery> _servers; | |
| BSONObj _sortKey; | | BSONObj _sortKey; | |
| | | | |
| auto_ptr<DBClientCursor> * _cursors; | | auto_ptr<DBClientCursor> * _cursors; | |
| | | | |
End of changes. 4 change blocks. |
| 0 lines changed or deleted | | 15 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 53 | | skipping to change at line 53 | |
| void dropDatabase(const char *ns); | | void dropDatabase(const char *ns); | |
| bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned
FilesOnFailure = false, bool backupOriginalFiles = false); | | bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned
FilesOnFailure = false, bool backupOriginalFiles = false); | |
| | | | |
| /* low level - only drops this ns */ | | /* low level - only drops this ns */ | |
| void dropNS(const string& dropNs); | | void dropNS(const string& dropNs); | |
| | | | |
| /* deletes this ns, indexes and cursors */ | | /* deletes this ns, indexes and cursors */ | |
| void dropCollection( const string &name, string &errmsg, BSONObjBuilder
&result ); | | void dropCollection( const string &name, string &errmsg, BSONObjBuilder
&result ); | |
| bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe
plication); | | bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe
plication); | |
| auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co
nst DiskLoc &startLoc=DiskLoc()); | | auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co
nst DiskLoc &startLoc=DiskLoc()); | |
|
| void getKeysFromObject( const BSONObj &keyPattern, const BSONObj &obj,
BSONObjSetDefaultOrder &keys ); | | | |
| | | | |
| // -1 if library unavailable. | | // -1 if library unavailable. | |
| boost::intmax_t freeSpace(); | | boost::intmax_t freeSpace(); | |
| | | | |
| /*---------------------------------------------------------------------
*/ | | /*---------------------------------------------------------------------
*/ | |
| | | | |
| class MDFHeader; | | class MDFHeader; | |
| class MongoDataFile { | | class MongoDataFile { | |
| friend class DataFileMgr; | | friend class DataFileMgr; | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| | | | |
| skipping to change at line 276 | | skipping to change at line 275 | |
| char data[4]; | | char data[4]; | |
| | | | |
| static int headerSize() { | | static int headerSize() { | |
| return sizeof(MDFHeader) - 4; | | return sizeof(MDFHeader) - 4; | |
| } | | } | |
| | | | |
| bool currentVersion() const { | | bool currentVersion() const { | |
| return ( version == VERSION ) && ( versionMinor == VERSION_MINO
R ); | | return ( version == VERSION ) && ( versionMinor == VERSION_MINO
R ); | |
| } | | } | |
| | | | |
|
| bool uninitialized() { | | bool uninitialized() const { | |
| if ( version == 0 ) return true; | | if ( version == 0 ) return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| Record* getRecord(DiskLoc dl) { | | Record* getRecord(DiskLoc dl) { | |
| int ofs = dl.getOfs(); | | int ofs = dl.getOfs(); | |
| assert( ofs >= headerSize() ); | | assert( ofs >= headerSize() ); | |
| return (Record*) (((char *) this) + ofs); | | return (Record*) (((char *) this) + ofs); | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 300 | | skipping to change at line 299 | |
| assert( headerSize() == 8192 ); | | assert( headerSize() == 8192 ); | |
| fileLength = filelength; | | fileLength = filelength; | |
| version = VERSION; | | version = VERSION; | |
| versionMinor = VERSION_MINOR; | | versionMinor = VERSION_MINOR; | |
| unused.setOfs( fileno, headerSize() ); | | unused.setOfs( fileno, headerSize() ); | |
| assert( (data-(char*)this) == headerSize() ); | | assert( (data-(char*)this) == headerSize() ); | |
| unusedLength = fileLength - headerSize() - 16; | | unusedLength = fileLength - headerSize() - 16; | |
| memcpy(data+unusedLength, " \nthe end\n", 16); | | memcpy(data+unusedLength, " \nthe end\n", 16); | |
| } | | } | |
| } | | } | |
|
| | | | |
| | | bool isEmpty() const { | |
| | | return uninitialized() || ( unusedLength == fileLength - header | |
| | | Size() - 16 ); | |
| | | } | |
| }; | | }; | |
| | | | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| inline Extent* MongoDataFile::_getExtent(DiskLoc loc) { | | inline Extent* MongoDataFile::_getExtent(DiskLoc loc) { | |
| loc.assertOk(); | | loc.assertOk(); | |
| Extent *e = (Extent *) (((char *)header) + loc.getOfs()); | | Extent *e = (Extent *) (((char *)header) + loc.getOfs()); | |
| return e; | | return e; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 410 | | skipping to change at line 413 | |
| _applyOpToDataFiles( database, deleter, true ); | | _applyOpToDataFiles( database, deleter, true ); | |
| } | | } | |
| | | | |
| boost::intmax_t dbSize( const char *database ); | | boost::intmax_t dbSize( const char *database ); | |
| | | | |
| inline NamespaceIndex* nsindex(const char *ns) { | | inline NamespaceIndex* nsindex(const char *ns) { | |
| Database *database = cc().database(); | | Database *database = cc().database(); | |
| assert( database ); | | assert( database ); | |
| DEV { | | DEV { | |
| char buf[256]; | | char buf[256]; | |
|
| nsToClient(ns, buf); | | nsToDatabase(ns, buf); | |
| if ( database->name != buf ) { | | if ( database->name != buf ) { | |
| out() << "ERROR: attempt to write to wrong database databas
e\n"; | | out() << "ERROR: attempt to write to wrong database databas
e\n"; | |
| out() << " ns:" << ns << '\n'; | | out() << " ns:" << ns << '\n'; | |
| out() << " database->name:" << database->name << endl; | | out() << " database->name:" << database->name << endl; | |
| assert( database->name == buf ); | | assert( database->name == buf ); | |
| } | | } | |
| } | | } | |
| return &database->namespaceIndex; | | return &database->namespaceIndex; | |
| } | | } | |
| | | | |
| inline NamespaceDetails* nsdetails(const char *ns) { | | inline NamespaceDetails* nsdetails(const char *ns) { | |
|
| // if this faults, did you set the current db first? (DBContext +
dblock) | | // if this faults, did you set the current db first? (Client::Cont
ext + dblock) | |
| return nsindex(ns)->details(ns); | | return nsindex(ns)->details(ns); | |
| } | | } | |
| | | | |
| inline MongoDataFile& DiskLoc::pdf() const { | | inline MongoDataFile& DiskLoc::pdf() const { | |
| assert( fileNo != -1 ); | | assert( fileNo != -1 ); | |
| return *cc().database()->getFile(fileNo); | | return *cc().database()->getFile(fileNo); | |
| } | | } | |
| | | | |
| inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { | | inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { | |
| assert( dl.a() != -1 ); | | assert( dl.a() != -1 ); | |
| | | | |
End of changes. 5 change blocks. |
| 4 lines changed or deleted | | 8 lines changed or added | |
|
| repl.h | | repl.h | |
| | | | |
| skipping to change at line 80 | | skipping to change at line 80 | |
| | | | |
| Can be a group of things to replicate for several databases. | | Can be a group of things to replicate for several databases. | |
| | | | |
| { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: .
.., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | | { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: .
.., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | |
| | | | |
| 'source' defaults to 'main'; support for multiple source names is | | 'source' defaults to 'main'; support for multiple source names is | |
| not done (always use main for now). | | not done (always use main for now). | |
| */ | | */ | |
| class ReplSource { | | class ReplSource { | |
| bool resync(string db); | | bool resync(string db); | |
|
| | | | |
| | | /* pull some operations from the master's oplog, and apply them. */ | |
| bool sync_pullOpLog(int& nApplied); | | bool sync_pullOpLog(int& nApplied); | |
|
| | | | |
| void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai
l); | | void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai
l); | |
| | | | |
| auto_ptr<DBClientConnection> conn; | | auto_ptr<DBClientConnection> conn; | |
| auto_ptr<DBClientCursor> cursor; | | auto_ptr<DBClientCursor> cursor; | |
| | | | |
|
| | | /* we only clone one database per pass, even if a lot need done. T | |
| | | his helps us | |
| | | avoid overflowing the master's transaction log by doing too much | |
| | | work before going | |
| | | back to read more transactions. (Imagine a scenario of slave sta | |
| | | rtup where we try to | |
| | | clone 100 databases in one pass.) | |
| | | */ | |
| set<string> addDbNextPass; | | set<string> addDbNextPass; | |
|
| | | | |
| set<string> incompleteCloneDbs; | | set<string> incompleteCloneDbs; | |
| | | | |
| ReplSource(); | | ReplSource(); | |
| | | | |
| // returns the dummy ns used to do the drop | | // returns the dummy ns used to do the drop | |
| string resyncDrop( const char *db, const char *requester ); | | string resyncDrop( const char *db, const char *requester ); | |
| // returns true if connected on return | | // returns true if connected on return | |
| bool connect(); | | bool connect(); | |
| // returns possibly unowned id spec for the operation. | | // returns possibly unowned id spec for the operation. | |
| static BSONObj idForOp( const BSONObj &op, bool &mod ); | | static BSONObj idForOp( const BSONObj &op, bool &mod ); | |
| | | | |
| skipping to change at line 121 | | skipping to change at line 130 | |
| static void applyOperation(const BSONObj& op); | | static void applyOperation(const BSONObj& op); | |
| bool replacing; // in "replace mode" -- see CmdReplacePeer | | bool replacing; // in "replace mode" -- see CmdReplacePeer | |
| bool paired; // --pair in use | | bool paired; // --pair in use | |
| string hostName; // ip addr or hostname plus optionally, ":<port
>" | | string hostName; // ip addr or hostname plus optionally, ":<port
>" | |
| string _sourceName; // a logical source name. | | string _sourceName; // a logical source name. | |
| string sourceName() const { | | string sourceName() const { | |
| return _sourceName.empty() ? "main" : _sourceName; | | return _sourceName.empty() ? "main" : _sourceName; | |
| } | | } | |
| string only; // only a certain db. note that in the sources collect
ion, this may not be changed once you start replicating. | | string only; // only a certain db. note that in the sources collect
ion, this may not be changed once you start replicating. | |
| | | | |
|
| /* the last time point we have already synced up to. */ | | /* the last time point we have already synced up to (in the remote/
master's oplog). */ | |
| OpTime syncedTo; | | OpTime syncedTo; | |
|
| OpTime lastSavedLocalTs_; | | | |
| | | /* This is for repl pairs. | |
| | | _lastSavedLocalTs is the most recent point in the local log that | |
| | | we know is consistent | |
| | | with the remote log ( ie say the local op log has entries ABCDE | |
| | | and the remote op log | |
| | | has ABCXY, then _lastSavedLocalTs won't be greater than C until | |
| | | we have reconciled | |
| | | the DE-XY difference.) | |
| | | */ | |
| | | OpTime _lastSavedLocalTs; | |
| | | | |
| int nClonedThisPass; | | int nClonedThisPass; | |
| | | | |
| typedef vector< shared_ptr< ReplSource > > SourceVector; | | typedef vector< shared_ptr< ReplSource > > SourceVector; | |
| static void loadAll(SourceVector&); | | static void loadAll(SourceVector&); | |
| explicit ReplSource(BSONObj); | | explicit ReplSource(BSONObj); | |
| bool sync(int& nApplied); | | bool sync(int& nApplied); | |
| void save(); // write ourself to local.sources | | void save(); // write ourself to local.sources | |
| void resetConnection() { | | void resetConnection() { | |
| cursor = auto_ptr<DBClientCursor>(0); | | cursor = auto_ptr<DBClientCursor>(0); | |
| | | | |
| skipping to change at line 219 | | skipping to change at line 235 | |
| b.appendAs( id.firstElement(), "id" ); | | b.appendAs( id.firstElement(), "id" ); | |
| return b.obj(); | | return b.obj(); | |
| } | | } | |
| DbSet impl_; | | DbSet impl_; | |
| }; | | }; | |
| | | | |
| // class for tracking ids and mod ids, in memory or on disk | | // class for tracking ids and mod ids, in memory or on disk | |
| // All functions must be called with db mutex held | | // All functions must be called with db mutex held | |
| // Kind of sloppy class structure, for now just want to keep the in mem | | // Kind of sloppy class structure, for now just want to keep the in mem | |
| // version speedy. | | // version speedy. | |
|
| | | // see http://www.mongodb.org/display/DOCS/Pairing+Internals | |
| class IdTracker { | | class IdTracker { | |
| public: | | public: | |
| IdTracker() : | | IdTracker() : | |
| dbIds_( "local.temp.replIds" ), | | dbIds_( "local.temp.replIds" ), | |
| dbModIds_( "local.temp.replModIds" ), | | dbModIds_( "local.temp.replModIds" ), | |
| inMem_( true ), | | inMem_( true ), | |
| maxMem_( opIdMem ) { | | maxMem_( opIdMem ) { | |
| } | | } | |
| void reset( int maxMem = opIdMem ) { | | void reset( int maxMem = opIdMem ) { | |
| memIds_.reset(); | | memIds_.reset(); | |
| | | | |
End of changes. 7 change blocks. |
| 2 lines changed or deleted | | 25 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, ... */ | | /* Used for modifiers such as $inc, $set, $push, ... */ | |
| struct Mod { | | struct Mod { | |
| // See opFromStr below | | // See opFromStr below | |
| // 0 1 2 3 4 5 6 7 8
9 10 | | // 0 1 2 3 4 5 6 7 8
9 10 | |
| enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI
TAND, BITOR , BIT } op; | | enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI
TAND, BITOR , BIT } 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? | | // kind of lame; fix one day? | |
| double *ndouble; | | double *ndouble; | |
| int *nint; | | int *nint; | |
| long long *nlong; | | 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; | | int pushStartSize; | |
|
| boost::shared_ptr<JSMatcher> 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 JSMatcher( 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; | |
| } | | } | |
| | | | |
| skipping to change at line 149 | | skipping to change at line 149 | |
| } | | } | |
| } | | } | |
| | | | |
| void appendForOpLog( BSONObjBuilder& b ) const { | | void appendForOpLog( BSONObjBuilder& b ) const { | |
| const char * name = modNames[op]; | | const char * name = modNames[op]; | |
| | | | |
| BSONObjBuilder bb( b.subobjStart( name ) ); | | BSONObjBuilder bb( b.subobjStart( name ) ); | |
| bb.append( elt ); | | bb.append( elt ); | |
| bb.done(); | | bb.done(); | |
| } | | } | |
|
| | | | |
| | | void _checkForAppending( BSONElement& e ){ | |
| | | if ( e.type() == Object ){ | |
| | | // this is a tiny bit slow, but rare and important | |
| | | // only when setting something TO an object, not setting so | |
| | | mething in an object | |
| | | // and it checks for { $set : { x : { 'a.b' : 1 } } } | |
| | | // which is feel has been common | |
| | | uassert( 12527 , "not okForStorage" , e.embeddedObject().ok | |
| | | ForStorage() ); | |
| | | } | |
| | | } | |
| | | | |
| }; | | }; | |
| | | | |
| class ModSet { | | class ModSet { | |
| typedef map<string,Mod> ModHolder; | | typedef map<string,Mod> ModHolder; | |
| ModHolder _mods; | | ModHolder _mods; | |
| | | | |
| 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() ); | |
| | | | |
| skipping to change at line 201 | | skipping to change at line 211 | |
| } | | } | |
| | | | |
| case Mod::UNSET: | | case Mod::UNSET: | |
| case Mod::PULL: | | case Mod::PULL: | |
| case Mod::PULL_ALL: | | case Mod::PULL_ALL: | |
| // no-op b/c unset/pull of nothing does nothing | | // no-op b/c unset/pull of nothing does nothing | |
| break; | | break; | |
| | | | |
| case Mod::INC: | | case Mod::INC: | |
| case Mod::SET: { | | case Mod::SET: { | |
|
| | | m._checkForAppending( m.elt ); | |
| b.appendAs( m.elt, m.shortFieldName ); | | b.appendAs( m.elt, m.shortFieldName ); | |
| break; | | break; | |
| } | | } | |
| default: | | default: | |
| stringstream ss; | | stringstream ss; | |
| ss << "unknown mod in appendNewFromMod: " << m.op; | | ss << "unknown mod in appendNewFromMod: " << m.op; | |
| throw UserException( 9015, ss.str() ); | | throw UserException( 9015, ss.str() ); | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 5 change blocks. |
| 3 lines changed or deleted | | 17 lines changed or added | |
|