| assert_util.h | | assert_util.h | |
| | | | |
| skipping to change at line 33 | | skipping to change at line 33 | |
| | | | |
| /* these are manipulated outside of mutexes, so be careful */ | | /* these are manipulated outside of mutexes, so be careful */ | |
| struct Assertion { | | struct Assertion { | |
| Assertion() { | | Assertion() { | |
| msg[0] = msg[127] = 0; | | msg[0] = msg[127] = 0; | |
| context[0] = context[127] = 0; | | context[0] = context[127] = 0; | |
| file = ""; | | file = ""; | |
| line = 0; | | line = 0; | |
| when = 0; | | when = 0; | |
| } | | } | |
|
| | | private: | |
| | | static boost::mutex *_mutex; | |
| char msg[128]; | | char msg[128]; | |
| char context[128]; | | char context[128]; | |
| const char *file; | | const char *file; | |
| unsigned line; | | unsigned line; | |
| time_t when; | | time_t when; | |
|
| | | public: | |
| void set(const char *m, const char *ctxt, const char *f, unsigned l
) { | | void set(const char *m, const char *ctxt, const char *f, unsigned l
) { | |
|
| | | if( _mutex == 0 ) { | |
| | | /* asserted during global variable initialization */ | |
| | | return; | |
| | | } | |
| | | boostlock lk(*_mutex); | |
| strncpy(msg, m, 127); | | strncpy(msg, m, 127); | |
| strncpy(context, ctxt, 127); | | strncpy(context, ctxt, 127); | |
| file = f; | | file = f; | |
| line = l; | | line = l; | |
| when = time(0); | | when = time(0); | |
| } | | } | |
| std::string toString(); | | std::string toString(); | |
| bool isSet() { | | bool isSet() { | |
| return when != 0; | | return when != 0; | |
| } | | } | |
| | | | |
| skipping to change at line 67 | | skipping to change at line 75 | |
| | | | |
| /* last assert of diff types: regular, wassert, msgassert, uassert: */ | | /* last assert of diff types: regular, wassert, msgassert, uassert: */ | |
| extern Assertion lastAssert[4]; | | extern Assertion lastAssert[4]; | |
| | | | |
| class DBException : public std::exception { | | class DBException : public std::exception { | |
| public: | | public: | |
| virtual const char* what() const throw() = 0; | | virtual const char* what() const throw() = 0; | |
| virtual string toString() const { | | virtual string toString() const { | |
| return what(); | | return what(); | |
| } | | } | |
|
| | | virtual int getCode() = 0; | |
| operator string() const { return toString(); } | | operator string() const { return toString(); } | |
| }; | | }; | |
| | | | |
| class AssertionException : public DBException { | | class AssertionException : public DBException { | |
| public: | | public: | |
|
| | | int code; | |
| string msg; | | string msg; | |
|
| AssertionException() { } | | AssertionException() { code = 0; } | |
| virtual ~AssertionException() throw() { } | | virtual ~AssertionException() throw() { } | |
| virtual bool severe() { | | virtual bool severe() { | |
| return true; | | return true; | |
| } | | } | |
| virtual bool isUserAssertion() { | | virtual bool isUserAssertion() { | |
| return false; | | return false; | |
| } | | } | |
|
| | | virtual int getCode(){ return code; } | |
| virtual const char* what() const throw() { return msg.c_str(); } | | virtual const char* what() const throw() { return msg.c_str(); } | |
| }; | | }; | |
| | | | |
| /* UserExceptions are valid errors that a user can cause, like out of d
isk space or duplicate key */ | | /* UserExceptions are valid errors that a user can cause, like out of d
isk space or duplicate key */ | |
| class UserException : public AssertionException { | | class UserException : public AssertionException { | |
| public: | | public: | |
|
| UserException(const char *_msg) { | | UserException(int c , const string& m) { | |
| msg = _msg; | | code = c; | |
| } | | msg = m; | |
| UserException(string _msg) { | | | |
| msg = _msg; | | | |
| } | | } | |
| virtual bool severe() { | | virtual bool severe() { | |
| return false; | | return false; | |
| } | | } | |
| virtual bool isUserAssertion() { | | virtual bool isUserAssertion() { | |
| return true; | | return true; | |
| } | | } | |
| virtual string toString() const { | | virtual string toString() const { | |
| return "userassert:" + msg; | | return "userassert:" + msg; | |
| } | | } | |
| }; | | }; | |
| | | | |
| class MsgAssertionException : public AssertionException { | | class MsgAssertionException : public AssertionException { | |
| public: | | public: | |
|
| MsgAssertionException(const char *_msg) { | | MsgAssertionException(int c, const char *m) { | |
| msg = _msg; | | code = c; | |
| | | msg = m; | |
| } | | } | |
| virtual bool severe() { | | virtual bool severe() { | |
| return false; | | return false; | |
| } | | } | |
| virtual string toString() const { | | virtual string toString() const { | |
| return "massert:" + msg; | | return "massert:" + msg; | |
| } | | } | |
| }; | | }; | |
| | | | |
| void asserted(const char *msg, const char *file, unsigned line); | | void asserted(const char *msg, const char *file, unsigned line); | |
| void wasserted(const char *msg, const char *file, unsigned line); | | void wasserted(const char *msg, const char *file, unsigned line); | |
|
| void uasserted(const char *msg); | | void uasserted(int msgid, const char *msg); | |
| inline void uasserted(string msg) { uasserted(msg.c_str()); } | | inline void uasserted(int msgid , string msg) { uasserted(msgid, msg.c_ | |
| | | str()); } | |
| void uassert_nothrow(const char *msg); // reported via lasterror, but d
on't throw exception | | void uassert_nothrow(const char *msg); // reported via lasterror, but d
on't throw exception | |
|
| void msgasserted(const char *msg); | | void msgasserted(int msgid, const char *msg); | |
| inline void msgasserted(string msg) { msgasserted(msg.c_str()); } | | inline void msgasserted(int msgid, string msg) { msgasserted(msgid, msg | |
| | | .c_str()); } | |
| | | | |
| #ifdef assert | | #ifdef assert | |
| #undef assert | | #undef assert | |
| #endif | | #endif | |
| | | | |
| #define assert(_Expression) (void)( (!!(_Expression)) || (mongo::asserted(#
_Expression, __FILE__, __LINE__), 0) ) | | #define assert(_Expression) (void)( (!!(_Expression)) || (mongo::asserted(#
_Expression, __FILE__, __LINE__), 0) ) | |
| | | | |
| /* "user assert". if asserts, user did something wrong, not our code *
/ | | /* "user assert". if asserts, user did something wrong, not our code *
/ | |
|
| //#define uassert(_Expression) (void)( (!!(_Expression)) || (uasserted(#_Ex | | //#define uassert( 10269 , _Expression) (void)( (!!(_Expression)) || (uasse | |
| pression, __FILE__, __LINE__), 0) ) | | rted(#_Expression, __FILE__, __LINE__), 0) ) | |
| #define uassert(msg,_Expression) (void)( (!!(_Expression)) || (mongo::uasse | | #define uassert(msgid, msg,_Expression) (void)( (!!(_Expression)) || (mongo | |
| rted(msg), 0) ) | | ::uasserted(msgid, msg), 0) ) | |
| | | | |
| #define xassert(_Expression) (void)( (!!(_Expression)) || (mongo::asserted(
#_Expression, __FILE__, __LINE__), 0) ) | | #define xassert(_Expression) (void)( (!!(_Expression)) || (mongo::asserted(
#_Expression, __FILE__, __LINE__), 0) ) | |
| | | | |
| #define yassert 1 | | #define yassert 1 | |
| | | | |
| /* warning only - keeps going */ | | /* warning only - keeps going */ | |
| #define wassert(_Expression) (void)( (!!(_Expression)) || (mongo::wasserted
(#_Expression, __FILE__, __LINE__), 0) ) | | #define wassert(_Expression) (void)( (!!(_Expression)) || (mongo::wasserted
(#_Expression, __FILE__, __LINE__), 0) ) | |
| | | | |
| /* display a message, no context, and throw assertionexception | | /* display a message, no context, and throw assertionexception | |
| | | | |
| easy way to throw an exception and log something without our stack t
race | | easy way to throw an exception and log something without our stack t
race | |
| display happening. | | display happening. | |
| */ | | */ | |
|
| #define massert(msg,_Expression) (void)( (!!(_Expression)) || (mongo::msgas
serted(msg), 0) ) | | #define massert(msgid, msg,_Expression) (void)( (!!(_Expression)) || (mongo
::msgasserted(msgid, msg), 0) ) | |
| | | | |
| /* dassert is 'debug assert' -- might want to turn off for production a
s these | | /* dassert is 'debug assert' -- might want to turn off for production a
s these | |
| could be slow. | | could be slow. | |
| */ | | */ | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| #define dassert assert | | #define dassert assert | |
| #else | | #else | |
| #define dassert(x) | | #define dassert(x) | |
| #endif | | #endif | |
| | | | |
|
| | | // some special ids that we want to duplicate | |
| | | | |
| | | // > 10000 asserts | |
| | | // < 10000 UserException | |
| | | | |
| | | #define ASSERT_ID_DUPKEY 11000 | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #define BOOST_CHECK_EXCEPTION( expression ) \ | | #define BOOST_CHECK_EXCEPTION( expression ) \ | |
| try { \ | | try { \ | |
| expression; \ | | expression; \ | |
| } catch ( const std::exception &e ) { \ | | } catch ( const std::exception &e ) { \ | |
| problem() << "caught boost exception: " << e.what() << endl;
\ | | problem() << "caught boost exception: " << e.what() << endl;
\ | |
| assert( false ); \ | | assert( false ); \ | |
| } catch ( ... ) { \ | | } catch ( ... ) { \ | |
|
| massert( "unknown boost failed" , false ); \ | | massert( 10437 , "unknown boost failed" , false ); \ | |
| } | | } | |
| | | | |
End of changes. 15 change blocks. |
| 18 lines changed or deleted | | 37 lines changed or added | |
|
| 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 _liveForever; // if true, never time out | | bool _noTimeout; // if true, never time out c | |
| cursor | | ursor | |
| | | 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: | |
| /*const*/ CursorId cursorid; | | /*const*/ CursorId cursorid; | |
| string ns; | | string ns; | |
|
| auto_ptr<KeyValJSMatcher> 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), _liveForever(false), pos(0) { | | ClientCursor() : _idleAgeMillis(0), _noTimeout(false), _doingDelete
s(false), 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; | |
| } | | } | |
| | | | |
| auto_ptr< FieldMatcher > filter; // which fields query wants return
ed | | auto_ptr< FieldMatcher > filter; // which fields query wants return
ed | |
| Message originalMessage; // this is effectively an auto ptr for dat
a the matcher points to | | Message originalMessage; // this is effectively an auto ptr for dat
a the matcher points to | |
| | | | |
| /* Get rid of cursors for namespaces that begin with nsprefix. | | /* Get rid of cursors for namespaces that begin with nsprefix. | |
| Used by drop, deleteIndexes, dropDatabase. | | Used by drop, deleteIndexes, dropDatabase. | |
| */ | | */ | |
| static void invalidate(const char *nsPrefix); | | static void invalidate(const char *nsPrefix); | |
| | | | |
|
| | | /** | |
| | | * do a dbtemprelease | |
| | | * note: caller should check matcher.docMatcher().atomic() first an | |
| | | d not yield if atomic - | |
| | | * we don't do herein as this->matcher (above) is only initia | |
| | | lized for true queries/getmore. | |
| | | * (ie not set for remote/update) | |
| | | * @return if the cursor is still valid. | |
| | | * if false is returned, then this ClientCursor should be c | |
| | | onsidered deleted | |
| | | */ | |
| | | bool yield(); | |
| private: | | private: | |
| void setLastLoc_inlock(DiskLoc); | | void setLastLoc_inlock(DiskLoc); | |
| | | | |
| static ClientCursor* find_inlock(CursorId id, bool warn = true) { | | static ClientCursor* find_inlock(CursorId id, bool warn = true) { | |
| CCById::iterator it = clientCursorsById.find(id); | | CCById::iterator it = clientCursorsById.find(id); | |
| if ( it == clientCursorsById.end() ) { | | if ( it == clientCursorsById.end() ) { | |
| if ( warn ) | | if ( warn ) | |
| OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | | OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | |
| return 0; | | return 0; | |
| } | | } | |
| | | | |
| skipping to change at line 137 | | skipping to change at line 147 | |
| 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 ! _liveForever && _idleAgeMillis > 600000; | | return ! _noTimeout && _idleAgeMillis > 600000; | |
| } | | } | |
| | | | |
| unsigned idleTime(){ | | unsigned idleTime(){ | |
| return _idleAgeMillis; | | return _idleAgeMillis; | |
| } | | } | |
| | | | |
| static void idleTimeReport(unsigned millis); | | static void idleTimeReport(unsigned millis); | |
| | | | |
|
| void liveForever() { | | void noTimeout() { | |
| _liveForever = true; | | _noTimeout = true; | |
| | | } | |
| | | | |
| | | void setDoingDeletes( bool doingDeletes ){ | |
| | | _doingDeletes = doingDeletes; | |
| } | | } | |
| | | | |
| static unsigned byLocSize(); // just for diagnostics | | static unsigned byLocSize(); // just for diagnostics | |
| // static void idleTimeReport(unsigned millis); | | // 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. 6 change blocks. |
| 7 lines changed or deleted | | 24 lines changed or added | |
|
| concurrency.h | | concurrency.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| | | | |
| End func name with _inlock to indicate "caller must lock before callin
g". | | End func name with _inlock to indicate "caller must lock before callin
g". | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #if BOOST_VERSION >= 103500 | | #if BOOST_VERSION >= 103500 | |
| #include <boost/thread/shared_mutex.hpp> | | #include <boost/thread/shared_mutex.hpp> | |
| #undef assert | | #undef assert | |
| #define assert xassert | | #define assert xassert | |
|
| #endif | | | |
| | | | |
| namespace mongo { | | | |
| | | | |
| #if 0 | | | |
| //#if BOOST_VERSION >= 103500 | | | |
| //typedef boost::shared_mutex MongoMutex; | | | |
| class MongoMutex { | | | |
| boost::shared_mutex m; | | | |
| public: | | | |
| void lock() { | | | |
| m.lock(); | | | |
| } | | | |
| void unlock() { m.unlock(); } | | | |
| void lock_shared() { m.lock_shared(); } | | | |
| void unlock_shared() { m.unlock_shared(); } | | | |
| }; | | | |
| #else | | | |
| /* this will be for old versions of boost */ | | | |
| class MongoMutex { | | | |
| boost::recursive_mutex m; | | | |
| int x; | | | |
| public: | | | |
| MongoMutex() { x=0; } | | | |
| void lock() { | | | |
| #if BOOST_VERSION >= 103500 | | | |
| m.lock(); | | | |
| #else | | #else | |
|
| boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m | | #warning built with boost version 1.34 or older limited concurrency | |
| ); | | | |
| #endif | | | |
| } | | | |
| | | | |
| void unlock() { | | | |
| #if BOOST_VERSION >= 103500 | | | |
| m.unlock(); | | | |
| #else | | | |
| boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock | | | |
| (m); | | | |
| #endif | | #endif | |
|
| } | | | |
| | | | |
|
| void lock_shared() { lock(); } | | namespace mongo { | |
| void unlock_shared() { unlock(); } | | | |
| }; | | | |
| #endif | | | |
| | | | |
| /* mutex time stats */ | | /* mutex time stats */ | |
| class MutexInfo { | | class MutexInfo { | |
| unsigned long long start, enter, timeLocked; // all in microseconds | | unsigned long long start, enter, timeLocked; // all in microseconds | |
| int locked; | | int locked; | |
| | | | |
| public: | | public: | |
| MutexInfo() : locked(0) { | | MutexInfo() : locked(0) { | |
| start = curTimeMicros64(); | | start = curTimeMicros64(); | |
| } | | } | |
| | | | |
| skipping to change at line 89 | | skipping to change at line 50 | |
| } | | } | |
| void leaving() { | | void leaving() { | |
| locked--; | | locked--; | |
| assert( locked >= 0 ); | | assert( locked >= 0 ); | |
| if ( locked == 0 ) | | if ( locked == 0 ) | |
| timeLocked += curTimeMicros64() - enter; | | timeLocked += curTimeMicros64() - enter; | |
| } | | } | |
| int isLocked() const { | | int isLocked() const { | |
| return locked; | | return locked; | |
| } | | } | |
|
| void timingInfo(unsigned long long &s, unsigned long long &tl) { | | void getTimingInfo(unsigned long long &s, unsigned long long &tl) c
onst { | |
| s = start; | | s = start; | |
| tl = timeLocked; | | tl = timeLocked; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| extern MongoMutex &dbMutex; | | #if BOOST_VERSION >= 103500 | |
| extern MutexInfo dbMutexInfo; | | //#if 0 | |
| | | class MongoMutex { | |
| | | MutexInfo _minfo; | |
| | | boost::shared_mutex _m; | |
| | | ThreadLocalValue<int> _state; | |
| | | public: | |
| | | /** | |
| | | * @return | |
| | | * > 0 write lock | |
| | | * = 0 no lock | |
| | | * < 0 read lock | |
| | | */ | |
| | | int getState(){ return _state.get(); } | |
| | | void assertWriteLocked() { assert( _state.get() > 0 ); } | |
| | | bool atLeastReadLocked() { return _state.get() != 0; } | |
| | | void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | |
| | | void lock() { | |
| | | DEV cout << "LOCK" << endl; | |
| | | int s = _state.get(); | |
| | | if( s > 0 ) { | |
| | | _state.set(s+1); | |
| | | return; | |
| | | } | |
| | | massert( 10293 , "internal error: locks are not upgradeable", s | |
| | | == 0 ); | |
| | | _state.set(1); | |
| | | _m.lock(); | |
| | | _minfo.entered(); | |
| | | } | |
| | | void unlock() { | |
| | | DEV cout << "UNLOCK" << endl; | |
| | | int s = _state.get(); | |
| | | if( s > 1 ) { | |
| | | _state.set(s-1); | |
| | | return; | |
| | | } | |
| | | assert( s == 1 ); | |
| | | _state.set(0); | |
| | | _minfo.leaving(); | |
| | | _m.unlock(); | |
| | | } | |
| | | void lock_shared() { | |
| | | DEV cout << " LOCKSHARED" << endl; | |
| | | int s = _state.get(); | |
| | | if( s ) { | |
| | | if( s > 0 ) { | |
| | | // already in write lock - just be recursive and stay w | |
| | | rite locked | |
| | | _state.set(s+1); | |
| | | return; | |
| | | } | |
| | | else { | |
| | | // already in read lock - recurse | |
| | | _state.set(s-1); | |
| | | return; | |
| | | } | |
| | | } | |
| | | _state.set(-1); | |
| | | _m.lock_shared(); | |
| | | } | |
| | | void unlock_shared() { | |
| | | DEV cout << " UNLOCKSHARED" << endl; | |
| | | int s = _state.get(); | |
| | | if( s > 0 ) { | |
| | | assert( s > 1 ); /* we must have done a lock write first to | |
| | | have s > 1 */ | |
| | | _state.set(s-1); | |
| | | return; | |
| | | } | |
| | | if( s < -1 ) { | |
| | | _state.set(s+1); | |
| | | return; | |
| | | } | |
| | | assert( s == -1 ); | |
| | | _state.set(0); | |
| | | _m.unlock_shared(); | |
| | | } | |
| | | MutexInfo& info() { return _minfo; } | |
| | | }; | |
| | | #else | |
| | | /* this will be for old versions of boost */ | |
| | | class MongoMutex { | |
| | | MutexInfo _minfo; | |
| | | boost::recursive_mutex m; | |
| | | public: | |
| | | MongoMutex() { } | |
| | | void lock() { | |
| | | #if BOOST_VERSION >= 103500 | |
| | | m.lock(); | |
| | | #else | |
| | | boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m | |
| | | ); | |
| | | #endif | |
| | | _minfo.entered(); | |
| | | } | |
| | | | |
|
| /* | | void unlock() { | |
| struct lock { | | _minfo.leaving(); | |
| recursive_boostlock bl_; | | #if BOOST_VERSION >= 103500 | |
| MutexInfo& info_; | | m.unlock(); | |
| lock( boost::recursive_mutex &mutex, MutexInfo &info ) : | | #else | |
| bl_( mutex ), | | boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock | |
| info_( info ) { | | (m); | |
| info_.entered(); | | #endif | |
| } | | } | |
|
| ~lock() { | | | |
| info_.leaving(); | | void lock_shared() { lock(); } | |
| | | void unlock_shared() { unlock(); } | |
| | | MutexInfo& info() { return _minfo; } | |
| | | void assertWriteLocked() { | |
| | | assert( info().isLocked() ); | |
| } | | } | |
|
| | | void assertAtLeastReadLocked() { | |
| | | assert( info().isLocked() ); | |
| | | } | |
| | | bool atLeastReadLocked() { return info().isLocked(); } | |
| | | int getState(){ return info().isLocked() ? 1 : 0; } | |
| }; | | }; | |
|
| */ | | #endif | |
| | | | |
| | | extern MongoMutex &dbMutex; | |
| | | | |
| void dbunlocking_write(); | | void dbunlocking_write(); | |
| void dbunlocking_read(); | | void dbunlocking_read(); | |
| | | | |
| struct writelock { | | struct writelock { | |
| writelock(const string& ns) { | | writelock(const string& ns) { | |
| dbMutex.lock(); | | dbMutex.lock(); | |
|
| dbMutexInfo.entered(); | | | |
| } | | } | |
| ~writelock() { | | ~writelock() { | |
| dbunlocking_write(); | | dbunlocking_write(); | |
|
| dbMutexInfo.leaving(); | | | |
| dbMutex.unlock(); | | dbMutex.unlock(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| struct readlock { | | struct readlock { | |
| readlock(const string& ns) { | | readlock(const string& ns) { | |
| dbMutex.lock_shared(); | | dbMutex.lock_shared(); | |
| } | | } | |
| ~readlock() { | | ~readlock() { | |
| dbunlocking_read(); | | dbunlocking_read(); | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| class mongolock { | | class mongolock { | |
| bool _writelock; | | bool _writelock; | |
| public: | | public: | |
| mongolock(bool write) : _writelock(write) { | | mongolock(bool write) : _writelock(write) { | |
| if( _writelock ) { | | if( _writelock ) { | |
| dbMutex.lock(); | | dbMutex.lock(); | |
|
| dbMutexInfo.entered(); | | | |
| } | | } | |
| else | | else | |
| dbMutex.lock_shared(); | | dbMutex.lock_shared(); | |
| } | | } | |
| ~mongolock() { | | ~mongolock() { | |
| if( _writelock ) { | | if( _writelock ) { | |
| dbunlocking_write(); | | dbunlocking_write(); | |
|
| dbMutexInfo.leaving(); | | | |
| dbMutex.unlock(); | | dbMutex.unlock(); | |
| } | | } | |
| else { | | else { | |
| dbunlocking_read(); | | dbunlocking_read(); | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| } | | } | |
| } | | } | |
| /* this unlocks, does NOT upgrade. that works for our current usage
*/ | | /* this unlocks, does NOT upgrade. that works for our current usage
*/ | |
|
| void releaseAndWriteLock() { | | void releaseAndWriteLock(); | |
| if( !_writelock ) { | | | |
| _writelock = true; | | | |
| dbMutex.unlock_shared(); | | | |
| dbMutex.lock(); | | | |
| dbMutexInfo.entered(); | | | |
| } | | | |
| } | | | |
| }; | | }; | |
| | | | |
| /* use writelock and readlock instead */ | | /* use writelock and readlock instead */ | |
| struct dblock : public writelock { | | struct dblock : public writelock { | |
| dblock() : writelock("") { } | | dblock() : writelock("") { } | |
| ~dblock() { | | ~dblock() { | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| /* a scoped release of a mutex temporarily -- like a scopedlock but rev | | // eliminate | |
| ersed. | | inline void assertInWriteLock() { dbMutex.assertWriteLocked(); } | |
| */ | | | |
| /* | | | |
| struct temprelease { | | | |
| boost::mutex& m; | | | |
| temprelease(boost::mutex& _m) : m(_m) { | | | |
| #if BOOST_VERSION >= 103500 | | | |
| m.unlock(); | | | |
| #else | | | |
| boost::detail::thread::lock_ops<boost::mutex>::unlock(m); | | | |
| #endif | | | |
| } | | | |
| ~temprelease() { | | | |
| #if BOOST_VERSION >= 103500 | | | |
| m.lock(); | | | |
| #else | | | |
| boost::detail::thread::lock_ops<boost::mutex>::lock(m); | | | |
| #endif | | | |
| } | | | |
| }; | | | |
| */ | | | |
| | | | |
| inline void assertInWriteLock() { | | | |
| /* TEMP assert( dbMutexInfo.isLocked() ); | | | |
| */ | | | |
| } | | | |
| | | | |
| } | | } | |
| | | | |
End of changes. 16 change blocks. |
| 96 lines changed or deleted | | 124 lines changed or added | |
|
| curop.h | | curop.h | |
| // curop.h | | // curop.h | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "namespace.h" | | #include "namespace.h" | |
| #include "security.h" | | #include "security.h" | |
| #include "client.h" | | #include "client.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| struct CurOp { | | class OpDebug { | |
| void reset(time_t now, const sockaddr_in &_client) { | | public: | |
| active = true; | | StringBuilder str; | |
| opNum++; | | | |
| startTime = now; | | void reset(){ | |
| ns[0] = '?'; // just in case not set later | | str.reset(); | |
| *query = 0; | | | |
| killCurrentOp = 0; | | | |
| client = _client; | | | |
| } | | } | |
|
| | | }; | |
| | | | |
|
| bool active; | | /* Current operation (for the current Client). | |
| unsigned opNum; | | an embedded member of Client class, and typically used from within t | |
| time_t startTime; | | he mutex there. */ | |
| int op; | | class CurOp : boost::noncopyable { | |
| char ns[Namespace::MaxNsLen+2]; | | static WrappingInt _nextOpNum; | |
| char query[128]; | | static BSONObj _tooBig; // { $msg : "query not recording (too large | |
| char zero; | | )" } | |
| | | | |
| | | bool _active; | |
| | | Timer _timer; | |
| | | int _op; | |
| | | WrappingInt _opNum; | |
| | | char _ns[Namespace::MaxNsLen+2]; | |
| struct sockaddr_in client; | | struct sockaddr_in client; | |
| | | | |
|
| | | char _queryBuf[256]; | |
| | | bool haveQuery() const { return *((int *) _queryBuf) != 0; } | |
| | | void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | |
| | | BSONObj query() { | |
| | | if( *((int *) _queryBuf) == 1 ) { | |
| | | return _tooBig; | |
| | | } | |
| | | BSONObj o(_queryBuf); | |
| | | return o; | |
| | | } | |
| | | | |
| | | OpDebug _debug; | |
| | | public: | |
| | | void reset( const sockaddr_in &_client) { | |
| | | _active = true; | |
| | | _opNum = _nextOpNum.atomicIncrement(); | |
| | | _timer.reset(); | |
| | | _ns[0] = '?'; // just in case not set later | |
| | | _debug.reset(); | |
| | | resetQuery(); | |
| | | client = _client; | |
| | | } | |
| | | | |
| | | OpDebug& debug(){ | |
| | | return _debug; | |
| | | } | |
| | | | |
| | | WrappingInt opNum() const { return _opNum; } | |
| | | bool active() const { return _active; } | |
| | | | |
| | | int elapsedMillis(){ return _timer.millis(); } | |
| | | | |
| | | /** micros */ | |
| | | unsigned long long startTime(){ | |
| | | return _timer.startTime(); | |
| | | } | |
| | | | |
| | | void setActive(bool active) { _active = active; } | |
| | | void setNS(const char *ns) { | |
| | | strncpy(_ns, ns, Namespace::MaxNsLen); | |
| | | } | |
| | | void setOp(int op) { _op = op; } | |
| | | void setQuery(const BSONObj& query) { | |
| | | if( query.objsize() > (int) sizeof(_queryBuf) ) { | |
| | | resetQuery(1); // flag as too big and return | |
| | | return; | |
| | | } | |
| | | memcpy(_queryBuf, query.objdata(), query.objsize()); | |
| | | } | |
| | | | |
| CurOp() { | | CurOp() { | |
|
| active = false; | | _active = false; | |
| opNum = 0; | | // opNum = 0; | |
| | | _op = 0; | |
| // These addresses should never be written to again. The zeroe
s are | | // These addresses should never be written to again. The zeroe
s are | |
| // placed here as a precaution because currentOp may be accesse
d | | // placed here as a precaution because currentOp may be accesse
d | |
| // without the db mutex. | | // without the db mutex. | |
|
| ns[sizeof(ns)-1] = 0; | | memset(_ns, 0, sizeof(_ns)); | |
| query[sizeof(query)-1] = 0; | | memset(_queryBuf, 0, sizeof(_queryBuf)); | |
| } | | } | |
| | | | |
| BSONObj info() { | | BSONObj info() { | |
| AuthenticationInfo *ai = currentClient.get()->ai; | | AuthenticationInfo *ai = currentClient.get()->ai; | |
| if( !ai->isAuthorized("admin") ) { | | if( !ai->isAuthorized("admin") ) { | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
| b.append("err", "unauthorized"); | | b.append("err", "unauthorized"); | |
| return b.obj(); | | return b.obj(); | |
| } | | } | |
| return infoNoauth(); | | return infoNoauth(); | |
| } | | } | |
| | | | |
| BSONObj infoNoauth() { | | BSONObj infoNoauth() { | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
|
| b.append("opid", opNum); | | b.append("opid", _opNum); | |
| b.append("active", active); | | b.append("active", _active); | |
| if( active ) | | if( _active ) | |
| b.append("secs_running", (int) (time(0)-startTime)); | | b.append("secs_running", _timer.seconds() ); | |
| if( op == 2004 ) | | if( _op == 2004 ) | |
| b.append("op", "query"); | | b.append("op", "query"); | |
|
| else if( op == 2005 ) | | else if( _op == 2005 ) | |
| b.append("op", "getMore"); | | b.append("op", "getMore"); | |
|
| else if( op == 2001 ) | | else if( _op == 2001 ) | |
| b.append("op", "update"); | | b.append("op", "update"); | |
|
| else if( op == 2002 ) | | else if( _op == 2002 ) | |
| b.append("op", "insert"); | | b.append("op", "insert"); | |
|
| else if( op == 2006 ) | | else if( _op == 2006 ) | |
| b.append("op", "delete"); | | b.append("op", "delete"); | |
| else | | else | |
|
| b.append("op", op); | | b.append("op", _op); | |
| b.append("ns", ns); | | b.append("ns", _ns); | |
| b.append("query", query); | | | |
| b.append("inLock", dbMutexInfo.isLocked()); | | if( haveQuery() ) { | |
| | | b.append("query", query()); | |
| | | } | |
| | | // b.append("inLock", ?? | |
| stringstream clientStr; | | stringstream clientStr; | |
| clientStr << inet_ntoa( client.sin_addr ) << ":" << ntohs( clie
nt.sin_port ); | | clientStr << inet_ntoa( client.sin_addr ) << ":" << ntohs( clie
nt.sin_port ); | |
| b.append("client", clientStr.str()); | | b.append("client", clientStr.str()); | |
| return b.obj(); | | return b.obj(); | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | /* 0 = ok | |
| | | 1 = kill current operation and reset this to 0 | |
| | | future: maybe use this as a "going away" thing on process terminatio | |
| | | n with a higher flag value | |
| | | */ | |
| | | extern class KillCurrentOp { | |
| | | enum { Off, On, All } state; | |
| | | WrappingInt toKill; | |
| | | public: | |
| | | void killAll() { state = All; } | |
| | | void kill(WrappingInt i) { toKill = i; state = On; } | |
| | | | |
| | | void checkForInterrupt() { | |
| | | if( state != Off ) { | |
| | | if( state == All ) | |
| | | uasserted(11600,"interrupted at shutdown"); | |
| | | if( cc().curop()->opNum() == toKill ) { | |
| | | state = Off; | |
| | | uasserted(11601,"interrupted"); | |
| | | } | |
| | | } | |
| | | } | |
| | | } killCurrentOp; | |
| } | | } | |
| | | | |
End of changes. 13 change blocks. |
| 33 lines changed or deleted | | 114 lines changed or added | |
|
| database.h | | database.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * 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" | |
| | | | |
| /* Database represents a database database | | /* Database represents a database database | |
| Each database database has its own set of files -- dbname.ns, dbname.0,
dbname.1, ... | | Each database database has its own set of files -- dbname.ns, dbname.0,
dbname.1, ... | |
| */ | | */ | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class Database { | | class Database { | |
| public: | | public: | |
|
| | | 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), | | name(nm), | |
| path(_path), | | path(_path), | |
| namespaceIndex( path, name ) | | namespaceIndex( path, name ) | |
| { | | { | |
| { | | { | |
| int L = strlen(nm); | | int L = strlen(nm); | |
|
| uassert( "db name is empty", L > 0 ); | | uassert( 10028 , "db name is empty", L > 0 ); | |
| uassert( "bad db name [1]", *nm != '.' ); | | uassert( 10029 , "bad db name [1]", *nm != '.' ); | |
| uassert( "bad db name [2]", nm[L-1] != '.' ); | | uassert( 10030 , "bad db name [2]", nm[L-1] != '.' ); | |
| uassert( "bad char(s) in db name", strchr(nm, ' ') == 0 ); | | uassert( 10031 , "bad char(s) in db name", strchr(nm, ' ') | |
| uassert( "db name too long", L < 64 ); | | == 0 ); | |
| | | uassert( 10032 , "db name too long", L < 64 ); | |
| } | | } | |
| | | | |
| newDb = namespaceIndex.exists(); | | newDb = namespaceIndex.exists(); | |
|
| | | profile = 0; | |
| | | profileName = name + ".system.profile"; | |
| | | | |
| // 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 ) | | if ( ! newDb || cmdLine.defaultProfile ) { | |
| namespaceIndex.init(); | | namespaceIndex.init(); | |
|
| profile = 0; | | if( _openAllFiles ) | |
| profileName = name + ".system.profile"; | | openAllFiles(); | |
| | | | |
| | | } | |
| | | | |
| } | | } | |
|
| | | | |
| ~Database() { | | ~Database() { | |
| 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]; | |
| } | | } | |
| | | | |
|
| | | bool exists(int n) { | |
| | | stringstream ss; | |
| | | ss << name << '.' << n; | |
| | | boost::filesystem::path fullName; | |
| | | fullName = boost::filesystem::path(path) / ss.str(); | |
| | | return boost::filesystem::exists(fullName); | |
| | | } | |
| | | | |
| | | void openAllFiles() { | |
| | | int n = 0; | |
| | | while( exists(n) ) { | |
| | | getFile(n); | |
| | | n++; | |
| | | } | |
| | | } | |
| | | | |
| 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 ) | |
|
| massert("getFile(): bad file number - using recstore db
w/nonrecstore db build?", false); | | massert( 10294 , "getFile(): bad file number - using re
cstore db w/nonrecstore db build?", false); | |
| #endif | | #endif | |
|
| massert("getFile(): bad file number value (corrupt db?): ru
n repair", false); | | massert( 10295 , "getFile(): bad file number value (corrupt
db?): run repair", false); | |
| } | | } | |
| DEV { | | DEV { | |
| if ( n > 100 ) | | if ( n > 100 ) | |
| out() << "getFile(): n=" << n << "?" << endl; | | out() << "getFile(): n=" << n << "?" << endl; | |
| } | | } | |
| MongoDataFile* p = 0; | | MongoDataFile* p = 0; | |
| if ( !preallocateOnly ) { | | if ( !preallocateOnly ) { | |
| while ( n >= (int) files.size() ) | | while ( n >= (int) files.size() ) | |
| files.push_back(0); | | files.push_back(0); | |
| p = files[n]; | | p = files[n]; | |
| | | | |
| skipping to change at line 115 | | skipping to change at line 142 | |
| } | | } | |
| | | | |
| MongoDataFile* addAFile( int sizeNeeded = 0, bool preallocateNextFi
le = false ) { | | MongoDataFile* addAFile( int sizeNeeded = 0, bool preallocateNextFi
le = false ) { | |
| int n = (int) files.size(); | | int n = (int) files.size(); | |
| MongoDataFile *ret = getFile( n, sizeNeeded ); | | MongoDataFile *ret = getFile( n, sizeNeeded ); | |
| if ( preallocateNextFile ) | | if ( preallocateNextFile ) | |
| preallocateAFile(); | | preallocateAFile(); | |
| return ret; | | return ret; | |
| } | | } | |
| | | | |
|
| // ok to call multiple times | | // safe to call this multiple times - the implementation will only
preallocate one file | |
| void preallocateAFile() { | | void preallocateAFile() { | |
| int n = (int) files.size(); | | int n = (int) files.size(); | |
| getFile( n, 0, true ); | | getFile( n, 0, true ); | |
| } | | } | |
| | | | |
| MongoDataFile* suitableFile( int sizeNeeded ) { | | MongoDataFile* suitableFile( int sizeNeeded ) { | |
| MongoDataFile* f = newestFile(); | | MongoDataFile* f = newestFile(); | |
| for ( int i = 0; i < 8; i++ ) { | | for ( int i = 0; i < 8; i++ ) { | |
| if ( f->getHeader()->unusedLength >= sizeNeeded ) | | if ( f->getHeader()->unusedLength >= sizeNeeded ) | |
| break; | | break; | |
| f = addAFile( sizeNeeded ); | | f = addAFile( sizeNeeded ); | |
| if ( f->getHeader()->fileLength >= MongoDataFile::maxSize()
) // this is as big as they get so might as well stop | | if ( f->getHeader()->fileLength >= MongoDataFile::maxSize()
) // this is as big as they get so might as well stop | |
| break; | | break; | |
| } | | } | |
| return f; | | return f; | |
| } | | } | |
| | | | |
|
| | | Extent* allocExtent( const char *ns, int size, bool capped ) { | |
| | | Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped ); | |
| | | if( e ) return e; | |
| | | return suitableFile( size )->createExtent( ns, size, capped ); | |
| | | } | |
| | | | |
| MongoDataFile* newestFile() { | | MongoDataFile* newestFile() { | |
| int n = (int) files.size(); | | int n = (int) files.size(); | |
| if ( n > 0 ) n--; | | if ( n > 0 ) n--; | |
| return getFile(n); | | return getFile(n); | |
| } | | } | |
| | | | |
|
| void finishInit(); // ugly... | | /** | |
| | | * @return true if success, false otherwise | |
| | | */ | |
| | | bool setProfilingLevel( int newLevel , string& errmsg ); | |
| | | | |
| | | 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" | |
| | | | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 13 change blocks. |
| 12 lines changed or deleted | | 51 lines changed or added | |
|
| db.h | | db.h | |
| | | | |
| skipping to change at line 55 | | skipping to change at line 55 | |
| extern map<string,Database*> databases; | | extern map<string,Database*> databases; | |
| extern bool master; | | extern bool master; | |
| | | | |
| /* sometimes we deal with databases with the same name in different dir
ectories - thus this */ | | /* sometimes we deal with databases with the same name in different dir
ectories - thus this */ | |
| inline string makeDbKeyStr( const char *ns, const string& path ) { | | inline string makeDbKeyStr( const char *ns, const string& path ) { | |
| char cl[256]; | | char cl[256]; | |
| nsToClient(ns, cl); | | nsToClient(ns, cl); | |
| return string( cl ) + ":" + path; | | return string( cl ) + ":" + path; | |
| } | | } | |
| | | | |
|
| | | inline void resetClient(const char *ns, const string& path=dbpath) { | |
| | | dbMutex.assertAtLeastReadLocked(); | |
| | | string key = makeDbKeyStr( ns, path ); | |
| | | map<string,Database*>::iterator it = databases.find(key); | |
| | | if ( it != databases.end() ) { | |
| | | cc().setns(ns, it->second); | |
| | | return; | |
| | | } | |
| | | assert(false); | |
| | | } | |
| | | | |
| /* 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) { | | inline bool setClient(const char *ns, const string& path=dbpath, mongol
ock *lock = 0) { | |
| if( logLevel > 5 ) | | if( logLevel > 5 ) | |
| log() << "setClient: " << ns << endl; | | log() << "setClient: " << ns << endl; | |
| | | | |
|
| | | dbMutex.assertAtLeastReadLocked(); | |
| | | | |
| cc().top.clientStart( ns ); | | cc().top.clientStart( ns ); | |
| | | | |
| string key = makeDbKeyStr( ns, path ); | | string key = makeDbKeyStr( ns, path ); | |
| map<string,Database*>::iterator it = databases.find(key); | | map<string,Database*>::iterator it = databases.find(key); | |
| if ( it != databases.end() ) { | | if ( it != databases.end() ) { | |
| cc().setns(ns, it->second); | | cc().setns(ns, it->second); | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| | | if( lock ) | |
| | | lock->releaseAndWriteLock(); | |
| | | | |
| // when master for replication, we advertise all the db's, and that | | // when master for replication, we advertise all the db's, and that | |
| // looks like a 'first operation'. so that breaks this log message'
s | | // looks like a 'first operation'. so that breaks this log message'
s | |
| // meaningfulness. instead of fixing (which would be better), we j
ust | | // meaningfulness. instead of fixing (which would be better), we j
ust | |
| // stop showing for now. | | // stop showing for now. | |
| // 2008-12-22 We now open every database on startup, so this log is | | // 2008-12-22 We now open every database on startup, so this log is | |
| // no longer helpful. Commenting. | | // no longer helpful. Commenting. | |
|
| // if( !master ) | | // if( !master ) | |
| // log() << "first operation for database " << key << endl; | | // log() << "first operation for database " << key << endl; | |
| | | | |
| assertInWriteLock(); | | assertInWriteLock(); | |
| | | | |
| char cl[256]; | | char cl[256]; | |
| nsToClient(ns, cl); | | nsToClient(ns, cl); | |
| bool justCreated; | | bool justCreated; | |
| Database *newdb = new Database(cl, justCreated, path); | | Database *newdb = new Database(cl, justCreated, path); | |
| databases[key] = newdb; | | databases[key] = newdb; | |
|
| newdb->finishInit(); | | | |
| cc().setns(ns, newdb); | | cc().setns(ns, newdb); | |
| | | | |
|
| | | newdb->finishInit(); | |
| | | | |
| return justCreated; | | return justCreated; | |
| } | | } | |
| | | | |
| // shared functionality for removing references to a database from this pro
gram instance | | // shared functionality for removing references to a database from this pro
gram instance | |
| // does not delete the files on disk | | // does not delete the files on disk | |
|
| void closeClient( const char *cl, const string& path = dbpath ); | | void closeDatabase( const char *cl, const string& path = dbpath ); | |
| | | | |
| /* remove database from the databases map */ | | /* remove database from the databases map */ | |
| inline void eraseDatabase( const char *ns, const string& path=dbpath )
{ | | inline void eraseDatabase( const char *ns, const string& path=dbpath )
{ | |
| string key = makeDbKeyStr( ns, path ); | | string key = makeDbKeyStr( ns, path ); | |
| databases.erase( key ); | | databases.erase( key ); | |
| } | | } | |
| | | | |
| inline bool clientIsEmpty() { | | inline bool clientIsEmpty() { | |
| return !cc().database()->namespaceIndex.allocated(); | | return !cc().database()->namespaceIndex.allocated(); | |
| } | | } | |
| | | | |
| struct dbtemprelease { | | struct dbtemprelease { | |
| string clientname; | | string clientname; | |
| string clientpath; | | string clientpath; | |
|
| | | 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; | |
| } | | } | |
| client.top.clientStop(); | | client.top.clientStop(); | |
|
| dbMutexInfo.leaving(); | | locktype = dbMutex.getState(); | |
| dbMutex.unlock(); | | assert( locktype ); | |
| | | if ( locktype > 0 ) { | |
| | | massert( 10298 , "can't temprelease nested w | |
| | | rite lock", locktype == 1); | |
| | | dbMutex.unlock(); | |
| | | } | |
| | | else { | |
| | | massert( 10299 , "can't temprelease nested r | |
| | | ead lock", locktype == -1); | |
| | | dbMutex.unlock_shared(); | |
| | | } | |
| } | | } | |
| ~dbtemprelease() { | | ~dbtemprelease() { | |
|
| dbMutex.lock(); | | if ( locktype > 0 ) | |
| dbMutexInfo.entered(); | | dbMutex.lock(); | |
| | | else | |
| | | dbMutex.lock_shared(); | |
| if ( clientname.empty() ) | | if ( clientname.empty() ) | |
| cc().setns("", 0); | | cc().setns("", 0); | |
| else | | else | |
| setClient(clientname.c_str(), clientpath.c_str()); | | setClient(clientname.c_str(), clientpath.c_str()); | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | /** | |
| | | only does a temp release if we're not nested and have a lock | |
| | | */ | |
| | | struct dbtempreleasecond { | |
| | | dbtemprelease * real; | |
| | | int locktype; | |
| | | | |
| | | dbtempreleasecond(){ | |
| | | real = 0; | |
| | | locktype = dbMutex.getState(); | |
| | | if ( locktype == 1 || locktype == -1 ) | |
| | | real = new dbtemprelease(); | |
| | | } | |
| | | | |
| | | ~dbtempreleasecond(){ | |
| | | if ( real ){ | |
| | | delete real; | |
| | | real = 0; | |
| | | } | |
| | | } | |
| | | | |
| | | }; | |
| | | | |
| | | extern TicketHolder connTicketHolder; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
|
| #include "dbinfo.h" | | //#include "dbinfo.h" | |
| #include "concurrency.h" | | #include "concurrency.h" | |
| | | | |
End of changes. 13 change blocks. |
| 10 lines changed or deleted | | 65 lines changed or added | |
|
| dbclient.h | | dbclient.h | |
| | | | |
| skipping to change at line 300 | | skipping to change at line 300 | |
| /** | | /** | |
| 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; | |
| | | | |
| virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo
ng cursorId, int nToReturn = 0, int options = 0 ) = 0; | | virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo
ng cursorId, int nToReturn = 0, int options = 0 ) = 0; | |
| | | | |
|
| virtual BSONObj findOne(const string &ns, Query query, const BSONOb | | | |
| j *fieldsToReturn = 0, int queryOptions = 0) = 0; | | | |
| | | | |
| virtual void insert( const string &ns, BSONObj obj ) = 0; | | virtual void insert( const string &ns, BSONObj obj ) = 0; | |
| | | | |
| virtual void insert( const string &ns, const vector< BSONObj >& v )
= 0; | | virtual void insert( const string &ns, const vector< BSONObj >& v )
= 0; | |
| | | | |
| virtual void remove( const string &ns , Query query, bool justOne =
0 ) = 0; | | virtual void remove( const string &ns , Query query, bool justOne =
0 ) = 0; | |
| | | | |
| virtual void update( const string &ns , Query query , BSONObj obj ,
bool upsert = 0 , bool multi = 0 ) = 0; | | virtual void update( const string &ns , Query query , BSONObj obj ,
bool upsert = 0 , bool multi = 0 ) = 0; | |
| | | | |
| virtual ~DBClientInterface() { } | | virtual ~DBClientInterface() { } | |
|
| | | | |
| | | /** | |
| | | @return a single object that matches the query. if none do, the | |
| | | n the object is empty | |
| | | @throws AssertionException | |
| | | */ | |
| | | virtual BSONObj findOne(const string &ns, Query query, const BSONOb | |
| | | j *fieldsToReturn = 0, int queryOptions = 0); | |
| | | | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| DB "commands" | | DB "commands" | |
| Basically just invocations of connection.$cmd.findOne({...}); | | Basically just invocations of connection.$cmd.findOne({...}); | |
| */ | | */ | |
| class DBClientWithCommands : public DBClientInterface { | | class DBClientWithCommands : public DBClientInterface { | |
| bool isOk(const BSONObj&); | | bool isOk(const BSONObj&); | |
| set<string> _seenIndexes; | | set<string> _seenIndexes; | |
| public: | | public: | |
| | | | |
| skipping to change at line 414 | | skipping to change at line 419 | |
| /** Reset the previous error state for this connection (accessed vi
a getLastError and | | /** Reset the previous error state for this connection (accessed vi
a getLastError and | |
| getPrevError). Useful when performing several operations at on
ce and then checking | | getPrevError). Useful when performing several operations at on
ce and then checking | |
| for an error after attempting all operations. | | for an error after attempting all operations. | |
| */ | | */ | |
| bool resetError() { return simpleCommand("admin", 0, "reseterror");
} | | bool resetError() { return simpleCommand("admin", 0, "reseterror");
} | |
| | | | |
| /** Delete the specified collection. */ | | /** Delete the specified collection. */ | |
| virtual bool dropCollection( const string &ns ){ | | virtual bool dropCollection( const string &ns ){ | |
| string db = nsGetDB( ns ); | | string db = nsGetDB( ns ); | |
| string coll = nsGetCollection( ns ); | | string coll = nsGetCollection( ns ); | |
|
| uassert( "no collection name", coll.size() ); | | uassert( 10011 , "no collection name", coll.size() ); | |
| | | | |
| BSONObj info; | | BSONObj info; | |
| | | | |
| bool res = runCommand( db.c_str() , BSON( "drop" << coll ) , in
fo ); | | bool res = runCommand( db.c_str() , BSON( "drop" << coll ) , in
fo ); | |
| resetIndexCache(); | | resetIndexCache(); | |
| return res; | | return res; | |
| } | | } | |
| | | | |
| /** Perform a repair and compaction of the specified database. May
take a long time to run. Disk space | | /** Perform a repair and compaction of the specified database. May
take a long time to run. Disk space | |
| must be available equal to the size of the database while repair
ing. | | must be available equal to the size of the database while repair
ing. | |
| | | | |
| skipping to change at line 644 | | skipping to change at line 649 | |
| 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); | | const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0); | |
| | | | |
| /** @param cursorId id of cursor to retrieve | | /** @param cursorId id of cursor to retrieve | |
| @return an handle to a previously allocated cursor | | @return an handle to a previously allocated cursor | |
| @throws AssertionException | | @throws AssertionException | |
| */ | | */ | |
| virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo
ng cursorId, int nToReturn = 0, int options = 0 ); | | virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo
ng cursorId, int nToReturn = 0, int options = 0 ); | |
| | | | |
| /** | | /** | |
|
| @return a single object that matches the query. if none do, the | | | |
| n the object is empty | | | |
| @throws AssertionException | | | |
| */ | | | |
| virtual BSONObj findOne(const string &ns, Query query, const BSONOb | | | |
| j *fieldsToReturn = 0, int queryOptions = 0); | | | |
| | | | |
| /** | | | |
| insert an object into the database | | insert an object into the database | |
| */ | | */ | |
| virtual void insert( const string &ns , BSONObj obj ); | | virtual void insert( const string &ns , BSONObj obj ); | |
| | | | |
| /** | | /** | |
| insert a vector of objects into the database | | insert a vector of objects into the database | |
| */ | | */ | |
| virtual void insert( const string &ns, const vector< BSONObj >& v )
; | | virtual void insert( const string &ns, const vector< BSONObj >& v )
; | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 680 | | skipping to change at line 679 | |
| virtual string getServerAddress() const = 0; | | virtual string getServerAddress() const = 0; | |
| | | | |
| virtual bool isFailed() const = 0; | | virtual bool isFailed() const = 0; | |
| | | | |
| }; | | }; | |
| | | | |
| class DBClientPaired; | | class DBClientPaired; | |
| | | | |
| class ConnectException : public UserException { | | class ConnectException : public UserException { | |
| public: | | public: | |
|
| ConnectException(string msg) : UserException(msg) { } | | ConnectException(string msg) : UserException(9000,msg) { } | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| A basic connection to the database. | | A basic connection to the database. | |
| This is the main entry point for talking to a simple Mongo setup | | This is the main entry point for talking to a simple Mongo setup | |
| */ | | */ | |
| class DBClientConnection : public DBClientBase { | | class DBClientConnection : public DBClientBase { | |
| DBClientPaired *clientPaired; | | DBClientPaired *clientPaired; | |
| auto_ptr<MessagingPort> p; | | auto_ptr<MessagingPort> p; | |
| auto_ptr<SockAddr> server; | | auto_ptr<SockAddr> server; | |
| | | | |
End of changes. 5 change blocks. |
| 13 lines changed or deleted | | 11 lines changed or added | |
|
| dbhelpers.h | | dbhelpers.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| /* db helpers are helper functions and classes that let us easily manipulat
e the local | | /* db helpers are helper functions and classes that let us easily manipulat
e the local | |
| database instance. | | database instance. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| | | #include "../stdafx.h" | |
| | | #include "client.h" | |
| | | #include "db.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | class Cursor; | |
| | | class CoveredIndexMatcher; | |
| | | | |
| | | class CursorIterator { | |
| | | public: | |
| | | CursorIterator( auto_ptr<Cursor> c , BSONObj filter = BSONObj() ); | |
| | | BSONObj next(); | |
| | | bool hasNext(); | |
| | | | |
| | | private: | |
| | | void _advance(); | |
| | | | |
| | | auto_ptr<Cursor> _cursor; | |
| | | auto_ptr<CoveredIndexMatcher> _matcher; | |
| | | BSONObj _o; | |
| | | }; | |
| | | | |
| | | /** | |
| | | all helpers assume locking is handled above them | |
| | | */ | |
| struct Helpers { | | struct Helpers { | |
| | | | |
| /* ensure the specified index exists. | | /* ensure the specified index exists. | |
| | | | |
| @param keyPattern key pattern, e.g., { ts : 1 } | | @param keyPattern key pattern, e.g., { ts : 1 } | |
| @param name index name, e.g., "name_1" | | @param name index name, e.g., "name_1" | |
| | | | |
| This method can be a little (not much) cpu-slow, so you may wish
to use | | This method can be a little (not much) cpu-slow, so you may wish
to use | |
| OCCASIONALLY ensureIndex(...); | | OCCASIONALLY ensureIndex(...); | |
| | | | |
| | | | |
| skipping to change at line 52 | | 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 | |
| -1 no index | | ); | |
| 0 not found | | | |
| 1 found | | static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer | |
| */ | | y = BSONObj() , bool requireIndex = false ); | |
| static int findById(const char *ns, BSONObj query, BSONObj& result | | | |
| ); | | | |
| | | | |
| /* 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. | |
| */ | | */ | |
| static bool getSingleton(const char *ns, BSONObj& result); | | static bool getSingleton(const char *ns, BSONObj& result); | |
| static void putSingleton(const char *ns, BSONObj obj); | | static void putSingleton(const char *ns, BSONObj obj); | |
| | | | |
| skipping to change at line 110 | | skipping to change at line 131 | |
| | | | |
| ~DBContext() { | | ~DBContext() { | |
| cc().setns(oldns.c_str(), olddb); | | 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; | |
| void set( const BSONObj &obj, bool val ); | | void set( const BSONObj &obj, bool val ); | |
| private: | | private: | |
| string name_; | | string name_; | |
| BSONObj key_; | | BSONObj key_; | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 4 change blocks. |
| 9 lines changed or deleted | | 31 lines changed or added | |
|
| engine.h | | engine.h | |
| | | | |
| skipping to change at line 73 | | skipping to change at line 73 | |
| virtual ScriptingFunction createFunction( const char * code ); | | virtual ScriptingFunction createFunction( const char * code ); | |
| | | | |
| /** | | /** | |
| * @return 0 on success | | * @return 0 on success | |
| */ | | */ | |
| virtual int invoke( ScriptingFunction func , const BSONObj& args, i
nt timeoutMs = 0 , bool ignoreReturn = false ) = 0; | | virtual int invoke( ScriptingFunction func , const BSONObj& args, i
nt timeoutMs = 0 , bool ignoreReturn = false ) = 0; | |
| void invokeSafe( ScriptingFunction func , const BSONObj& args, int
timeoutMs = 0 ){ | | void invokeSafe( ScriptingFunction func , const BSONObj& args, int
timeoutMs = 0 ){ | |
| int res = invoke( func , args , timeoutMs ); | | int res = invoke( func , args , timeoutMs ); | |
| if ( res == 0 ) | | if ( res == 0 ) | |
| return; | | return; | |
|
| throw UserException( (string)"invoke failed: " + getError() ); | | throw UserException( 9004 , (string)"invoke failed: " + getErro
r() ); | |
| } | | } | |
| virtual string getError() = 0; | | virtual string getError() = 0; | |
| | | | |
| int invoke( const char* code , const BSONObj& args, int timeoutMs =
0 ); | | int invoke( const char* code , const BSONObj& args, int timeoutMs =
0 ); | |
| void invokeSafe( const char* code , const BSONObj& args, int timeou
tMs = 0 ){ | | void invokeSafe( const char* code , const BSONObj& args, int timeou
tMs = 0 ){ | |
| if ( invoke( code , args , timeoutMs ) == 0 ) | | if ( invoke( code , args , timeoutMs ) == 0 ) | |
| return; | | return; | |
|
| throw UserException( (string)"invoke failed: " + getError() ); | | throw UserException( 9005 , (string)"invoke failed: " + getErro
r() ); | |
| } | | } | |
| | | | |
| virtual bool exec( const string& code , const string& name , bool p
rintResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0
; | | virtual bool exec( const string& code , const string& name , bool p
rintResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0
; | |
| virtual void execSetup( const string& code , const string& name = "
setup" ){ | | virtual void execSetup( const string& code , const string& name = "
setup" ){ | |
| exec( code , name , false , true , true , 0 ); | | exec( code , name , false , true , true , 0 ); | |
| } | | } | |
| virtual bool execFile( const string& filename , bool printResult ,
bool reportError , bool assertOnError, int timeoutMs = 0 ); | | virtual bool execFile( const string& filename , bool printResult ,
bool reportError , bool assertOnError, int timeoutMs = 0 ); | |
| | | | |
| virtual void injectNative( const char *field, NativeFunction func )
= 0; | | virtual void injectNative( const char *field, NativeFunction func )
= 0; | |
| | | | |
| | | | |
| skipping to change at line 106 | | skipping to change at line 106 | |
| /** | | /** | |
| if any changes are made to .system.js, call this | | if any changes are made to .system.js, call this | |
| right now its just global - slightly inefficient, but a lot simple
r | | right now its just global - slightly inefficient, but a lot simple
r | |
| */ | | */ | |
| static void storedFuncMod(); | | static void storedFuncMod(); | |
| | | | |
| static int getNumScopes(){ | | static int getNumScopes(){ | |
| return _numScopes; | | return _numScopes; | |
| } | | } | |
| | | | |
|
| | | static void validateObjectIdString( const string &str ); | |
| | | | |
| protected: | | protected: | |
| | | | |
| virtual ScriptingFunction _createFunction( const char * code ) = 0; | | virtual ScriptingFunction _createFunction( const char * code ) = 0; | |
| | | | |
| string _localDBName; | | string _localDBName; | |
| long long _loadedVersion; | | long long _loadedVersion; | |
| static long long _lastVersion; | | static long long _lastVersion; | |
| map<string,ScriptingFunction> _cachedFunctions; | | map<string,ScriptingFunction> _cachedFunctions; | |
| | | | |
| static int _numScopes; | | static int _numScopes; | |
| }; | | }; | |
| | | | |
| class ScriptEngine : boost::noncopyable { | | class ScriptEngine : boost::noncopyable { | |
| public: | | public: | |
| ScriptEngine(); | | ScriptEngine(); | |
| virtual ~ScriptEngine(); | | virtual ~ScriptEngine(); | |
| | | | |
|
| virtual Scope * createScope() = 0; | | virtual Scope * newScope() { | |
| | | Scope *s = createScope(); | |
| | | if ( s && _scopeInitCallback ) | |
| | | _scopeInitCallback( *s ); | |
| | | return s; | |
| | | } | |
| | | | |
| virtual void runTest() = 0; | | virtual void runTest() = 0; | |
| | | | |
| virtual bool utf8Ok() const = 0; | | virtual bool utf8Ok() const = 0; | |
| | | | |
| static void setup(); | | static void setup(); | |
| | | | |
| auto_ptr<Scope> getPooledScope( const string& pool ); | | auto_ptr<Scope> getPooledScope( const string& pool ); | |
| void threadDone(); | | void threadDone(); | |
|
| | | | |
| | | struct Unlocker { virtual ~Unlocker() {} }; | |
| | | virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U | |
| | | nlocker >( new Unlocker ); } | |
| | | | |
| | | void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInit | |
| | | Callback = func; } | |
| | | | |
| | | protected: | |
| | | virtual Scope * createScope() = 0; | |
| | | | |
| | | private: | |
| | | void ( *_scopeInitCallback )( Scope & ); | |
| }; | | }; | |
| | | | |
| extern ScriptEngine * globalScriptEngine; | | extern ScriptEngine * globalScriptEngine; | |
| } | | } | |
| | | | |
End of changes. 5 change blocks. |
| 3 lines changed or deleted | | 23 lines changed or added | |
|
| engine_v8.h | | engine_v8.h | |
| | | | |
| skipping to change at line 42 | | skipping to change at line 42 | |
| | | | |
| V8Scope( V8ScriptEngine * engine ); | | V8Scope( V8ScriptEngine * engine ); | |
| ~V8Scope(); | | ~V8Scope(); | |
| | | | |
| virtual void reset(); | | virtual void reset(); | |
| virtual void init( BSONObj * data ); | | virtual void init( BSONObj * data ); | |
| | | | |
| virtual void localConnect( const char * dbName ); | | virtual void localConnect( const char * dbName ); | |
| virtual void externalSetup(); | | virtual void externalSetup(); | |
| | | | |
|
| v8::Handle<v8::Value> get( const char * field ); | | v8::Handle<v8::Value> get( const char * field ); // caller must cre
ate context and handle scopes | |
| virtual double getNumber( const char *field ); | | virtual double getNumber( const char *field ); | |
| virtual int getNumberInt( const char *field ); | | virtual int getNumberInt( const char *field ); | |
| virtual long long getNumberLongLong( const char *field ); | | virtual long long getNumberLongLong( const char *field ); | |
| virtual string getString( const char *field ); | | virtual string getString( const char *field ); | |
| virtual bool getBoolean( const char *field ); | | virtual bool getBoolean( const char *field ); | |
| virtual BSONObj getObject( const char *field ); | | virtual BSONObj getObject( const char *field ); | |
| | | | |
| virtual int type( const char *field ); | | virtual int type( const char *field ); | |
| | | | |
| virtual void setNumber( const char *field , double val ); | | virtual void setNumber( const char *field , double val ); | |
| virtual void setString( const char *field , const char * val ); | | virtual void setString( const char *field , const char * val ); | |
| virtual void setBoolean( const char *field , bool val ); | | virtual void setBoolean( const char *field , bool val ); | |
| virtual void setElement( const char *field , const BSONElement& e )
; | | virtual void setElement( const char *field , const BSONElement& e )
; | |
| virtual void setObject( const char *field , const BSONObj& obj , bo
ol readOnly); | | virtual void setObject( const char *field , const BSONObj& obj , bo
ol readOnly); | |
| virtual void setThis( const BSONObj * obj ); | | virtual void setThis( const BSONObj * obj ); | |
| | | | |
| virtual ScriptingFunction _createFunction( const char * code ); | | virtual ScriptingFunction _createFunction( const char * code ); | |
|
| | | Local< v8::Function > __createFunction( const char * code ); | |
| virtual int invoke( ScriptingFunction func , const BSONObj& args, i
nt timeoutMs = 0 , bool ignoreReturn = false ); | | virtual int invoke( ScriptingFunction func , const BSONObj& args, i
nt timeoutMs = 0 , bool ignoreReturn = false ); | |
| virtual bool exec( const string& code , const string& name , bool p
rintResult , bool reportError , bool assertOnError, int timeoutMs ); | | virtual bool exec( const string& code , const string& name , bool p
rintResult , bool reportError , bool assertOnError, int timeoutMs ); | |
| virtual string getError(){ return _error; } | | virtual string getError(){ return _error; } | |
| | | | |
|
| virtual void injectNative( const char *field, NativeFunction func ) | | virtual void injectNative( const char *field, NativeFunction func ) | |
| { | | ; | |
| Handle< FunctionTemplate > f( v8::FunctionTemplate::New( native | | | |
| Callback ) ); | | | |
| f->Set( v8::String::New( "_native_function" ), External::New( ( | | | |
| void*)func ) ); | | | |
| _global->Set( v8::String::New( field ), f->GetFunction() ); | | | |
| } | | | |
| | | | |
|
| void gc(){} // no-op in v8 | | void gc(); | |
| | | | |
| | | Handle< Context > context() const { return _context; } | |
| | | | |
| private: | | private: | |
| void _startCall(); | | void _startCall(); | |
| | | | |
| static Handle< Value > nativeCallback( const Arguments &args ); | | static Handle< Value > nativeCallback( const Arguments &args ); | |
| | | | |
| static Handle< Value > loadCallback( const Arguments &args ); | | static Handle< Value > loadCallback( const Arguments &args ); | |
| | | | |
| V8ScriptEngine * _engine; | | V8ScriptEngine * _engine; | |
| | | | |
|
| HandleScope _handleScope; | | Persistent<Context> _context; | |
| Handle<Context> _context; | | Persistent<v8::Object> _global; | |
| Context::Scope _scope; | | | |
| Handle<v8::Object> _global; | | | |
| | | | |
| string _error; | | string _error; | |
|
| vector< v8::Handle<Value> > _funcs; | | vector< Persistent<Value> > _funcs; | |
| v8::Handle<v8::Object> _this; | | v8::Persistent<v8::Object> _this; | |
| | | | |
|
| v8::Handle<v8::Function> _wrapper; | | v8::Persistent<v8::Function> _wrapper; | |
| | | | |
| enum ConnectState { NOT , LOCAL , EXTERNAL }; | | enum ConnectState { NOT , LOCAL , EXTERNAL }; | |
| ConnectState _connectState; | | ConnectState _connectState; | |
|
| string _localDBName; | | | |
| }; | | }; | |
| | | | |
| class V8ScriptEngine : public ScriptEngine { | | class V8ScriptEngine : public ScriptEngine { | |
| public: | | public: | |
| V8ScriptEngine(); | | V8ScriptEngine(); | |
| virtual ~V8ScriptEngine(); | | virtual ~V8ScriptEngine(); | |
| | | | |
| virtual Scope * createScope(){ return new V8Scope( this ); } | | virtual Scope * createScope(){ return new V8Scope( this ); } | |
| | | | |
| virtual void runTest(){} | | virtual void runTest(){} | |
| | | | |
| bool utf8Ok() const { return true; } | | bool utf8Ok() const { return true; } | |
| | | | |
|
| private: | | class V8Unlocker : public Unlocker { | |
| //HandleScope _handleScope; | | v8::Unlocker u_; | |
| //Handle<ObjectTemplate> _globalTemplate; | | }; | |
| | | | |
|
| //Handle<FunctionTemplate> _externalTemplate; | | virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U | |
| //Handle<FunctionTemplate> _localTemplate; | | nlocker >( new V8Unlocker ); } | |
| | | | |
| | | private: | |
| friend class V8Scope; | | friend class V8Scope; | |
| }; | | }; | |
| | | | |
| extern ScriptEngine * globalScriptEngine; | | extern ScriptEngine * globalScriptEngine; | |
| } | | } | |
| | | | |
End of changes. 10 change blocks. |
| 23 lines changed or deleted | | 19 lines changed or added | |
|
| file.h | | file.h | |
| | | | |
| skipping to change at line 70 | | skipping to change at line 70 | |
| } | | } | |
| public: | | public: | |
| File() { | | File() { | |
| fd = INVALID_HANDLE_VALUE; | | fd = INVALID_HANDLE_VALUE; | |
| _bad = true; | | _bad = true; | |
| } | | } | |
| ~File() { | | ~File() { | |
| if( is_open() ) CloseHandle(fd); | | if( is_open() ) CloseHandle(fd); | |
| fd = INVALID_HANDLE_VALUE; | | fd = INVALID_HANDLE_VALUE; | |
| } | | } | |
|
| void open(const char *filename) { | | void open(const char *filename, bool readOnly=false ) { | |
| std::wstring filenamew = toWideString(filename); | | std::wstring filenamew = toWideString(filename); | |
| fd = CreateFile( | | fd = CreateFile( | |
|
| filenamew.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHAR
E_READ, | | filenamew.c_str(), ( readOnly ? 0 : GENERIC_WRITE ) | GENE
RIC_READ, FILE_SHARE_READ, | |
| NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | | NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | |
| if( !is_open() ) { | | if( !is_open() ) { | |
| out() << "CreateFile failed " << filename << endl; | | out() << "CreateFile failed " << filename << endl; | |
| } | | } | |
| else | | else | |
| _bad = false; | | _bad = false; | |
| } | | } | |
| void write(fileofs o, const char *data, unsigned len) { | | void write(fileofs o, const char *data, unsigned len) { | |
| LARGE_INTEGER li; | | LARGE_INTEGER li; | |
| li.QuadPart = o; | | li.QuadPart = o; | |
| | | | |
| skipping to change at line 97 | | skipping to change at line 97 | |
| } | | } | |
| void read(fileofs o, char *data, unsigned len) { | | void read(fileofs o, char *data, unsigned len) { | |
| DWORD read; | | DWORD read; | |
| LARGE_INTEGER li; | | LARGE_INTEGER li; | |
| li.QuadPart = o; | | li.QuadPart = o; | |
| SetFilePointerEx(fd, li, NULL, FILE_BEGIN); | | SetFilePointerEx(fd, li, NULL, FILE_BEGIN); | |
| int ok = ReadFile(fd, data, len, &read, 0); | | int ok = ReadFile(fd, data, len, &read, 0); | |
| if( !ok ) | | if( !ok ) | |
| err(ok); | | err(ok); | |
| else | | else | |
|
| massert("ReadFile error - truncated file?", read == len); | | massert( 10438 , "ReadFile error - truncated file?", read == le
n); | |
| } | | } | |
| bool bad() { return _bad; } | | bool bad() { return _bad; } | |
| bool is_open() { return fd != INVALID_HANDLE_VALUE; } | | bool is_open() { return fd != INVALID_HANDLE_VALUE; } | |
| fileofs len() { | | fileofs len() { | |
| LARGE_INTEGER li; | | LARGE_INTEGER li; | |
| li.LowPart = GetFileSize(fd, (DWORD *) &li.HighPart); | | li.LowPart = GetFileSize(fd, (DWORD *) &li.HighPart); | |
| if( li.HighPart == 0 && li.LowPart == INVALID_FILE_SIZE ) { | | if( li.HighPart == 0 && li.LowPart == INVALID_FILE_SIZE ) { | |
| err( false ); | | err( false ); | |
| return 0; | | return 0; | |
| } | | } | |
| | | | |
| skipping to change at line 121 | | skipping to change at line 121 | |
| }; | | }; | |
| | | | |
| #else | | #else | |
| | | | |
| class File : public FileInterface { | | class File : public FileInterface { | |
| int fd; | | int fd; | |
| bool _bad; | | bool _bad; | |
| void err(bool ok) { | | void err(bool ok) { | |
| if( !ok && !_bad ) { | | if( !ok && !_bad ) { | |
| _bad = true; | | _bad = true; | |
|
| log() << "File I/O error " << errno << '\n'; | | log() << "File I/O " << OUTPUT_ERRNO << '\n'; | |
| } | | } | |
| } | | } | |
| public: | | public: | |
| File() { | | File() { | |
| fd = -1; | | fd = -1; | |
| _bad = true; | | _bad = true; | |
| } | | } | |
| ~File() { | | ~File() { | |
| if( is_open() ) ::close(fd); | | if( is_open() ) ::close(fd); | |
| fd = -1; | | fd = -1; | |
| } | | } | |
| | | | |
| #ifndef O_NOATIME | | #ifndef O_NOATIME | |
| #define O_NOATIME 0 | | #define O_NOATIME 0 | |
| #define lseek64 lseek | | #define lseek64 lseek | |
| #endif | | #endif | |
| | | | |
|
| void open(const char *filename) { | | void open(const char *filename, bool readOnly=false ) { | |
| fd = ::open(filename, O_CREAT | O_RDWR | O_NOATIME, S_IRUSR | S_IWU | | fd = ::open(filename, O_CREAT | ( readOnly ? 0 : O_RDWR ) | O_NOATI | |
| SR); | | ME, S_IRUSR | S_IWUSR); | |
| if ( fd <= 0 ) { | | if ( fd <= 0 ) { | |
|
| out() << "couldn't open " << filename << ' ' << errno << endl; | | out() << "couldn't open " << filename << ' ' << OUTPUT_ERRNO <<
endl; | |
| return; | | return; | |
| } | | } | |
| _bad = false; | | _bad = false; | |
| } | | } | |
| void write(fileofs o, const char *data, unsigned len) { | | void write(fileofs o, const char *data, unsigned len) { | |
| lseek64(fd, o, SEEK_SET); | | lseek64(fd, o, SEEK_SET); | |
| err( ::write(fd, data, len) == (int) len ); | | err( ::write(fd, data, len) == (int) len ); | |
| } | | } | |
| void read(fileofs o, char *data, unsigned len) { | | void read(fileofs o, char *data, unsigned len) { | |
| lseek(fd, o, SEEK_SET); | | lseek(fd, o, SEEK_SET); | |
| | | | |
End of changes. 6 change blocks. |
| 8 lines changed or deleted | | 8 lines changed or added | |
|
| file_allocator.h | | file_allocator.h | |
| | | | |
| skipping to change at line 52 | | skipping to change at line 52 | |
| #endif | | #endif | |
| void start() { | | void start() { | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| Runner r( *this ); | | Runner r( *this ); | |
| boost::thread t( r ); | | boost::thread t( r ); | |
| #endif | | #endif | |
| } | | } | |
| // 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, | |
| | | 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. | |
| | | */ | |
| #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; | |
| return; | | return; | |
| } | | } | |
| pending_.push_back( name ); | | pending_.push_back( name ); | |
| pendingSize_[ name ] = size; | | pendingSize_[ name ] = size; | |
| pendingUpdated_.notify_all(); | | pendingUpdated_.notify_all(); | |
| | | | |
| skipping to change at line 150 | | skipping to change at line 154 | |
| boostlock lk( a_.pendingMutex_ ); | | boostlock lk( a_.pendingMutex_ ); | |
| if ( a_.pending_.size() == 0 ) | | if ( a_.pending_.size() == 0 ) | |
| break; | | break; | |
| name = a_.pending_.front(); | | name = a_.pending_.front(); | |
| size = a_.pendingSize_[ name ]; | | size = a_.pendingSize_[ name ]; | |
| } | | } | |
| try { | | try { | |
| long fd = open(name.c_str(), O_CREAT | O_RDWR |
O_NOATIME, S_IRUSR | S_IWUSR); | | long fd = open(name.c_str(), O_CREAT | O_RDWR |
O_NOATIME, S_IRUSR | S_IWUSR); | |
| if ( fd <= 0 ) { | | if ( fd <= 0 ) { | |
| stringstream ss; | | stringstream ss; | |
|
| ss << "couldn't open " << name << ' ' << er | | ss << "couldn't open " << name << ' ' << OU | |
| rno; | | TPUT_ERRNO; | |
| massert( ss.str(), fd <= 0 ); | | massert( 10439 , ss.str(), fd <= 0 ); | |
| } | | } | |
| | | | |
| #if defined(POSIX_FADV_DONTNEED) | | #if defined(POSIX_FADV_DONTNEED) | |
| if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN
EED) ) { | | if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN
EED) ) { | |
|
| log() << "warning: posix_fadvise fails " <<
name << ' ' << errno << endl; | | log() << "warning: posix_fadvise fails " <<
name << ' ' << OUTPUT_ERRNO << endl; | |
| } | | } | |
| #endif | | #endif | |
| | | | |
| /* make sure the file is the full desired lengt
h */ | | /* make sure the file is the full desired lengt
h */ | |
| off_t filelen = lseek(fd, 0, SEEK_END); | | off_t filelen = lseek(fd, 0, SEEK_END); | |
| if ( filelen < size ) { | | if ( filelen < size ) { | |
|
| massert( "failure creating new datafile", f
ilelen == 0 ); | | massert( 10440 , "failure creating new dat
afile", filelen == 0 ); | |
| // Check for end of disk. | | // Check for end of disk. | |
|
| massert( "Unable to allocate file of desire
d size", | | massert( 10441 , "Unable to allocate file
of desired size", | |
| size - 1 == lseek(fd, size - 1, SEE
K_SET) ); | | size - 1 == lseek(fd, size - 1, SEE
K_SET) ); | |
|
| massert( "Unable to allocate file of desire
d size", | | massert( 10442 , "Unable to allocate file
of desired size", | |
| 1 == write(fd, "", 1) ); | | 1 == write(fd, "", 1) ); | |
| lseek(fd, 0, SEEK_SET); | | lseek(fd, 0, SEEK_SET); | |
| log() << "allocating new datafile " << name
<< ", filling with zeroes..." << endl; | | log() << "allocating new datafile " << name
<< ", filling with zeroes..." << endl; | |
| Timer t; | | Timer t; | |
| long z = 256 * 1024; | | long z = 256 * 1024; | |
| char buf[z]; | | char buf[z]; | |
| memset(buf, 0, z); | | memset(buf, 0, z); | |
| long left = size; | | long left = size; | |
| while ( 1 ) { | | while ( 1 ) { | |
| if ( left <= z ) { | | if ( left <= z ) { | |
|
| massert( "write failed", left == wr
ite(fd, buf, left) ); | | massert( 10443 , "write failed", l
eft == write(fd, buf, left) ); | |
| break; | | break; | |
| } | | } | |
|
| massert( "write failed", z == write(fd,
buf, z) ); | | massert( 10444 , "write failed", z ==
write(fd, buf, z) ); | |
| left -= z; | | left -= z; | |
| } | | } | |
| log() << "done allocating datafile " << nam
e << ", size: " << size/1024/1024 << "MB, took " << ((double)t.millis())/10
00.0 << " secs" << endl; | | log() << "done allocating datafile " << nam
e << ", size: " << size/1024/1024 << "MB, took " << ((double)t.millis())/10
00.0 << " secs" << endl; | |
| } | | } | |
| close( fd ); | | close( fd ); | |
| | | | |
| } catch ( ... ) { | | } catch ( ... ) { | |
| problem() << "Failed to allocate new file: " <<
name | | problem() << "Failed to allocate new file: " <<
name | |
| << ", size: " << size << ", aborting.
" << endl; | | << ", size: " << size << ", aborting.
" << endl; | |
| try { | | try { | |
| | | | |
End of changes. 8 change blocks. |
| 9 lines changed or deleted | | 16 lines changed or added | |
|
| goodies.h | | goodies.h | |
| | | | |
| skipping to change at line 27 | | skipping to change at line 27 | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| # include <windows.h> | | # include <windows.h> | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) | | #if !defined(_WIN32) && !defined(NOEXECINFO) | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include <pthread.h> | | #include <pthread.h> | |
| #include <execinfo.h> | | #include <execinfo.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline pthread_t GetCurrentThreadId() { | | inline pthread_t GetCurrentThreadId() { | |
| return pthread_self(); | | return pthread_self(); | |
| | | | |
| skipping to change at line 107 | | skipping to change at line 107 | |
| p -= 16; | | p -= 16; | |
| for ( int i = 0; i < 16; i++ ) | | for ( int i = 0; i < 16; i++ ) | |
| cout << (unsigned) ((unsigned char)*p++) << ' '; | | cout << (unsigned) ((unsigned char)*p++) << ' '; | |
| cout << endl; | | cout << endl; | |
| len -= 16; | | len -= 16; | |
| } | | } | |
| } catch (...) { | | } catch (...) { | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | // PRINT(2+2); prints "2+2: 4" | |
| | | #define PRINT(x) cout << #x ": " << (x) << endl | |
| | | // PRINTFL; prints file:line | |
| | | #define PRINTFL cout << __FILE__ ":" << __LINE__ << endl | |
| | | | |
| #undef yassert | | #undef yassert | |
| | | | |
| #undef assert | | #undef assert | |
| #define assert xassert | | #define assert xassert | |
| #define yassert 1 | | #define yassert 1 | |
| | | | |
| struct WrappingInt { | | struct WrappingInt { | |
| WrappingInt() { | | WrappingInt() { | |
| x = 0; | | x = 0; | |
| } | | } | |
| | | | |
| skipping to change at line 182 | | 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 211 | | skipping to change at line 215 | |
| 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; | |
| } | | } | |
| | | | |
| struct Date_t { | | struct Date_t { | |
| // TODO: make signed (and look for related TODO's) | | // TODO: make signed (and look for related TODO's) | |
| unsigned long long millis; | | unsigned long long millis; | |
| Date_t(): millis(0) {} | | Date_t(): millis(0) {} | |
| Date_t(unsigned long long m): millis(m) {} | | Date_t(unsigned long long m): millis(m) {} | |
| operator unsigned long long&() { return millis; } | | operator unsigned long long&() { return millis; } | |
| operator const unsigned long long&() const { return millis; } | | operator const unsigned long long&() const { return millis; } | |
| }; | | }; | |
| | | | |
|
| inline unsigned long long jsTime() { | | inline Date_t jsTime() { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| unsigned long long t = xt.nsec / 1000000; | | unsigned long long t = xt.nsec / 1000000; | |
| return ((unsigned long long) xt.sec * 1000) + t; | | return ((unsigned long long) xt.sec * 1000) + t; | |
| } | | } | |
| | | | |
| inline unsigned long long curTimeMicros64() { | | inline unsigned long long curTimeMicros64() { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| unsigned long long t = xt.nsec / 1000; | | unsigned long long t = xt.nsec / 1000; | |
| | | | |
| skipping to change at line 285 | | skipping to change at line 267 | |
| using namespace boost; | | using namespace boost; | |
| typedef boost::mutex::scoped_lock boostlock; | | typedef boost::mutex::scoped_lock boostlock; | |
| typedef boost::recursive_mutex::scoped_lock recursive_boostlock; | | typedef boost::recursive_mutex::scoped_lock recursive_boostlock; | |
| | | | |
| // simple scoped timer | | // simple scoped timer | |
| class Timer { | | class Timer { | |
| public: | | public: | |
| Timer() { | | Timer() { | |
| reset(); | | reset(); | |
| } | | } | |
|
| | | Timer( unsigned long long start ) { | |
| | | old = start; | |
| | | } | |
| int seconds(){ | | int seconds(){ | |
| return (int)(micros() / 1000000); | | return (int)(micros() / 1000000); | |
| } | | } | |
| int millis() { | | int millis() { | |
| return (long)(micros() / 1000); | | return (long)(micros() / 1000); | |
| } | | } | |
| unsigned long long micros() { | | unsigned long long micros() { | |
| unsigned long long n = curTimeMicros64(); | | unsigned long long n = curTimeMicros64(); | |
| return n - old; | | return n - old; | |
| } | | } | |
| unsigned long long micros(unsigned long long & n) { // returns cur
time in addition to timer result | | unsigned long long micros(unsigned long long & n) { // returns cur
time in addition to timer result | |
| n = curTimeMicros64(); | | n = curTimeMicros64(); | |
| return n - old; | | return n - old; | |
| } | | } | |
|
| | | unsigned long long startTime(){ | |
| | | return old; | |
| | | } | |
| void reset() { | | void reset() { | |
| old = curTimeMicros64(); | | old = curTimeMicros64(); | |
| } | | } | |
| private: | | private: | |
| unsigned long long old; | | unsigned long long old; | |
| }; | | }; | |
| | | | |
| /* | | /* | |
| | | | |
| class DebugMutex : boost::noncopyable { | | class DebugMutex : boost::noncopyable { | |
| | | | |
| skipping to change at line 386 | | skipping to change at line 374 | |
| /* thread local "value" rather than a pointer | | /* thread local "value" rather than a pointer | |
| good for things which have copy constructors (and the copy construct
or is fast enough) | | good for things which have copy constructors (and the copy construct
or is fast enough) | |
| e.g. | | e.g. | |
| ThreadLocalValue<int> myint; | | ThreadLocalValue<int> myint; | |
| */ | | */ | |
| template<class T> | | template<class T> | |
| class ThreadLocalValue { | | class ThreadLocalValue { | |
| public: | | public: | |
| ThreadLocalValue( T def = 0 ) : _default( def ) { } | | ThreadLocalValue( T def = 0 ) : _default( def ) { } | |
| | | | |
|
| int get() { | | T get() { | |
| T * val = _val.get(); | | T * val = _val.get(); | |
| if ( val ) | | if ( val ) | |
| return *val; | | return *val; | |
| return _default; | | return _default; | |
| } | | } | |
|
| | | | |
| void set( const T& i ) { | | void set( const T& i ) { | |
| T *v = _val.get(); | | T *v = _val.get(); | |
| if( v ) { | | if( v ) { | |
| *v = i; | | *v = i; | |
| return; | | return; | |
| } | | } | |
| v = new T(i); | | v = new T(i); | |
| _val.reset( v ); | | _val.reset( v ); | |
| } | | } | |
|
| | | | |
| private: | | private: | |
| T _default; | | T _default; | |
| boost::thread_specific_ptr<T> _val; | | boost::thread_specific_ptr<T> _val; | |
| }; | | }; | |
| | | | |
| class ProgressMeter { | | class ProgressMeter { | |
| public: | | public: | |
| ProgressMeter( long long total , int secondsBetween = 3 , int check
Interval = 100 ) | | ProgressMeter( long long total , int secondsBetween = 3 , int check
Interval = 100 ) | |
| : _total( total ) , _secondsBetween( secondsBetween ) , _checkI
nterval( checkInterval ) , | | : _total( total ) , _secondsBetween( secondsBetween ) , _checkI
nterval( checkInterval ) , | |
| _done(0) , _hits(0) , _lastTime( (int) time(0) ){ | | _done(0) , _hits(0) , _lastTime( (int) time(0) ){ | |
| | | | |
| skipping to change at line 450 | | skipping to change at line 440 | |
| | | | |
| long long _total; | | long long _total; | |
| int _secondsBetween; | | int _secondsBetween; | |
| int _checkInterval; | | int _checkInterval; | |
| | | | |
| long long _done; | | long long _done; | |
| long long _hits; | | long long _hits; | |
| int _lastTime; | | int _lastTime; | |
| }; | | }; | |
| | | | |
|
| | | class TicketHolder { | |
| | | public: | |
| | | TicketHolder( int num ){ | |
| | | _outof = num; | |
| | | _num = num; | |
| | | } | |
| | | | |
| | | bool tryAcquire(){ | |
| | | boostlock lk( _mutex ); | |
| | | if ( _num <= 0 ){ | |
| | | if ( _num < 0 ){ | |
| | | cerr << "DISASTER! in TicketHolder" << endl; | |
| | | } | |
| | | return false; | |
| | | } | |
| | | _num--; | |
| | | return true; | |
| | | } | |
| | | | |
| | | void release(){ | |
| | | boostlock lk( _mutex ); | |
| | | _num++; | |
| | | } | |
| | | | |
| | | void resize( int newSize ){ | |
| | | boostlock lk( _mutex ); | |
| | | int used = _outof - _num; | |
| | | if ( used > newSize ){ | |
| | | cout << "ERROR: can't resize since we're using (" << used < | |
| | | < ") more than newSize(" << newSize << ")" << endl; | |
| | | return; | |
| | | } | |
| | | | |
| | | _outof = newSize; | |
| | | _num = _outof - used; | |
| | | } | |
| | | | |
| | | int available(){ | |
| | | return _num; | |
| | | } | |
| | | | |
| | | int used(){ | |
| | | return _outof - _num; | |
| | | } | |
| | | | |
| | | private: | |
| | | int _outof; | |
| | | int _num; | |
| | | boost::mutex _mutex; | |
| | | }; | |
| | | | |
| | | class TicketHolderReleaser { | |
| | | public: | |
| | | TicketHolderReleaser( TicketHolder * holder ){ | |
| | | _holder = holder; | |
| | | } | |
| | | | |
| | | ~TicketHolderReleaser(){ | |
| | | _holder->release(); | |
| | | } | |
| | | private: | |
| | | TicketHolder * _holder; | |
| | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 11 change blocks. |
| 26 lines changed or deleted | | 80 lines changed or added | |
|
| instance.h | | instance.h | |
| | | | |
| skipping to change at line 46 | | skipping to change at line 46 | |
| ofstream *f; | | ofstream *f; | |
| /* 0 = off; 1 = writes, 2 = reads, 3 = both | | /* 0 = off; 1 = writes, 2 = reads, 3 = both | |
| 7 = log a few reads, and all writes. | | 7 = log a few reads, and all writes. | |
| */ | | */ | |
| int level; | | int level; | |
| DiagLog() : f(0) , level(0) { } | | DiagLog() : f(0) , level(0) { } | |
| void init() { | | void init() { | |
| if ( ! f && level ){ | | if ( ! f && level ){ | |
| log() << "diagLogging = " << level << endl; | | log() << "diagLogging = " << level << endl; | |
| stringstream ss; | | stringstream ss; | |
|
| ss << dbpath << "/diaglog." << hex << time(0); | | ss << "diaglog." << hex << time(0); | |
| string name = ss.str(); | | string name = ss.str(); | |
| f = new ofstream(name.c_str(), ios::out | ios::binary); | | f = new ofstream(name.c_str(), ios::out | ios::binary); | |
| if ( ! f->good() ) { | | if ( ! f->good() ) { | |
| problem() << "couldn't open log stream" << endl; | | problem() << "couldn't open log stream" << endl; | |
| throw 1717; | | throw 1717; | |
| } | | } | |
| } | | } | |
| } | | } | |
| /** | | /** | |
| * @return old | | * @return old | |
| | | | |
| skipping to change at line 102 | | skipping to change at line 102 | |
| } | | } | |
| ~DbResponse() { | | ~DbResponse() { | |
| delete response; | | delete response; | |
| } | | } | |
| }; | | }; | |
| | | | |
| static SockAddr unknownAddress( "0.0.0.0", 0 ); | | static SockAddr unknownAddress( "0.0.0.0", 0 ); | |
| | | | |
| bool assembleResponse( Message &m, DbResponse &dbresponse, const sockad
dr_in &client = unknownAddress.sa ); | | bool assembleResponse( Message &m, DbResponse &dbresponse, const sockad
dr_in &client = unknownAddress.sa ); | |
| | | | |
|
| void receivedKillCursors(Message& m); | | | |
| void receivedUpdate(Message& m, stringstream& ss); | | | |
| void receivedDelete(Message& m, stringstream& ss); | | | |
| void receivedInsert(Message& m, stringstream& ss); | | | |
| bool receivedGetMore(DbResponse& dbresponse, Message& m, stringstream& | | | |
| ss); | | | |
| bool receivedQuery(DbResponse& dbresponse, Message& m, stringstream& ss | | | |
| , bool logit); | | | |
| void getDatabaseNames( vector< string > &names ); | | void getDatabaseNames( vector< string > &names ); | |
| | | | |
| // must call with db lock | | // must call with db lock | |
| void registerListenerSocket( int socket ); | | void registerListenerSocket( int socket ); | |
| | | | |
| // --- local client --- | | // --- local client --- | |
| | | | |
| class DBDirectClient : public DBClientBase { | | class DBDirectClient : public DBClientBase { | |
|
| | | | |
| | | public: | |
| | | 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); | |
| | | | |
| virtual bool isFailed() const { | | virtual bool isFailed() const { | |
| return false; | | return false; | |
| } | | } | |
| virtual string toString() { | | virtual string toString() { | |
| return "DBDirectClient"; | | return "DBDirectClient"; | |
| } | | } | |
| virtual string getServerAddress() const{ | | virtual string getServerAddress() const{ | |
| return "localhost"; // TODO: should this have the port? | | return "localhost"; // TODO: should this have the port? | |
| } | | } | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k=true ); | | virtual bool call( Message &toSend, Message &response, bool assertO
k=true ); | |
| | | | |
| skipping to change at line 136 | | skipping to change at line 135 | |
| virtual void say( Message &toSend ); | | virtual void say( Message &toSend ); | |
| virtual void sayPiggyBack( Message &toSend ) { | | virtual void sayPiggyBack( Message &toSend ) { | |
| // don't need to piggy back when connected locally | | // don't need to piggy back when connected locally | |
| return say( toSend ); | | return say( toSend ); | |
| } | | } | |
| class AlwaysAuthorized : public AuthenticationInfo { | | class AlwaysAuthorized : public AuthenticationInfo { | |
| virtual bool isAuthorized( const char *dbname ) { | | virtual bool isAuthorized( const char *dbname ) { | |
| return true; | | return true; | |
| } | | } | |
| }; | | }; | |
|
| | | | |
| /* TODO: this looks bad that auth is set to always. is that really
always safe? */ | | /* TODO: this looks bad that auth is set to always. is that really
always safe? */ | |
| class SavedContext { | | class SavedContext { | |
| public: | | public: | |
| SavedContext() { | | SavedContext() { | |
|
| dblock lk; | | _save = dbMutex.atLeastReadLocked(); | |
| | | | |
| Client *c = currentClient.get(); | | Client *c = currentClient.get(); | |
|
| if ( c->database() ) | | | |
| oldName = c->database()->name; | | | |
| oldAuth = c->ai; | | oldAuth = c->ai; | |
| // careful, don't want to free this: | | // careful, don't want to free this: | |
| c->ai = &always; | | c->ai = &always; | |
|
| | | | |
| | | /* it only makes sense to manipulate a pointer - c->databas | |
| | | e() - if locked. | |
| | | thus the _saved flag. | |
| | | */ | |
| | | if( _save ) { | |
| | | if ( c->database() ) { | |
| | | dbMutex.assertAtLeastReadLocked(); | |
| | | _oldName = c->database()->name; | |
| | | } | |
| | | } | |
| } | | } | |
| ~SavedContext() { | | ~SavedContext() { | |
| Client *c = currentClient.get(); | | Client *c = currentClient.get(); | |
| c->ai = oldAuth; | | c->ai = oldAuth; | |
|
| if ( !oldName.empty() ) { | | if( _save ) { | |
| dblock lk; | | if ( !_oldName.empty() ) { | |
| setClient( oldName.c_str() ); | | dbMutex.assertAtLeastReadLocked(); | |
| | | setClient( _oldName.c_str() ); | |
| | | } | |
| | | } | |
| | | else { | |
| | | // defensive | |
| | | cc().clearns(); | |
| } | | } | |
| } | | } | |
| private: | | private: | |
|
| | | bool _save; | |
| static AlwaysAuthorized always; | | static AlwaysAuthorized always; | |
| AuthenticationInfo *oldAuth; | | AuthenticationInfo *oldAuth; | |
|
| string oldName; | | string _oldName; | |
| }; | | }; | |
| }; | | }; | |
| | | | |
| extern int lockFile; | | extern int lockFile; | |
| void acquirePathLock(); | | void acquirePathLock(); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 10 change blocks. |
| 16 lines changed or deleted | | 33 lines changed or added | |
|
| jsobj.h | | jsobj.h | |
| | | | |
| skipping to change at line 323 | | skipping to change at line 323 | |
| /** @return value of a boolean element. | | /** @return value of a boolean element. | |
| You must assure element is a boolean before | | You must assure element is a boolean before | |
| calling. */ | | calling. */ | |
| bool boolean() const { | | bool boolean() const { | |
| return *value() ? true : false; | | return *value() ? true : false; | |
| } | | } | |
| | | | |
| /** Retrieve a java style date value from the element. | | /** Retrieve a java style date value from the element. | |
| Ensure element is of type Date before calling. | | Ensure element is of type Date before calling. | |
| */ | | */ | |
|
| unsigned long long date() const { | | Date_t date() const { | |
| return *reinterpret_cast< const unsigned long long* >( value() | | return *reinterpret_cast< const Date_t* >( value() ); | |
| ); | | | |
| } | | } | |
| | | | |
| /** Convert the value to boolean, regardless of its type, in a java
script-like fashion | | /** Convert the value to boolean, regardless of its type, in a java
script-like fashion | |
| (i.e., treat zero and null as false). | | (i.e., treat zero and null as false). | |
| */ | | */ | |
| bool trueValue() const { | | bool trueValue() const { | |
| switch( type() ) { | | switch( type() ) { | |
| case NumberLong: | | case NumberLong: | |
| return *reinterpret_cast< const long long* >( value() )
!= 0; | | return *reinterpret_cast< const long long* >( value() )
!= 0; | |
| case NumberDouble: | | case NumberDouble: | |
| return *reinterpret_cast< const double* >( value() ) !=
0; | | return *reinterpret_cast< const double* >( value() ) !=
0; | |
| case NumberInt: | | case NumberInt: | |
| return *reinterpret_cast< const int* >( value() ) != 0; | | return *reinterpret_cast< const int* >( value() ) != 0; | |
| case Bool: | | case Bool: | |
| return boolean(); | | return boolean(); | |
| case EOO: | | case EOO: | |
| case jstNULL: | | case jstNULL: | |
|
| | | case Undefined: | |
| return false; | | return false; | |
| | | | |
| default: | | default: | |
| ; | | ; | |
| } | | } | |
| return true; | | return true; | |
| } | | } | |
| | | | |
| /** True if element is of a numeric type. */ | | /** True if element is of a numeric type. */ | |
| bool isNumber() const { | | bool isNumber() const { | |
| | | | |
| skipping to change at line 496 | | skipping to change at line 497 | |
| string ascode() const { | | string ascode() const { | |
| switch( type() ){ | | switch( type() ){ | |
| case String: | | case String: | |
| case Code: | | case Code: | |
| return valuestr(); | | return valuestr(); | |
| case CodeWScope: | | case CodeWScope: | |
| return codeWScopeCode(); | | return codeWScopeCode(); | |
| default: | | default: | |
| log() << "can't convert type: " << (int)(type()) << " to co
de" << endl; | | log() << "can't convert type: " << (int)(type()) << " to co
de" << endl; | |
| } | | } | |
|
| uassert( "not code" , 0 ); | | uassert( 10062 , "not code" , 0 ); | |
| return ""; | | return ""; | |
| } | | } | |
| | | | |
| /** Get binary data. Element must be of type BinData */ | | /** Get binary data. Element must be of type BinData */ | |
| const char *binData(int& len) const { | | const char *binData(int& len) const { | |
| // BinData: <int len> <byte subtype> <byte[len] data> | | // BinData: <int len> <byte subtype> <byte[len] data> | |
| assert( type() == BinData ); | | assert( type() == BinData ); | |
| len = valuestrsize(); | | len = valuestrsize(); | |
| return value() + 5; | | return value() + 5; | |
| } | | } | |
| | | | |
| skipping to change at line 586 | | skipping to change at line 587 | |
| /** Check that data is internally consistent. */ | | /** Check that data is internally consistent. */ | |
| void validate() const; | | void validate() const; | |
| | | | |
| /** True if this element may contain subobjects. */ | | /** True if this element may contain subobjects. */ | |
| bool mayEncapsulate() const { | | bool mayEncapsulate() const { | |
| return type() == Object || | | return type() == Object || | |
| type() == Array || | | type() == Array || | |
| type() == CodeWScope; | | type() == CodeWScope; | |
| } | | } | |
| | | | |
|
| unsigned long long timestampTime() const{ | | Date_t timestampTime() const{ | |
| unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | | unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | |
| return t * 1000; | | return t * 1000; | |
| } | | } | |
| unsigned int timestampInc() const{ | | unsigned int timestampInc() const{ | |
| return ((unsigned int*)(value() ))[0]; | | return ((unsigned int*)(value() ))[0]; | |
| } | | } | |
| | | | |
| const char * dbrefNS() const { | | const char * dbrefNS() const { | |
|
| uassert( "not a dbref" , type() == DBRef ); | | uassert( 10063 , "not a dbref" , type() == DBRef ); | |
| return value() + 4; | | return value() + 4; | |
| } | | } | |
| | | | |
| const OID& dbrefOID() const { | | const OID& dbrefOID() const { | |
|
| uassert( "not a dbref" , type() == DBRef ); | | uassert( 10064 , "not a dbref" , type() == DBRef ); | |
| const char * start = value(); | | const char * start = value(); | |
| start += 4 + *reinterpret_cast< const int* >( start ); | | start += 4 + *reinterpret_cast< const int* >( start ); | |
| return *reinterpret_cast< const OID* >( start ); | | return *reinterpret_cast< const OID* >( start ); | |
| } | | } | |
| | | | |
| bool operator<( const BSONElement& other ) const { | | bool operator<( const BSONElement& other ) const { | |
| int x = (int)canonicalType() - (int)other.canonicalType(); | | int x = (int)canonicalType() - (int)other.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(*this,other) < 0; | | return compareElementValues(*this,other) < 0; | |
| } | | } | |
| | | | |
|
| protected: | | | |
| // If maxLen is specified, don't scan more than maxLen bytes. | | // If maxLen is specified, don't scan more than maxLen bytes. | |
| BSONElement(const char *d, int maxLen = -1) : data(d) { | | BSONElement(const char *d, int maxLen = -1) : data(d) { | |
| fieldNameSize_ = -1; | | fieldNameSize_ = -1; | |
| if ( eoo() ) | | if ( eoo() ) | |
| fieldNameSize_ = 0; | | fieldNameSize_ = 0; | |
| else { | | else { | |
| if ( maxLen != -1 ) { | | if ( maxLen != -1 ) { | |
| int size = strnlen( fieldName(), maxLen - 1 ); | | int size = strnlen( fieldName(), maxLen - 1 ); | |
|
| massert( "Invalid field name", size != -1 ); | | massert( 10333 , "Invalid field name", size != -1 ); | |
| fieldNameSize_ = size + 1; | | fieldNameSize_ = size + 1; | |
| } | | } | |
| } | | } | |
| totalSize = -1; | | totalSize = -1; | |
| } | | } | |
| private: | | private: | |
| const char *data; | | const char *data; | |
| mutable int fieldNameSize_; // cached value | | mutable int fieldNameSize_; // cached value | |
| int fieldNameSize() const { | | int fieldNameSize() const { | |
| if ( fieldNameSize_ == -1 ) | | if ( fieldNameSize_ == -1 ) | |
| fieldNameSize_ = strlen( fieldName() ) + 1; | | fieldNameSize_ = strlen( fieldName() ) + 1; | |
| return fieldNameSize_; | | return fieldNameSize_; | |
| } | | } | |
| mutable int totalSize; /* caches the computed size */ | | mutable int totalSize; /* caches the computed size */ | |
| }; | | }; | |
| | | | |
| int getGtLtOp(const BSONElement& e); | | int getGtLtOp(const BSONElement& e); | |
| | | | |
|
| /* compare values with type check. | | | |
| note: as is now, not smart about int/double comingling. TODO | | | |
| */ | | | |
| inline int compareValues(const BSONElement& l, const BSONElement& r) | | | |
| { | | | |
| int x = (int) l.type() - (int) r.type(); | | | |
| if( x ) return x; | | | |
| return compareElementValues(l,r); | | | |
| } | | | |
| | | | |
| struct BSONElementCmpWithoutField { | | struct BSONElementCmpWithoutField { | |
| bool operator()( const BSONElement &l, const BSONElement &r ) const
{ | | bool operator()( const BSONElement &l, const BSONElement &r ) const
{ | |
| return l.woCompare( r, false ); | | return l.woCompare( r, false ); | |
| } | | } | |
| }; | | }; | |
| | | | |
| typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; | | typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; | |
| | | | |
| /** | | /** | |
| C++ representation of a "BSON" object -- that is, an extended JSO
N-style | | C++ representation of a "BSON" object -- that is, an extended JSO
N-style | |
| | | | |
| skipping to change at line 718 | | skipping to change at line 708 | |
| }; | | }; | |
| const char *_objdata; | | const char *_objdata; | |
| boost::shared_ptr< Holder > _holder; | | boost::shared_ptr< Holder > _holder; | |
| void init(const char *data, bool ifree) { | | void init(const char *data, bool ifree) { | |
| if ( ifree ) | | if ( ifree ) | |
| _holder.reset( new Holder( data ) ); | | _holder.reset( new Holder( data ) ); | |
| _objdata = data; | | _objdata = data; | |
| if ( ! isValid() ){ | | if ( ! isValid() ){ | |
| stringstream ss; | | stringstream ss; | |
| ss << "Invalid BSONObj spec size: " << objsize(); | | ss << "Invalid BSONObj spec size: " << objsize(); | |
|
| try { | | | |
| BSONElement e = firstElement(); | | | |
| ss << " first element:" << e.toString() << " "; | | | |
| } | | | |
| catch ( ... ){} | | | |
| string s = ss.str(); | | string s = ss.str(); | |
|
| massert( s , 0 ); | | massert( 10334 , s , 0 ); | |
| } | | } | |
| } | | } | |
| #pragma pack(1) | | #pragma pack(1) | |
| static struct EmptyObject { | | static struct EmptyObject { | |
| EmptyObject() { | | EmptyObject() { | |
| len = 5; | | len = 5; | |
| jstype = EOO; | | jstype = EOO; | |
| } | | } | |
| int len; | | int len; | |
| char jstype; | | char jstype; | |
| | | | |
| skipping to change at line 829 | | skipping to change at line 814 | |
| bool hasField( const char * name )const { | | bool hasField( const char * name )const { | |
| return ! getField( name ).eoo(); | | return ! getField( name ).eoo(); | |
| } | | } | |
| | | | |
| /** @return "" if DNE or wrong type */ | | /** @return "" if DNE or wrong type */ | |
| const char * getStringField(const char *name) const; | | const char * getStringField(const char *name) const; | |
| | | | |
| /** @return subobject of the given name */ | | /** @return subobject of the given name */ | |
| BSONObj getObjectField(const char *name) const; | | BSONObj getObjectField(const char *name) const; | |
| | | | |
|
| /** @return INT_MIN if not present */ | | /** @return INT_MIN if not present - does some type conversions */ | |
| int getIntField(const char *name) const; | | int getIntField(const char *name) const; | |
| | | | |
| /** @return false if not present */ | | /** @return false if not present */ | |
| bool getBoolField(const char *name) const; | | bool getBoolField(const char *name) const; | |
| | | | |
| /** makes a new BSONObj with the fields specified in pattern. | | /** makes a new BSONObj with the fields specified in pattern. | |
| fields returned in the order they appear in pattern. | | fields returned in the order they appear in pattern. | |
|
| if any field is missing from the object, that field in the | | if any field is missing or undefined in the object, that field i | |
| key will be null. | | n the | |
| | | output will be null. | |
| | | | |
|
| sets element field names to empty string | | sets output field names to match pattern field names. | |
| If an array is encountered while scanning the dotted names in pa
ttern, | | If an array is encountered while scanning the dotted names in pa
ttern, | |
|
| that array is added to the returned obj, rather than any subobje | | that field is treated as missing. | |
| cts | | | |
| referenced within the array. The variable nameWithinArray is se | | | |
| t to the | | | |
| name of the requested field within the returned array. | | | |
| */ | | */ | |
|
| BSONObj extractFieldsDotted(BSONObj pattern, BSONObjBuilder& b, con
st char *&nameWithinArray) const; // this version, builder owns the returne
d obj buffer | | BSONObj extractFieldsDotted(BSONObj pattern) const; | |
| | | | |
| /** | | /** | |
| sets element field names to empty string | | sets element field names to empty string | |
| If a field in pattern is missing, it is omitted from the returne
d | | If a field in pattern is missing, it is omitted from the returne
d | |
| object. | | object. | |
| */ | | */ | |
| BSONObj extractFieldsUnDotted(BSONObj pattern) const; | | BSONObj extractFieldsUnDotted(BSONObj pattern) const; | |
| | | | |
| /** extract items from object which match a pattern object. | | /** extract items from object which match a pattern object. | |
| e.g., if pattern is { x : 1, y : 1 }, builds an obje
ct with | | e.g., if pattern is { x : 1, y : 1 }, builds an obje
ct with | |
| | | | |
| skipping to change at line 938 | | skipping to change at line 921 | |
| } | | } | |
| | | | |
| /** @return true if field exists in the object */ | | /** @return true if field exists in the object */ | |
| bool hasElement(const char *name) const; | | bool hasElement(const char *name) const; | |
| | | | |
| /** Get the _id field from the object. For good performance
drivers should | | /** Get the _id field from the object. For good performance
drivers should | |
| assure that _id is the first element of the object; however, co
rrect operation | | assure that _id is the first element of the object; however, co
rrect operation | |
| is assured regardless. | | is assured regardless. | |
| @return true if found | | @return true if found | |
| */ | | */ | |
|
| bool getObjectID(BSONElement& e); | | bool getObjectID(BSONElement& e) const; | |
| | | | |
| /** makes a copy of the object. | | /** makes a copy of the object. | |
| */ | | */ | |
| BSONObj copy() const; | | BSONObj copy() const; | |
| | | | |
| /* make sure the data buffer is under the control of BSONObj's and
not a remote buffer */ | | /* make sure the data buffer is under the control of BSONObj's and
not a remote buffer */ | |
| BSONObj getOwned() const{ | | BSONObj getOwned() const{ | |
| if ( !isOwned() ) | | if ( !isOwned() ) | |
| return copy(); | | return copy(); | |
| return *this; | | return *this; | |
| | | | |
| skipping to change at line 1157 | | skipping to change at line 1140 | |
| } | | } | |
| | | | |
| /** append an element but with a new name */ | | /** append an element but with a new name */ | |
| void appendAs(const BSONElement& e, const char *as) { | | void appendAs(const BSONElement& e, const char *as) { | |
| assert( !e.eoo() ); // do not append eoo, that would corrupt us
. the builder auto appends when done() is called. | | assert( !e.eoo() ); // do not append eoo, that would corrupt us
. the builder auto appends when done() is called. | |
| b.append((char) e.type()); | | b.append((char) e.type()); | |
| b.append(as); | | b.append(as); | |
| b.append((void *) e.value(), e.valuesize()); | | b.append((void *) e.value(), e.valuesize()); | |
| } | | } | |
| | | | |
|
| | | void appendAs(const BSONElement& e, const string& as) { | |
| | | appendAs( e , as.c_str() ); | |
| | | } | |
| | | | |
| /** add a subobject as a member */ | | /** add a subobject as a member */ | |
| void append(const char *fieldName, BSONObj subObj) { | | void append(const char *fieldName, BSONObj subObj) { | |
| b.append((char) Object); | | b.append((char) Object); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append((void *) subObj.objdata(), subObj.objsize()); | | b.append((void *) subObj.objdata(), subObj.objsize()); | |
| } | | } | |
| | | | |
| void append(const string& fieldName , BSONObj subObj) { | | void append(const string& fieldName , BSONObj subObj) { | |
| append( fieldName.c_str() , subObj ); | | append( fieldName.c_str() , subObj ); | |
| } | | } | |
| | | | |
| skipping to change at line 1271 | | skipping to change at line 1258 | |
| */ | | */ | |
| void appendTimeT(const char *fieldName, time_t dt) { | | void appendTimeT(const char *fieldName, time_t dt) { | |
| b.append((char) Date); | | b.append((char) Date); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(static_cast<unsigned long long>(dt) * 1000); | | b.append(static_cast<unsigned long long>(dt) * 1000); | |
| } | | } | |
| /** Append a date. | | /** Append a date. | |
| @param dt a Java-style 64 bit date value, that is | | @param dt a Java-style 64 bit date value, that is | |
| the number of milliseconds since January 1, 1970, 00:
00:00 GMT | | the number of milliseconds since January 1, 1970, 00:
00:00 GMT | |
| */ | | */ | |
|
| void appendDate(const char *fieldName, unsigned long long dt) { | | void appendDate(const char *fieldName, Date_t dt) { | |
| b.append((char) Date); | | b.append((char) Date); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(dt); | | b.append(dt); | |
| } | | } | |
|
| | | void append(const char *fieldName, Date_t dt) { | |
| | | appendDate(fieldName, dt); | |
| | | } | |
| | | | |
| /** Append a regular expression value | | /** Append a regular expression value | |
| @param regex the regular expression pattern | | @param regex the regular expression pattern | |
| @param regex options such as "i" or "g" | | @param regex options such as "i" or "g" | |
| */ | | */ | |
| void appendRegex(const char *fieldName, const char *regex, const ch
ar *options = "") { | | void appendRegex(const char *fieldName, const char *regex, const ch
ar *options = "") { | |
| b.append((char) RegEx); | | b.append((char) RegEx); | |
| b.append(fieldName); | | b.append(fieldName); | |
| b.append(regex); | | b.append(regex); | |
| b.append(options); | | b.append(options); | |
| } | | } | |
| | | | |
| skipping to change at line 1442 | | skipping to change at line 1433 | |
| /* Append an array of ints | | /* Append an array of ints | |
| void appendArray( const char *fieldName, const vector< int >& vals
) { | | void appendArray( const char *fieldName, const vector< int >& vals
) { | |
| BSONObjBuilder arrBuilder; | | BSONObjBuilder arrBuilder; | |
| for ( unsigned i = 0; i < vals.size(); ++i ) | | for ( unsigned i = 0; i < vals.size(); ++i ) | |
| arrBuilder.append( numStr( i ).c_str(), vals[ i ] ); | | arrBuilder.append( numStr( i ).c_str(), vals[ i ] ); | |
| marshalArray( fieldName, arrBuilder.done() ); | | marshalArray( fieldName, arrBuilder.done() ); | |
| }*/ | | }*/ | |
| | | | |
| /** The returned BSONObj will free the buffer when it is finished.
*/ | | /** The returned BSONObj will free the buffer when it is finished.
*/ | |
| BSONObj obj() { | | BSONObj obj() { | |
|
| massert( "builder does not own memory", owned() ); | | massert( 10335 , "builder does not own memory", owned() ); | |
| int l; | | int l; | |
| return BSONObj(decouple(l), true); | | return BSONObj(decouple(l), true); | |
| } | | } | |
| | | | |
| /** Fetch the object we have built. | | /** Fetch the object we have built. | |
| BSONObjBuilder still frees the object when the build
er goes out of | | BSONObjBuilder still frees the object when the build
er goes out of | |
| scope -- very important to keep in mind. Use obj()
if you | | scope -- very important to keep in mind. Use obj()
if you | |
| would like the BSONObj to last longer than the build
er. | | would like the BSONObj to last longer than the build
er. | |
| */ | | */ | |
| BSONObj done() { | | BSONObj done() { | |
| | | | |
| skipping to change at line 1498 | | skipping to change at line 1489 | |
| ForceExplicitString( const string &str ) : str_( str ) {} | | ForceExplicitString( const string &str ) : str_( str ) {} | |
| string str_; | | string str_; | |
| }; | | }; | |
| | | | |
| /** Stream oriented way to add field names and values. */ | | /** Stream oriented way to add field names and values. */ | |
| BSONObjBuilderValueStream &operator<<( const ForceExplicitString& n
ame ) { | | BSONObjBuilderValueStream &operator<<( const ForceExplicitString& n
ame ) { | |
| return operator<<( name.str_.c_str() ); | | return operator<<( name.str_.c_str() ); | |
| } | | } | |
| | | | |
| Labeler operator<<( const Labeler::Label &l ) { | | Labeler operator<<( const Labeler::Label &l ) { | |
|
| massert( "No subobject started", s_.subobjStarted() ); | | massert( 10336 , "No subobject started", s_.subobjStarted() ); | |
| return s_ << l; | | return s_ << l; | |
| } | | } | |
| | | | |
| bool owned() const { | | bool owned() const { | |
| return &b == &buf_; | | return &b == &buf_; | |
| } | | } | |
| | | | |
| private: | | private: | |
| // Append the provided arr object as an array. | | // Append the provided arr object as an array. | |
| void marshalArray( const char *fieldName, const BSONObj &arr ) { | | void marshalArray( const char *fieldName, const BSONObj &arr ) { | |
| | | | |
| skipping to change at line 1651 | | skipping to change at line 1642 | |
| char sname[7]; | | char sname[7]; | |
| unsigned slen; | | unsigned slen; | |
| char sval[10]; | | char sval[10]; | |
| | | | |
| char eoo; | | char eoo; | |
| }; | | }; | |
| #pragma pack() | | #pragma pack() | |
| extern JSObj1 js1; | | extern JSObj1 js1; | |
| | | | |
| #ifdef _DEBUG | | #ifdef _DEBUG | |
|
| #define CHECK_OBJECT( o , msg ) massert( (string)"object not valid" + (msg)
, (o).isValid() ) | | #define CHECK_OBJECT( o , msg ) massert( 10337 , (string)"object not valid
" + (msg) , (o).isValid() ) | |
| #else | | #else | |
| #define CHECK_OBJECT( o , msg ) | | #define CHECK_OBJECT( o , msg ) | |
| #endif | | #endif | |
| | | | |
| inline BSONObj BSONElement::embeddedObjectUserCheck() { | | inline BSONObj BSONElement::embeddedObjectUserCheck() { | |
|
| uassert( "invalid parameter: expected an object", type()==Object ||
type()==Array ); | | uassert( 10065 , "invalid parameter: expected an object", type()==
Object || type()==Array ); | |
| return BSONObj(value()); | | return BSONObj(value()); | |
| } | | } | |
| | | | |
| inline BSONObj BSONElement::embeddedObject() const { | | inline BSONObj BSONElement::embeddedObject() const { | |
| assert( type()==Object || type()==Array ); | | assert( type()==Object || type()==Array ); | |
| return BSONObj(value()); | | return BSONObj(value()); | |
| } | | } | |
| | | | |
| inline BSONObj BSONElement::codeWScopeObject() const { | | inline BSONObj BSONElement::codeWScopeObject() const { | |
| assert( type() == CodeWScope ); | | assert( type() == CodeWScope ); | |
| | | | |
| skipping to change at line 1730 | | skipping to change at line 1721 | |
| if ( e.eoo() ) break; | | if ( e.eoo() ) break; | |
| append(e); | | append(e); | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| inline bool BSONObj::isValid(){ | | inline bool BSONObj::isValid(){ | |
| return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | | return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | |
| } | | } | |
| | | | |
|
| inline bool BSONObj::getObjectID(BSONElement& e) { | | inline bool BSONObj::getObjectID(BSONElement& e) const { | |
| BSONElement f = findElement("_id"); | | BSONElement f = findElement("_id"); | |
| if( !f.eoo() ) { | | if( !f.eoo() ) { | |
| e = f; | | e = f; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBui
lder * builder ) { | | inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBui
lder * builder ) { | |
| _fieldName = 0; | | _fieldName = 0; | |
| | | | |
| skipping to change at line 1825 | | skipping to change at line 1816 | |
| inline BSONMap bson2map(const BSONObj& obj){ | | inline BSONMap bson2map(const BSONObj& obj){ | |
| BSONMap m; | | BSONMap m; | |
| BSONObjIterator it(obj); | | BSONObjIterator it(obj); | |
| while (it.more()){ | | while (it.more()){ | |
| BSONElement e = it.next(); | | BSONElement e = it.next(); | |
| m[e.fieldName()] = e; | | m[e.fieldName()] = e; | |
| } | | } | |
| return m; | | return m; | |
| } | | } | |
| | | | |
|
| | | struct BSONElementFieldNameCmp { | |
| | | bool operator()( const BSONElement &l, const BSONElement &r ) const | |
| | | { | |
| | | return strcmp( l.fieldName() , r.fieldName() ) <= 0; | |
| | | } | |
| | | }; | |
| | | | |
| | | typedef set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements; | |
| | | inline BSONSortedElements bson2set( const BSONObj& obj ){ | |
| | | BSONSortedElements s; | |
| | | BSONObjIterator it(obj); | |
| | | while ( it.more() ) | |
| | | s.insert( it.next() ); | |
| | | return s; | |
| | | } | |
| | | | |
| | | class BSONObjIteratorSorted { | |
| | | public: | |
| | | BSONObjIteratorSorted( const BSONObj& o ); | |
| | | | |
| | | ~BSONObjIteratorSorted(){ | |
| | | assert( _fields ); | |
| | | delete _fields; | |
| | | _fields = 0; | |
| | | } | |
| | | | |
| | | bool more(){ | |
| | | return _cur < _nfields; | |
| | | } | |
| | | | |
| | | BSONElement next(){ | |
| | | assert( _fields ); | |
| | | if ( _cur < _nfields ) | |
| | | return BSONElement( _fields[_cur++] ); | |
| | | return BSONElement(); | |
| | | } | |
| | | | |
| | | private: | |
| | | const char ** _fields; | |
| | | int _nfields; | |
| | | int _cur; | |
| | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 26 change blocks. |
| 42 lines changed or deleted | | 74 lines changed or added | |
|
| matcher.h | | matcher.h | |
| | | | |
| skipping to change at line 28 | | skipping to change at line 28 | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include <pcrecpp.h> | | #include <pcrecpp.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| class KeyValJSMatcher; | | class CoveredIndexMatcher; | |
| | | | |
| 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 65 | | skipping to change at line 65 | |
| | | | |
| BasicMatcher(){ | | BasicMatcher(){ | |
| } | | } | |
| | | | |
| BasicMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareO
p( _op ){ | | BasicMatcher( BSONElement _e , int _op ) : toMatch( _e ) , compareO
p( _op ){ | |
| if ( _op == BSONObj::opMOD ){ | | if ( _op == BSONObj::opMOD ){ | |
| BSONObj o = _e.embeddedObject().firstElement().embeddedObje
ct(); | | BSONObj o = _e.embeddedObject().firstElement().embeddedObje
ct(); | |
| mod = o["0"].numberInt(); | | mod = o["0"].numberInt(); | |
| modm = o["1"].numberInt(); | | modm = o["1"].numberInt(); | |
| | | | |
|
| uassert( "mod can't be 0" , mod ); | | uassert( 10073 , "mod can't be 0" , mod ); | |
| } | | } | |
| else if ( _op == BSONObj::opTYPE ){ | | else if ( _op == BSONObj::opTYPE ){ | |
| type = (BSONType)(_e.embeddedObject().firstElement().number
Int()); | | type = (BSONType)(_e.embeddedObject().firstElement().number
Int()); | |
| } | | } | |
| } | | } | |
| | | | |
| BasicMatcher( BSONElement _e , int _op , const BSONObj& array ) : t
oMatch( _e ) , compareOp( _op ){ | | BasicMatcher( BSONElement _e , int _op , const BSONObj& array ) : t
oMatch( _e ) , compareOp( _op ){ | |
| | | | |
| myset.reset( new set<BSONElement,element_lt>() ); | | myset.reset( new set<BSONElement,element_lt>() ); | |
| | | | |
| | | | |
| skipping to change at line 102 | | skipping to change at line 102 | |
| | | | |
| // 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. | | { 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 JSMatcher : 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 BasicMatcher& bm, bool isArr = false); | |
| | | | |
| int matchesNe( | | int matchesNe( | |
| | | | |
| skipping to change at line 137 | | skipping to change at line 136 | |
| | | | |
| // 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()); | | JSMatcher(const BSONObj &pattern, const BSONObj &constrainIndexKey
= BSONObj()); | |
| | | | |
| ~JSMatcher(); | | ~JSMatcher(); | |
| | | | |
| 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; } | |
| | | | |
| 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( BasicMatcher( 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 BasicMatcher& bm); | |
| | | | |
| | | | |
| skipping to change at line 150 | | skipping to change at line 152 | |
| 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( BasicMatcher( 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 BasicMatcher& 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<BasicMatcher> 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) | |
| | | should be done atomically. in that case, we do not yi | |
| | | eld - | |
| | | i.e. we stay locked the whole time. | |
| | | */ | |
| | | 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 KeyValJSMatcher; | | friend class CoveredIndexMatcher; | |
| }; | | }; | |
| | | | |
|
| // If match succeeds on index key, then attempt to match full record. | | // If match succeeds on index key, then attempt to match full document. | |
| class KeyValJSMatcher : boost::noncopyable { | | class CoveredIndexMatcher : boost::noncopyable { | |
| public: | | public: | |
|
| KeyValJSMatcher(const BSONObj &pattern, const BSONObj &indexKeyPatt | | CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey | |
| ern); | | Pattern); | |
| bool matches(const BSONObj &j); | | 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; } | |
| private: | | private: | |
| JSMatcher _keyMatcher; | | JSMatcher _keyMatcher; | |
|
| JSMatcher _recordMatcher; | | JSMatcher _docMatcher; | |
| bool _needRecord; | | bool _needRecord; | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 14 change blocks. |
| 15 lines changed or deleted | | 26 lines changed or added | |
|
| namespace.h | | namespace.h | |
| | | | |
| skipping to change at line 91 | | skipping to change at line 91 | |
| }; | | }; | |
| | | | |
| /* This helper class is used to make the HashMap below in NamespaceD
etails */ | | /* This helper class is used to make the HashMap below in NamespaceD
etails */ | |
| class Namespace { | | class Namespace { | |
| public: | | public: | |
| enum MaxNsLenValue { MaxNsLen = 128 }; | | enum MaxNsLenValue { MaxNsLen = 128 }; | |
| Namespace(const char *ns) { | | Namespace(const char *ns) { | |
| *this = ns; | | *this = ns; | |
| } | | } | |
| Namespace& operator=(const char *ns) { | | Namespace& operator=(const char *ns) { | |
|
| uassert("ns name too long, max size is 128", strlen(ns) < MaxNs
Len); | | uassert( 10080 , "ns name too long, max size is 128", strlen(ns
) < MaxNsLen); | |
| //memset(buf, 0, MaxNsLen); /* this is just to keep stuff clean
in the files for easy dumping and reading */ | | //memset(buf, 0, MaxNsLen); /* this is just to keep stuff clean
in the files for easy dumping and reading */ | |
| strcpy_s(buf, MaxNsLen, ns); | | strcpy_s(buf, MaxNsLen, ns); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /* for more than 10 indexes -- see NamespaceDetails::Extra */ | | /* for more than 10 indexes -- see NamespaceDetails::Extra */ | |
| string extraName() { | | string extraName() { | |
| string s = string(buf) + "$extra"; | | string s = string(buf) + "$extra"; | |
|
| massert("ns name too long", s.size() < MaxNsLen); | | massert( 10348 , "ns name too long", s.size() < MaxNsLen); | |
| return s; | | return s; | |
| } | | } | |
| | | | |
| void kill() { | | void kill() { | |
| buf[0] = 0x7f; | | buf[0] = 0x7f; | |
| } | | } | |
| | | | |
| bool operator==(const char *r) { | | bool operator==(const char *r) { | |
| return strcmp(buf, r) == 0; | | return strcmp(buf, r) == 0; | |
| } | | } | |
| | | | |
| skipping to change at line 313 | | skipping to change at line 313 | |
| 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; | |
| 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. | |
| | | deletedList[0] points to a list of free records (DeletedRe | |
| | | cord's) for all extents in | |
| | | the namespace. | |
| | | 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 | |
| | | yet computed. | |
| | | */ | |
| DiskLoc deletedList[Buckets]; | | DiskLoc deletedList[Buckets]; | |
|
| | | | |
| long long datasize; | | long long datasize; | |
| long long nrecords; | | long long nrecords; | |
| int lastExtentSize; | | int lastExtentSize; | |
| int nIndexes; | | int nIndexes; | |
| private: | | private: | |
| IndexDetails _indexes[NIndexesBase]; | | IndexDetails _indexes[NIndexesBase]; | |
| public: | | public: | |
| int capped; | | int capped; | |
| int max; // max # of objects for a capped table. | | int max; // max # of objects for a capped table. | |
| double paddingFactor; // 1.0 = no padding. | | double paddingFactor; // 1.0 = no padding. | |
| | | | |
| skipping to change at line 388 | | skipping to change at line 397 | |
| } | | } | |
| | | | |
| /* hackish - find our index # in the indexes array | | /* hackish - find our index # in the indexes array | |
| */ | | */ | |
| int idxNo(IndexDetails& idx) { | | int idxNo(IndexDetails& idx) { | |
| IndexIterator i = ii(); | | IndexIterator i = ii(); | |
| while( i.more() ) { | | while( i.more() ) { | |
| if( &i.next() == &idx ) | | if( &i.next() == &idx ) | |
| return i.pos()-1; | | return i.pos()-1; | |
| } | | } | |
|
| massert("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 object 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; | |
| } | | } | |
| | | | |
| skipping to change at line 516 | | skipping to change at line 525 | |
| | | | |
| #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. | |
| | | | |
| CAUTION: Are you maintaining this properly on a collection drop()?
A dropdatabase()? Be careful. | | CAUTION: Are you maintaining this properly on a collection drop()?
A dropdatabase()? Be careful. | |
| The current field "allIndexKeys" may have too many keys in
it on such an occurrence; | | The current field "allIndexKeys" may have too many keys in
it on such an occurrence; | |
| as currently used that does not cause anything terrible to
happen. | | as currently used that does not cause anything terrible to
happen. | |
|
| | | | |
| | | todo: cleanup code, need abstractions and separation | |
| */ | | */ | |
| class NamespaceDetailsTransient : boost::noncopyable { | | class NamespaceDetailsTransient : boost::noncopyable { | |
|
| string ns; | | /* general -------------------------------------------------------- | |
| bool haveIndexKeys; | | ----- */ | |
| set<string> allIndexKeys; | | private: | |
| void computeIndexKeys(); | | string _ns; | |
| int writeCount_; | | void reset(); | |
| map< QueryPattern, pair< BSONObj, long long > > queryCache_; | | static std::map< string, shared_ptr< NamespaceDetailsTransient > > | |
| string logNS_; | | _map; | |
| bool logValid_; | | | |
| public: | | public: | |
|
| NamespaceDetailsTransient(const char *_ns) : ns(_ns), haveIndexKeys | | NamespaceDetailsTransient(const char *ns) : _ns(ns), _keysComputed( | |
| (), writeCount_(), logValid_() { | | false), _qcWriteCount(), _cll_enabled() { } | |
| haveIndexKeys=false; /*lazy load them*/ | | /* _get() is not threadsafe */ | |
| | | static NamespaceDetailsTransient& _get(const char *ns); | |
| | | /* use get_w() when doing write operations */ | |
| | | static NamespaceDetailsTransient& get_w(const char *ns) { | |
| | | DEV assertInWriteLock(); | |
| | | return _get(ns); | |
| } | | } | |
|
| | | void addedIndex() { reset(); } | |
| | | void deletedIndex() { reset(); } | |
| | | /* Drop cached information on all namespaces beginning with the spe | |
| | | cified prefix. | |
| | | Can be useful as index namespaces share the same start as the re | |
| | | gular collection. | |
| | | SLOW - sequential scan of all NamespaceDetailsTransient objects | |
| | | */ | |
| | | static void clearForPrefix(const char *prefix); | |
| | | | |
|
| | | /* indexKeys() cache ---------------------------------------------- | |
| | | ------ */ | |
| | | /* assumed to be in write lock for this */ | |
| | | private: | |
| | | bool _keysComputed; | |
| | | set<string> _indexKeys; | |
| | | void computeIndexKeys(); | |
| | | public: | |
| /* 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 seconary component of a com
pound index.) | | field is indexed (Note it might be a secondary component of a co
mpound index.) | |
| */ | | */ | |
| set<string>& indexKeys() { | | set<string>& indexKeys() { | |
|
| if ( !haveIndexKeys ) { | | DEV assertInWriteLock(); | |
| haveIndexKeys=true; | | if ( !_keysComputed ) | |
| computeIndexKeys(); | | computeIndexKeys(); | |
|
| } | | return _indexKeys; | |
| return allIndexKeys; | | | |
| } | | } | |
| | | | |
|
| void addedIndex() { reset(); } | | /* query cache (for query optimizer) ------------------------------ | |
| void deletedIndex() { reset(); } | | ------- */ | |
| void registerWriteOp() { | | private: | |
| if ( queryCache_.empty() ) | | int _qcWriteCount; | |
| | | map< QueryPattern, pair< BSONObj, long long > > _qcCache; | |
| | | public: | |
| | | static boost::mutex _qcMutex; | |
| | | /* you must be in the qcMutex when calling this (and using the retu | |
| | | rned val): */ | |
| | | static NamespaceDetailsTransient& get_inlock(const char *ns) { | |
| | | return _get(ns); | |
| | | } | |
| | | void clearQueryCache() { // public for unit tests | |
| | | _qcCache.clear(); | |
| | | _qcWriteCount = 0; | |
| | | } | |
| | | /* you must notify the cache if you are doing writes, as query plan | |
| | | optimality will change */ | |
| | | void notifyOfWriteOp() { | |
| | | if ( _qcCache.empty() ) | |
| return; | | return; | |
|
| if ( ++writeCount_ >= 100 ) | | if ( ++_qcWriteCount >= 100 ) | |
| clearQueryCache(); | | clearQueryCache(); | |
| } | | } | |
|
| void clearQueryCache() { | | | |
| queryCache_.clear(); | | | |
| writeCount_ = 0; | | | |
| } | | | |
| BSONObj indexForPattern( const QueryPattern &pattern ) { | | BSONObj indexForPattern( const QueryPattern &pattern ) { | |
|
| return queryCache_[ pattern ].first; | | return _qcCache[ pattern ].first; | |
| } | | } | |
| long long nScannedForPattern( const QueryPattern &pattern ) { | | long long nScannedForPattern( const QueryPattern &pattern ) { | |
|
| return queryCache_[ pattern ].second; | | return _qcCache[ pattern ].second; | |
| } | | } | |
| void registerIndexForPattern( const QueryPattern &pattern, const BS
ONObj &indexKey, long long nScanned ) { | | void registerIndexForPattern( const QueryPattern &pattern, const BS
ONObj &indexKey, long long nScanned ) { | |
|
| queryCache_[ pattern ] = make_pair( indexKey, nScanned ); | | _qcCache[ pattern ] = make_pair( indexKey, nScanned ); | |
| } | | } | |
| | | | |
|
| void startLog( int logSizeMb = 128 ); | | /* for collection-level logging -- see CmdLogCollection ----------- | |
| void invalidateLog(); | | ------ */ | |
| bool validateCompleteLog(); | | /* assumed to be in write lock for this */ | |
| string logNS() const { return logNS_; } | | | |
| bool logValid() const { return logValid_; } | | | |
| | | | |
| private: | | private: | |
|
| void reset(); | | string _cll_ns; // "local.temp.oplog." + _ns; | |
| void dropLog(); | | bool _cll_enabled; | |
| static std::map< string, shared_ptr< NamespaceDetailsTransient > > | | void cllDrop(); // drop _cll_ns | |
| map_; | | | |
| public: | | public: | |
|
| static NamespaceDetailsTransient& get(const char *ns); | | string cllNS() const { return _cll_ns; } | |
| // Drop cached information on all namespaces beginning with the spe | | bool cllEnabled() const { return _cll_enabled; } | |
| cified prefix. | | void cllStart( int logSizeMb = 256 ); // begin collection level log | |
| static void drop(const char *prefix); | | ging | |
| }; | | void cllInvalidate(); | |
| | | bool cllValidateComplete(); | |
| | | | |
| | | }; /* NamespaceDetailsTransient */ | |
| | | | |
| | | inline NamespaceDetailsTransient& NamespaceDetailsTransient::_get(const | |
| | | char *ns) { | |
| | | shared_ptr< NamespaceDetailsTransient > &t = _map[ ns ]; | |
| | | if ( t.get() == 0 ) | |
| | | t.reset( new NamespaceDetailsTransient(ns) ); | |
| | | return *t; | |
| | | } | |
| | | | |
| /* NamespaceIndex is the ".ns" file you see in the data directory. It
is the "system catalog" | | /* NamespaceIndex is the ".ns" file you see in the data directory. It
is the "system catalog" | |
| if you will: at least the core parts. (Additional info in system.*
collections.) | | if you will: at least the core parts. (Additional info in system.*
collections.) | |
| */ | | */ | |
| class NamespaceIndex { | | class NamespaceIndex { | |
| friend class NamespaceCursor; | | friend class NamespaceCursor; | |
| BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name
spaceDetails) ); | | BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) <= sizeof(Name
spaceDetails) ); | |
| public: | | public: | |
| NamespaceIndex(const string &dir, const string &database) : | | NamespaceIndex(const string &dir, const string &database) : | |
| ht( 0 ), | | ht( 0 ), | |
| | | | |
| skipping to change at line 605 | | skipping to change at line 645 | |
| void init(); | | void init(); | |
| | | | |
| void add_ns(const char *ns, DiskLoc& loc, bool capped) { | | void add_ns(const char *ns, DiskLoc& loc, bool capped) { | |
| NamespaceDetails details( loc, capped ); | | NamespaceDetails details( loc, capped ); | |
| add_ns( ns, details ); | | add_ns( ns, details ); | |
| } | | } | |
| | | | |
| void add_ns( const char *ns, const NamespaceDetails &details
) { | | void add_ns( const char *ns, const NamespaceDetails &details
) { | |
| init(); | | init(); | |
| Namespace n(ns); | | Namespace n(ns); | |
|
| uassert("too many namespaces/collections", ht->put(n, details))
; | | uassert( 10081 , "too many namespaces/collections", ht->put(n,
details)); | |
| } | | } | |
| | | | |
| /* just for diagnostics */ | | /* just for diagnostics */ | |
| size_t detailsOffset(NamespaceDetails *d) { | | size_t detailsOffset(NamespaceDetails *d) { | |
| if ( !ht ) | | if ( !ht ) | |
| return -1; | | return -1; | |
| return ((char *) d) - (char *) ht->nodes; | | return ((char *) d) - (char *) ht->nodes; | |
| } | | } | |
| | | | |
| /* extra space for indexes when more than 10 */ | | /* extra space for indexes when more than 10 */ | |
| NamespaceDetails::Extra* allocExtra(const char *ns) { | | NamespaceDetails::Extra* allocExtra(const char *ns) { | |
| Namespace n(ns); | | Namespace n(ns); | |
| Namespace extra(n.extraName().c_str()); // throws userexception
if ns name too long | | Namespace extra(n.extraName().c_str()); // throws userexception
if ns name too long | |
| NamespaceDetails *d = details(ns); | | NamespaceDetails *d = details(ns); | |
|
| massert( "allocExtra: base ns missing?", d ); | | massert( 10350 , "allocExtra: base ns missing?", d ); | |
| assert( d->extraOffset == 0 ); | | assert( d->extraOffset == 0 ); | |
|
| massert( "allocExtra: extra already exists", ht->get(extra) ==
0 ); | | massert( 10351 , "allocExtra: extra already exists", ht->get(e
xtra) == 0 ); | |
| NamespaceDetails::Extra temp; | | NamespaceDetails::Extra temp; | |
| memset(&temp, 0, sizeof(temp)); | | memset(&temp, 0, sizeof(temp)); | |
|
| uassert( "allocExtra: too many namespaces/collections", ht->put
(extra, (NamespaceDetails&) temp)); | | uassert( 10082 , "allocExtra: too many namespaces/collections"
, ht->put(extra, (NamespaceDetails&) temp)); | |
| NamespaceDetails::Extra *e = (NamespaceDetails::Extra *) ht->ge
t(extra); | | NamespaceDetails::Extra *e = (NamespaceDetails::Extra *) ht->ge
t(extra); | |
| d->extraOffset = ((char *) e) - ((char *) d); | | d->extraOffset = ((char *) e) - ((char *) d); | |
| assert( d->extra() == e ); | | assert( d->extra() == e ); | |
| return e; | | return e; | |
| } | | } | |
| | | | |
| NamespaceDetails* details(const char *ns) { | | NamespaceDetails* details(const char *ns) { | |
| if ( !ht ) | | if ( !ht ) | |
| return 0; | | return 0; | |
| Namespace n(ns); | | Namespace n(ns); | |
| | | | |
End of changes. 26 change blocks. |
| 50 lines changed or deleted | | 103 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 41 | | skipping to change at line 41 | |
| #include "jsobjmanipulator.h" | | #include "jsobjmanipulator.h" | |
| #include "namespace.h" | | #include "namespace.h" | |
| #include "client.h" | | #include "client.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class MDFHeader; | | class MDFHeader; | |
| class Extent; | | class Extent; | |
| class Record; | | class Record; | |
| class Cursor; | | class Cursor; | |
|
| | | class OpDebug; | |
| | | | |
| 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); | |
| | | | |
| skipping to change at line 73 | | skipping to change at line 74 | |
| public: | | public: | |
| MongoDataFile(int fn) : fileNo(fn) { } | | MongoDataFile(int fn) : fileNo(fn) { } | |
| void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | | void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | |
| | | | |
| /* allocate a new extent from this datafile. | | /* allocate a new extent from this datafile. | |
| @param capped - true if capped collection | | @param capped - true if capped collection | |
| @param loops is our recursion check variable - you want to pass
in zero | | @param loops is our recursion check variable - you want to pass
in zero | |
| */ | | */ | |
| Extent* createExtent(const char *ns, int approxSize, bool capped =
false, int loops = 0); | | Extent* createExtent(const char *ns, int approxSize, bool capped =
false, int loops = 0); | |
| | | | |
|
| /* see if we can find an extent of the right size in the freelist. | | | |
| if not, | | | |
| createExtent. | | | |
| */ | | | |
| Extent* allocExtent(const char *ns, int approxSize, bool capped = f | | | |
| alse); | | | |
| | | | |
| MDFHeader *getHeader() { | | MDFHeader *getHeader() { | |
| return header; | | return header; | |
| } | | } | |
| | | | |
| /* return max size an extent may be */ | | /* return max size an extent may be */ | |
| static int maxSize(); | | static int maxSize(); | |
| | | | |
| private: | | private: | |
| int defaultSize( const char *filename ) const; | | int defaultSize( const char *filename ) const; | |
| | | | |
| | | | |
| skipping to change at line 102 | | skipping to change at line 98 | |
| MemoryMappedFile mmf; | | MemoryMappedFile mmf; | |
| MDFHeader *header; | | MDFHeader *header; | |
| int fileNo; | | int fileNo; | |
| }; | | }; | |
| | | | |
| class DataFileMgr { | | class DataFileMgr { | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| public: | | public: | |
| void init(const string& path ); | | void init(const string& path ); | |
| | | | |
|
| | | /* see if we can find an extent of the right size in the freelist. | |
| | | */ | |
| | | static Extent* allocFromFreeList(const char *ns, int approxSize, bo | |
| | | ol capped = false); | |
| | | | |
| /** @return DiskLoc where item ends up */ | | /** @return DiskLoc where item ends up */ | |
| const DiskLoc update( | | const DiskLoc update( | |
| const char *ns, | | const char *ns, | |
| Record *toupdate, const DiskLoc& dl, | | Record *toupdate, const DiskLoc& dl, | |
|
| const char *buf, int len, stringstream& profiling); | | const char *buf, int len, OpDebug& debug); | |
| // The object o may be updated if modified on insert. | | // The object o may be updated if modified on insert. | |
| void insertAndLog( const char *ns, const BSONObj &o, bool god = fal
se ); | | void insertAndLog( const char *ns, const BSONObj &o, bool god = fal
se ); | |
| DiskLoc insert(const char *ns, BSONObj &o, bool god = false); | | DiskLoc insert(const char *ns, BSONObj &o, bool god = false); | |
| DiskLoc insert(const char *ns, const void *buf, int len, bool god =
false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true
); | | DiskLoc insert(const char *ns, const void *buf, int len, bool god =
false, const BSONElement &writeId = BSONElement(), bool mayAddIndex = true
); | |
| void deleteRecord(const char *ns, Record *todelete, const DiskLoc&
dl, bool cappedOK = false, bool noWarn = false); | | void deleteRecord(const char *ns, Record *todelete, const DiskLoc&
dl, bool cappedOK = false, bool noWarn = false); | |
| static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star
tLoc = DiskLoc()); | | static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star
tLoc = DiskLoc()); | |
| | | | |
| /* special version of insert for transaction logging -- streamlined
a bit. | | /* special version of insert for transaction logging -- streamlined
a bit. | |
| assumes ns is capped and no indexes | | assumes ns is capped and no indexes | |
| no _id field check | | no _id field check | |
| | | | |
End of changes. 4 change blocks. |
| 8 lines changed or deleted | | 7 lines changed or added | |
|
| sock.h | | sock.h | |
| | | | |
| skipping to change at line 66 | | skipping to change at line 66 | |
| #include <errno.h> | | #include <errno.h> | |
| #include <netdb.h> | | #include <netdb.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline void closesocket(int s) { | | inline void closesocket(int s) { | |
| close(s); | | close(s); | |
| } | | } | |
| const int INVALID_SOCKET = -1; | | const int INVALID_SOCKET = -1; | |
| typedef int SOCKET; | | typedef int SOCKET; | |
|
| //#define h_errno errno | | | |
| inline int getLastError() { | | | |
| return errno; | | | |
| } | | | |
| inline void disableNagle(int sock) { | | inline void disableNagle(int sock) { | |
| int x = 1; | | int x = 1; | |
| | | | |
| #ifdef SOL_TCP | | #ifdef SOL_TCP | |
| int level = SOL_TCP; | | int level = SOL_TCP; | |
| #else | | #else | |
| int level = SOL_SOCKET; | | int level = SOL_SOCKET; | |
| #endif | | #endif | |
| | | | |
| if ( setsockopt(sock, level, TCP_NODELAY, (char *) &x, sizeof(x)) ) | | if ( setsockopt(sock, level, TCP_NODELAY, (char *) &x, sizeof(x)) ) | |
| | | | |
| skipping to change at line 99 | | skipping to change at line 96 | |
| | | | |
| #endif | | #endif | |
| | | | |
| inline void setSockReceiveTimeout(int sock, int secs) { | | inline void setSockReceiveTimeout(int sock, int secs) { | |
| // todo - finish - works? | | // todo - finish - works? | |
| struct timeval tv; | | struct timeval tv; | |
| tv.tv_sec = 0;//secs; | | tv.tv_sec = 0;//secs; | |
| tv.tv_usec = 1000; | | tv.tv_usec = 1000; | |
| int rc = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, si
zeof(tv)); | | int rc = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, si
zeof(tv)); | |
| if ( rc ) { | | if ( rc ) { | |
|
| out() << "ERROR: setsockopt RCVTIMEO failed rc:" << rc << " err
no:" << getLastError() << " secs:" << secs << " sock:" << sock << endl; | | out() << "ERROR: setsockopt RCVTIMEO failed rc:" << rc << " " <
< OUTPUT_ERRNO << " secs:" << secs << " sock:" << sock << endl; | |
| } | | } | |
| } | | } | |
| | | | |
| // If an ip address is passed in, just return that. If a hostname is p
assed | | // If an ip address is passed in, just return that. If a hostname is p
assed | |
| // in, look up its ip and return that. Returns "" on failure. | | // in, look up its ip and return that. Returns "" on failure. | |
| string hostbyname(const char *hostname); | | string hostbyname(const char *hostname); | |
| | | | |
| struct SockAddr { | | struct SockAddr { | |
| SockAddr() { | | SockAddr() { | |
| addressSize = sizeof(sockaddr_in); | | addressSize = sizeof(sockaddr_in); | |
| | | | |
| skipping to change at line 196 | | skipping to change at line 193 | |
| out() << " NOTSENT "; | | out() << " NOTSENT "; | |
| // out() << curTimeMillis() << " .TEST: NOT SENDING PAC
KET" << endl; | | // out() << curTimeMillis() << " .TEST: NOT SENDING PAC
KET" << endl; | |
| return 0; | | return 0; | |
| } | | } | |
| return ::sendto(sock, buf, len, 0, (sockaddr *) &EndPoint.sa, EndPo
int.addressSize); | | return ::sendto(sock, buf, len, 0, (sockaddr *) &EndPoint.sa, EndPo
int.addressSize); | |
| } | | } | |
| | | | |
| inline bool UDPConnection::init(const SockAddr& myAddr) { | | inline bool UDPConnection::init(const SockAddr& myAddr) { | |
| sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
| if ( sock == INVALID_SOCKET ) { | | if ( sock == INVALID_SOCKET ) { | |
|
| out() << "invalid socket? " << errno << endl; | | out() << "invalid socket? " << OUTPUT_ERRNO << endl; | |
| return false; | | return false; | |
| } | | } | |
| //out() << sizeof(sockaddr_in) << ' ' << myAddr.addressSize << endl
; | | //out() << sizeof(sockaddr_in) << ' ' << myAddr.addressSize << endl
; | |
| if ( ::bind(sock, (sockaddr *) &myAddr.sa, myAddr.addressSize) != 0
) { | | if ( ::bind(sock, (sockaddr *) &myAddr.sa, myAddr.addressSize) != 0
) { | |
| out() << "udp init failed" << endl; | | out() << "udp init failed" << endl; | |
| closesocket(sock); | | closesocket(sock); | |
| sock = 0; | | sock = 0; | |
| return false; | | return false; | |
| } | | } | |
| socklen_t optLen; | | socklen_t optLen; | |
| | | | |
| skipping to change at line 238 | | skipping to change at line 235 | |
| sa.sin_family = AF_INET; | | sa.sin_family = AF_INET; | |
| sa.sin_port = htons(port); | | sa.sin_port = htons(port); | |
| sa.sin_addr.s_addr = inet_addr(ip.c_str()); | | sa.sin_addr.s_addr = inet_addr(ip.c_str()); | |
| addressSize = sizeof(sa); | | addressSize = sizeof(sa); | |
| } | | } | |
| | | | |
| inline string getHostName() { | | inline string getHostName() { | |
| char buf[256]; | | char buf[256]; | |
| int ec = gethostname(buf, 127); | | int ec = gethostname(buf, 127); | |
| if ( ec || *buf == 0 ) { | | if ( ec || *buf == 0 ) { | |
|
| log() << "can't get this server's hostname errno:" << ec << end
l; | | log() << "can't get this server's hostname " << OUTPUT_ERRNO <<
endl; | |
| return ""; | | return ""; | |
| } | | } | |
| return buf; | | return buf; | |
| } | | } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 4 change blocks. |
| 7 lines changed or deleted | | 4 lines changed or added | |
|
| top.h | | top.h | |
| | | | |
| skipping to change at line 26 | | skipping to change at line 26 | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include <boost/date_time/posix_time/posix_time.hpp> | | #include <boost/date_time/posix_time/posix_time.hpp> | |
| #undef assert | | #undef assert | |
| #define assert xassert | | #define assert xassert | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| /* Records per namespace utilization of the mongod process. | | /* Records per namespace utilization of the mongod process. | |
| No two functions of this class may be called concurrently. | | No two functions of this class may be called concurrently. | |
| */ | | */ | |
| class Top { | | class Top { | |
| typedef boost::posix_time::ptime T; | | typedef boost::posix_time::ptime T; | |
| typedef boost::posix_time::time_duration D; | | typedef boost::posix_time::time_duration D; | |
| public: | | typedef boost::tuple< D, int, int, int > UsageData; | |
| Top() : read_(false), write_(false) { } | | public: | |
| | | Top() : _read(false), _write(false) { } | |
| | | | |
|
| /* these are used to record activity: */ | | /* these are used to record activity: */ | |
| | | | |
|
| void clientStart( const char *client ) { | | void clientStart( const char *client ) { | |
| clientStop(); | | clientStop(); | |
| currentStart_ = currentTime(); | | _currentStart = currentTime(); | |
| current_ = client; | | _current = client; | |
| } | | } | |
| | | | |
|
| /* indicate current request is a read operation. */ | | /* indicate current request is a read operation. */ | |
| void setRead() { read_ = true; } | | void setRead() { _read = true; } | |
| | | | |
|
| void setWrite() { write_ = true; } | | void setWrite() { _write = true; } | |
| | | | |
|
| void clientStop() { | | void clientStop() { | |
| if ( currentStart_ == T() ) | | if ( _currentStart == T() ) | |
| return; | | return; | |
| D d = currentTime() - currentStart_; | | D d = currentTime() - _currentStart; | |
| | | | |
|
| { | | { | |
| boostlock L(topMutex); | | boostlock L(topMutex); | |
| recordUsage( current_, d ); | | recordUsage( _current, d ); | |
| } | | } | |
| | | | |
|
| currentStart_ = T(); | | _currentStart = T(); | |
| read_ = false; | | _read = false; | |
| write_ = false; | | _write = false; | |
| } | | } | |
| | | | |
|
| /* these are used to fetch the stats: */ | | /* these are used to fetch the stats: */ | |
| | | | |
|
| struct Usage { | | struct Usage { | |
| string ns; | | string ns; | |
| D time; | | D time; | |
| double pct; | | double pct; | |
| int reads, writes, calls; | | int reads, writes, calls; | |
| }; | | }; | |
| | | | |
|
| static void usage( vector< Usage > &res ) { | | static void usage( vector< Usage > &res ) { | |
| boostlock L(topMutex); | | boostlock L(topMutex); | |
| | | | |
|
| // Populate parent namespaces | | // Populate parent namespaces | |
| UsageMap snapshot; | | UsageMap snapshot; | |
| UsageMap totalUsage; | | UsageMap totalUsage; | |
| fillParentNamespaces( snapshot, snapshot_ ); | | fillParentNamespaces( snapshot, _snapshot ); | |
| fillParentNamespaces( totalUsage, totalUsage_ ); | | fillParentNamespaces( totalUsage, _totalUsage ); | |
| | | | |
|
| multimap< D, string, more > sorted; | | multimap< D, string, more > sorted; | |
| for( UsageMap::iterator i = snapshot.begin(); i != snapshot.end(); | | for( UsageMap::iterator i = snapshot.begin(); i != snapshot.end | |
| ++i ) | | (); ++i ) | |
| sorted.insert( make_pair( i->second.get<0>(), i->first ) ); | | sorted.insert( make_pair( i->second.get<0>(), i->first ) ); | |
| for( multimap< D, string, more >::iterator i = sorted.begin(); i != | | for( multimap< D, string, more >::iterator i = sorted.begin(); | |
| sorted.end(); ++i ) { | | i != sorted.end(); ++i ) { | |
| if ( trivialNs( i->second.c_str() ) ) | | if ( trivialNs( i->second.c_str() ) ) | |
| continue; | | continue; | |
| Usage u; | | Usage u; | |
| u.ns = i->second; | | u.ns = i->second; | |
| u.time = totalUsage[ u.ns ].get<0>(); | | u.time = totalUsage[ u.ns ].get<0>(); | |
| u.pct = snapshotDuration_ != D() ? 100.0 * i->first.ticks() / s | | u.pct = _snapshotDuration != D() ? 100.0 * i->first.ticks() | |
| napshotDuration_.ticks() : 0; | | / _snapshotDuration.ticks() : 0; | |
| u.reads = snapshot[ u.ns ].get<1>(); | | u.reads = snapshot[ u.ns ].get<1>(); | |
| u.writes = snapshot[ u.ns ].get<2>(); | | u.writes = snapshot[ u.ns ].get<2>(); | |
| u.calls = snapshot[ u.ns ].get<3>(); | | u.calls = snapshot[ u.ns ].get<3>(); | |
| res.push_back( u ); | | res.push_back( u ); | |
| } | | } | |
| for( UsageMap::iterator i = totalUsage.begin(); i != totalUsage.end | | for( UsageMap::iterator i = totalUsage.begin(); i != totalUsage | |
| (); ++i ) { | | .end(); ++i ) { | |
| if ( snapshot.count( i->first ) != 0 || trivialNs( i->first.c_s | | if ( snapshot.count( i->first ) != 0 || trivialNs( i->first | |
| tr() ) ) | | .c_str() ) ) | |
| continue; | | continue; | |
| Usage u; | | Usage u; | |
| u.ns = i->first; | | u.ns = i->first; | |
| u.time = i->second.get<0>(); | | u.time = i->second.get<0>(); | |
| u.pct = 0; | | u.pct = 0; | |
| u.reads = 0; | | u.reads = 0; | |
| u.writes = 0; | | u.writes = 0; | |
| u.calls = 0; | | u.calls = 0; | |
| res.push_back( u ); | | res.push_back( u ); | |
| | | } | |
| } | | } | |
|
| } | | | |
| | | | |
|
| static void completeSnapshot() { | | static void completeSnapshot() { | |
| boostlock L(topMutex); | | boostlock L(topMutex); | |
| | | | |
|
| if ( &snapshot_ == &snapshotA_ ) { | | if ( &_snapshot == &_snapshotA ) { | |
| snapshot_ = snapshotB_; | | _snapshot = _snapshotB; | |
| nextSnapshot_ = snapshotA_; | | _nextSnapshot = _snapshotA; | |
| } else { | | } else { | |
| snapshot_ = snapshotA_; | | _snapshot = _snapshotA; | |
| nextSnapshot_ = snapshotB_; | | _nextSnapshot = _snapshotB; | |
| | | } | |
| | | _snapshotDuration = currentTime() - _snapshotStart; | |
| | | _snapshotStart = currentTime(); | |
| | | _nextSnapshot.clear(); | |
| } | | } | |
|
| snapshotDuration_ = currentTime() - snapshotStart_; | | | |
| snapshotStart_ = currentTime(); | | | |
| nextSnapshot_.clear(); | | | |
| } | | | |
| | | | |
|
| private: | | private: | |
| static boost::mutex topMutex; | | static boost::mutex topMutex; | |
| static bool trivialNs( const char *ns ) { | | static bool trivialNs( const char *ns ) { | |
| const char *ret = strrchr( ns, '.' ); | | const char *ret = strrchr( ns, '.' ); | |
| return ret && ret[ 1 ] == '\0'; | | return ret && ret[ 1 ] == '\0'; | |
| } | | } | |
| typedef map< string, boost::tuple< D, int, int, int > > UsageMap; // du | | typedef map<string,UsageData> UsageMap; // duration, # reads, # wri | |
| ration, # reads, # writes, # total calls | | tes, # total calls | |
| static T currentTime() { | | static T currentTime() { | |
| return boost::posix_time::microsec_clock::universal_time(); | | return boost::posix_time::microsec_clock::universal_time(); | |
| } | | } | |
| void recordUsage( const string &client, D duration ) { | | void recordUsage( const string &client, D duration ) { | |
| recordUsageForMap( totalUsage_, client, duration ); | | recordUsageForMap( _totalUsage, client, duration ); | |
| recordUsageForMap( nextSnapshot_, client, duration ); | | recordUsageForMap( _nextSnapshot, client, duration ); | |
| } | | } | |
| void recordUsageForMap( UsageMap &map, const string &client, D duration | | void recordUsageForMap( UsageMap &map, const string &client, D dura | |
| ) { | | tion ) { | |
| map[ client ].get< 0 >() += duration; | | UsageData& g = map[client]; | |
| if ( read_ && !write_ ) | | g.get< 0 >() += duration; | |
| map[ client ].get< 1 >()++; | | if ( _read && !_write ) | |
| else if ( !read_ && write_ ) | | g.get< 1 >()++; | |
| map[ client ].get< 2 >()++; | | else if ( !_read && _write ) | |
| map[ client ].get< 3 >()++; | | g.get< 2 >()++; | |
| } | | g.get< 3 >()++; | |
| static void fillParentNamespaces( UsageMap &to, const UsageMap &from ) | | } | |
| { | | static void fillParentNamespaces( UsageMap &to, const UsageMap &fro | |
| for( UsageMap::const_iterator i = from.begin(); i != from.end(); ++ | | m ) { | |
| i ) { | | for( UsageMap::const_iterator i = from.begin(); i != from.end() | |
| string current = i->first; | | ; ++i ) { | |
| size_t dot = current.rfind( "." ); | | string current = i->first; | |
| if ( dot == string::npos || dot != current.length() - 1 ) { | | size_t dot = current.rfind( "." ); | |
| inc( to[ current ], i->second ); | | if ( dot == string::npos || dot != current.length() - 1 ) { | |
| } | | inc( to[ current ], i->second ); | |
| while( dot != string::npos ) { | | } | |
| current = current.substr( 0, dot ); | | while( dot != string::npos ) { | |
| inc( to[ current ], i->second ); | | current = current.substr( 0, dot ); | |
| dot = current.rfind( "." ); | | inc( to[ current ], i->second ); | |
| | | dot = current.rfind( "." ); | |
| | | } | |
| } | | } | |
| } | | } | |
|
| } | | static void inc( UsageData &to, const UsageData &from ) { | |
| static void inc( boost::tuple< D, int, int, int > &to, const boost::tup | | to.get<0>() += from.get<0>(); | |
| le< D, int, int, int > &from ) { | | to.get<1>() += from.get<1>(); | |
| to.get<0>() += from.get<0>(); | | to.get<2>() += from.get<2>(); | |
| to.get<1>() += from.get<1>(); | | to.get<3>() += from.get<3>(); | |
| to.get<2>() += from.get<2>(); | | } | |
| to.get<3>() += from.get<3>(); | | struct more { bool operator()( const D &a, const D &b ) { return a | |
| } | | > b; } }; | |
| struct more { bool operator()( const D &a, const D &b ) { return a > b; | | string _current; | |
| } }; | | T _currentStart; | |
| string current_; | | static T _snapshotStart; | |
| T currentStart_; | | static D _snapshotDuration; | |
| static T snapshotStart_; | | static UsageMap _totalUsage; | |
| static D snapshotDuration_; | | static UsageMap _snapshotA; | |
| static UsageMap totalUsage_; | | static UsageMap _snapshotB; | |
| static UsageMap snapshotA_; | | static UsageMap &_snapshot; | |
| static UsageMap snapshotB_; | | static UsageMap &_nextSnapshot; | |
| static UsageMap &snapshot_; | | bool _read; | |
| static UsageMap &nextSnapshot_; | | bool _write; | |
| bool read_; | | }; | |
| bool write_; | | | |
| }; | | | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 19 change blocks. |
| 146 lines changed or deleted | | 147 lines changed or added | |
|