Couldn't find wdiff. Falling back to builtin diff colouring... mongoDB: headers diff between 1.7.0 and 1.9.2 versions
 array.h   array.h 
skipping to change at line 21 skipping to change at line 21
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
namespace mongo { namespace mongo {
/*
* simple array class that does no allocations
* same api as vector
* fixed buffer, so once capacity is exceeded, will assert
* meant to be-reused with clear()
*/
template<typename T> template<typename T>
class FastArray { class FastArray {
public: public:
FastArray( int capacity=10000 ) FastArray( int capacity=10000 )
: _capacity( capacity ) , _size(0) , _end(this,capacity){ : _capacity( capacity ) , _size(0) , _end(this,capacity) {
_data = new T[capacity]; _data = new T[capacity];
} }
~FastArray(){ ~FastArray() {
delete[] _data; delete[] _data;
} }
void clear(){ void clear() {
_size = 0; _size = 0;
} }
T& operator[]( int x ){ T& operator[]( int x ) {
assert( x >= 0 && x < _capacity ); assert( x >= 0 && x < _capacity );
return _data[x]; return _data[x];
} }
T& getNext(){ T& getNext() {
return _data[_size++]; return _data[_size++];
} }
void push_back( const T& t ){ void push_back( const T& t ) {
assert( _size < _capacity );
_data[_size++] = t; _data[_size++] = t;
} }
void sort( int (*comp)(const void *, const void *) ){ void sort( int (*comp)(const void *, const void *) ) {
qsort( _data , _size , sizeof(T) , comp ); qsort( _data , _size , sizeof(T) , comp );
} }
int size(){ int size() {
return _size; return _size;
} }
bool hasSpace(){ bool hasSpace() {
return _size < _capacity; return _size < _capacity;
} }
class iterator { class iterator {
public: public:
iterator(){ iterator() {
_it = 0; _it = 0;
_pos = 0; _pos = 0;
} }
iterator( FastArray * it , int pos=0 ){ iterator( FastArray * it , int pos=0 ) {
_it = it; _it = it;
_pos = pos; _pos = pos;
} }
bool operator==(const iterator& other ) const { bool operator==(const iterator& other ) const {
return _pos == other._pos; return _pos == other._pos;
} }
bool operator!=(const iterator& other ) const { bool operator!=(const iterator& other ) const {
return _pos != other._pos; return _pos != other._pos;
} }
void operator++(){ void operator++() {
_pos++; _pos++;
} }
T& operator*(){ T& operator*() {
return _it->_data[_pos]; return _it->_data[_pos];
} }
string toString() const { string toString() const {
stringstream ss; stringstream ss;
ss << _pos; ss << _pos;
return ss.str(); return ss.str();
} }
private: private:
FastArray * _it; FastArray * _it;
int _pos; int _pos;
friend class FastArray; friend class FastArray;
}; };
iterator begin(){ iterator begin() {
return iterator(this); return iterator(this);
} }
iterator end(){ iterator end() {
_end._pos = _size; _end._pos = _size;
return _end; return _end;
} }
private: private:
int _capacity; int _capacity;
int _size; int _size;
iterator _end; iterator _end;
 End of changes. 16 change blocks. 
15 lines changed or deleted 22 lines changed or added


 assert_util.h   assert_util.h 
skipping to change at line 22 skipping to change at line 22
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../db/lasterror.h" #include "../db/lasterror.h"
// MONGO_NORETURN undefed at end of file
#ifdef __GNUC__
# define MONGO_NORETURN __attribute__((__noreturn__))
#else
# define MONGO_NORETURN
#endif
namespace mongo { namespace mongo {
enum CommonErrorCodes { enum CommonErrorCodes {
DatabaseDifferCaseCode = 13297 , DatabaseDifferCaseCode = 13297 ,
StaleConfigInContextCode = 13388 StaleConfigInContextCode = 13388
}; };
/* these are manipulated outside of mutexes, so be careful */
struct Assertion {
Assertion() {
msg[0] = msg[127] = 0;
context[0] = context[127] = 0;
file = "";
line = 0;
when = 0;
}
private:
static mongo::mutex *_mutex;
char msg[128];
char context[128];
const char *file;
unsigned line;
time_t when;
public:
void set(const char *m, const char *ctxt, const char *f, unsigned l ) {
if( _mutex == 0 ) {
/* asserted during global variable initialization */
return;
}
scoped_lock lk(*_mutex);
strncpy(msg, m, 127);
strncpy(context, ctxt, 127);
file = f;
line = l;
when = time(0);
}
std::string toString();
bool isSet() {
return when != 0;
}
};
enum {
AssertRegular = 0,
AssertW = 1,
AssertMsg = 2,
AssertUser = 3
};
/* last assert of diff types: regular, wassert, msgassert, uassert: */
extern Assertion lastAssert[4];
class AssertionCount { class AssertionCount {
public: public:
AssertionCount(); AssertionCount();
void rollover(); void rollover();
void condrollover( int newValue ); void condrollover( int newValue );
int regular; int regular;
int warning; int warning;
int msg; int msg;
int user; int user;
int rollovers; int rollovers;
}; };
extern AssertionCount assertionCount; extern AssertionCount assertionCount;
struct ExceptionInfo { struct ExceptionInfo {
ExceptionInfo() : msg(""),code(-1){} ExceptionInfo() : msg(""),code(-1) {}
ExceptionInfo( const char * m , int c ) ExceptionInfo( const char * m , int c )
: msg( m ) , code( c ){ : msg( m ) , code( c ) {
} }
ExceptionInfo( const string& m , int c ) ExceptionInfo( const string& m , int c )
: msg( m ) , code( c ){ : msg( m ) , code( c ) {
} }
void append( BSONObjBuilder& b , const char * m = "$err" , const ch ar * c = "code" ) const ; void append( BSONObjBuilder& b , const char * m = "$err" , const ch ar * c = "code" ) const ;
string toString() const { stringstream ss; ss << "exception: " << c ode << " " << msg; return ss.str(); } string toString() const { stringstream ss; ss << "exception: " << c ode << " " << msg; return ss.str(); }
bool empty() const { return msg.empty(); } bool empty() const { return msg.empty(); }
void reset(){ msg = ""; code=-1; }
string msg; string msg;
int code; int code;
}; };
/** helper class that builds error strings. lighter weight than a Stri ngBuilder, albeit less flexible.
NOINLINE_DECL used in the constructor implementations as we are ass uming this is a cold code path when used.
example:
throw UserException(123, ErrorMsg("blah", num_val));
*/
class ErrorMsg {
public:
ErrorMsg(const char *msg, char ch);
ErrorMsg(const char *msg, unsigned val);
operator string() const { return buf; }
private:
char buf[256];
};
class DBException : public std::exception { class DBException : public std::exception {
public: public:
DBException( const ExceptionInfo& ei ) : _ei(ei){} DBException( const ExceptionInfo& ei ) : _ei(ei) {}
DBException( const char * msg , int code ) : _ei(msg,code){} DBException( const char * msg , int code ) : _ei(msg,code) {}
DBException( const string& msg , int code ) : _ei(msg,code){} DBException( const string& msg , int code ) : _ei(msg,code) {}
virtual ~DBException() throw() { } virtual ~DBException() throw() { }
virtual const char* what() const throw(){ return _ei.msg.c_str(); } virtual const char* what() const throw() { return _ei.msg.c_str(); }
virtual int getCode() const { return _ei.code; } virtual int getCode() const { return _ei.code; }
virtual void appendPrefix( stringstream& ss ) const { } virtual void appendPrefix( stringstream& ss ) const { }
virtual string toString() const { virtual string toString() const {
stringstream ss; ss << getCode() << " " << what(); return ss.st r(); stringstream ss; ss << getCode() << " " << what(); return ss.st r();
return ss.str(); return ss.str();
} }
const ExceptionInfo& getInfo() const { return _ei; } const ExceptionInfo& getInfo() const { return _ei; }
protected: protected:
ExceptionInfo _ei; ExceptionInfo _ei;
}; };
class AssertionException : public DBException { class AssertionException : public DBException {
public: public:
AssertionException( const ExceptionInfo& ei ) : DBException(ei){} AssertionException( const ExceptionInfo& ei ) : DBException(ei) {}
AssertionException( const char * msg , int code ) : DBException(msg ,code){} AssertionException( const char * msg , int code ) : DBException(msg ,code) {}
AssertionException( const string& msg , int code ) : DBException(ms g,code){} AssertionException( const string& msg , int code ) : DBException(ms g,code) {}
virtual ~AssertionException() throw() { } virtual ~AssertionException() throw() { }
virtual bool severe() { return true; } virtual bool severe() { return true; }
virtual bool isUserAssertion() { return false; } virtual bool isUserAssertion() { return false; }
/* true if an interrupted exception - see KillCurrentOp */ /* true if an interrupted exception - see KillCurrentOp */
bool interrupted() { bool interrupted() {
return _ei.code == 11600 || _ei.code == 11601; return _ei.code == 11600 || _ei.code == 11601;
} }
}; };
/* 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(int c , const string& m) : AssertionException( m , c ){} UserException(int c , const string& m) : AssertionException( m , c ) {}
virtual bool severe() { return false; } virtual bool severe() { return false; }
virtual bool isUserAssertion() { return true; } virtual bool isUserAssertion() { return true; }
virtual void appendPrefix( stringstream& ss ) const { ss << "useras sert:"; } virtual void appendPrefix( stringstream& ss ) const { ss << "useras sert:"; }
}; };
class MsgAssertionException : public AssertionException { class MsgAssertionException : public AssertionException {
public: public:
MsgAssertionException( const ExceptionInfo& ei ) : AssertionExcepti on( ei ){} MsgAssertionException( const ExceptionInfo& ei ) : AssertionExcepti on( ei ) {}
MsgAssertionException(int c, const string& m) : AssertionException( m , c ){} MsgAssertionException(int c, const string& m) : AssertionException( m , c ) {}
virtual bool severe() { return false; } virtual bool severe() { return false; }
virtual void appendPrefix( stringstream& ss ) const { ss << "masser t:"; } virtual void appendPrefix( stringstream& ss ) const { ss << "masser t:"; }
}; };
void asserted(const char *msg, const char *file, unsigned line); void asserted(const char *msg, const char *file, unsigned line) MONGO_N ORETURN;
void wasserted(const char *msg, const char *file, unsigned line); void wasserted(const char *msg, const char *file, unsigned line);
void uasserted(int msgid, const char *msg); void verifyFailed( int msgid );
/** a "user assertion". throws UserAssertion. logs. typically used f or errors that a user
could cause, such as duplicate key, disk full, etc.
*/
void uasserted(int msgid, const char *msg) MONGO_NORETURN;
inline void uasserted(int msgid , string msg) { uasserted(msgid, 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 msgassertedNoTrace(int msgid, const char *msg); /** reported via lasterror, but don't throw exception */
void msgasserted(int msgid, const char *msg); void uassert_nothrow(const char *msg);
/** msgassert and massert are for errors that are internal but have a w ell defined error text string.
a stack trace is logged.
*/
void msgassertedNoTrace(int msgid, const char *msg) MONGO_NORETURN;
inline void msgassertedNoTrace(int msgid, const string& msg) { msgasser tedNoTrace( msgid , msg.c_str() ); }
void msgasserted(int msgid, const char *msg) MONGO_NORETURN;
inline void msgasserted(int msgid, string msg) { msgasserted(msgid, msg .c_str()); } inline void msgasserted(int msgid, string msg) { msgasserted(msgid, msg .c_str()); }
/* convert various types of exceptions to strings */
inline string causedBy( const char* e ){ return (string)" :: caused by :: " + e; }
inline string causedBy( const DBException& e ){ return causedBy( e.toSt ring().c_str() ); }
inline string causedBy( const std::exception& e ){ return causedBy( e.w hat() ); }
inline string causedBy( const string& e ){ return causedBy( e.c_str() ) ; }
/** in the mongodb source, use verify() instead of assert(). verify is always evaluated even in release builds. */
inline void verify( int msgid , bool testOK ) { if ( ! testOK ) verifyF ailed( msgid ); }
#ifdef assert #ifdef assert
#undef assert #undef assert
#endif #endif
#define MONGO_assert(_Expression) (void)( (!!(_Expression)) || (mongo::asse rted(#_Expression, __FILE__, __LINE__), 0) ) #define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::asserted(#_Expression, __FILE__, __LINE__), 0) )
#define assert MONGO_assert #define assert MONGO_assert
/* "user assert". if asserts, user did something wrong, not our code * / /* "user assert". if asserts, user did something wrong, not our code * /
#define MONGO_uassert(msgid, msg, expr) (void)( (!!(expr)) || (mongo::uasse rted(msgid, msg), 0) ) #define MONGO_uassert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || ( mongo::uasserted(msgid, msg), 0) )
#define uassert MONGO_uassert #define uassert MONGO_uassert
/* warning only - keeps going */ /* warning only - keeps going */
#define MONGO_wassert(_Expression) (void)( (!!(_Expression)) || (mongo::was serted(#_Expression, __FILE__, __LINE__), 0) ) #define MONGO_wassert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::wasserted(#_Expression, __FILE__, __LINE__), 0) )
#define wassert MONGO_wassert #define wassert MONGO_wassert
/* 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 MONGO_massert(msgid, msg, expr) (void)( (!!(expr)) || (mongo::msgas serted(msgid, msg), 0) ) #define MONGO_massert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || ( mongo::msgasserted(msgid, msg), 0) )
#define massert MONGO_massert #define massert MONGO_massert
/* 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 MONGO_dassert assert # define MONGO_dassert assert
#else #else
# define MONGO_dassert(x) # define MONGO_dassert(x)
#endif #endif
#define dassert MONGO_dassert #define dassert MONGO_dassert
// some special ids that we want to duplicate // some special ids that we want to duplicate
// > 10000 asserts // > 10000 asserts
// < 10000 UserException // < 10000 UserException
enum { ASSERT_ID_DUPKEY = 11000 }; enum { ASSERT_ID_DUPKEY = 11000 };
/* throws a uassertion with an appropriate msg */ /* throws a uassertion with an appropriate msg */
void streamNotGood( int code , string msg , std::ios& myios ); void streamNotGood( int code , string msg , std::ios& myios ) MONGO_NOR ETURN;
inline void assertStreamGood(unsigned msgid, string msg, std::ios& myio s) { inline void assertStreamGood(unsigned msgid, string msg, std::ios& myio s) {
if( !myios.good() ) streamNotGood(msgid, msg, myios); if( !myios.good() ) streamNotGood(msgid, msg, myios);
} }
string demangleName( const type_info& typeinfo ); string demangleName( const type_info& typeinfo );
} // namespace mongo } // namespace mongo
#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION #define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION
#define MONGO_BOOST_CHECK_EXCEPTION( expression ) \ #define MONGO_BOOST_CHECK_EXCEPTION( expression ) \
try { \ try { \
expression; \ expression; \
} catch ( const std::exception &e ) { \ } catch ( const std::exception &e ) { \
stringstream ss; \ stringstream ss; \
ss << "caught boost exception: " << e.what(); \ ss << "caught boost exception: " << e.what() << ' ' << __FILE__ << ' ' << __LINE__; \
msgasserted( 13294 , ss.str() ); \ msgasserted( 13294 , ss.str() ); \
} catch ( ... ) { \ } catch ( ... ) { \
massert( 10437 , "unknown boost failed" , false ); \ massert( 10437 , "unknown boost failed" , false ); \
} }
#define MONGO_BOOST_CHECK_EXCEPTION_WITH_MSG( expression, msg ) \
try { \
expression; \
} catch ( const std::exception &e ) { \
stringstream ss; \
ss << msg << " caught boost exception: " << e.what(); \
msgasserted( 14043 , ss.str() ); \
} catch ( ... ) { \
msgasserted( 14044 , string("unknown boost failed ") + msg ); \
}
#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD #define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD
#define MONGO_DESTRUCTOR_GUARD( expression ) \ #define MONGO_DESTRUCTOR_GUARD( expression ) \
try { \ try { \
expression; \ expression; \
} catch ( const std::exception &e ) { \ } catch ( const std::exception &e ) { \
problem() << "caught exception (" << e.what() << ") in destructor ( " << __FUNCTION__ << ")" << endl; \ problem() << "caught exception (" << e.what() << ") in destructor ( " << __FUNCTION__ << ")" << endl; \
} catch ( ... ) { \ } catch ( ... ) { \
problem() << "caught unknown exception in destructor (" << __FUNCTI ON__ << ")" << endl; \ problem() << "caught unknown exception in destructor (" << __FUNCTI ON__ << ")" << endl; \
} }
#undef MONGO_NORETURN
 End of changes. 27 change blocks. 
79 lines changed or deleted 87 lines changed or added


 atomic_int.h   atomic_int.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 {
struct AtomicUInt{ struct AtomicUInt {
AtomicUInt() : x(0) {} AtomicUInt() : x(0) {}
AtomicUInt(unsigned z) : x(z) { } AtomicUInt(unsigned z) : x(z) { }
volatile unsigned x;
operator unsigned() const { operator unsigned() const { return x; }
return x; unsigned get() const { return x; }
}
inline AtomicUInt operator++(); // ++prefix inline AtomicUInt operator++(); // ++prefix
inline AtomicUInt operator++(int);// postfix++ inline AtomicUInt operator++(int);// postfix++
inline AtomicUInt operator--(); // --prefix inline AtomicUInt operator--(); // --prefix
inline AtomicUInt operator--(int); // postfix-- inline AtomicUInt operator--(int); // postfix--
inline void zero();
volatile unsigned x;
}; };
#if defined(_WIN32) #if defined(_WIN32)
AtomicUInt AtomicUInt::operator++(){ void AtomicUInt::zero() {
// InterlockedIncrement returns the new value InterlockedExchange((volatile long*)&x, 0);
return InterlockedIncrement((volatile long*)&x); //long is 32bits i n Win64
} }
AtomicUInt AtomicUInt::operator++(int){ AtomicUInt AtomicUInt::operator++() {
return InterlockedIncrement((volatile long*)&x);
}
AtomicUInt AtomicUInt::operator++(int) {
return InterlockedIncrement((volatile long*)&x)-1; return InterlockedIncrement((volatile long*)&x)-1;
} }
AtomicUInt AtomicUInt::operator--(){ AtomicUInt AtomicUInt::operator--() {
return InterlockedDecrement((volatile long*)&x); return InterlockedDecrement((volatile long*)&x);
} }
AtomicUInt AtomicUInt::operator--(int){ AtomicUInt AtomicUInt::operator--(int) {
return InterlockedDecrement((volatile long*)&x)+1; return InterlockedDecrement((volatile long*)&x)+1;
} }
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
// this is in GCC >= 4.1 // this is in GCC >= 4.1
AtomicUInt AtomicUInt::operator++(){ inline void AtomicUInt::zero() { x = 0; } // TODO: this isn't thread sa fe - maybe
AtomicUInt AtomicUInt::operator++() {
return __sync_add_and_fetch(&x, 1); return __sync_add_and_fetch(&x, 1);
} }
AtomicUInt AtomicUInt::operator++(int){ AtomicUInt AtomicUInt::operator++(int) {
return __sync_fetch_and_add(&x, 1); return __sync_fetch_and_add(&x, 1);
} }
AtomicUInt AtomicUInt::operator--(){ AtomicUInt AtomicUInt::operator--() {
return __sync_add_and_fetch(&x, -1); return __sync_add_and_fetch(&x, -1);
} }
AtomicUInt AtomicUInt::operator--(int){ AtomicUInt AtomicUInt::operator--(int) {
return __sync_fetch_and_add(&x, -1); return __sync_fetch_and_add(&x, -1);
} }
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
inline void AtomicUInt::zero() { x = 0; } // TODO: this isn't thread sa fe
// from boost 1.39 interprocess/detail/atomic.hpp // from boost 1.39 interprocess/detail/atomic.hpp
inline unsigned atomic_int_helper(volatile unsigned *x, int val) {
inline unsigned atomic_int_helper(volatile unsigned *x, int val){
int r; int r;
asm volatile asm volatile
( (
"lock\n\t" "lock\n\t"
"xadd %1, %0": "xadd %1, %0":
"+m"( *x ), "=r"( r ): // outputs (%0, %1) "+m"( *x ), "=r"( r ): // outputs (%0, %1)
"1"( val ): // inputs (%2 == %1) "1"( val ): // inputs (%2 == %1)
"memory", "cc" // clobbers "memory", "cc" // clobbers
); );
return r; return r;
} }
AtomicUInt AtomicUInt::operator++(){ AtomicUInt AtomicUInt::operator++() {
return atomic_int_helper(&x, 1)+1; return atomic_int_helper(&x, 1)+1;
} }
AtomicUInt AtomicUInt::operator++(int){ AtomicUInt AtomicUInt::operator++(int) {
return atomic_int_helper(&x, 1); return atomic_int_helper(&x, 1);
} }
AtomicUInt AtomicUInt::operator--(){ AtomicUInt AtomicUInt::operator--() {
return atomic_int_helper(&x, -1)-1; return atomic_int_helper(&x, -1)-1;
} }
AtomicUInt AtomicUInt::operator--(int){ AtomicUInt AtomicUInt::operator--(int) {
return atomic_int_helper(&x, -1); return atomic_int_helper(&x, -1);
} }
#else #else
# error "unsupported compiler or platform" # error "unsupported compiler or platform"
#endif #endif
} // namespace mongo } // namespace mongo
 End of changes. 17 change blocks. 
21 lines changed or deleted 28 lines changed or added


 balance.h   balance.h 
// balance.h //@file balance.h
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
skipping to change at line 28 skipping to change at line 28
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/background.h" #include "../util/background.h"
#include "../client/dbclient.h" #include "../client/dbclient.h"
#include "balancer_policy.h" #include "balancer_policy.h"
namespace mongo { namespace mongo {
/**
* The balancer is a background task that tries to keep the number of c hunks across all servers of the cluster even. Although
* every mongos will have one balancer running, only one of them will b e active at the any given point in time. The balancer
* uses a 'DistributedLock' for that coordination.
*
* The balancer does act continuously but in "rounds". At a given round , it would decide if there is an imbalance by
* checking the difference in chunks between the most and least loaded shards. It would issue a request for a chunk
* migration per round, if it found so.
*/
class Balancer : public BackgroundJob { class Balancer : public BackgroundJob {
public: public:
Balancer(); Balancer();
virtual ~Balancer(); virtual ~Balancer();
// BackgroundJob methods // BackgroundJob methods
virtual void run(); virtual void run();
virtual string name() { return "Balancer"; } virtual string name() const { return "Balancer"; }
private: private:
typedef BalancerPolicy::ChunkInfo CandidateChunk; typedef BalancerPolicy::ChunkInfo CandidateChunk;
typedef shared_ptr<CandidateChunk> CandidateChunkPtr; typedef shared_ptr<CandidateChunk> CandidateChunkPtr;
// hostname:port of my mongos
string _myid;
// time the Balancer started running
time_t _started;
// number of moved chunks in last round
int _balancedLastTime;
// decide which chunks to move; owned here.
scoped_ptr<BalancerPolicy> _policy;
/** /**
* Gathers all the necessary information about shards and chunks, a nd * Checks that the balancer can connect to all servers it needs to do its job.
* decides whether there are candidate chunks to be moved. *
* @return true if balancing can be started
*
* This method throws on a network exception
*/
bool _init();
/**
* Gathers all the necessary information about shards and chunks, a nd decides whether there are candidate chunks to
* be moved.
*
* @param conn is the connection with the config server(s)
* @param candidateChunks (IN/OUT) filled with candidate chunks, on e per collection, that could possibly be moved
*/ */
void _doBalanceRound( DBClientBase& conn, vector<CandidateChunkPtr> * candidateChunks ); void _doBalanceRound( DBClientBase& conn, vector<CandidateChunkPtr> * candidateChunks );
/** /**
* Execute the chunk migrations described in 'candidateChunks' and * Issues chunk migration request, one at a time.
* returns the number of chunks effectively moved. *
* @param candidateChunks possible chunks to move
* @return number of chunks effectively moved
*/ */
int _moveChunks( const vector<CandidateChunkPtr>* candidateChunks ) ; int _moveChunks( const vector<CandidateChunkPtr>* candidateChunks ) ;
/** /**
* Check the health of the master configuration server * Marks this balancer as being live on the config server(s).
*
* @param conn is the connection with the config server(s)
*/ */
void _ping();
void _ping( DBClientBase& conn ); void _ping( DBClientBase& conn );
/** /**
* @return true if everything is ok * @return true if all the servers listed in configdb as being shar ds are reachable and are distinct processes
*/ */
bool _checkOIDs(); bool _checkOIDs();
// internal state
string _myid; // hostname:port of my mongos
time_t _started; // time Balancer starte running
int _balancedLastTime; // number of moved chunks in las t round
BalancerPolicy* _policy; // decide which chunks to move; owned here.
// non-copyable, non-assignable
Balancer(const Balancer&);
Balancer operator=(const Balancer&);
}; };
extern Balancer balancer; extern Balancer balancer;
} }
 End of changes. 10 change blocks. 
20 lines changed or deleted 45 lines changed or added


 balancer_policy.h   balancer_policy.h 
// balancer_policy.h // @file balancer_policy.h
/** /**
* Copyright (C) 2010 10gen Inc. * Copyright (C) 2010 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
skipping to change at line 65 skipping to change at line 65
* in MB, on 'shardLimits'. * in MB, on 'shardLimits'.
*/ */
static bool isSizeMaxed( BSONObj shardLimits ); static bool isSizeMaxed( BSONObj shardLimits );
/** /**
* Returns true if 'shardLimist' contains a field "draining". Expec ts the optional field * Returns true if 'shardLimist' contains a field "draining". Expec ts the optional field
* "isDraining" on 'shrdLimits'. * "isDraining" on 'shrdLimits'.
*/ */
static bool isDraining( BSONObj shardLimits ); static bool isDraining( BSONObj shardLimits );
/**
* Returns true if a shard currently has operations in any of its w riteback queues
*/
static bool hasOpsQueued( BSONObj shardLimits );
private: private:
// Convenience types // Convenience types
typedef ShardToChunksMap::const_iterator ShardToChunksIter; typedef ShardToChunksMap::const_iterator ShardToChunksIter;
typedef ShardToLimitsMap::const_iterator ShardToLimitsIter; typedef ShardToLimitsMap::const_iterator ShardToLimitsIter;
}; };
struct BalancerPolicy::ChunkInfo { struct BalancerPolicy::ChunkInfo {
const string ns; const string ns;
const string to; const string to;
const string from; const string from;
const BSONObj chunk; const BSONObj chunk;
ChunkInfo( const string& a_ns , const string& a_to , const string& a_from , const BSONObj& a_chunk ) ChunkInfo( const string& a_ns , const string& a_to , const string& a_from , const BSONObj& a_chunk )
: ns( a_ns ) , to( a_to ) , from( a_from ), chunk( a_chunk ){} : ns( a_ns ) , to( a_to ) , from( a_from ), chunk( a_chunk ) {}
};
/**
* Field names used in the 'limits' map.
*/
struct LimitsFields {
// we use 'draining' and 'maxSize' from the 'shards' collection plu s the following
static BSONField<long long> currSize; // currently used disk space in bytes
static BSONField<bool> hasOpsQueued; // writeback queue is not emp ty?
}; };
} // namespace mongo } // namespace mongo
#endif // S_BALANCER_POLICY_HEADER #endif // S_BALANCER_POLICY_HEADER
 End of changes. 3 change blocks. 
2 lines changed or deleted 16 lines changed or added


 base64.h   base64.h 
skipping to change at line 27 skipping to change at line 27
#pragma once #pragma once
namespace mongo { namespace mongo {
namespace base64 { namespace base64 {
class Alphabet { class Alphabet {
public: public:
Alphabet() Alphabet()
: encode((unsigned char*) : encode((unsigned char*)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
"0123456789" "0123456789"
"+/") "+/")
, decode(new unsigned char[257]) , decode(new unsigned char[257]) {
{
memset( decode.get() , 0 , 256 ); memset( decode.get() , 0 , 256 );
for ( int i=0; i<64; i++ ){ for ( int i=0; i<64; i++ ) {
decode[ encode[i] ] = i; decode[ encode[i] ] = i;
} }
test(); test();
} }
void test(){ void test() {
assert( strlen( (char*)encode ) == 64 ); assert( strlen( (char*)encode ) == 64 );
for ( int i=0; i<26; i++ ) for ( int i=0; i<26; i++ )
assert( encode[i] == toupper( encode[i+26] ) ); assert( encode[i] == toupper( encode[i+26] ) );
} }
char e( int x ){ char e( int x ) {
return encode[x&0x3f]; return encode[x&0x3f];
} }
private: private:
const unsigned char * encode; const unsigned char * encode;
public: public:
boost::scoped_array<unsigned char> decode; boost::scoped_array<unsigned char> decode;
}; };
extern Alphabet alphabet; extern Alphabet alphabet;
 End of changes. 4 change blocks. 
9 lines changed or deleted 8 lines changed or added


 bson.h   bson.h 
/* NOTE: Standalone bson header for when not using MongoDB. /** @file bson.h
See also: bsondemo.
MongoDB includes ../db/jsobj.h instead. This file, however, pulls in muc h less code / dependencies. Main bson include file for mongodb c++ clients. MongoDB includes ../db/ jsobj.h instead.
*/ This file, however, pulls in much less code / dependencies.
/** @file bson.h @see bsondemo
BSON classes
*/ */
/* /*
* Copyright 2009 10gen Inc. * Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/** /**
BSONObj and its helpers Main include file for C++ BSON module when using standalone (sans MongoD B client).
"BSON" stands for "binary JSON" -- ie a binary way to represent objects that would be "BSON" stands for "binary JSON" -- ie a binary way to represent objects that would be
represented in JSON (plus a few extensions useful for databases & other languages). represented in JSON (plus a few extensions useful for databases & other languages).
http://www.bsonspec.org/ http://www.bsonspec.org/
*/ */
#pragma once #pragma once
#if defined(MONGO_EXPOSE_MACROS) #if defined(MONGO_EXPOSE_MACROS)
#error this header is for client programs, not the mongo database itself. i nclude jsobj.h instead. #error this header is for client programs, not the mongo database itself. i nclude jsobj.h instead.
/* because we define simplistic assert helpers here that don't pull in a bu nch of util -- so that /* because we define simplistic assert helpers here that don't pull in a bu nch of util -- so that
BSON can be used header only. BSON can be used header only.
*/ */
#endif #endif
#include <cstdlib>
#include <memory>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <boost/utility.hpp> #include <boost/utility.hpp>
#include "util/builder.h"
namespace bson { namespace bson {
using std::string; using std::string;
using std::stringstream; using std::stringstream;
class assertion : public std::exception { class assertion : public std::exception {
public: public:
assertion( unsigned u , const string& s ) assertion( unsigned u , const string& s )
: id( u ) , msg( s ){ : id( u ) , msg( s ) {
mongo::StringBuilder ss; stringstream ss;
ss << "BsonAssertion id: " << u << " " << s; ss << "BsonAssertion id: " << u << " " << s;
full = ss.str(); full = ss.str();
} }
virtual ~assertion() throw() {} virtual ~assertion() throw() {}
virtual const char* what() const throw() { return full.c_str(); } virtual const char* what() const throw() { return full.c_str(); }
unsigned id; unsigned id;
string msg; string msg;
skipping to change at line 104 skipping to change at line 103
inline void msgasserted(int msgid, const std::string &msg) { msgasserte d(msgid, msg.c_str()); } inline void msgasserted(int msgid, const std::string &msg) { msgasserte d(msgid, msg.c_str()); }
inline void massert(unsigned msgid, std::string msg, bool expr) { inline void massert(unsigned msgid, std::string msg, bool expr) {
if(!expr) { if(!expr) {
std::cout << "assertion failure in bson library: " << msgid << ' ' << msg << std::endl; std::cout << "assertion failure in bson library: " << msgid << ' ' << msg << std::endl;
throw bson::assertion( msgid , msg ); throw bson::assertion( msgid , msg );
} }
} }
#endif #endif
} }
#include "../bson/bsontypes.h" #include "util/builder.h"
#include "../bson/oid.h" #include "bsontypes.h"
#include "../bson/bsonelement.h" #include "oid.h"
#include "../bson/bsonobj.h" #include "bsonelement.h"
#include "../bson/bsonmisc.h" #include "bsonobj.h"
#include "../bson/bsonobjbuilder.h" #include "bsonobjbuilder.h"
#include "../bson/bsonobjiterator.h" #include "bsonobjiterator.h"
#include "../bson/bsoninlines.h" #include "bson-inl.h"
namespace mongo {
inline unsigned getRandomNumber() {
#if defined(_WIN32)
return rand();
#else
return random();
#endif
}
}
 End of changes. 8 change blocks. 
10 lines changed or deleted 9 lines changed or added


 bson_db.h   bson_db.h 
skipping to change at line 29 skipping to change at line 29
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../util/optime.h" #include "../util/optime.h"
#include "../util/time_support.h"
namespace mongo { namespace mongo {
/** /**
Timestamps are a special BSON datatype that is used internally for repl ication. Timestamps are a special BSON datatype that is used internally for repl ication.
Append a timestamp element to the object being ebuilt. Append a timestamp element to the object being ebuilt.
@param time - in millis (but stored in seconds) @param time - in millis (but stored in seconds)
*/ */
inline BSONObjBuilder& BSONObjBuilder::appendTimestamp( const StringDat a& fieldName , unsigned long long time , unsigned int inc ){ inline BSONObjBuilder& BSONObjBuilder::appendTimestamp( const StringDat a& fieldName , unsigned long long time , unsigned int inc ) {
OpTime t( (unsigned) (time / 1000) , inc ); OpTime t( (unsigned) (time / 1000) , inc );
appendTimestamp( fieldName , t.asDate() ); appendTimestamp( fieldName , t.asDate() );
return *this; return *this;
} }
inline OpTime BSONElement::_opTime() const { inline OpTime BSONElement::_opTime() const {
if( type() == mongo::Date || type() == Timestamp ) if( type() == mongo::Date || type() == Timestamp )
return OpTime( *reinterpret_cast< const unsigned long long* >( value() ) ); return OpTime( *reinterpret_cast< const unsigned long long* >( value() ) );
return OpTime(); return OpTime();
} }
inline string BSONElement::_asCode() const { inline string BSONElement::_asCode() const {
switch( type() ){ switch( type() ) {
case mongo::String: case mongo::String:
case Code: case Code:
return string(valuestr(), valuestrsize()-1); return string(valuestr(), valuestrsize()-1);
case CodeWScope: case CodeWScope:
return string(codeWScopeCode(), *(int*)(valuestr())-1); return string(codeWScopeCode(), *(int*)(valuestr())-1);
default: default:
log() << "can't convert type: " << (int)(type()) << " to code" << endl; log() << "can't convert type: " << (int)(type()) << " to code" << endl;
} }
uassert( 10062 , "not code" , 0 ); uassert( 10062 , "not code" , 0 );
return ""; return "";
} }
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLab eler& id){ inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLab eler& id) {
_builder->appendDate(_fieldName, jsTime()); _builder->appendDate(_fieldName, jsTime());
_fieldName = 0; _fieldName = 0;
return *_builder; return *_builder;
} }
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(MinKeyLabe ler& id) {
_builder->appendMinKey(_fieldName);
_fieldName = 0;
return *_builder;
}
inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(MaxKeyLabe ler& id) {
_builder->appendMaxKey(_fieldName);
_fieldName = 0;
return *_builder;
}
} }
 End of changes. 5 change blocks. 
3 lines changed or deleted 16 lines changed or added


 bsonelement.h   bsonelement.h 
skipping to change at line 23 skipping to change at line 23
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <vector> #include <vector>
#include <string.h> #include <string.h>
#include "util/builder.h" #include "util/builder.h"
#include "bsontypes.h"
namespace mongo {
class OpTime;
class BSONObj;
class BSONElement;
class BSONObjBuilder;
}
namespace bson { namespace bson {
typedef mongo::BSONElement be; typedef mongo::BSONElement be;
typedef mongo::BSONObj bo; typedef mongo::BSONObj bo;
typedef mongo::BSONObjBuilder bob; typedef mongo::BSONObjBuilder bob;
} }
namespace mongo { namespace mongo {
class OpTime;
class BSONElement;
/* l and r MUST have same type when called: check that first. */ /* l and r MUST have same type when called: check that first. */
int compareElementValues(const BSONElement& l, const BSONElement& r); int compareElementValues(const BSONElement& l, const BSONElement& r);
/** BSONElement represents an "element" in a BSONObj. So for the object { a : 3, b : "abc" }, /** BSONElement represents an "element" in a BSONObj. So for the objec t { a : 3, b : "abc" },
'a : 3' is the first element (key+value). 'a : 3' is the first element (key+value).
The BSONElement object points into the BSONObj's data. Thus the BSONOb j must stay in scope
for the life of the BSONElement.
internals: The BSONElement object points into the BSONObj's data. Thus the BS ONObj must stay in scope
<type><fieldName ><value> for the life of the BSONElement.
-------- size() ------------
-fieldNameSize-
value()
type()
*/
class BSONElement {
public:
/** These functions, which start with a capital letter, throw a UserExc eption if the
element is not of the required type. Example:
string foo = obj["foo"].String(); // exception if not a string type or DNE internals:
<type><fieldName ><value>
-------- size() ------------
-fieldNameSize-
value()
type()
*/ */
string String() const { return chk(mongo::String).valuestr( ); } class BSONElement {
Date_t Date() const { return chk(mongo::Date).date(); } public:
double Number() const { return chk(isNumber()).number(); } /** These functions, which start with a capital letter, throw a Use rException if the
double Double() const { return chk(NumberDouble)._numberDou ble(); } element is not of the required type. Example:
long long Long() const { return chk(NumberLong)._numberLong( ); }
int Int() const { return chk(NumberInt)._numberInt(); }
bool Bool() const { return chk(mongo::Bool).boolean(); }
BSONObj Obj() const;
vector<BSONElement> Array() const; // see implementation for detailed c omments
mongo::OID OID() const { return chk(jstOID).__oid(); }
void Null() const { chk(isNull()); }
void OK() const { chk(ok()); }
/** populate v with the value of the element. If type does not match, throw exception. string foo = obj["foo"].String(); // exception if not a string type or DNE
useful in templates -- see also BSONObj::Vals().
*/ */
void Val(Date_t& v) const { v = Date(); } string String() const { return chk(mongo::String).value str(); }
void Val(long long& v) const { v = Long(); } Date_t Date() const { return chk(mongo::Date).date(); }
void Val(bool& v) const { v = Bool(); } double Number() const { return chk(isNumber()).number() ; }
void Val(BSONObj& v) const; double Double() const { return chk(NumberDouble)._numbe rDouble(); }
void Val(mongo::OID& v) const { v = OID(); } long long Long() const { return chk(NumberLong)._numberL ong(); }
void Val(int& v) const { v = Int(); } int Int() const { return chk(NumberInt)._numberIn t(); }
void Val(double& v) const { v = Double(); } bool Bool() const { return chk(mongo::Bool).boolean (); }
void Val(string& v) const { v = String(); } vector<BSONElement> Array() const; // see implementation for detail ed comments
mongo::OID OID() const { return chk(jstOID).__oid(); }
void Null() const { chk(isNull()); } // throw UserE xception if not null
void OK() const { chk(ok()); } // throw UserE xception if element DNE
/** Use ok() to check if a value is assigned: /** @return the embedded object associated with this field.
if( myObj["foo"].ok() ) ... Note the returned object is a reference to within the parent bs on object. If that
*/ object is out of scope, this pointer will no longer be valid. Ca ll getOwned() on the
bool ok() const { return !eoo(); } returned BSONObj if you need your own copy.
throws UserException if the element is not of type object.
*/
BSONObj Obj() const;
string toString( bool includeFieldName = true, bool full=false) const; /** populate v with the value of the element. If type does not mat ch, throw exception.
void toString(StringBuilder& s, bool includeFieldName = true, bool full =false) const; useful in templates -- see also BSONObj::Vals().
string jsonString( JsonStringFormat format, bool includeFieldNames = tr ue, int pretty = 0 ) const; */
operator string() const { return toString(); } void Val(Date_t& v) const { v = Date(); }
void Val(long long& v) const { v = Long(); }
void Val(bool& v) const { v = Bool(); }
void Val(BSONObj& v) const;
void Val(mongo::OID& v) const { v = OID(); }
void Val(int& v) const { v = Int(); }
void Val(double& v) const { v = Double(); }
void Val(string& v) const { v = String(); }
/** Returns the type of the element */ /** Use ok() to check if a value is assigned:
BSONType type() const { return (BSONType) *data; } if( myObj["foo"].ok() ) ...
*/
bool ok() const { return !eoo(); }
/** retrieve a field within this element string toString( bool includeFieldName = true, bool full=false) con st;
throws exception if *this is not an embedded object void toString(StringBuilder& s, bool includeFieldName = true, bool full=false) const;
*/ string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const;
BSONElement operator[] (const string& field) const; operator string() const { return toString(); }
/** returns the tyoe of the element fixed for the main type /** Returns the type of the element */
the main purpose is numbers. any numeric type will return NumberDo uble BSONType type() const { return (BSONType) *data; }
Note: if the order changes, indexes have to be re-built or than can be corruption
*/
int canonicalType() const;
/** Indicates if it is the end-of-object element, which is present at t he end of /** retrieve a field within this element
every BSON object. throws exception if *this is not an embedded object
*/ */
bool eoo() const { return type() == EOO; } BSONElement operator[] (const string& field) const;
/** Size of the element. /** returns the tyoe of the element fixed for the main type
@param maxLen If maxLen is specified, don't scan more than maxLen b ytes to calculate size. the main purpose is numbers. any numeric type will return Numb erDouble
*/ Note: if the order changes, indexes have to be re-built or than can be corruption
int size( int maxLen = -1 ) const; */
int canonicalType() const;
/** Wrap this element up as a singleton object. */ /** Indicates if it is the end-of-object element, which is present at the end of
BSONObj wrap() const; every BSON object.
*/
bool eoo() const { return type() == EOO; }
/** Wrap this element up as a singleton object with a new name. */ /** Size of the element.
BSONObj wrap( const char* newName) const; @param maxLen If maxLen is specified, don't scan more than maxL en bytes to calculate size.
*/
int size( int maxLen ) const;
int size() const;
/** field name of the element. e.g., for /** Wrap this element up as a singleton object. */
name : "Joe" BSONObj wrap() const;
"name" is the fieldname
*/
const char * fieldName() const {
if ( eoo() ) return ""; // no fieldname for it.
return data + 1;
}
/** raw data of the element's value (so be careful). */ /** Wrap this element up as a singleton object with a new name. */
const char * value() const { BSONObj wrap( const char* newName) const;
return (data + fieldNameSize() + 1);
}
/** size in bytes of the element's value (when applicable). */
int valuesize() const {
return size() - fieldNameSize() - 1;
}
bool isBoolean() const { return type() == mongo::Bool; } /** field name of the element. e.g., for
name : "Joe"
"name" is the fieldname
*/
const char * fieldName() const {
if ( eoo() ) return ""; // no fieldname for it.
return data + 1;
}
/** @return value of a boolean element. /** raw data of the element's value (so be careful). */
You must assure element is a boolean before const char * value() const {
calling. */ return (data + fieldNameSize() + 1);
bool boolean() const { }
return *value() ? true : false; /** size in bytes of the element's value (when applicable). */
} int valuesize() const {
return size() - fieldNameSize() - 1;
}
/** Retrieve a java style date value from the element. bool isBoolean() const { return type() == mongo::Bool; }
Ensure element is of type Date before calling.
*/
Date_t date() const {
return *reinterpret_cast< const Date_t* >( value() );
}
/** Convert the value to boolean, regardless of its type, in a javascri pt-like fashion /** @return value of a boolean element.
(i.e., treat zero and null as false). You must assure element is a boolean before
*/ calling. */
bool trueValue() const; bool boolean() const {
return *value() ? true : false;
}
/** True if number, string, bool, date, OID */ bool booleanSafe() const { return isBoolean() && boolean(); }
bool isSimpleType() const;
/** True if element is of a numeric type. */ /** Retrieve a java style date value from the element.
bool isNumber() const; Ensure element is of type Date before calling.
@see Bool(), trueValue()
*/
Date_t date() const {
return *reinterpret_cast< const Date_t* >( value() );
}
/** Return double value for this field. MUST be NumberDouble type. */ /** Convert the value to boolean, regardless of its type, in a java script-like fashion
double _numberDouble() const {return *reinterpret_cast< const double* > ( value() ); } (i.e., treats zero and null and eoo as false).
/** Return double value for this field. MUST be NumberInt type. */ */
int _numberInt() const {return *reinterpret_cast< const int* >( value() ); } bool trueValue() const;
/** Return double value for this field. MUST be NumberLong type. */
long long _numberLong() const {return *reinterpret_cast< const long lon g* >( value() ); }
/** Retrieve int value for the element safely. Zero returned if not a number. */ /** True if number, string, bool, date, OID */
int numberInt() const; bool isSimpleType() const;
/** Retrieve long value for the element safely. Zero returned if not a number. */
long long numberLong() const;
/** Retrieve the numeric value of the element. If not of a numeric typ e, returns 0.
Note: casts to double, data loss may occur with large (>52 bit) Num berLong values.
*/
double numberDouble() const;
/** Retrieve the numeric value of the element. If not of a numeric typ e, returns 0.
Note: casts to double, data loss may occur with large (>52 bit) Num berLong values.
*/
double number() const { return numberDouble(); }
/** Retrieve the object ID stored in the object. /** True if element is of a numeric type. */
You must ensure the element is of type jstOID first. */ bool isNumber() const;
const mongo::OID &__oid() const { return *reinterpret_cast< const mongo ::OID* >( value() ); }
/** True if element is null. */ /** Return double value for this field. MUST be NumberDouble type. */
bool isNull() const { double _numberDouble() const {return *reinterpret_cast< const doubl e* >( value() ); }
return type() == jstNULL; /** Return double value for this field. MUST be NumberInt type. */
} int _numberInt() const {return *reinterpret_cast< const int* >( val ue() ); }
/** Return double value for this field. MUST be NumberLong type. */
long long _numberLong() const {return *reinterpret_cast< const long long* >( value() ); }
/** Size (length) of a string element. /** Retrieve int value for the element safely. Zero returned if no t a number. */
You must assure of type String first. */ int numberInt() const;
int valuestrsize() const { /** Retrieve long value for the element safely. Zero returned if n ot a number. */
return *reinterpret_cast< const int* >( value() ); long long numberLong() const;
} /** Retrieve the numeric value of the element. If not of a numeric type, returns 0.
Note: casts to double, data loss may occur with large (>52 bit) NumberLong values.
*/
double numberDouble() const;
/** Retrieve the numeric value of the element. If not of a numeric type, returns 0.
Note: casts to double, data loss may occur with large (>52 bit) NumberLong values.
*/
double number() const { return numberDouble(); }
// for objects the size *includes* the size of the size field /** Retrieve the object ID stored in the object.
int objsize() const { You must ensure the element is of type jstOID first. */
return *reinterpret_cast< const int* >( value() ); const mongo::OID &__oid() const { return *reinterpret_cast< const m ongo::OID* >( value() ); }
}
/** Get a string's value. Also gives you start of the real data for an embedded object. /** True if element is null. */
You must assure data is of an appropriate type first -- see also va luestrsafe(). bool isNull() const {
*/ return type() == jstNULL;
const char * valuestr() const { }
return value() + 4;
}
/** Get the string value of the element. If not a string returns "". * / /** Size (length) of a string element.
const char *valuestrsafe() const { You must assure of type String first.
return type() == mongo::String ? valuestr() : ""; @return string size including terminating null
} */
/** Get the string value of the element. If not a string returns "". * / int valuestrsize() const {
string str() const { return *reinterpret_cast< const int* >( value() );
return type() == mongo::String ? string(valuestr(), valuestrsize()- 1) : string(); }
}
/** Get javascript code of a CodeWScope data element. */ // for objects the size *includes* the size of the size field
const char * codeWScopeCode() const { int objsize() const {
return value() + 8; return *reinterpret_cast< const int* >( value() );
} }
/** Get the scope SavedContext of a CodeWScope data element. */
const char * codeWScopeScopeData() const {
// TODO fix
return codeWScopeCode() + strlen( codeWScopeCode() ) + 1;
}
/** Get the embedded object this element holds. */ /** Get a string's value. Also gives you start of the real data fo r an embedded object.
BSONObj embeddedObject() const; You must assure data is of an appropriate type first -- see als o valuestrsafe().
*/
const char * valuestr() const {
return value() + 4;
}
/* uasserts if not an object */ /** Get the string value of the element. If not a string returns " ". */
BSONObj embeddedObjectUserCheck() const; const char *valuestrsafe() const {
return type() == mongo::String ? valuestr() : "";
}
/** Get the string value of the element. If not a string returns " ". */
string str() const {
return type() == mongo::String ? string(valuestr(), valuestrsiz e()-1) : string();
}
BSONObj codeWScopeObject() const; /** Get javascript code of a CodeWScope data element. */
const char * codeWScopeCode() const {
return value() + 8;
}
/** Get the scope SavedContext of a CodeWScope data element. */
const char * codeWScopeScopeData() const {
// TODO fix
return codeWScopeCode() + strlen( codeWScopeCode() ) + 1;
}
/** Get raw binary data. Element must be of type BinData. Doesn't hand le type 2 specially */ /** Get the embedded object this element holds. */
const char *binData(int& len) const { BSONObj embeddedObject() const;
// BinData: <int len> <byte subtype> <byte[len] data>
assert( type() == BinData ); /* uasserts if not an object */
len = valuestrsize(); BSONObj embeddedObjectUserCheck() const;
return value() + 5;
} BSONObj codeWScopeObject() const;
/** Get binary data. Element must be of type BinData. Handles type 2 * /
const char *binDataClean(int& len) const { /** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */
// BinData: <int len> <byte subtype> <byte[len] data> const char *binData(int& len) const {
if (binDataType() != ByteArrayDeprecated){ // BinData: <int len> <byte subtype> <byte[len] data>
return binData(len); assert( type() == BinData );
} else { len = valuestrsize();
// Skip extra size return value() + 5;
len = valuestrsize() - 4; }
return value() + 5 + 4; /** Get binary data. Element must be of type BinData. Handles type 2 */
const char *binDataClean(int& len) const {
// BinData: <int len> <byte subtype> <byte[len] data>
if (binDataType() != ByteArrayDeprecated) {
return binData(len);
}
else {
// Skip extra size
len = valuestrsize() - 4;
return value() + 5 + 4;
}
} }
}
BinDataType binDataType() const { BinDataType binDataType() const {
// BinData: <int len> <byte subtype> <byte[len] data> // BinData: <int len> <byte subtype> <byte[len] data>
assert( type() == BinData ); assert( type() == BinData );
unsigned char c = (value() + 4)[0]; unsigned char c = (value() + 4)[0];
return (BinDataType)c; return (BinDataType)c;
} }
/** Retrieve the regex string for a Regex element */ /** Retrieve the regex string for a Regex element */
const char *regex() const { const char *regex() const {
assert(type() == RegEx); assert(type() == RegEx);
return value(); return value();
} }
/** Retrieve the regex flags (options) for a Regex element */ /** Retrieve the regex flags (options) for a Regex element */
const char *regexFlags() const { const char *regexFlags() const {
const char *p = regex(); const char *p = regex();
return p + strlen(p) + 1; return p + strlen(p) + 1;
} }
/** like operator== but doesn't check the fieldname, /** like operator== but doesn't check the fieldname,
just the value. just the value.
*/ */
bool valuesEqual(const BSONElement& r) const { bool valuesEqual(const BSONElement& r) const {
return woCompare( r , false ) == 0; return woCompare( r , false ) == 0;
} }
/** Returns true if elements are equal. */ /** Returns true if elements are equal. */
bool operator==(const BSONElement& r) const { bool operator==(const BSONElement& r) const {
return woCompare( r , true ) == 0; return woCompare( r , true ) == 0;
} }
/** Well ordered comparison. /** Well ordered comparison.
@return <0: l<r. 0:l==r. >0:l>r @return <0: l<r. 0:l==r. >0:l>r
order by type, field name, and field value. order by type, field name, and field value.
If considerFieldName is true, pay attention to the field name. If considerFieldName is true, pay attention to the field name.
*/ */
int woCompare( const BSONElement &e, bool considerFieldName = true ) co nst; int woCompare( const BSONElement &e, bool considerFieldName = true ) const;
const char * rawdata() const { const char * rawdata() const { return data; }
return data;
}
/** 0 == Equality, just not defined yet */ /** 0 == Equality, just not defined yet */
int getGtLtOp( int def = 0 ) const; int getGtLtOp( int def = 0 ) const;
/** Constructs an empty element */ /** Constructs an empty element */
BSONElement(); BSONElement();
/** Check that data is internally consistent. */ /** Check that data is internally consistent. */
void validate() const; void validate() const;
/** True if this element may contain subobjects. */ /** True if this element may contain subobjects. */
bool mayEncapsulate() const { bool mayEncapsulate() const {
switch ( type() ){ switch ( type() ) {
case Object: case Object:
case mongo::Array: case mongo::Array:
case CodeWScope: case CodeWScope:
return true; return true;
default: default:
return false; return false;
}
} }
}
/** True if this element can be a BSONObj */ /** True if this element can be a BSONObj */
bool isABSONObj() const { bool isABSONObj() const {
switch( type() ){ switch( type() ) {
case Object: case Object:
case mongo::Array: case mongo::Array:
return true; return true;
default: default:
return false; return false;
}
} }
}
Date_t timestampTime() const{ Date_t timestampTime() const {
unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; unsigned long long t = ((unsigned int*)(value() + 4 ))[0];
return t * 1000; return t * 1000;
} }
unsigned int timestampInc() const{ unsigned int timestampInc() const {
return ((unsigned int*)(value() ))[0]; return ((unsigned int*)(value() ))[0];
} }
const char * dbrefNS() const { const char * dbrefNS() const {
uassert( 10063 , "not a dbref" , type() == DBRef ); uassert( 10063 , "not a dbref" , type() == DBRef );
return value() + 4; return value() + 4;
} }
const mongo::OID& dbrefOID() const { const mongo::OID& dbrefOID() const {
uassert( 10064 , "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 mongo::OID* >( start ); return *reinterpret_cast< const mongo::OID* >( start );
} }
bool operator<( const BSONElement& other ) const { /** this does not use fieldName in the comparison, just the value * /
int x = (int)canonicalType() - (int)other.canonicalType(); bool operator<( const BSONElement& other ) const {
if ( x < 0 ) return true; int x = (int)canonicalType() - (int)other.canonicalType();
else if ( x > 0 ) return false; if ( x < 0 ) return true;
return compareElementValues(*this,other) < 0; else if ( x > 0 ) return false;
} return compareElementValues(*this,other) < 0;
}
// If maxLen is specified, don't scan more than maxLen bytes. // @param maxLen don't scan more than maxLen bytes
explicit BSONElement(const char *d, int maxLen = -1) : data(d) { explicit BSONElement(const char *d, int maxLen) : data(d) {
fieldNameSize_ = -1; if ( eoo() ) {
if ( eoo() ) totalSize = 1;
fieldNameSize_ = 0; fieldNameSize_ = 0;
else { }
if ( maxLen != -1 ) { else {
int size = (int) strnlen( fieldName(), maxLen - 1 ); totalSize = -1;
massert( 10333 , "Invalid field name", size != -1 ); fieldNameSize_ = -1;
fieldNameSize_ = size + 1; if ( maxLen != -1 ) {
int size = (int) strnlen( fieldName(), maxLen - 1 );
massert( 10333 , "Invalid field name", size != -1 );
fieldNameSize_ = size + 1;
}
} }
} }
totalSize = -1;
}
string _asCode() const; explicit BSONElement(const char *d) : data(d) {
OpTime _opTime() const; fieldNameSize_ = -1;
totalSize = -1;
if ( eoo() ) {
fieldNameSize_ = 0;
totalSize = 1;
}
}
private: string _asCode() const;
const char *data; OpTime _opTime() const;
mutable int fieldNameSize_; // cached value
int fieldNameSize() const {
if ( fieldNameSize_ == -1 )
fieldNameSize_ = (int)strlen( fieldName() ) + 1;
return fieldNameSize_;
}
mutable int totalSize; /* caches the computed size */
friend class BSONObjIterator; private:
friend class BSONObj; const char *data;
const BSONElement& chk(int t) const { mutable int fieldNameSize_; // cached value
if ( t != type() ){ int fieldNameSize() const {
StringBuilder ss; if ( fieldNameSize_ == -1 )
ss << "wrong type for BSONElement (" << fieldName() << ") " << type() << " != " << t; fieldNameSize_ = (int)strlen( fieldName() ) + 1;
uasserted(13111, ss.str() ); return fieldNameSize_;
} }
return *this; mutable int totalSize; /* caches the computed size */
}
const BSONElement& chk(bool expr) const { friend class BSONObjIterator;
uassert(13118, "unexpected or missing type value in BSON object", e xpr); friend class BSONObj;
return *this; const BSONElement& chk(int t) const {
} if ( t != type() ) {
}; StringBuilder ss;
if( eoo() )
ss << "field not found, expected type " << t;
else
ss << "wrong type for field (" << fieldName() << ") " < < type() << " != " << t;
uasserted(13111, ss.str() );
}
return *this;
}
const BSONElement& chk(bool expr) const {
uassert(13118, "unexpected or missing type value in BSON object ", expr);
return *this;
}
};
inline int BSONElement::canonicalType() const { inline int BSONElement::canonicalType() const {
BSONType t = type(); BSONType t = type();
switch ( t ){ switch ( t ) {
case MinKey: case MinKey:
case MaxKey: case MaxKey:
return t; return t;
case EOO: case EOO:
case Undefined: case Undefined:
return 0; return 0;
case jstNULL: case jstNULL:
return 5; return 5;
case NumberDouble: case NumberDouble:
case NumberInt: case NumberInt:
skipping to change at line 449 skipping to change at line 481
case DBRef: case DBRef:
return 55; return 55;
case Code: case Code:
return 60; return 60;
case CodeWScope: case CodeWScope:
return 65; return 65;
default: default:
assert(0); assert(0);
return -1; return -1;
} }
} }
inline bool BSONElement::trueValue() const { inline bool BSONElement::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 mongo::Bool: case mongo::Bool:
skipping to change at line 472 skipping to change at line 504
case jstNULL: case jstNULL:
case Undefined: case Undefined:
return false; return false;
default: default:
; ;
} }
return true; return true;
} }
/** True if element is of a numeric type. */ /** @return true if element is of a numeric type. */
inline bool BSONElement::isNumber() const { inline bool BSONElement::isNumber() const {
switch( type() ) { switch( type() ) {
case NumberLong: case NumberLong:
case NumberDouble: case NumberDouble:
case NumberInt: case NumberInt:
return true; return true;
default: default:
return false; return false;
} }
} }
inline bool BSONElement::isSimpleType() const { inline bool BSONElement::isSimpleType() const {
switch( type() ){ switch( type() ) {
case NumberLong: case NumberLong:
case NumberDouble: case NumberDouble:
case NumberInt: case NumberInt:
case mongo::String: case mongo::String:
case mongo::Bool: case mongo::Bool:
case mongo::Date: case mongo::Date:
case jstOID: case jstOID:
return true; return true;
default: default:
return false; return false;
 End of changes. 67 change blocks. 
313 lines changed or deleted 345 lines changed or added


 bsonmisc.h   bsonmisc.h 
skipping to change at line 32 skipping to change at line 32
int getGtLtOp(const BSONElement& e); int getGtLtOp(const BSONElement& e);
struct BSONElementCmpWithoutField { struct BSONElementCmpWithoutField {
bool operator()( const BSONElement &l, const BSONElement &r ) const { bool operator()( const BSONElement &l, const BSONElement &r ) const {
return l.woCompare( r, false ) < 0; return l.woCompare( r, false ) < 0;
} }
}; };
class BSONObjCmp { class BSONObjCmp {
public: public:
BSONObjCmp( const BSONObj &_order = BSONObj() ) : order( _order ) { } BSONObjCmp( const BSONObj &order = BSONObj() ) : _order( order ) {}
bool operator()( const BSONObj &l, const BSONObj &r ) const { bool operator()( const BSONObj &l, const BSONObj &r ) const {
return l.woCompare( r, order ) < 0; return l.woCompare( r, _order ) < 0;
} }
BSONObj order() const { return _order; }
private: private:
BSONObj order; BSONObj _order;
};
class BSONObjCmpDefaultOrder : public BSONObjCmp {
public:
BSONObjCmpDefaultOrder() : BSONObjCmp( BSONObj() ) {}
}; };
typedef set< BSONObj, BSONObjCmpDefaultOrder > BSONObjSetDefaultOrder; typedef set<BSONObj,BSONObjCmp> BSONObjSet;
enum FieldCompareResult { enum FieldCompareResult {
LEFT_SUBFIELD = -2, LEFT_SUBFIELD = -2,
LEFT_BEFORE = -1, LEFT_BEFORE = -1,
SAME = 0, SAME = 0,
RIGHT_BEFORE = 1 , RIGHT_BEFORE = 1 ,
RIGHT_SUBFIELD = 2 RIGHT_SUBFIELD = 2
}; };
FieldCompareResult compareDottedFieldNames( const string& l , const str ing& r ); FieldCompareResult compareDottedFieldNames( const string& l , const str ing& r );
/** Use BSON macro to build a BSONObj from a stream /** Use BSON macro to build a BSONObj from a stream
e.g., e.g.,
BSON( "name" << "joe" << "age" << 33 ) BSON( "name" << "joe" << "age" << 33 )
with auto-generated object id: with auto-generated object id:
BSON( GENOID << "name" << "joe" << "age" << 33 ) BSON( GENOID << "name" << "joe" << "age" << 33 )
The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented cons truction The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction
of a BSONObj, particularly when assembling a Query. For example, of a BSONObj, particularly when assembling a Query. For example,
BSON( "a" << GT << 23.4 << NE << 30 << "b" << 2 ) produces the object BSON( "a" << GT << 23.4 << NE << 30 << "b" << 2 ) produces the obje ct
{ a: { \$gt: 23.4, \$ne: 30 }, b: 2 }. { a: { \$gt: 23.4, \$ne: 30 }, b: 2 }.
*/ */
#define BSON(x) (( mongo::BSONObjBuilder(64) << x ).obj()) #define BSON(x) (( mongo::BSONObjBuilder(64) << x ).obj())
/** Use BSON_ARRAY macro like BSON macro, but without keys /** Use BSON_ARRAY macro like BSON macro, but without keys
BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) ); BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARR AY( "bar" << "baz" << "qux" ) ) );
*/ */
#define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr()) #define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr())
/* Utility class to auto assign object IDs. /* Utility class to auto assign object IDs.
Example: Example:
cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 } cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 }
*/ */
extern struct GENOIDLabeler { } GENOID; extern struct GENOIDLabeler { } GENOID;
/* Utility class to add a Date element with the current time /* Utility class to add a Date element with the current time
Example: Example:
cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 1 1:41:42" } cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 1 1:41:42" }
*/ */
extern struct DateNowLabeler { } DATENOW; extern struct DateNowLabeler { } DATENOW;
/* Utility class to add the minKey (minus infinity) to a given attribut e
Example:
cout << BSON( "a" << MINKEY ); // { "a" : { "$minKey" : 1 } }
*/
extern struct MinKeyLabeler { } MINKEY;
extern struct MaxKeyLabeler { } MAXKEY;
// Utility class to implement GT, GTE, etc as described above. // Utility class to implement GT, GTE, etc as described above.
class Labeler { class Labeler {
public: public:
struct Label { struct Label {
Label( const char *l ) : l_( l ) {} Label( const char *l ) : l_( l ) {}
const char *l_; const char *l_;
}; };
Labeler( const Label &l, BSONObjBuilderValueStream *s ) : l_( l ), s_( s ) {} Labeler( const Label &l, BSONObjBuilderValueStream *s ) : l_( l ), s_( s ) {}
template<class T> template<class T>
BSONObjBuilder& operator<<( T value ); BSONObjBuilder& operator<<( T value );
skipping to change at line 142 skipping to change at line 145
friend class Labeler; friend class Labeler;
BSONObjBuilderValueStream( BSONObjBuilder * builder ); BSONObjBuilderValueStream( BSONObjBuilder * builder );
BSONObjBuilder& operator<<( const BSONElement& e ); BSONObjBuilder& operator<<( const BSONElement& e );
template<class T> template<class T>
BSONObjBuilder& operator<<( T value ); BSONObjBuilder& operator<<( T value );
BSONObjBuilder& operator<<(DateNowLabeler& id); BSONObjBuilder& operator<<(DateNowLabeler& id);
BSONObjBuilder& operator<<(MinKeyLabeler& id);
BSONObjBuilder& operator<<(MaxKeyLabeler& id);
Labeler operator<<( const Labeler::Label &l ); Labeler operator<<( const Labeler::Label &l );
void endField( const char *nextFieldName = 0 ); void endField( const char *nextFieldName = 0 );
bool subobjStarted() const { return _fieldName != 0; } bool subobjStarted() const { return _fieldName != 0; }
private: private:
const char * _fieldName; const char * _fieldName;
BSONObjBuilder * _builder; BSONObjBuilder * _builder;
bool haveSubobj() const { return _subobj.get() != 0; } bool haveSubobj() const { return _subobj.get() != 0; }
BSONObjBuilder *subobj(); BSONObjBuilder *subobj();
auto_ptr< BSONObjBuilder > _subobj; auto_ptr< BSONObjBuilder > _subobj;
}; };
/** /**
used in conjuction with BSONObjBuilder, allows for proper buffer siz e to prevent crazy memory usage used in conjuction with BSONObjBuilder, allows for proper buffer siz e to prevent crazy memory usage
*/ */
class BSONSizeTracker { class BSONSizeTracker {
public: public:
BSONSizeTracker(){ BSONSizeTracker() {
_pos = 0; _pos = 0;
for ( int i=0; i<SIZE; i++ ) for ( int i=0; i<SIZE; i++ )
_sizes[i] = 512; // this is the default, so just be consist ent _sizes[i] = 512; // this is the default, so just be consist ent
} }
~BSONSizeTracker(){ ~BSONSizeTracker() {
} }
void got( int size ){ void got( int size ) {
_sizes[_pos++] = size; _sizes[_pos++] = size;
if ( _pos >= SIZE ) if ( _pos >= SIZE )
_pos = 0; _pos = 0;
} }
/** /**
* right now choosing largest size * right now choosing largest size
*/ */
int getSize() const { int getSize() const {
int x = 16; // sane min int x = 16; // sane min
for ( int i=0; i<SIZE; i++ ){ for ( int i=0; i<SIZE; i++ ) {
if ( _sizes[i] > x ) if ( _sizes[i] > x )
x = _sizes[i]; x = _sizes[i];
} }
return x; return x;
} }
private: private:
enum { SIZE = 10 }; enum { SIZE = 10 };
int _pos; int _pos;
int _sizes[SIZE]; int _sizes[SIZE];
}; };
// considers order
bool fieldsMatch(const BSONObj& lhs, const BSONObj& rhs);
} }
 End of changes. 19 change blocks. 
26 lines changed or deleted 34 lines changed or added


 bsonobj.h   bsonobj.h 
skipping to change at line 20 skipping to change at line 20
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <boost/intrusive_ptr.hpp>
#include <set> #include <set>
#include <list> #include <list>
#include <vector> #include <vector>
#include "util/atomic_int.h"
#include "util/builder.h" #include "util/builder.h"
#include "stringdata.h" #include "stringdata.h"
namespace mongo { namespace mongo {
typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet;
typedef multiset< BSONElement, BSONElementCmpWithoutField > BSONElement MSet;
const int BSONObjMaxSize = 32 * 1024 * 1024;
/** /**
C++ representation of a "BSON" object -- that is, an extended JSO N-style C++ representation of a "BSON" object -- that is, an extended JSON-s tyle
object in a binary representation. object in a binary representation.
See bsonspec.org. See bsonspec.org.
Note that BSONObj's have a smart pointer capability built in -- so y ou can Note that BSONObj's have a smart pointer capability built in -- so y ou can
pass them around by value. The reference counts used to implement t his pass them around by value. The reference counts used to implement t his
do not use locking, so copying and destroying BSONObj's are not thre ad-safe do not use locking, so copying and destroying BSONObj's are not thre ad-safe
operations. operations.
BSON object format: BSON object format:
skipping to change at line 74 skipping to change at line 75
BinData: <int len> <byte subtype> <byte[len] data> BinData: <int len> <byte subtype> <byte[len] data>
Code: a function (not a closure): same format as String. Code: a function (not a closure): same format as String.
Symbol: a language symbol (say a python symbol). same format as St ring. Symbol: a language symbol (say a python symbol). same format as St ring.
Code With Scope: <total size><String><Object> Code With Scope: <total size><String><Object>
\endcode \endcode
*/ */
class BSONObj { class BSONObj {
public: public:
/** Construct a BSONObj from data in the proper format. /** Construct a BSONObj from data in the proper format.
@param ifree true if the BSONObj should free() the msgdata when * Use this constructor when something else owns msgdata's buffer
it destructs. */
*/ explicit BSONObj(const char *msgdata) {
explicit BSONObj(const char *msgdata, bool ifree = false) { init(msgdata);
init(msgdata, ifree);
} }
BSONObj(const Record *r);
/** Construct a BSONObj from data in the proper format.
* Use this constructor when you want BSONObj to free(holder) when it is no longer needed
* BSONObj::Holder has an extra 4 bytes for a ref-count before the start of the object
*/
class Holder;
explicit BSONObj(Holder* holder) {
init(holder);
}
explicit BSONObj(const Record *r);
/** Construct an empty BSONObj -- that is, {}. */ /** Construct an empty BSONObj -- that is, {}. */
BSONObj(); BSONObj();
// defensive
~BSONObj() { _objdata = 0; }
void appendSelfToBufBuilder(BufBuilder& b) const { ~BSONObj() {
assert( objsize() ); _objdata = 0; // defensive
b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsiz e());
} }
/**
A BSONObj can use a buffer it "owns" or one it does not.
OWNED CASE
If the BSONObj owns the buffer, the buffer can be shared among s everal BSONObj's (by assignment).
In this case the buffer is basically implemented as a shared_ptr .
Since BSONObj's are typically immutable, this works well.
UNOWNED CASE
A BSONObj can also point to BSON data in some other data structu re it does not "own" or free later.
For example, in a memory mapped file. In this case, it is impor tant the original data stays in
scope for as long as the BSONObj is in use. If you think the or iginal data may go out of scope,
call BSONObj::getOwned() to promote your BSONObj to having its o wn copy.
On a BSONObj assignment, if the source is unowned, both the sour ce and dest will have unowned
pointers to the original buffer after the assignment.
If you are not sure about ownership but need the buffer to last as long as the BSONObj, call
getOwned(). getOwned() is a no-op if the buffer is already owne d. If not already owned, a malloc
and memcpy will result.
Most ways to create BSONObj's create 'owned' variants. Unowned versions can be created with:
(1) specifying true for the ifree parameter in the constructor
(2) calling BSONObjBuilder::done(). Use BSONObjBuilder::obj() t o get an owned copy
(3) retrieving a subobject retrieves an unowned pointer into the parent BSON object
@return true if this is in owned mode
*/
bool isOwned() const { return _holder.get() != 0; }
/** assure the data buffer is under the control of this BSONObj and not a remote buffer
@see isOwned()
*/
BSONObj getOwned() const;
/** @return a new full (and owned) copy of the object. */
BSONObj copy() const;
/** Readable representation of a BSON object in an extended JSON-st yle notation. /** Readable representation of a BSON object in an extended JSON-st yle notation.
This is an abbreviated representation which might be used for l ogging. This is an abbreviated representation which might be used for l ogging.
*/ */
string toString( bool isArray = false, bool full=false ) const; string toString( bool isArray = false, bool full=false ) const;
void toString(StringBuilder& s, bool isArray = false, bool full=fal se ) const; void toString(StringBuilder& s, bool isArray = false, bool full=fal se ) const;
/** Properly formatted JSON string. /** Properly formatted JSON string.
@param pretty if true we try to add some lf's and indentation @param pretty if true we try to add some lf's and indentation
*/ */
string jsonString( JsonStringFormat format = Strict, int pretty = 0 ) const; string jsonString( JsonStringFormat format = Strict, int pretty = 0 ) const;
/** note: addFields always adds _id even if not specified */ /** note: addFields always adds _id even if not specified */
int addFields(BSONObj& from, set<string>& fields); /* returns n add ed */ int addFields(BSONObj& from, set<string>& fields); /* returns n add ed */
/** remove specified field and return a new object with the remaini ng fields.
slowish as builds a full new object
*/
BSONObj removeField(const StringData& name) const;
/** returns # of top level fields in the object /** returns # of top level fields in the object
note: iterates to count the fields note: iterates to count the fields
*/ */
int nFields() const; int nFields() const;
/** adds the field names to the fields set. does NOT clear it (app ends). */ /** adds the field names to the fields set. does NOT clear it (app ends). */
int getFieldNames(set<string>& fields) const; int getFieldNames(set<string>& fields) const;
/** return has eoo() true if no match /** @return the specified element. element.eoo() will be true if n ot found.
supports "." notation to reach into embedded objects @param name field to find. supports dot (".") notation to reach into embedded objects.
for example "x.y" means "in the nested object in field x, retr ieve field y"
*/ */
BSONElement getFieldDotted(const char *name) const; BSONElement getFieldDotted(const char *name) const;
/** return has eoo() true if no match /** @return the specified element. element.eoo() will be true if n ot found.
supports "." notation to reach into embedded objects @param name field to find. supports dot (".") notation to reach into embedded objects.
for example "x.y" means "in the nested object in field x, retr ieve field y"
*/ */
BSONElement getFieldDotted(const string& name) const { BSONElement getFieldDotted(const string& name) const {
return getFieldDotted( name.c_str() ); return getFieldDotted( name.c_str() );
} }
/** Like getFieldDotted(), but expands multikey arrays and returns all matching objects /** Like getFieldDotted(), but expands arrays and returns all match ing objects.
* Turning off expandLastArray allows you to retrieve nested array objects instead of
* their contents.
*/ */
void getFieldsDotted(const StringData& name, BSONElementSet &ret ) const; void getFieldsDotted(const StringData& name, BSONElementSet &ret, b ool expandLastArray = true ) const;
void getFieldsDotted(const StringData& name, BSONElementMSet &ret, bool expandLastArray = true ) const;
/** Like getFieldDotted(), but returns first array encountered whil e traversing the /** Like getFieldDotted(), but returns first array encountered whil e traversing the
dotted fields of name. The name variable is updated to represe nt field dotted fields of name. The name variable is updated to represe nt field
names with respect to the returned element. */ names with respect to the returned element. */
BSONElement getFieldDottedOrArray(const char *&name) const; BSONElement getFieldDottedOrArray(const char *&name) const;
/** Get the field of the specified name. eoo() is true on the retur ned /** Get the field of the specified name. eoo() is true on the retur ned
element if not found. element if not found.
*/ */
BSONElement getField(const StringData& name) const; BSONElement getField(const StringData& name) const;
/** Get several fields at once. This is faster than separate getFie ld() calls as the size of
elements iterated can then be calculated only once each.
@param n number of fieldNames, and number of elements in the fi elds array
@param fields if a field is found its element is stored in its corresponding position in this array.
if not found the array element is unchanged.
*/
void getFields(unsigned n, const char **fieldNames, BSONElement *fi elds) const;
/** Get the field of the specified name. eoo() is true on the retur ned /** Get the field of the specified name. eoo() is true on the retur ned
element if not found. element if not found.
*/ */
BSONElement operator[] (const char *field) const { BSONElement operator[] (const char *field) const {
return getField(field); return getField(field);
} }
BSONElement operator[] (const string& field) const { BSONElement operator[] (const string& field) const {
return getField(field); return getField(field);
} }
BSONElement operator[] (int field) const { BSONElement operator[] (int field) const {
StringBuilder ss; StringBuilder ss;
ss << field; ss << field;
string s = ss.str(); string s = ss.str();
return getField(s.c_str()); return getField(s.c_str());
} }
/** @return true if field exists */ /** @return true if field exists */
bool hasField( const char * name )const { bool hasField( const char * name ) const { return !getField(name).e oo(); }
return ! getField( name ).eoo(); /** @return true if field exists */
} bool hasElement(const char *name) const { return hasField(name); }
/** @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 - does some type conversions */ /** @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
@see BSONElement::trueValue()
*/
bool getBoolField(const char *name) const; bool getBoolField(const char *name) 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 object with
x and y elements of this object, if they are present . x and y elements of this object, if they are present.
returns elements with original field names returns elements with original field names
*/ */
BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=fa lse) const; BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=fa lse) const;
BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter) const; BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter) const;
BSONElement getFieldUsingIndexNames(const char *fieldName, const BS ONObj &indexKey) const; BSONElement getFieldUsingIndexNames(const char *fieldName, const BS ONObj &indexKey) const;
/** @return the raw data of the object */ /** @return the raw data of the object */
const char *objdata() const { const char *objdata() const {
return _objdata; return _objdata;
} }
/** @return total size of the BSON object in bytes */ /** @return total size of the BSON object in bytes */
int objsize() const { int objsize() const { return *(reinterpret_cast<const int*>(objdata ())); }
return *(reinterpret_cast<const int*>(objdata()));
}
/** performs a cursory check on the object's size only. */ /** performs a cursory check on the object's size only. */
bool isValid(); bool isValid() const;
/** @return if the user is a valid user doc /** @return if the user is a valid user doc
criter: isValid() no . or $ field names criter: isValid() no . or $ field names
*/ */
bool okForStorage() const; bool okForStorage() const;
/** @return true if object is empty -- i.e., {} */ /** @return true if object is empty -- i.e., {} */
bool isEmpty() const { bool isEmpty() const { return objsize() <= 5; }
return objsize() <= 5;
}
void dump() const; void dump() const;
/** Alternative output format */ /** Alternative output format */
string hexDump() const; string hexDump() const;
/**wo='well ordered'. fields must be in same order in each object. /**wo='well ordered'. fields must be in same order in each object.
Ordering is with respect to the signs of the elements Ordering is with respect to the signs of the elements
and allows ascending / descending key mixing. and allows ascending / descending key mixing.
@return <0 if l<r. 0 if l==r. >0 if l>r @return <0 if l<r. 0 if l==r. >0 if l>r
*/ */
int woCompare(const BSONObj& r, const Ordering &o, int woCompare(const BSONObj& r, const Ordering &o,
bool considerFieldName=true) const; bool considerFieldName=true) const;
/**wo='well ordered'. fields must be in same order in each object. /**wo='well ordered'. fields must be in same order in each object.
Ordering is with respect to the signs of the elements Ordering is with respect to the signs of the elements
and allows ascending / descending key mixing. and allows ascending / descending key mixing.
@return <0 if l<r. 0 if l==r. >0 if l>r @return <0 if l<r. 0 if l==r. >0 if l>r
*/ */
int woCompare(const BSONObj& r, const BSONObj &ordering = BSONObj() , int woCompare(const BSONObj& r, const BSONObj &ordering = BSONObj() ,
bool considerFieldName=true) const; bool considerFieldName=true) const;
bool operator<( const BSONObj& other ) const { return woCompare( ot her ) < 0; } bool operator<( const BSONObj& other ) const { return woCompare( ot her ) < 0; }
bool operator<=( const BSONObj& other ) const { return woCompare( o ther ) <= 0; } bool operator<=( const BSONObj& other ) const { return woCompare( o ther ) <= 0; }
bool operator>( const BSONObj& other ) const { return woCompare( ot her ) > 0; } bool operator>( const BSONObj& other ) const { return woCompare( ot her ) > 0; }
bool operator>=( const BSONObj& other ) const { return woCompare( o ther ) >= 0; } bool operator>=( const BSONObj& other ) const { return woCompare( o ther ) >= 0; }
/** /**
* @param useDotted whether to treat sort key fields as possibly do tted and expand into them * @param useDotted whether to treat sort key fields as possibly do tted and expand into them
*/ */
int woSortOrder( const BSONObj& r , const BSONObj& sortKey , bool u seDotted=false ) const; int woSortOrder( const BSONObj& r , const BSONObj& sortKey , bool u seDotted=false ) const;
bool equal(const BSONObj& r) const;
/** This is "shallow equality" -- ints and doubles won't match. fo r a /** This is "shallow equality" -- ints and doubles won't match. fo r a
deep equality test use woCompare (which is slower). deep equality test use woCompare (which is slower).
*/ */
bool woEqual(const BSONObj& r) const { bool shallowEqual(const BSONObj& r) const {
int os = objsize(); int os = objsize();
if ( os == r.objsize() ) { if ( os == r.objsize() ) {
return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); return (os == 0 || memcmp(objdata(),r.objdata(),os)==0);
} }
return false; return false;
} }
/** @return first field of the object */ /** @return first field of the object */
BSONElement firstElement() const { BSONElement firstElement() const { return BSONElement(objdata() + 4 ); }
return BSONElement(objdata() + 4);
}
/** @return true if field exists in the object */ /** faster than firstElement().fieldName() - for the first element we can easily find the fieldname without
bool hasElement(const char *name) const; computing the element size.
*/
const char * firstElementFieldName() const {
const char *p = objdata() + 4;
return *p == EOO ? "" : p+1;
}
/** Get the _id field from the object. For good performance drivers should /** Get the _id field from the object. For good performance driver s should
assure that _id is the first element of the object; however, co rrect operation assure that _id is the first element of the object; however, co rrect operation
is assured regardless. is assured regardless.
@return true if found @return true if found
*/ */
bool getObjectID(BSONElement& e) const; bool getObjectID(BSONElement& e) const;
/** makes a copy of the object. */
BSONObj copy() const;
/* make sure the data buffer is under the control of this BSONObj a nd not a remote buffer */
BSONObj getOwned() const{
if ( !isOwned() )
return copy();
return *this;
}
bool isOwned() const { return _holder.get() != 0; }
/** @return A hash code for the object */ /** @return A hash code for the object */
int hash() const { int hash() const {
unsigned x = 0; unsigned x = 0;
const char *p = objdata(); const char *p = objdata();
for ( int i = 0; i < objsize(); i++ ) for ( int i = 0; i < objsize(); i++ )
x = x * 131 + p[i]; x = x * 131 + p[i];
return (x & 0x7fffffff) | 0x8000000; // must be > 0 return (x & 0x7fffffff) | 0x8000000; // must be > 0
} }
skipping to change at line 305 skipping to change at line 362
/** Return new object with the field names replaced by those in the /** Return new object with the field names replaced by those in the
passed object. */ passed object. */
BSONObj replaceFieldNames( const BSONObj &obj ) const; BSONObj replaceFieldNames( const BSONObj &obj ) const;
/** true unless corrupt */ /** true unless corrupt */
bool valid() const; bool valid() const;
/** @return an md5 value for this object. */ /** @return an md5 value for this object. */
string md5() const; string md5() const;
bool operator==( const BSONObj& other ) const{ bool operator==( const BSONObj& other ) const { return equal( other ); }
return woCompare( other ) == 0;
}
enum MatchType { enum MatchType {
Equality = 0, Equality = 0,
LT = 0x1, LT = 0x1,
LTE = 0x3, LTE = 0x3,
GTE = 0x6, GTE = 0x6,
GT = 0x4, GT = 0x4,
opIN = 0x8, // { x : { $in : [1,2,3] } } opIN = 0x8, // { x : { $in : [1,2,3] } }
NE = 0x9, NE = 0x9,
opSIZE = 0x0A, opSIZE = 0x0A,
skipping to change at line 336 skipping to change at line 391
opNEAR = 0x13, opNEAR = 0x13,
opWITHIN = 0x14, opWITHIN = 0x14,
opMAX_DISTANCE=0x15 opMAX_DISTANCE=0x15
}; };
/** add all elements of the object to the specified vector */ /** add all elements of the object to the specified vector */
void elems(vector<BSONElement> &) const; void elems(vector<BSONElement> &) const;
/** add all elements of the object to the specified list */ /** add all elements of the object to the specified list */
void elems(list<BSONElement> &) const; void elems(list<BSONElement> &) const;
/** add all values of the object to the specified vector. If type mismatches, exception. */ /** add all values of the object to the specified vector. If type mismatches, exception.
this is most useful when the BSONObj is an array, but can be us ed with non-arrays too in theory.
example:
bo sub = y["subobj"].Obj();
vector<int> myints;
sub.Vals(myints);
*/
template <class T> template <class T>
void Vals(vector<T> &) const; void Vals(vector<T> &) const;
/** add all values of the object to the specified list. If type mi smatches, exception. */ /** add all values of the object to the specified list. If type mi smatches, exception. */
template <class T> template <class T>
void Vals(list<T> &) const; void Vals(list<T> &) const;
/** add all values of the object to the specified vector. If type mismatches, skip. */ /** add all values of the object to the specified vector. If type mismatches, skip. */
template <class T> template <class T>
void vals(vector<T> &) const; void vals(vector<T> &) const;
/** add all values of the object to the specified list. If type mi smatches, skip. */ /** add all values of the object to the specified list. If type mi smatches, skip. */
template <class T> template <class T>
void vals(list<T> &) const; void vals(list<T> &) const;
friend class BSONObjIterator; friend class BSONObjIterator;
typedef BSONObjIterator iterator; typedef BSONObjIterator iterator;
BSONObjIterator begin();
private: /** use something like this:
class Holder { for( BSONObj::iterator i = myObj.begin(); i.more(); ) {
public: BSONElement e = i.next();
Holder( const char *objdata ) : ...
_objdata( objdata ) {
}
~Holder() {
free((void *)_objdata);
_objdata = 0;
} }
*/
BSONObjIterator begin() const;
void appendSelfToBufBuilder(BufBuilder& b) const {
assert( objsize() );
b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsiz e());
}
#pragma pack(1)
class Holder : boost::noncopyable {
private: private:
const char *_objdata; Holder(); // this class should never be explicitly created
AtomicUInt refCount;
public:
char data[4]; // start of object
void zero() { refCount.zero(); }
// these are called automatically by boost::intrusive_ptr
friend void intrusive_ptr_add_ref(Holder* h) { h->refCount++; }
friend void intrusive_ptr_release(Holder* h) {
#if defined(_DEBUG) // cant use dassert or DEV here
assert((int)h->refCount > 0); // make sure we haven't alrea dy freed the buffer
#endif
if(--(h->refCount) == 0){
#if defined(_DEBUG)
unsigned sz = (unsigned&) *h->data;
assert(sz < BSONObjMaxInternalSize * 3);
memset(h->data, 0xdd, sz);
#endif
free(h);
}
}
}; };
#pragma pack()
private:
const char *_objdata; const char *_objdata;
boost::shared_ptr< Holder > _holder; boost::intrusive_ptr< Holder > _holder;
void init(const char *data, bool ifree) {
if ( ifree ) void _assertInvalid() const;
_holder.reset( new Holder( data ) );
void init(Holder *holder) {
_holder = holder; // holder is now managed by intrusive_ptr
init(holder->data);
}
void init(const char *data) {
_objdata = data; _objdata = data;
if ( ! isValid() ){ if ( !isValid() )
StringBuilder ss; _assertInvalid();
int os = objsize();
ss << "Invalid BSONObj spec size: " << os << " (" << toHex( &os, 4 ) << ")";
try {
BSONElement e = firstElement();
ss << " first element:" << e.toString() << " ";
}
catch ( ... ){}
string s = ss.str();
massert( 10334 , s , 0 );
}
} }
}; };
ostream& operator<<( ostream &s, const BSONObj &o ); ostream& operator<<( ostream &s, const BSONObj &o );
ostream& operator<<( ostream &s, const BSONElement &e ); ostream& operator<<( ostream &s, const BSONElement &e );
StringBuilder& operator<<( StringBuilder &s, const BSONObj &o );
StringBuilder& operator<<( StringBuilder &s, const BSONElement &e );
struct BSONArray : BSONObj { struct BSONArray : BSONObj {
// Don't add anything other than forwarding constructors!!! // Don't add anything other than forwarding constructors!!!
BSONArray(): BSONObj() {} BSONArray(): BSONObj() {}
explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} explicit BSONArray(const BSONObj& obj): BSONObj(obj) {}
}; };
} }
 End of changes. 41 change blocks. 
90 lines changed or deleted 179 lines changed or added


 bsonobjbuilder.h   bsonobjbuilder.h 
skipping to change at line 27 skipping to change at line 27
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <limits> #include <limits>
#include <cmath> #include <cmath>
using namespace std; #include "bsonelement.h"
#include "bsonobj.h"
#include "bsonmisc.h"
namespace mongo { namespace mongo {
using namespace std;
#if defined(_WIN32) #if defined(_WIN32)
// warning: 'this' : used in base member initializer list // warning: 'this' : used in base member initializer list
#pragma warning( disable : 4355 ) #pragma warning( disable : 4355 )
#endif #endif
template<typename T> template<typename T>
class BSONFieldValue { class BSONFieldValue {
public: public:
BSONFieldValue( const string& name , const T& t ){ BSONFieldValue( const string& name , const T& t ) {
_name = name; _name = name;
_t = t; _t = t;
} }
const T& value() const { return _t; } const T& value() const { return _t; }
const string& name() const { return _name; } const string& name() const { return _name; }
private: private:
string _name; string _name;
T _t; T _t;
}; };
template<typename T> template<typename T>
class BSONField { class BSONField {
public: public:
BSONField( const string& name , const string& longName="" ) BSONField( const string& name , const string& longName="" )
: _name(name), _longName(longName){} : _name(name), _longName(longName) {}
const string& name() const { return _name; } const string& name() const { return _name; }
operator string() const { return _name; } operator string() const { return _name; }
BSONFieldValue<T> make( const T& t ) const { BSONFieldValue<T> make( const T& t ) const {
return BSONFieldValue<T>( _name , t ); return BSONFieldValue<T>( _name , t );
} }
BSONFieldValue<BSONObj> gt( const T& t ) const { return query( "$gt " , t ); } BSONFieldValue<BSONObj> gt( const T& t ) const { return query( "$gt " , t ); }
BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt " , t ); } BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt " , t ); }
skipping to change at line 84 skipping to change at line 88
string _name; string _name;
string _longName; string _longName;
}; };
/** Utility for creating a BSONObj. /** Utility for creating a BSONObj.
See also the BSON() and BSON_ARRAY() macros. See also the BSON() and BSON_ARRAY() macros.
*/ */
class BSONObjBuilder : boost::noncopyable { class BSONObjBuilder : boost::noncopyable {
public: public:
/** @param initsize this is just a hint as to the final size of the object */ /** @param initsize this is just a hint as to the final size of the object */
BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize), _offse t( 0 ), _s( this ) , _tracker(0) , _doneCalled(false) { BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof (unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneC alled(false) {
_b.skip(4); /*leave room for size field*/ _b.appendNum((unsigned)0); // ref-count
_b.skip(4); /*leave room for size field and ref-count*/
} }
/** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder */ /** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder
* This is for more efficient adding of subobjects/arrays. See doc s for subobjStart for example.
*/
BSONObjBuilder( BufBuilder &baseBuilder ) : _b( baseBuilder ), _buf ( 0 ), _offset( baseBuilder.len() ), _s( this ) , _tracker(0) , _doneCalled (false) { BSONObjBuilder( BufBuilder &baseBuilder ) : _b( baseBuilder ), _buf ( 0 ), _offset( baseBuilder.len() ), _s( this ) , _tracker(0) , _doneCalled (false) {
_b.skip( 4 ); _b.skip( 4 );
} }
BSONObjBuilder( const BSONSizeTracker & tracker ) : _b(_buf) , _buf (tracker.getSize() ), _offset(0), _s( this ) , _tracker( (BSONSizeTracker*) (&tracker) ) , _doneCalled(false) { BSONObjBuilder( const BSONSizeTracker & tracker ) : _b(_buf) , _buf (tracker.getSize() + sizeof(unsigned) ), _offset( sizeof(unsigned) ), _s( t his ) , _tracker( (BSONSizeTracker*)(&tracker) ) , _doneCalled(false) {
_b.skip( 4 ); _b.appendNum((unsigned)0); // ref-count
_b.skip(4);
} }
~BSONObjBuilder(){ ~BSONObjBuilder() {
if ( !_doneCalled && _b.buf() && _buf.getSize() == 0 ){ if ( !_doneCalled && _b.buf() && _buf.getSize() == 0 ) {
_done(); _done();
} }
} }
/** add all the fields from the object specified to this object */ /** add all the fields from the object specified to this object */
BSONObjBuilder& appendElements(BSONObj x); BSONObjBuilder& appendElements(BSONObj x);
/** add all the fields from the object specified to this object if they don't exist already */
BSONObjBuilder& appendElementsUnique( BSONObj x );
/** append element to the object we are building */ /** append element to the object we are building */
BSONObjBuilder& append( const BSONElement& e) { BSONObjBuilder& append( const BSONElement& e) {
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.appendBuf((void*) e.rawdata(), e.size()); _b.appendBuf((void*) e.rawdata(), e.size());
return *this; return *this;
} }
/** append an element but with a new name */ /** append an element but with a new name */
BSONObjBuilder& appendAs(const BSONElement& e, const StringData& f ieldName) { BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fi eldName) {
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.appendNum((char) e.type()); _b.appendNum((char) e.type());
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendBuf((void *) e.value(), e.valuesize()); _b.appendBuf((void *) e.value(), e.valuesize());
return *this; return *this;
} }
/** add a subobject as a member */ /** add a subobject as a member */
BSONObjBuilder& append(const StringData& fieldName, BSONObj subObj) { BSONObjBuilder& append(const StringData& fieldName, BSONObj subObj) {
_b.appendNum((char) Object); _b.appendNum((char) Object);
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendBuf((void *) subObj.objdata(), subObj.objsize()); _b.appendBuf((void *) subObj.objdata(), subObj.objsize());
return *this; return *this;
} }
/** add a subobject as a member */ /** add a subobject as a member */
BSONObjBuilder& appendObject(const StringData& fieldName, const cha r * objdata , int size = 0 ){ BSONObjBuilder& appendObject(const StringData& fieldName, const cha r * objdata , int size = 0 ) {
assert( objdata ); assert( objdata );
if ( size == 0 ){ if ( size == 0 ) {
size = *((int*)objdata); size = *((int*)objdata);
} }
assert( size > 4 && size < 100000000 ); assert( size > 4 && size < 100000000 );
_b.appendNum((char) Object); _b.appendNum((char) Object);
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendBuf((void*)objdata, size ); _b.appendBuf((void*)objdata, size );
return *this; return *this;
} }
/** add header for a new subobject and return bufbuilder for writin g to /** add header for a new subobject and return bufbuilder for writin g to
the subobject's body */ * the subobject's body
*
* example:
*
* BSONObjBuilder b;
* BSONObjBuilder sub (b.subobjStart("fieldName"));
* // use sub
* sub.done()
* // use b and convert to object
*/
BufBuilder &subobjStart(const StringData& fieldName) { BufBuilder &subobjStart(const StringData& fieldName) {
_b.appendNum((char) Object); _b.appendNum((char) Object);
_b.appendStr(fieldName); _b.appendStr(fieldName);
return _b; return _b;
} }
/** add a subobject as a member with type Array. Thus arr object s hould have "0", "1", ... /** add a subobject as a member with type Array. Thus arr object s hould have "0", "1", ...
style fields in it. style fields in it.
*/ */
BSONObjBuilder& appendArray(const StringData& fieldName, const BSON Obj &subObj) { BSONObjBuilder& appendArray(const StringData& fieldName, const BSON Obj &subObj) {
skipping to change at line 212 skipping to change at line 232
/** Append a NumberLong */ /** Append a NumberLong */
BSONObjBuilder& append(const StringData& fieldName, long long n) { BSONObjBuilder& append(const StringData& fieldName, long long n) {
_b.appendNum((char) NumberLong); _b.appendNum((char) NumberLong);
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendNum(n); _b.appendNum(n);
return *this; return *this;
} }
/** appends a number. if n < max(int)/2 then uses int, otherwise l ong long */ /** appends a number. if n < max(int)/2 then uses int, otherwise l ong long */
BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long l ong n ){ BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long l ong n ) {
long long x = n; long long x = n;
if ( x < 0 ) if ( x < 0 )
x = x * -1; x = x * -1;
if ( x < ( numeric_limits<int>::max() / 2 ) ) if ( x < ( (numeric_limits<int>::max)() / 2 ) ) // extra () to avoid max macro on windows
append( fieldName , (int)n ); append( fieldName , (int)n );
else else
append( fieldName , n ); append( fieldName , n );
return *this; return *this;
} }
/** /**
* appendNumber is a series of method for appending the smallest se nsible type * appendNumber is a series of method for appending the smallest se nsible type
* mostly for JS * mostly for JS
*/ */
BSONObjBuilder& appendNumber( const StringData& fieldName , int n ) { BSONObjBuilder& appendNumber( const StringData& fieldName , int n ) {
return append( fieldName , n ); return append( fieldName , n );
} }
BSONObjBuilder& appendNumber( const StringData& fieldName , double d ){ BSONObjBuilder& appendNumber( const StringData& fieldName , double d ) {
return append( fieldName , d ); return append( fieldName , d );
} }
BSONObjBuilder& appendNumber( const StringData& fieldName , size_t n ){ BSONObjBuilder& appendNumber( const StringData& fieldName , size_t n ) {
static size_t maxInt = (size_t)pow( 2.0 , 30.0 ); static size_t maxInt = (size_t)pow( 2.0 , 30.0 );
if ( n < maxInt ) if ( n < maxInt )
append( fieldName , (int)n ); append( fieldName , (int)n );
else else
append( fieldName , (long long)n ); append( fieldName , (long long)n );
return *this; return *this;
} }
BSONObjBuilder& appendNumber( const StringData& fieldName , long lo ng l ){ BSONObjBuilder& appendNumber( const StringData& fieldName , long lo ng l ) {
static long long maxInt = (int)pow( 2.0 , 30.0 ); static long long maxInt = (int)pow( 2.0 , 30.0 );
static long long maxDouble = (long long)pow( 2.0 , 40.0 ); static long long maxDouble = (long long)pow( 2.0 , 40.0 );
long long x = l >= 0 ? l : -l;
if ( l < maxInt ) if ( x < maxInt )
append( fieldName , (int)l ); append( fieldName , (int)l );
else if ( l < maxDouble ) else if ( x < maxDouble )
append( fieldName , (double)l ); append( fieldName , (double)l );
else else
append( fieldName , l ); append( fieldName , l );
return *this; return *this;
} }
/** Append a double element */ /** Append a double element */
BSONObjBuilder& append(const StringData& fieldName, double n) { BSONObjBuilder& append(const StringData& fieldName, double n) {
_b.appendNum((char) NumberDouble); _b.appendNum((char) NumberDouble);
_b.appendStr(fieldName); _b.appendStr(fieldName);
skipping to change at line 363 skipping to change at line 383
} }
BSONObjBuilder& appendCode(const StringData& fieldName, const Strin gData& code) { BSONObjBuilder& appendCode(const StringData& fieldName, const Strin gData& code) {
_b.appendNum((char) Code); _b.appendNum((char) Code);
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendNum((int) code.size()+1); _b.appendNum((int) code.size()+1);
_b.appendStr(code); _b.appendStr(code);
return *this; return *this;
} }
/** Append a string element. len DOES include terminating nul */ /** Append a string element.
BSONObjBuilder& append(const StringData& fieldName, const char *str , int len) { @param sz size includes terminating null character */
BSONObjBuilder& append(const StringData& fieldName, const char *str , int sz) {
_b.appendNum((char) String); _b.appendNum((char) String);
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendNum((int)len); _b.appendNum((int)sz);
_b.appendBuf(str, len); _b.appendBuf(str, sz);
return *this; return *this;
} }
/** Append a string element */ /** Append a string element */
BSONObjBuilder& append(const StringData& fieldName, const char *str ) { BSONObjBuilder& append(const StringData& fieldName, const char *str ) {
return append(fieldName, str, (int) strlen(str)+1); return append(fieldName, str, (int) strlen(str)+1);
} }
/** Append a string element */ /** Append a string element */
BSONObjBuilder& append(const StringData& fieldName, string str) { BSONObjBuilder& append(const StringData& fieldName, const string& s tr) {
return append(fieldName, str.c_str(), (int) str.size()+1); return append(fieldName, str.c_str(), (int) str.size()+1);
} }
BSONObjBuilder& appendSymbol(const StringData& fieldName, const Str ingData& symbol) { BSONObjBuilder& appendSymbol(const StringData& fieldName, const Str ingData& symbol) {
_b.appendNum((char) Symbol); _b.appendNum((char) Symbol);
_b.appendStr(fieldName); _b.appendStr(fieldName);
_b.appendNum((int) symbol.size()+1); _b.appendNum((int) symbol.size()+1);
_b.appendStr(symbol); _b.appendStr(symbol);
return *this; } return *this;
}
/** Append a Null element to the object */ /** Append a Null element to the object */
BSONObjBuilder& appendNull( const StringData& fieldName ) { BSONObjBuilder& appendNull( const StringData& fieldName ) {
_b.appendNum( (char) jstNULL ); _b.appendNum( (char) jstNULL );
_b.appendStr( fieldName ); _b.appendStr( fieldName );
return *this; } return *this;
}
// Append an element that is less than all other keys. // Append an element that is less than all other keys.
BSONObjBuilder& appendMinKey( const StringData& fieldName ) { BSONObjBuilder& appendMinKey( const StringData& fieldName ) {
_b.appendNum( (char) MinKey ); _b.appendNum( (char) MinKey );
_b.appendStr( fieldName ); _b.appendStr( fieldName );
return *this; return *this;
} }
// Append an element that is greater than all other keys. // Append an element that is greater than all other keys.
BSONObjBuilder& appendMaxKey( const StringData& fieldName ) { BSONObjBuilder& appendMaxKey( const StringData& fieldName ) {
_b.appendNum( (char) MaxKey ); _b.appendNum( (char) MaxKey );
skipping to change at line 466 skipping to change at line 489
BSONObjBuilder& appendBinData( const StringData& fieldName, int len , BinDataType type, const unsigned char *data ) { BSONObjBuilder& appendBinData( const StringData& fieldName, int len , BinDataType type, const unsigned char *data ) {
return appendBinData(fieldName, len, type, (const char *) data) ; return appendBinData(fieldName, len, type, (const char *) data) ;
} }
/** /**
Subtype 2 is deprecated. Subtype 2 is deprecated.
Append a BSON bindata bytearray element. Append a BSON bindata bytearray element.
@param data a byte array @param data a byte array
@param len the length of data @param len the length of data
*/ */
BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldNam e , const char * data , int len ){ BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldNam e , const char * data , int len ) {
_b.appendNum( (char) BinData ); _b.appendNum( (char) BinData );
_b.appendStr( fieldName ); _b.appendStr( fieldName );
_b.appendNum( len + 4 ); _b.appendNum( len + 4 );
_b.appendNum( (char)0x2 ); _b.appendNum( (char)0x2 );
_b.appendNum( len ); _b.appendNum( len );
_b.appendBuf( (void *) data, len ); _b.appendBuf( (void *) data, len );
return *this; return *this;
} }
/** Append to the BSON object a field of type CodeWScope. This is a javascript code /** Append to the BSON object a field of type CodeWScope. This is a javascript code
skipping to change at line 495 skipping to change at line 518
_b.appendBuf( ( void * )scope.objdata(), scope.objsize() ); _b.appendBuf( ( void * )scope.objdata(), scope.objsize() );
return *this; return *this;
} }
void appendUndefined( const StringData& fieldName ) { void appendUndefined( const StringData& fieldName ) {
_b.appendNum( (char) Undefined ); _b.appendNum( (char) Undefined );
_b.appendStr( fieldName ); _b.appendStr( fieldName );
} }
/* helper function -- see Query::where() for primary way to do this . */ /* helper function -- see Query::where() for primary way to do this . */
void appendWhere( const StringData& code, const BSONObj &scope ){ void appendWhere( const StringData& code, const BSONObj &scope ) {
appendCodeWScope( "$where" , code , scope ); appendCodeWScope( "$where" , code , scope );
} }
/** /**
these are the min/max when comparing, not strict min/max element s for a given type these are the min/max when comparing, not strict min/max element s for a given type
*/ */
void appendMinForType( const StringData& fieldName , int type ); void appendMinForType( const StringData& fieldName , int type );
void appendMaxForType( const StringData& fieldName , int type ); void appendMaxForType( const StringData& fieldName , int type );
/** Append an array of values. */ /** Append an array of values. */
template < class T > template < class T >
BSONObjBuilder& append( const StringData& fieldName, const vector< T >& vals ); BSONObjBuilder& append( const StringData& fieldName, const vector< T >& vals );
template < class T > template < class T >
BSONObjBuilder& append( const StringData& fieldName, const list< T >& vals ); BSONObjBuilder& append( const StringData& fieldName, const list< T >& vals );
/** The returned BSONObj will free the buffer when it is finished. */ /** Append a set of values. */
template < class T >
BSONObjBuilder& append( const StringData& fieldName, const set< T > & vals );
/**
* destructive
* The returned BSONObj will free the buffer when it is finished.
* @return owned BSONObj
*/
BSONObj obj() { BSONObj obj() {
bool own = owned(); bool own = owned();
massert( 10335 , "builder does not own memory", own ); massert( 10335 , "builder does not own memory", own );
int l; doneFast();
return BSONObj(decouple(l), true); BSONObj::Holder* h = (BSONObj::Holder*)_b.buf();
decouple(); // sets _b.buf() to NULL
return BSONObj(h);
} }
/** 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 builder 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 builder.
*/ */
BSONObj done() { BSONObj done() {
return BSONObj(_done()); return BSONObj(_done());
} }
// Like 'done' above, but does not construct a BSONObj to return to the caller. // Like 'done' above, but does not construct a BSONObj to return to the caller.
void doneFast() { void doneFast() {
(void)_done(); (void)_done();
} }
skipping to change at line 560 skipping to change at line 593
_b.decouple(); _b.decouple();
return x; return x;
} }
void decouple() { void decouple() {
_b.decouple(); // post done() call version. be sure jsobj f rees... _b.decouple(); // post done() call version. be sure jsobj f rees...
} }
void appendKeys( const BSONObj& keyPattern , const BSONObj& values ); void appendKeys( const BSONObj& keyPattern , const BSONObj& values );
static string numStr( int i ) { static string numStr( int i ) {
if (i>=0 && i<100) if (i>=0 && i<100 && numStrsReady)
return numStrs[i]; return numStrs[i];
StringBuilder o; StringBuilder o;
o << i; o << i;
return o.str(); return o.str();
} }
/** Stream oriented way to add field names and values. */ /** Stream oriented way to add field names and values. */
BSONObjBuilderValueStream &operator<<(const char * name ) { BSONObjBuilderValueStream &operator<<(const char * name ) {
_s.endField( name ); _s.endField( name );
return _s; return _s;
skipping to change at line 609 skipping to change at line 642
BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) { BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) {
append( v.name().c_str() , v.value() ); append( v.name().c_str() , v.value() );
return *this; return *this;
} }
/** @return true if we are using our own bufbuilder, and not an alt ernate that was given to us in our constructor */ /** @return true if we are using our own bufbuilder, and not an alt ernate that was given to us in our constructor */
bool owned() const { return &_b == &_buf; } bool owned() const { return &_b == &_buf; }
BSONObjIterator iterator() const ; BSONObjIterator iterator() const ;
bool hasField( const StringData& name ) const ;
int len() const { return _b.len(); }
BufBuilder& bb() { return _b; }
private: private:
char* _done() { char* _done() {
if ( _doneCalled ) if ( _doneCalled )
return _b.buf() + _offset; return _b.buf() + _offset;
_doneCalled = true; _doneCalled = true;
_s.endField(); _s.endField();
_b.appendNum((char) EOO); _b.appendNum((char) EOO);
char *data = _b.buf() + _offset; char *data = _b.buf() + _offset;
int size = _b.len() - _offset; int size = _b.len() - _offset;
skipping to change at line 633 skipping to change at line 672
} }
BufBuilder &_b; BufBuilder &_b;
BufBuilder _buf; BufBuilder _buf;
int _offset; int _offset;
BSONObjBuilderValueStream _s; BSONObjBuilderValueStream _s;
BSONSizeTracker * _tracker; BSONSizeTracker * _tracker;
bool _doneCalled; bool _doneCalled;
static const string numStrs[100]; // cache of 0 to 99 inclusive static const string numStrs[100]; // cache of 0 to 99 inclusive
static bool numStrsReady; // for static init safety. see comments i n db/jsobj.cpp
}; };
class BSONArrayBuilder : boost::noncopyable { class BSONArrayBuilder : boost::noncopyable {
public: public:
BSONArrayBuilder() : _i(0), _b() {} BSONArrayBuilder() : _i(0), _b() {}
BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {}
BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {}
template <typename T> template <typename T>
BSONArrayBuilder& append(const T& x){ BSONArrayBuilder& append(const T& x) {
_b.append(num().c_str(), x); _b.append(num(), x);
return *this; return *this;
} }
BSONArrayBuilder& append(const BSONElement& e){ BSONArrayBuilder& append(const BSONElement& e) {
_b.appendAs(e, num()); _b.appendAs(e, num());
return *this; return *this;
} }
template <typename T> template <typename T>
BSONArrayBuilder& operator<<(const T& x){ BSONArrayBuilder& operator<<(const T& x) {
return append(x); return append(x);
} }
void appendNull() { void appendNull() {
_b.appendNull(num().c_str()); _b.appendNull(num());
} }
BSONArray arr(){ return BSONArray(_b.obj()); } /**
* destructive - ownership moves to returned BSONArray
* @return owned BSONArray
*/
BSONArray arr() { return BSONArray(_b.obj()); }
BSONObj done() { return _b.done(); } BSONObj done() { return _b.done(); }
void doneFast() { _b.doneFast(); } void doneFast() { _b.doneFast(); }
template <typename T> template <typename T>
BSONArrayBuilder& append(const StringData& name, const T& x){ BSONArrayBuilder& append(const StringData& name, const T& x) {
fill( name ); fill( name );
append( x ); append( x );
return *this; return *this;
} }
BufBuilder &subobjStart( const StringData& name = "0" ) { // These two just use next position
BufBuilder &subobjStart() { return _b.subobjStart( num() ); }
BufBuilder &subarrayStart() { return _b.subarrayStart( num() ); }
// These fill missing entries up to pos. if pos is < next pos is ig nored
BufBuilder &subobjStart(int pos) {
fill(pos);
return _b.subobjStart( num() );
}
BufBuilder &subarrayStart(int pos) {
fill(pos);
return _b.subarrayStart( num() );
}
// These should only be used where you really need interface compat ability with BSONObjBuilder
// Currently they are only used by update.cpp and it should probabl y stay that way
BufBuilder &subobjStart( const StringData& name ) {
fill( name ); fill( name );
return _b.subobjStart( num() ); return _b.subobjStart( num() );
} }
BufBuilder &subarrayStart( const char *name ) { BufBuilder &subarrayStart( const char *name ) {
fill( name ); fill( name );
return _b.subarrayStart( num() ); return _b.subarrayStart( num() );
} }
void appendArray( const StringData& name, BSONObj subObj ) { void appendArray( const StringData& name, BSONObj subObj ) {
fill( name ); fill( name );
_b.appendArray( num(), subObj ); _b.appendArray( num(), subObj );
} }
void appendAs( const BSONElement &e, const char *name ) { void appendAs( const BSONElement &e, const char *name) {
fill( name ); fill( name );
append( e ); append( e );
} }
int len() const { return _b.len(); }
private: private:
void fill( const StringData& name ) { void fill( const StringData& name ) {
char *r; char *r;
int n = strtol( name.data(), &r, 10 ); long int n = strtol( name.data(), &r, 10 );
if ( *r ) if ( *r )
uasserted( 13048, (string)"can't append to array using stri ng field name [" + name.data() + "]" ); uasserted( 13048, (string)"can't append to array using stri ng field name [" + name.data() + "]" );
while( _i < n ) fill(n);
}
void fill (int upTo){
while( _i < upTo )
append( nullElt() ); append( nullElt() );
} }
static BSONElement nullElt() { static BSONElement nullElt() {
static BSONObj n = nullObj(); static BSONObj n = nullObj();
return n.firstElement(); return n.firstElement();
} }
static BSONObj nullObj() { static BSONObj nullObj() {
BSONObjBuilder _b; BSONObjBuilder _b;
_b.appendNull( "" ); _b.appendNull( "" );
return _b.obj(); return _b.obj();
} }
string num(){ return _b.numStr(_i++); } string num() { return _b.numStr(_i++); }
int _i; int _i;
BSONObjBuilder _b; BSONObjBuilder _b;
}; };
template < class T > template < class T >
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const vector< T >& vals ) { inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const vector< T >& vals ) {
BSONObjBuilder arrBuilder; BSONObjBuilder arrBuilder;
for ( unsigned int i = 0; i < vals.size(); ++i ) for ( unsigned int i = 0; i < vals.size(); ++i )
arrBuilder.append( numStr( i ), vals[ i ] ); arrBuilder.append( numStr( i ), vals[ i ] );
appendArray( fieldName, arrBuilder.done() ); appendArray( fieldName, arrBuilder.done() );
return *this; return *this;
} }
template < class T > template < class L >
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const list< T >& vals ) { inline BSONObjBuilder& _appendIt( BSONObjBuilder& _this, const StringDa ta& fieldName, const L& vals ) {
BSONObjBuilder arrBuilder; BSONObjBuilder arrBuilder;
int n = 0; int n = 0;
for( typename list< T >::const_iterator i = vals.begin(); i != vals .end(); i++ ) for( typename L::const_iterator i = vals.begin(); i != vals.end(); i++ )
arrBuilder.append( numStr(n++), *i ); arrBuilder.append( BSONObjBuilder::numStr(n++), *i );
appendArray( fieldName, arrBuilder.done() ); _this.appendArray( fieldName, arrBuilder.done() );
return *this; return _this;
}
template < class T >
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const list< T >& vals ) {
return _appendIt< list< T > >( *this, fieldName, vals );
}
template < class T >
inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldN ame, const set< T >& vals ) {
return _appendIt< set< T > >( *this, fieldName, vals );
} }
// $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6));
inline BSONObj OR(const BSONObj& a, const BSONObj& b) inline BSONObj OR(const BSONObj& a, const BSONObj& b)
{ return BSON( "$or" << BSON_ARRAY(a << b) ); } { return BSON( "$or" << BSON_ARRAY(a << b) ); }
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c)
{ return BSON( "$or" << BSON_ARRAY(a << b << c) ); } { return BSON( "$or" << BSON_ARRAY(a << b << c) ); }
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d) inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d)
{ return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } { return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); }
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e) inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e)
{ return BSON( "$or" << BSON_ARRAY(a << b << c << d << e) ); } { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e) ); }
inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f) inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f)
{ return BSON( "$or" << BSON_ARRAY(a << b << c << d << e << f) ); } { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e << f) ); }
} }
 End of changes. 55 change blocks. 
63 lines changed or deleted 140 lines changed or added


 bsonobjiterator.h   bsonobjiterator.h 
skipping to change at line 23 skipping to change at line 23
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <boost/preprocessor/cat.hpp> // like the ## operator but works wit h __LINE__ #include <boost/preprocessor/cat.hpp> // like the ## operator but works wit h __LINE__
namespace mongo { namespace mongo {
/** iterator for a BSONObj /** iterator for a BSONObj
Note each BSONObj ends with an EOO element: so you will get more() o n an empty Note each BSONObj ends with an EOO element: so you will get more() o n an empty
object, although next().eoo() will be true. object, although next().eoo() will be true.
The BSONObj must stay in scope for the duration of the iterator's ex ecution.
todo: we may want to make a more stl-like iterator interface for thi s todo: we may want to make a more stl-like iterator interface for thi s
with things like begin() and end() with things like begin() and end()
*/ */
class BSONObjIterator { class BSONObjIterator {
public: public:
/** Create an iterator for a BSON object. /** Create an iterator for a BSON object.
*/ */
BSONObjIterator(const BSONObj& jso) { BSONObjIterator(const BSONObj& jso) {
int sz = jso.objsize(); int sz = jso.objsize();
if ( sz == 0 ) { if ( MONGO_unlikely(sz == 0) ) {
_pos = _theend = 0; _pos = _theend = 0;
return; return;
} }
_pos = jso.objdata() + 4; _pos = jso.objdata() + 4;
_theend = jso.objdata() + sz; _theend = jso.objdata() + sz - 1;
} }
BSONObjIterator( const char * start , const char * end ){ BSONObjIterator( const char * start , const char * end ) {
_pos = start + 4; _pos = start + 4;
_theend = end; _theend = end - 1;
} }
/** @return true if more elements exist to be enumerated. */ /** @return true if more elements exist to be enumerated. */
bool moreWithEOO() { bool more() { return _pos < _theend; }
return _pos < _theend;
} /** @return true if more elements exist to be enumerated INCLUDING the EOO element which is always at the end. */
bool more(){ bool moreWithEOO() { return _pos <= _theend; }
return _pos < _theend && _pos[0];
}
/** @return the next element in the object. For the final element, element.eoo() will be true. */ /** @return the next element in the object. For the final element, element.eoo() will be true. */
BSONElement next( bool checkEnd = false ) { BSONElement next( bool checkEnd ) {
assert( _pos < _theend ); assert( _pos <= _theend );
BSONElement e( _pos, checkEnd ? (int)(_theend - _pos) : -1 ); BSONElement e( _pos, checkEnd ? (int)(_theend + 1 - _pos) : -1 );
_pos += e.size( checkEnd ? (int)(_theend - _pos) : -1 ); _pos += e.size( checkEnd ? (int)(_theend + 1 - _pos) : -1 );
return e;
}
BSONElement next() {
assert( _pos <= _theend );
BSONElement e(_pos);
_pos += e.size();
return e; return e;
} }
void operator++() { next(); } void operator++() { next(); }
void operator++(int) { next(); } void operator++(int) { next(); }
BSONElement operator*() { BSONElement operator*() {
assert( _pos < _theend ); assert( _pos <= _theend );
return BSONElement(_pos, -1); return BSONElement(_pos);
} }
private: private:
const char* _pos; const char* _pos;
const char* _theend; const char* _theend;
}; };
class BSONObjIteratorSorted { class BSONObjIteratorSorted {
public: public:
BSONObjIteratorSorted( const BSONObj& o ); BSONObjIteratorSorted( const BSONObj& o );
~BSONObjIteratorSorted(){ ~BSONObjIteratorSorted() {
assert( _fields ); assert( _fields );
delete[] _fields; delete[] _fields;
_fields = 0; _fields = 0;
} }
bool more(){ bool more() {
return _cur < _nfields; return _cur < _nfields;
} }
BSONElement next(){ BSONElement next() {
assert( _fields ); assert( _fields );
if ( _cur < _nfields ) if ( _cur < _nfields )
return BSONElement( _fields[_cur++] ); return BSONElement( _fields[_cur++] );
return BSONElement(); return BSONElement();
} }
private: private:
const char ** _fields; const char ** _fields;
int _nfields; int _nfields;
int _cur; int _cur;
}; };
/** Similar to BOOST_FOREACH /** transform a BSON array into a vector of BSONElements.
* we match array # positions with their vector position, and ignore
* because the iterator is defined outside of the for, you must use {} aro und any fields with non-numeric field names.
* the surrounding scope. Don't do this: */
* inline vector<BSONElement> BSONElement::Array() const {
* if (foo) chk(mongo::Array);
* BSONForEach(e, obj) vector<BSONElement> v;
* doSomething(e); BSONObjIterator i(Obj());
* while( i.more() ) {
* but this is OK: BSONElement e = i.next();
* const char *f = e.fieldName();
* if (foo) { try {
* BSONForEach(e, obj) unsigned u = stringToNum(f);
* doSomething(e); assert( u < 1000000 );
* } if( u >= v.size() )
* v.resize(u+1);
*/ v[u] = e;
}
catch(unsigned) { }
}
return v;
}
/** Similar to BOOST_FOREACH
*
* because the iterator is defined outside of the for, you must use {} around
* the surrounding scope. Don't do this:
*
* if (foo)
* BSONForEach(e, obj)
* doSomething(e);
*
* but this is OK:
*
* if (foo) {
* BSONForEach(e, obj)
* doSomething(e);
* }
*
*/
#define BSONForEach(e, obj) \ #define BSONForEach(e, obj) \
BSONObjIterator BOOST_PP_CAT(it_,__LINE__)(obj); \ BSONObjIterator BOOST_PP_CAT(it_,__LINE__)(obj); \
for ( BSONElement e; \ for ( BSONElement e; \
(BOOST_PP_CAT(it_,__LINE__).more() ? \ (BOOST_PP_CAT(it_,__LINE__).more() ? \
(e = BOOST_PP_CAT(it_,__LINE__).next(), true) : \ (e = BOOST_PP_CAT(it_,__LINE__).next(), true) : \
false) ; \ false) ; \
/*nothing*/ ) /*nothing*/ )
} }
 End of changes. 15 change blocks. 
41 lines changed or deleted 71 lines changed or added


 bsontypes.h   bsontypes.h 
skipping to change at line 42 skipping to change at line 42
class BSONObjBuilderValueStream; class BSONObjBuilderValueStream;
class BSONObjIterator; class BSONObjIterator;
class Ordering; class Ordering;
class Record; class Record;
struct BSONArray; // empty subclass of BSONObj useful for overloading struct BSONArray; // empty subclass of BSONObj useful for overloading
struct BSONElementCmpWithoutField; struct BSONElementCmpWithoutField;
extern BSONObj maxKey; extern BSONObj maxKey;
extern BSONObj minKey; extern BSONObj minKey;
/** /**
the complete list of valid BSON types the complete list of valid BSON types
see also bsonspec.org see also bsonspec.org
*/ */
enum BSONType { enum BSONType {
/** smaller than all other types */ /** smaller than all other types */
MinKey=-1, MinKey=-1,
/** end of object */ /** end of object */
EOO=0, EOO=0,
/** double precision floating point value */ /** double precision floating point value */
NumberDouble=1, NumberDouble=1,
/** character string, stored in utf8 */ /** character string, stored in utf8 */
String=2, String=2,
/** an embedded object */ /** an embedded object */
Object=3, Object=3,
/** an embedded array */ /** an embedded array */
Array=4, Array=4,
/** binary data */ /** binary data */
BinData=5, BinData=5,
/** Undefined type */ /** Undefined type */
Undefined=6, Undefined=6,
/** ObjectId */ /** ObjectId */
jstOID=7, jstOID=7,
/** boolean type */ /** boolean type */
Bool=8, Bool=8,
/** date type */ /** date type */
Date=9, Date=9,
/** null type */ /** null type */
jstNULL=10, jstNULL=10,
/** regular expression, a pattern with options */ /** regular expression, a pattern with options */
RegEx=11, RegEx=11,
/** deprecated / will be redesigned */ /** deprecated / will be redesigned */
DBRef=12, DBRef=12,
/** deprecated / use CodeWScope */ /** deprecated / use CodeWScope */
Code=13, Code=13,
/** a programming language (e.g., Python) symbol */ /** a programming language (e.g., Python) symbol */
Symbol=14, Symbol=14,
/** javascript code that can execute on the database server, with Saved Context */ /** javascript code that can execute on the database server, with S avedContext */
CodeWScope=15, CodeWScope=15,
/** 32 bit signed integer */ /** 32 bit signed integer */
NumberInt = 16, NumberInt = 16,
/** Updated to a Date with value next OpTime on insert */ /** Updated to a Date with value next OpTime on insert */
Timestamp = 17, Timestamp = 17,
/** 64 bit integer */ /** 64 bit integer */
NumberLong = 18, NumberLong = 18,
/** max type that is not MaxKey */ /** max type that is not MaxKey */
JSTypeMax=18, JSTypeMax=18,
/** larger than all other types */ /** larger than all other types */
MaxKey=127 MaxKey=127
}; };
/* subtypes of BinData. /* subtypes of BinData.
bdtCustom and above are ones that the JS compiler understands, but a re bdtCustom and above are ones that the JS compiler understands, but a re
opaque to the database. opaque to the database.
*/ */
enum BinDataType { enum BinDataType {
BinDataGeneral=0, BinDataGeneral=0,
Function=1, Function=1,
ByteArrayDeprecated=2, /* use BinGeneral instead */ ByteArrayDeprecated=2, /* use BinGeneral instead */
bdtUUID = 3, bdtUUID = 3,
 End of changes. 1 change blocks. 
50 lines changed or deleted 50 lines changed or added


 builder.h   builder.h 
skipping to change at line 23 skipping to change at line 23
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <boost/shared_ptr.hpp>
#include "../inline_decls.h" #include "../inline_decls.h"
#include "../stringdata.h" #include "../stringdata.h"
namespace mongo { namespace mongo {
/* Note the limit here is rather arbitrary and is simply a standard. ge nerally the code works
with any object that fits in ram.
Also note that the server has some basic checks to enforce this limi t but those checks are not exhaustive
for example need to check for size too big after
update $push (append) operation
various db.eval() type operations
*/
const int BSONObjMaxUserSize = 16 * 1024 * 1024;
/*
Sometimeswe we need objects slightly larger - an object in the repli cation local.oplog
is slightly larger than a user object for example.
*/
const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 );
const int BufferMaxSize = 64 * 1024 * 1024;
class StringBuilder; class StringBuilder;
void msgasserted(int msgid, const char *msg); void msgasserted(int msgid, const char *msg);
class BufBuilder { class TrivialAllocator {
public: public:
BufBuilder(int initsize = 512) : size(initsize) { void* Malloc(size_t sz) { return malloc(sz); }
void* Realloc(void *p, size_t sz) { return realloc(p, sz); }
void Free(void *p) { free(p); }
};
class StackAllocator {
public:
enum { SZ = 512 };
void* Malloc(size_t sz) {
if( sz <= SZ ) return buf;
return malloc(sz);
}
void* Realloc(void *p, size_t sz) {
if( p == buf ) {
if( sz <= SZ ) return buf;
void *d = malloc(sz);
memcpy(d, p, SZ);
return d;
}
return realloc(p, sz);
}
void Free(void *p) {
if( p != buf )
free(p);
}
private:
char buf[SZ];
};
template< class Allocator >
class _BufBuilder {
// non-copyable, non-assignable
_BufBuilder( const _BufBuilder& );
_BufBuilder& operator=( const _BufBuilder& );
Allocator al;
public:
_BufBuilder(int initsize = 512) : size(initsize) {
if ( size > 0 ) { if ( size > 0 ) {
data = (char *) malloc(size); data = (char *) al.Malloc(size);
if( data == 0 ) if( data == 0 )
msgasserted(10000, "out of memory BufBuilder"); msgasserted(10000, "out of memory BufBuilder");
} else { }
else {
data = 0; data = 0;
} }
l = 0; l = 0;
} }
~BufBuilder() { ~_BufBuilder() { kill(); }
kill();
}
void kill() { void kill() {
if ( data ) { if ( data ) {
free(data); al.Free(data);
data = 0; data = 0;
} }
} }
void reset( int maxSize = 0 ){ void reset() {
l = 0; l = 0;
if ( maxSize && size > maxSize ){ }
free(data); void reset( int maxSize ) {
data = (char*)malloc(maxSize); l = 0;
if ( maxSize && size > maxSize ) {
al.Free(data);
data = (char*)al.Malloc(maxSize);
size = maxSize; size = maxSize;
} }
} }
/* leave room for some stuff later */ /** leave room for some stuff later
@return point to region that was skipped. pointer may change l ater (on realloc), so for immediate use only
*/
char* skip(int n) { return grow(n); } char* skip(int n) { return grow(n); }
/* note this may be deallocated (realloced) if you keep writing. */ /* note this may be deallocated (realloced) if you keep writing. */
char* buf() { return data; } char* buf() { return data; }
const char* buf() const { return data; } const char* buf() const { return data; }
/* assume ownership of the buffer - you must then free() it */ /* assume ownership of the buffer - you must then free() it */
void decouple() { data = 0; } void decouple() { data = 0; }
void appendChar(char j){ void appendUChar(unsigned char j) {
*((unsigned char*)grow(sizeof(unsigned char))) = j;
}
void appendChar(char j) {
*((char*)grow(sizeof(char))) = j; *((char*)grow(sizeof(char))) = j;
} }
void appendNum(char j){ void appendNum(char j) {
*((char*)grow(sizeof(char))) = j; *((char*)grow(sizeof(char))) = j;
} }
void appendNum(short j) { void appendNum(short j) {
*((short*)grow(sizeof(short))) = j; *((short*)grow(sizeof(short))) = j;
} }
void appendNum(int j) { void appendNum(int j) {
*((int*)grow(sizeof(int))) = j; *((int*)grow(sizeof(int))) = j;
} }
void appendNum(unsigned j) { void appendNum(unsigned j) {
*((unsigned*)grow(sizeof(unsigned))) = j; *((unsigned*)grow(sizeof(unsigned))) = j;
skipping to change at line 108 skipping to change at line 167
*((long long*)grow(sizeof(long long))) = j; *((long long*)grow(sizeof(long long))) = j;
} }
void appendNum(unsigned long long j) { void appendNum(unsigned long long j) {
*((unsigned long long*)grow(sizeof(unsigned long long))) = j; *((unsigned long long*)grow(sizeof(unsigned long long))) = j;
} }
void appendBuf(const void *src, size_t len) { void appendBuf(const void *src, size_t len) {
memcpy(grow((int) len), src, len); memcpy(grow((int) len), src, len);
} }
void appendStr(const StringData &str , bool includeEOO = true ) { template<class T>
const int len = str.size() + ( includeEOO ? 1 : 0 ); void appendStruct(const T& s) {
memcpy(grow(len), str.data(), len); appendBuf(&s, sizeof(T));
} }
int len() const { void appendStr(const StringData &str , bool includeEndingNull = tru e ) {
return l; const int len = str.size() + ( includeEndingNull ? 1 : 0 );
memcpy(grow(len), str.data(), len);
} }
void setlen( int newLen ){ /** @return length of current string */
l = newLen; int len() const { return l; }
} void setlen( int newLen ) { l = newLen; }
/** @return size of the buffer */
int getSize() const { return size; }
/* returns the pre-grow write position */ /* returns the pre-grow write position */
inline char* grow(int by) { inline char* grow(int by) {
int oldlen = l; int oldlen = l;
l += by; l += by;
if ( l > size ) { if ( l > size ) {
grow_reallocate(); grow_reallocate();
} }
return data + oldlen; return data + oldlen;
} }
int getSize() const { return size; }
private: private:
/* "slow" portion of 'grow()' */ /* "slow" portion of 'grow()' */
void NOINLINE_DECL grow_reallocate(){ void NOINLINE_DECL grow_reallocate() {
int a = size * 2; int a = size * 2;
if ( a == 0 ) if ( a == 0 )
a = 512; a = 512;
if ( l > a ) if ( l > a )
a = l + 16 * 1024; a = l + 16 * 1024;
if( a > 64 * 1024 * 1024 ) if ( a > BufferMaxSize )
msgasserted(10000, "BufBuilder grow() > 64MB"); msgasserted(13548, "BufBuilder grow() > 64MB");
data = (char *) realloc(data, a); data = (char *) al.Realloc(data, a);
size= a; size= a;
} }
char *data; char *data;
int l; int l;
int size; int size;
friend class StringBuilder; friend class StringBuilder;
}; };
typedef _BufBuilder<TrivialAllocator> BufBuilder;
/** The StackBufBuilder builds smaller datasets on the stack instead of using malloc.
this can be significantly faster for small bufs. However, you ca n not decouple() the
buffer with StackBufBuilder.
While designed to be a variable on the stack, if you were to dynami cally allocate one,
nothing bad would happen. In fact in some circumstances this mig ht make sense, say,
embedded in some other object.
*/
class StackBufBuilder : public _BufBuilder<StackAllocator> {
public:
StackBufBuilder() : _BufBuilder<StackAllocator>(StackAllocator::SZ) { }
void decouple(); // not allowed. not implemented.
};
#if defined(_WIN32) #if defined(_WIN32)
#pragma warning( push )
// warning C4996: 'sprintf': This function or variable may be unsafe. Consi der using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WAR NINGS.
#pragma warning( disable : 4996 ) #pragma warning( disable : 4996 )
#endif #endif
/** stringstream deals with locale so this is a lot faster than std::st ringstream for UTF8 */
class StringBuilder { class StringBuilder {
public: public:
StringBuilder( int initsize=256 ) StringBuilder( int initsize=256 )
: _buf( initsize ){ : _buf( initsize ) {
} }
#define SBNUM(val,maxSize,macro) \ StringBuilder& operator<<( double x ) {
int prev = _buf.l; \ return SBNUM( x , 25 , "%g" );
int z = sprintf( _buf.grow(maxSize) , macro , (val) ); \
assert( z >= 0 ); \
_buf.l = prev + z; \
return *this;
StringBuilder& operator<<( double x ){
SBNUM( x , 25 , "%g" );
} }
StringBuilder& operator<<( int x ){ StringBuilder& operator<<( int x ) {
SBNUM( x , 11 , "%d" ); return SBNUM( x , 11 , "%d" );
} }
StringBuilder& operator<<( unsigned x ){ StringBuilder& operator<<( unsigned x ) {
SBNUM( x , 11 , "%u" ); return SBNUM( x , 11 , "%u" );
} }
StringBuilder& operator<<( long x ){ StringBuilder& operator<<( long x ) {
SBNUM( x , 22 , "%ld" ); return SBNUM( x , 22 , "%ld" );
} }
StringBuilder& operator<<( unsigned long x ){ StringBuilder& operator<<( unsigned long x ) {
SBNUM( x , 22 , "%lu" ); return SBNUM( x , 22 , "%lu" );
} }
StringBuilder& operator<<( long long x ){ StringBuilder& operator<<( long long x ) {
SBNUM( x , 22 , "%lld" ); return SBNUM( x , 22 , "%lld" );
} }
StringBuilder& operator<<( unsigned long long x ){ StringBuilder& operator<<( unsigned long long x ) {
SBNUM( x , 22 , "%llu" ); return SBNUM( x , 22 , "%llu" );
} }
StringBuilder& operator<<( short x ){ StringBuilder& operator<<( short x ) {
SBNUM( x , 8 , "%hd" ); return SBNUM( x , 8 , "%hd" );
} }
StringBuilder& operator<<( char c ){ StringBuilder& operator<<( char c ) {
_buf.grow( 1 )[0] = c; _buf.grow( 1 )[0] = c;
return *this; return *this;
} }
#undef SBNUM
void appendDoubleNice( double x ){ void appendDoubleNice( double x ) {
int prev = _buf.l; int prev = _buf.l;
char * start = _buf.grow( 32 ); char * start = _buf.grow( 32 );
int z = sprintf( start , "%.16g" , x ); int z = sprintf( start , "%.16g" , x );
assert( z >= 0 ); assert( z >= 0 );
_buf.l = prev + z; _buf.l = prev + z;
if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strch r(start, 'N') == 0 ){ if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strch r(start, 'N') == 0 ) {
write( ".0" , 2 ); write( ".0" , 2 );
} }
} }
void write( const char* buf, int len){ void write( const char* buf, int len) { memcpy( _buf.grow( len ) , buf , len ); }
memcpy( _buf.grow( len ) , buf , len );
}
void append( const StringData& str ){ void append( const StringData& str ) { memcpy( _buf.grow( str.size( ) ) , str.data() , str.size() ); }
memcpy( _buf.grow( str.size() ) , str.data() , str.size() );
} StringBuilder& operator<<( const StringData& str ) {
StringBuilder& operator<<( const StringData& str ){
append( str ); append( str );
return *this; return *this;
} }
// access void reset( int maxSize = 0 ) { _buf.reset( maxSize ); }
void reset( int maxSize = 0 ){ std::string str() const { return std::string(_buf.data, _buf.l); }
_buf.reset( maxSize );
}
std::string str(){ int len() const { return _buf.l; }
return std::string(_buf.data, _buf.l);
}
private: private:
BufBuilder _buf; BufBuilder _buf;
// non-copyable, non-assignable
StringBuilder( const StringBuilder& );
StringBuilder& operator=( const StringBuilder& );
template <typename T>
StringBuilder& SBNUM(T val,int maxSize,const char *macro) {
int prev = _buf.l;
int z = sprintf( _buf.grow(maxSize) , macro , (val) );
assert( z >= 0 );
_buf.l = prev + z;
return *this;
}
}; };
#if defined(_WIN32)
#pragma warning( pop )
#endif
} // namespace mongo } // namespace mongo
 End of changes. 42 change blocks. 
73 lines changed or deleted 153 lines changed or added


 checksum.h   checksum.h 
/** @checksum.h */
/* Copyright 2009 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once #pragma once
#include "../pch.h"
namespace mongo {
/** a simple, rather dumb, but very fast checksum. see perftests.cpp f or unit tests. */
struct Checksum {
union {
unsigned char bytes[16];
unsigned long long words[2];
};
namespace mongoutils { // if you change this you must bump dur::CurrentVersion
void gen(const void *buf, unsigned len) {
/** wassert( ((size_t)buf) % 8 == 0 ); // performance warning
* this is a silly temporary implementation unsigned n = len / 8 / 2;
*/ const unsigned long long *p = (const unsigned long long *) buf;
inline int checksum( const char* x , int size ){ unsigned long long a = 0;
int ck = 0; for( unsigned i = 0; i < n; i++ ) {
for ( int i=0; i<size; i++ ) a += (*p ^ i);
ck += ( (int)x[i] * ( i + 1 ) ); p++;
return ck; }
} unsigned long long b = 0;
for( unsigned i = 0; i < n; i++ ) {
b += (*p ^ i);
p++;
}
unsigned long long c = 0;
for( unsigned i = n * 2 * 8; i < len; i++ ) { // 0-7 bytes left
c = (c << 8) | ((const char *)buf)[i];
}
words[0] = a ^ len;
words[1] = b ^ c;
}
bool operator==(const Checksum& rhs) const { return words[0]==rhs.w ords[0] && words[1]==rhs.words[1]; }
bool operator!=(const Checksum& rhs) const { return words[0]!=rhs.w ords[0] || words[1]!=rhs.words[1]; }
};
} }
 End of changes. 4 change blocks. 
28 lines changed or deleted 33 lines changed or added


 chunk.h   chunk.h 
// shard.h // @file chunk.h
/*
A "shard" is a database (replica pair typically) which represents
one partition of the overall database.
*/
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 "../pch.h" #include "../pch.h"
#include "../client/dbclient.h"
#include "../client/model.h"
#include "../bson/util/atomic_int.h" #include "../bson/util/atomic_int.h"
#include "../client/dbclient.h"
#include "../client/distlock.h"
#include "shardkey.h" #include "shardkey.h"
#include "shard.h" #include "shard.h"
#include "config.h"
#include "util.h" #include "util.h"
namespace mongo { namespace mongo {
class DBConfig; class DBConfig;
class Chunk; class Chunk;
class ChunkRange; class ChunkRange;
class ChunkManager; class ChunkManager;
class ChunkRangeMangager; class ChunkRangeMangager;
class ChunkObjUnitTest; class ChunkObjUnitTest;
typedef shared_ptr<Chunk> ChunkPtr; typedef shared_ptr<const Chunk> ChunkPtr;
// key is max for each Chunk or ChunkRange // key is max for each Chunk or ChunkRange
typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap; typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap;
typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap; typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap;
typedef shared_ptr<const ChunkManager> ChunkManagerPtr;
/** /**
config.chunks config.chunks
{ ns : "alleyinsider.fs.chunks" , min : {} , max : {} , server : "lo calhost:30001" } { ns : "alleyinsider.fs.chunks" , min : {} , max : {} , server : "lo calhost:30001" }
x is in a shard iff x is in a shard iff
min <= x < max min <= x < max
*/ */
class Chunk : boost::noncopyable, public boost::enable_shared_from_this <Chunk> { class Chunk : boost::noncopyable {
public: public:
Chunk( const ChunkManager * info , BSONObj from);
Chunk( const ChunkManager * info , const BSONObj& min, const BSONOb j& max, const Shard& shard);
Chunk( ChunkManager * info ); //
Chunk( ChunkManager * info , const BSONObj& min, const BSONObj& max , const Shard& shard); // serialization support
//
const BSONObj& getMin() const { return _min; } void serialize(BSONObjBuilder& to, ShardChunkVersion myLastMod=0);
const BSONObj& getMax() const { return _max; }
void setMin(const BSONObj& o){ //
_min = o; // chunk boundary support
} //
void setMax(const BSONObj& o){
_max = o;
}
string getns() const; const BSONObj& getMin() const { return _min; }
Shard getShard() const { return _shard; } const BSONObj& getMax() const { return _max; }
void setShard( const Shard& shard ); // if min/max key is pos/neg infinity
bool minIsInf() const;
bool maxIsInf() const;
bool contains( const BSONObj& obj ) const; bool contains( const BSONObj& obj ) const;
string toString() const; string genID() const;
static string genID( const string& ns , const BSONObj& min );
friend ostream& operator << (ostream& out, const Chunk& c){ return (out << c.toString()); }
bool operator==(const Chunk& s) const; //
// chunk version support
//
bool operator!=(const Chunk& s) const{ void appendShortVersion( const char * name , BSONObjBuilder& b ) co nst;
return ! ( *this == s );
}
// if min/max key is pos/neg infinity ShardChunkVersion getLastmod() const { return _lastmod; }
bool minIsInf() const; void setLastmod( ShardChunkVersion v ) { _lastmod = v; }
bool maxIsInf() const;
BSONObj pickSplitPoint() const; //
ChunkPtr split(); // split support
//
void pickSplitVector( vector<BSONObj>* splitPoints , int chunkSize ) const; /**
ChunkPtr multiSplit( const vector<BSONObj>& splitPoints ); * if the amount of data written nears the max size of a shard
* then we check the real size, and if its too big, we split
* @return if something was split
*/
bool splitIfShould( long dataWritten ) const;
/** /**
* @return size of shard in bytes * Splits this chunk at a non-specificed split key to be chosen by the mongod holding this chunk.
* talks to mongod to do this *
* @param force if set to true, will split the chunk regardless if the split is really necessary size wise
* if set to false, will only split if the chunk has r eached the currently desired maximum size
* @param res the object containing details about the split executi on
* @return splitPoint if found a key and split successfully, else e mpty BSONObj
*/ */
long getPhysicalSize() const; BSONObj singleSplit( bool force , BSONObj& res ) const;
int countObjects(int maxcount=0) const; /**
* Splits this chunk at the given key (or keys)
*
* @param splitPoints the vector of keys that should be used to div ide this chunk
* @param res the object containing details about the split executi on
* @return if the split was successful
*/
bool multiSplit( const vector<BSONObj>& splitPoints , BSONObj& res ) const;
/** /**
* if the amount of data written nears the max size of a shard * Asks the mongod holding this chunk to find a key that approximat ely divides this chunk in two
* then we check the real size, and if its too big, we split *
* @param medianKey the key that divides this chunk, if there is on e, or empty
*/ */
bool splitIfShould( long dataWritten ); void pickMedianKey( BSONObj& medianKey ) const;
/* /**
* moves either this shard or newShard if it makes sense too * @param splitPoints vector to be filled in
* @return whether or not a shard was moved * @param chunkSize chunk size to target in bytes
* @param maxPoints limits the number of split points that are need ed, zero is max (optional)
* @param maxObjs limits the number of objects in each chunk, zero is as max (optional)
*/ */
bool moveIfShould( ChunkPtr newShard = ChunkPtr() ); void pickSplitVector( vector<BSONObj>& splitPoints , int chunkSize , int maxPoints = 0, int maxObjs = 0) const;
bool moveAndCommit( const Shard& to , string& errmsg ); //
// migration support
//
const char * getNS(){ return "config.chunks"; } /**
void serialize(BSONObjBuilder& to, ShardChunkVersion myLastMod=0); * Issues a migrate request for this chunk
void unserialize(const BSONObj& from); *
string modelServer() const; * @param to shard to move this chunk to
* @param chunSize maximum number of bytes beyond which the migrate should no go trhough
* @param res the object containing details about the migrate execu tion
* @return true if move was successful
*/
bool moveAndCommit( const Shard& to , long long chunkSize , BSONObj & res ) const;
void appendShortVersion( const char * name , BSONObjBuilder& b ); /**
* @return size of shard in bytes
* talks to mongod to do this
*/
long getPhysicalSize() const;
void _markModified(); //
// public constants
//
static string chunkMetadataNS;
static int MaxChunkSize; static int MaxChunkSize;
static int MaxObjectPerChunk;
//
// accessors and helpers
//
string genID() const; string toString() const;
static string genID( const string& ns , const BSONObj& min );
const ChunkManager* getManager() const { return _manager; } friend ostream& operator << (ostream& out, const Chunk& c) { return (out << c.toString()); }
bool operator==(const Chunk& s) const;
bool operator!=(const Chunk& s) const { return ! ( *this == s ); }
bool modified(); string getns() const;
const char * getNS() { return "config.chunks"; }
Shard getShard() const { return _shard; }
const ChunkManager* getManager() const { return _manager; }
ShardChunkVersion getVersionOnConfigServer() const;
private: private:
bool _splitIfShould( long dataWritten );
// main shard info // main shard info
ChunkManager * _manager; const ChunkManager * _manager;
ShardKeyPattern skey() const;
BSONObj _min; BSONObj _min;
BSONObj _max; BSONObj _max;
Shard _shard; Shard _shard;
ShardChunkVersion _lastmod; ShardChunkVersion _lastmod;
bool _modified;
// transient stuff // transient stuff
long _dataWritten; mutable long _dataWritten;
// methods, etc.. // methods, etc..
void _split( BSONObj& middle ); /**
* if sort 1, return lowest key
* if sort -1, return highest key
* will return empty object if have none
*/
BSONObj _getExtremeKey( int sort ) const;
friend class ChunkManager; /** initializes _dataWritten with a random value so that a mongos r estart wouldn't cause delay in splitting */
friend class ShardObjUnitTest; static long mkDataWritten();
ShardKeyPattern skey() const;
}; };
class ChunkRange{ class ChunkRange {
public: public:
const ChunkManager* getManager() const{ return _manager; } const ChunkManager* getManager() const { return _manager; }
Shard getShard() const{ return _shard; } Shard getShard() const { return _shard; }
const BSONObj& getMin() const { return _min; } const BSONObj& getMin() const { return _min; }
const BSONObj& getMax() const { return _max; } const BSONObj& getMax() const { return _max; }
// clones of Chunk methods // clones of Chunk methods
bool contains(const BSONObj& obj) const; bool contains(const BSONObj& obj) const;
ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it erator end) ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it erator end)
: _manager(begin->second->getManager()) : _manager(begin->second->getManager())
, _shard(begin->second->getShard()) , _shard(begin->second->getShard())
, _min(begin->second->getMin()) , _min(begin->second->getMin())
, _max(prior(end)->second->getMax()) , _max(prior(end)->second->getMax()) {
{
assert( begin != end ); assert( begin != end );
DEV while (begin != end){ DEV while (begin != end) {
assert(begin->second->getManager() == _manager); assert(begin->second->getManager() == _manager);
assert(begin->second->getShard() == _shard); assert(begin->second->getShard() == _shard);
++begin; ++begin;
} }
} }
// Merge min and max (must be adjacent ranges) // Merge min and max (must be adjacent ranges)
ChunkRange(const ChunkRange& min, const ChunkRange& max) ChunkRange(const ChunkRange& min, const ChunkRange& max)
: _manager(min.getManager()) : _manager(min.getManager())
, _shard(min.getShard()) , _shard(min.getShard())
, _min(min.getMin()) , _min(min.getMin())
, _max(max.getMax()) , _max(max.getMax()) {
{
assert(min.getShard() == max.getShard()); assert(min.getShard() == max.getShard());
assert(min.getManager() == max.getManager()); assert(min.getManager() == max.getManager());
assert(min.getMax() == max.getMin()); assert(min.getMax() == max.getMin());
} }
friend ostream& operator<<(ostream& out, const ChunkRange& cr){ friend ostream& operator<<(ostream& out, const ChunkRange& cr) {
return (out << "ChunkRange(min=" << cr._min << ", max=" << cr._ max << ", shard=" << cr._shard <<")"); return (out << "ChunkRange(min=" << cr._min << ", max=" << cr._ max << ", shard=" << cr._shard <<")");
} }
private: private:
const ChunkManager* _manager; const ChunkManager* _manager;
const Shard _shard; const Shard _shard;
const BSONObj _min; const BSONObj _min;
const BSONObj _max; const BSONObj _max;
}; };
class ChunkRangeManager { class ChunkRangeManager {
public: public:
const ChunkRangeMap& ranges() const { return _ranges; } const ChunkRangeMap& ranges() const { return _ranges; }
void clear() { _ranges.clear(); } void clear() { _ranges.clear(); }
void reloadAll(const ChunkMap& chunks); void reloadAll(const ChunkMap& chunks);
void reloadRange(const ChunkMap& chunks, const BSONObj& min, const BSONObj& max);
// Slow operation -- wrap with DEV // Slow operation -- wrap with DEV
void assertValid() const; void assertValid() const;
ChunkRangeMap::const_iterator upper_bound(const BSONObj& o) const { return _ranges.upper_bound(o); } ChunkRangeMap::const_iterator upper_bound(const BSONObj& o) const { return _ranges.upper_bound(o); }
ChunkRangeMap::const_iterator lower_bound(const BSONObj& o) const { return _ranges.lower_bound(o); } ChunkRangeMap::const_iterator lower_bound(const BSONObj& o) const { return _ranges.lower_bound(o); }
private: private:
// assumes nothing in this range exists in _ranges // assumes nothing in this range exists in _ranges
void _insertRange(ChunkMap::const_iterator begin, const ChunkMap::c onst_iterator end); void _insertRange(ChunkMap::const_iterator begin, const ChunkMap::c onst_iterator end);
skipping to change at line 248 skipping to change at line 284
}; };
/* config.sharding /* config.sharding
{ ns: 'alleyinsider.fs.chunks' , { ns: 'alleyinsider.fs.chunks' ,
key: { ts : 1 } , key: { ts : 1 } ,
shards: [ { min: 1, max: 100, server: a } , { min: 101, max: 200 , server : b } ] shards: [ { min: 1, max: 100, server: a } , { min: 101, max: 200 , server : b } ]
} }
*/ */
class ChunkManager { class ChunkManager {
public: public:
typedef map<Shard,ShardChunkVersion> ShardVersionMap;
ChunkManager( DBConfig * config , string ns , ShardKeyPattern patte rn , bool unique ); ChunkManager( string ns , ShardKeyPattern pattern , bool unique );
virtual ~ChunkManager();
string getns() const { return _ns; } string getns() const { return _ns; }
int numChunks() const { rwlock lk( _lock , false ); return _chunkMa p.size(); } int numChunks() const { return _chunkMap.size(); }
bool hasShardKey( const BSONObj& obj ); bool hasShardKey( const BSONObj& obj ) const;
ChunkPtr findChunk( const BSONObj& obj , bool retry = false ); void createFirstChunk( const Shard& shard ) const; // only call fro m DBConfig::shardCollection
ChunkPtr findChunk( const BSONObj& obj ) const;
ChunkPtr findChunkOnServer( const Shard& shard ) const; ChunkPtr findChunkOnServer( const Shard& shard ) const;
ShardKeyPattern& getShardKey(){ return _key; }
const ShardKeyPattern& getShardKey() const { return _key; } const ShardKeyPattern& getShardKey() const { return _key; }
bool isUnique(){ return _unique; } bool isUnique() const { return _unique; }
void maybeChunkCollection();
void getShardsForQuery( set<Shard>& shards , const BSONObj& query ) ; void maybeChunkCollection() const;
void getAllShards( set<Shard>& all );
void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons t BSONObj& max); // [min, max)
void save( bool major ); void getShardsForQuery( set<Shard>& shards , const BSONObj& query ) const;
void getAllShards( set<Shard>& all ) const;
void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons t BSONObj& max) const; // [min, max)
string toString() const; string toString() const;
ShardChunkVersion getVersion( const Shard& shard ) const; ShardChunkVersion getVersion( const Shard& shard ) const;
ShardChunkVersion getVersion() const; ShardChunkVersion getVersion() const;
/** /**
* actually does a query on the server
* doesn't look at any local data
*/
ShardChunkVersion getVersionOnConfigServer() const;
/**
* this is just an increasing number of how many ChunkManagers we h ave so we know if something has been updated * this is just an increasing number of how many ChunkManagers we h ave so we know if something has been updated
*/ */
unsigned long long getSequenceNumber(){ unsigned long long getSequenceNumber() const { return _sequenceNumb er; }
return _sequenceNumber;
}
void getInfo( BSONObjBuilder& b ){ void getInfo( BSONObjBuilder& b ) const {
b.append( "key" , _key.key() ); b.append( "key" , _key.key() );
b.appendBool( "unique" , _unique ); b.appendBool( "unique" , _unique );
} }
/** /**
* @param me - so i don't get deleted before i'm done * @param me - so i don't get deleted before i'm done
*/ */
void drop( ChunkManagerPtr me ); void drop( ChunkManagerPtr me ) const;
void _printChunks() const; void _printChunks() const;
private: int getCurrentDesiredChunkSize() const;
void _reload(); private:
void _reload_inlock(); ChunkManagerPtr reload(bool force=true) const; // doesn't modify se lf!
void _load();
void save_inlock( bool major ); // helpers for constructor
ShardChunkVersion getVersion_inlock() const; void _load(ChunkMap& chunks, set<Shard>& shards, ShardVersionMap& s hardVersions);
void ensureIndex_inlock(); static bool _isValid(const ChunkMap& chunks);
DBConfig * _config; // All members should be const for thread-safety
string _ns; const string _ns;
ShardKeyPattern _key; const ShardKeyPattern _key;
bool _unique; const bool _unique;
map<string,unsigned long long> _maxMarkers; const ChunkMap _chunkMap;
const ChunkRangeManager _chunkRanges;
ChunkMap _chunkMap; const set<Shard> _shards;
ChunkRangeManager _chunkRanges;
set<Shard> _shards; const ShardVersionMap _shardVersions; // max version per shard
unsigned long long _sequenceNumber; ShardChunkVersion _version; // max version of any chunk
mutable RWLock _lock; mutable mutex _mutex; // only used with _nsLock
mutable DistributedLock _nsLock;
// This should only be called from Chunk after it has been migrated const unsigned long long _sequenceNumber;
void _migrationNotification(Chunk* c);
friend class Chunk; friend class Chunk;
friend class ChunkRangeManager; // only needed for CRM::assertValid () friend class ChunkRangeManager; // only needed for CRM::assertValid ()
static AtomicUInt NextSequenceNumber; static AtomicUInt NextSequenceNumber;
bool _isValid() const;
}; };
// like BSONObjCmp. for use as an STL comparison functor // like BSONObjCmp. for use as an STL comparison functor
// key-order in "order" argument must match key-order in shardkey // key-order in "order" argument must match key-order in shardkey
class ChunkCmp { class ChunkCmp {
public: public:
ChunkCmp( const BSONObj &order = BSONObj() ) : _cmp( order ) {} ChunkCmp( const BSONObj &order = BSONObj() ) : _cmp( order ) {}
bool operator()( const Chunk &l, const Chunk &r ) const { bool operator()( const Chunk &l, const Chunk &r ) const {
return _cmp(l.getMin(), r.getMin()); return _cmp(l.getMin(), r.getMin());
} }
skipping to change at line 372 skipping to change at line 395
struct chunk_lock { struct chunk_lock {
chunk_lock( const Chunk* c ){ chunk_lock( const Chunk* c ){
} }
Chunk _c; Chunk _c;
}; };
*/ */
inline string Chunk::genID() const { return genID(_manager->getns(), _m in); } inline string Chunk::genID() const { return genID(_manager->getns(), _m in); }
bool setShardVersion( DBClientBase & conn , const string& ns , ShardChu nkVersion version , bool authoritative , BSONObj& result );
} // namespace mongo } // namespace mongo
 End of changes. 73 change blocks. 
126 lines changed or deleted 151 lines changed or added


 client.h   client.h 
skipping to change at line 29 skipping to change at line 29
/* Client represents a connection to the database (the server-side) and cor responds /* Client represents a connection to the database (the server-side) and cor responds
to an open socket (or logical connection if pooling on sockets) from a c lient. to an open socket (or logical connection if pooling on sockets) from a c lient.
todo: switch to asio...this will fit nicely with that. todo: switch to asio...this will fit nicely with that.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "security.h" #include "security.h"
#include "namespace.h" #include "namespace-inl.h"
#include "lasterror.h" #include "lasterror.h"
#include "stats/top.h" #include "stats/top.h"
namespace mongo { namespace mongo {
extern class ReplSet *theReplSet; extern class ReplSet *theReplSet;
class AuthenticationInfo; class AuthenticationInfo;
class Database; class Database;
class CurOp; class CurOp;
class Command; class Command;
class Client; class Client;
class MessagingPort; class AbstractMessagingPort;
extern boost::thread_specific_ptr<Client> currentClient; extern boost::thread_specific_ptr<Client> currentClient;
typedef long long ConnectionId;
/** the database's concept of an outside "client" */
class Client : boost::noncopyable { class Client : boost::noncopyable {
public: public:
class Context;
static mongo::mutex clientsMutex;
static set<Client*> clients; // always be in clientsMutex when mani pulating this
static int recommendedYieldMicros( int * writers = 0 , int * reader s = 0 );
static int getActiveClientCount( int& writers , int& readers );
static Client *syncThread; static Client *syncThread;
/* each thread which does db operations has a Client object in TLS.
call this when your thread starts.
*/
static Client& initThread(const char *desc, AbstractMessagingPort * mp = 0);
~Client();
/*
this has to be called as the client goes away, but before thread termination
@return true if anything was done
*/
bool shutdown();
/** set so isSyncThread() works */
void iAmSyncThread() { void iAmSyncThread() {
wassert( syncThread == 0 ); wassert( syncThread == 0 );
syncThread = this; syncThread = this;
} }
bool isSyncThread() const { return this == syncThread; } // true if this client is the replication secondary pull thread /** @return true if this client is the replication secondary pull t hread. not used much, is used in create index sync code. */
bool isSyncThread() const { return this == syncThread; }
static mongo::mutex clientsMutex; string clientAddress(bool includePort=false) const;
static set<Client*> clients; // always be in clientsMutex when mani pulating this const AuthenticationInfo * getAuthenticationInfo() const { return & _ai; }
static int recommendedYieldMicros( int * writers = 0 , int * reader s = 0 ); AuthenticationInfo * getAuthenticationInfo() { return &_ai; }
bool isAdmin() { return _ai.isAuthorized( "admin" ); }
CurOp* curop() const { return _curOp; }
Context* getContext() const { return _context; }
Database* database() const { return _context ? _context->db() : 0; }
const char *ns() const { return _context->ns(); }
const char *desc() const { return _desc; }
void setLastOp( ReplTime op ) { _lastOp = op; }
ReplTime getLastOp() const { return _lastOp; }
/* report what the last operation was. used by getlasterror */
void appendLastOp( BSONObjBuilder& b ) const;
bool isGod() const { return _god; } /* this is for map/reduce write s */
string toString() const;
void gotHandshake( const BSONObj& o );
BSONObj getRemoteID() const { return _remoteId; }
BSONObj getHandshake() const { return _handshake; }
AbstractMessagingPort * port() const { return _mp; }
ConnectionId getConnectionId() const { return _connectionId; }
private:
ConnectionId _connectionId; // > 0 for things "conn", 0 otherwise
string _threadId; // "" on non support systems
CurOp * _curOp;
Context * _context;
bool _shutdown;
const char *_desc;
bool _god;
AuthenticationInfo _ai;
ReplTime _lastOp;
BSONObj _handshake;
BSONObj _remoteId;
AbstractMessagingPort * const _mp;
Client(const char *desc, AbstractMessagingPort *p = 0);
friend class CurOp;
public:
/* set _god=true temporarily, safely */ /* set _god=true temporarily, safely */
class GodScope { class GodScope {
bool _prev; bool _prev;
public: public:
GodScope(); GodScope();
~GodScope(); ~GodScope();
}; };
/* Set database we want to use, then, restores when we finish (are out of scope) /* Set database we want to use, then, restores when we finish (are out of scope)
Note this is also helpful if an exception happens as the state i f fixed up. Note this is also helpful if an exception happens as the state i f fixed up.
*/ */
class Context : boost::noncopyable{ class Context : boost::noncopyable {
Client * _client; public:
Context * _oldContext;
string _path;
mongolock * _lock;
bool _justCreated;
string _ns;
Database * _db;
/** /**
* at this point _client, _oldContext and _ns have to be set * this is the main constructor
* _db should not have been touched * use this unless there is a good reason not to
* this will set _db and create if needed
* will also set _client->_context to this
*/ */
void _finishInit( bool doauth=true); Context(const string& ns, string path=dbpath, mongolock * lock = 0 , bool doauth=true );
void _auth( int lockState = dbMutex.getState() );
public:
Context(const string& ns, string path=dbpath, mongolock * lock = 0 , bool doauth=true )
: _client( currentClient.get() ) , _oldContext( _client->_c ontext ) ,
_path( path ) , _lock( lock ) ,
_ns( ns ), _db(0){
_finishInit( doauth );
}
/* this version saves the context but doesn't yet set the new o ne: */ /* this version saves the context but doesn't yet set the new o ne: */
Context();
Context()
: _client( currentClient.get() ) , _oldContext( _client->_c ontext ),
_path( dbpath ) , _lock(0) , _justCreated(false), _db(0){
_client->_context = this;
clear();
}
/** /**
* if you are doing this after allowing a write there could be a race condition * if you are doing this after allowing a write there could be a race condition
* if someone closes that db. this checks that the DB is still valid * if someone closes that db. this checks that the DB is still valid
*/ */
Context( string ns , Database * db, bool doauth=true ); Context( string ns , Database * db, bool doauth=true );
~Context(); ~Context();
Client* getClient() const { return _client; } Client* getClient() const { return _client; }
Database* db() const { return _db; } Database* db() const { return _db; }
const char * ns() const { return _ns.c_str(); } const char * ns() const { return _ns.c_str(); }
bool justCreated() const { return _justCreated; }
bool equals( const string& ns , const string& path=dbpath ) con st {
return _ns == ns && _path == path;
}
bool inDB( const string& db , const string& path=dbpath ) const { /** @return if the db was created by this Context */
if ( _path != path ) bool justCreated() const { return _justCreated; }
return false;
if ( db == _ns )
return true;
string::size_type idx = _ns.find( db ); bool equals( const string& ns , const string& path=dbpath ) con st { return _ns == ns && _path == path; }
if ( idx != 0 )
return false;
return _ns[db.size()] == '.'; /**
} * @return true iff the current Context is using db/path
*/
bool inDB( const string& db , const string& path=dbpath ) const ;
void clear(){ void clear() { _ns = ""; _db = 0; }
_ns = "";
_db = 0;
}
/** /**
* call before unlocking, so clear any non-thread safe state * call before unlocking, so clear any non-thread safe state
*/ */
void unlocked(){ void unlocked() { _db = 0; }
_db = 0;
}
/** /**
* call after going back into the lock, will re-establish non-t hread safe stuff * call after going back into the lock, will re-establish non-t hread safe stuff
*/ */
void relocked(){ void relocked() { _finishInit(); }
_finishInit();
}
friend class CurOp; friend class CurOp;
}; // class Client::Context
private:
void _dropns( const string& ns );
CurOp * _curOp; private:
Context * _context; /**
bool _shutdown; * at this point _client, _oldContext and _ns have to be set
set<string> _tempCollections; * _db should not have been touched
const char *_desc; * this will set _db and create if needed
bool _god; * will also set _client->_context to this
AuthenticationInfo _ai; */
ReplTime _lastOp; void _finishInit( bool doauth=true);
BSONObj _handshake;
BSONObj _remoteId;
public:
MessagingPort * const _mp;
string clientAddress() const;
AuthenticationInfo * getAuthenticationInfo(){ return &_ai; }
bool isAdmin() { return _ai.isAuthorized( "admin" ); }
CurOp* curop() { return _curOp; }
Context* getContext(){ return _context; }
Database* database() { return _context ? _context->db() : 0; }
const char *ns() const { return _context->ns(); }
const char *desc() const { return _desc; }
Client(const char *desc, MessagingPort *p = 0);
~Client();
void addTempCollection( const string& ns );
void _invalidateDB(const string& db);
static void invalidateDB(const string& db);
static void invalidateNS( const string& ns );
void setLastOp( ReplTime op ) { _lastOp = op; }
ReplTime getLastOp() const { return _lastOp; }
/* report what the last operation was. used by getlasterror */ void _auth( int lockState = dbMutex.getState() );
void appendLastOp( BSONObjBuilder& b ) {
if( theReplSet ) {
b.append("lastOp" , (long long) _lastOp);
}
else {
OpTime lo(_lastOp);
if ( ! lo.isNull() )
b.appendTimestamp( "lastOp" , lo.asDate() );
}
}
/* each thread which does db operations has a Client object in TLS. Client * _client;
call this when your thread starts. Context * _oldContext;
*/
static Client& initThread(const char *desc, MessagingPort *mp = 0);
/* string _path;
this has to be called as the client goes away, but before thread termination mongolock * _lock;
@return true if anything was done bool _justCreated;
*/
bool shutdown();
/* this is for map/reduce writes */ string _ns;
bool isGod() const { return _god; } Database * _db;
friend class CurOp; }; // class Client::Context
string toString() const;
void gotHandshake( const BSONObj& o );
BSONObj getRemoteID() const { return _remoteId; }
BSONObj getHandshake() const { return _handshake; }
}; };
/** get the Client object for this thread. */ /** get the Client object for this thread. */
inline Client& cc() { inline Client& cc() {
Client * c = currentClient.get(); Client * c = currentClient.get();
assert( c ); assert( c );
return *c; return *c;
} }
/* each thread which does db operations has a Client object in TLS. inline Client::GodScope::GodScope() {
call this when your thread starts.
*/
inline Client& Client::initThread(const char *desc, MessagingPort *mp) {
setThreadName(desc);
assert( currentClient.get() == 0 );
Client *c = new Client(desc, mp);
currentClient.reset(c);
mongo::lastError.initThread();
return *c;
}
inline Client::GodScope::GodScope(){
_prev = cc()._god; _prev = cc()._god;
cc()._god = true; cc()._god = true;
} }
inline Client::GodScope::~GodScope(){ inline Client::GodScope::~GodScope() { cc()._god = _prev; }
cc()._god = _prev;
}
/* this unlocks, does NOT upgrade. that works for our current usage */ /* this unlocks, does NOT upgrade. that works for our current usage */
inline void mongolock::releaseAndWriteLock() { inline void mongolock::releaseAndWriteLock() {
if( !_writelock ) { if( !_writelock ) {
#if BOOST_VERSION >= 103500 #if BOOST_VERSION >= 103500
int s = dbMutex.getState(); int s = dbMutex.getState();
if( s != -1 ) { if( s != -1 ) {
log() << "error: releaseAndWriteLock() s == " << s << endl; log() << "error: releaseAndWriteLock() s == " << s << endl;
msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai led, probably recursive" ); msgasserted( 12600, "releaseAndWriteLock: unlock_shared fai led, probably recursive" );
} }
#endif #endif
 End of changes. 29 change blocks. 
146 lines changed or deleted 106 lines changed or added


 clientcursor.h   clientcursor.h 
skipping to change at line 30 skipping to change at line 30
ClientCursor is a wrapper that represents a cursorid from our database ClientCursor is a wrapper that represents a cursorid from our database
application's perspective. application's perspective.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "cursor.h" #include "cursor.h"
#include "jsobj.h" #include "jsobj.h"
#include "../util/message.h" #include "../util/net/message.h"
#include "../util/net/listen.h"
#include "../util/background.h" #include "../util/background.h"
#include "diskloc.h" #include "diskloc.h"
#include "dbhelpers.h" #include "dbhelpers.h"
#include "matcher.h" #include "matcher.h"
#include "../client/dbclient.h" #include "../client/dbclient.h"
#include "projection.h"
#include "s/d_chunk_manager.h"
namespace mongo { namespace mongo {
typedef long long CursorId; /* passed to the client so it can send back on getMore */ typedef long long CursorId; /* passed to the client so it can send back on getMore */
class Cursor; /* internal server cursor base class */ class Cursor; /* internal server cursor base class */
class ClientCursor; class ClientCursor;
class ParsedQuery; class ParsedQuery;
struct ByLocKey {
ByLocKey( const DiskLoc & l , const CursorId& i ) : loc(l), id(i) { }
static ByLocKey min( const DiskLoc& l ) { return ByLocKey( l , nume ric_limits<long long>::min() ); }
static ByLocKey max( const DiskLoc& l ) { return ByLocKey( l , nume ric_limits<long long>::max() ); }
bool operator<( const ByLocKey &other ) const {
int x = loc.compare( other.loc );
if ( x )
return x < 0;
return id < other.id;
}
DiskLoc loc;
CursorId id;
};
/* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps. /* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps.
* ERH: 9/2010 this may not work since some drivers send getMore over a different connection
*/ */
typedef map<CursorId, ClientCursor*> CCById; typedef map<CursorId, ClientCursor*> CCById;
typedef map<ByLocKey, 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)
unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time
/* 0 = normal
1 = no timeout allowed
100 = in use (pinned) -- see Pointer class
*/
unsigned _pinValue;
bool _doingDeletes;
ElapsedTracker _yieldSometimesTracker;
static CCById clientCursorsById;
static long long numberTimedOut;
static boost::recursive_mutex ccmutex; // must use this for all s tatics above!
static CursorId allocCursorId_inlock();
public: public:
static void assertNoCursors(); static void assertNoCursors();
/* use this to assure we don't in the background time out cursor wh ile it is under use. /* use this to assure we don't in the background time out cursor wh ile it is under use.
if you are using noTimeout() already, there is no risk anyway. if you are using noTimeout() already, there is no risk anyway.
Further, this mechanism guards against two getMore requests on t he same cursor executing Further, this mechanism guards against two getMore requests on t he same cursor executing
at the same time - which might be bad. That should never happen , but if a client driver at the same time - which might be bad. That should never happen , but if a client driver
had a bug, it could (or perhaps some sort of attack situation). had a bug, it could (or perhaps some sort of attack situation).
*/ */
class Pointer : boost::noncopyable { class Pointer : boost::noncopyable {
ClientCursor *_c; ClientCursor *_c;
public: public:
ClientCursor * c() { return _c; } ClientCursor * c() { return _c; }
void release() { void release() {
if( _c ) { if( _c ) {
assert( _c->_pinValue >= 100 ); assert( _c->_pinValue >= 100 );
_c->_pinValue -= 100; _c->_pinValue -= 100;
_c = 0; _c = 0;
} }
} }
/**
* call this if during a yield, the cursor got deleted
* if so, we don't want to use the point address
*/
void deleted() {
_c = 0;
}
~Pointer() { release(); } ~Pointer() { release(); }
Pointer(long long cursorid) { Pointer(long long cursorid) {
recursive_scoped_lock lock(ccmutex); recursive_scoped_lock lock(ccmutex);
_c = ClientCursor::find_inlock(cursorid, true); _c = ClientCursor::find_inlock(cursorid, true);
if( _c ) { if( _c ) {
if( _c->_pinValue >= 100 ) { if( _c->_pinValue >= 100 ) {
_c = 0; _c = 0;
uasserted(12051, "clientcursor already in use? driv er problem?"); uasserted(12051, "clientcursor already in use? driv er problem?");
} }
_c->_pinValue += 100; _c->_pinValue += 100;
skipping to change at line 118 skipping to change at line 132
CleanupPointer() : _c( 0 ), _id( -1 ) {} CleanupPointer() : _c( 0 ), _id( -1 ) {}
void reset( ClientCursor *c = 0 ) { void reset( ClientCursor *c = 0 ) {
if ( c == _c ) if ( c == _c )
return; return;
if ( _c ) { if ( _c ) {
// be careful in case cursor was deleted by someone els e // be careful in case cursor was deleted by someone els e
ClientCursor::erase( _id ); ClientCursor::erase( _id );
} }
if ( c ) { if ( c ) {
_c = c; _c = c;
_id = c->cursorid; _id = c->_cursorid;
} else { }
else {
_c = 0; _c = 0;
_id = -1; _id = -1;
} }
} }
~CleanupPointer() { ~CleanupPointer() {
DESTRUCTOR_GUARD ( reset(); ); DESTRUCTOR_GUARD ( reset(); );
} }
operator bool() { return _c; } operator bool() { return _c; }
ClientCursor * operator-> () { return _c; } ClientCursor * operator-> () { return _c; }
private: private:
ClientCursor *_c; ClientCursor *_c;
CursorId _id; CursorId _id;
}; };
/*const*/ CursorId cursorid; ClientCursor(int queryOptions, const shared_ptr<Cursor>& c, const s tring& ns, BSONObj query = BSONObj() );
const string ns;
const shared_ptr<Cursor> c;
int pos; // # objects into the cursor so far
const BSONObj query; // used for logging diags only; opt ional in constructor
const int _queryOptions; // see enum QueryOptions dbclient.h
OpTime _slaveReadTill;
Database * const _db;
ClientCursor(int queryOptions, shared_ptr<Cursor>& _c, const string & _ns, BSONObj _query = BSONObj()) :
_idleAgeMillis(0), _pinValue(0),
_doingDeletes(false), _yieldSometimesTracker(128,10),
ns(_ns), c(_c),
pos(0), query(_query),
_queryOptions(queryOptions),
_db( cc().database() )
{
assert( _db );
assert( str::startsWith(_ns, _db->name) );
if( queryOptions & QueryOption_NoCursorTimeout )
noTimeout();
recursive_scoped_lock lock(ccmutex);
cursorid = allocCursorId_inlock();
clientCursorsById.insert( make_pair(cursorid, this) );
}
~ClientCursor(); ~ClientCursor();
DiskLoc lastLoc() const { return _lastLoc; } // *************** basic accessors *******************
shared_ptr< ParsedQuery > pq; CursorId cursorid() const { return _cursorid; }
shared_ptr< FieldMatcher > fields; // which fields query wants retu rned string ns() const { return _ns; }
Message originalMessage; // this is effectively an auto ptr for dat a the matcher points to Database * db() const { return _db; }
const BSONObj& query() const { return _query; }
int queryOptions() const { return _queryOptions; }
/* Get rid of cursors for namespaces that begin with nsprefix. DiskLoc lastLoc() const { return _lastLoc; }
/* Get rid of cursors for namespaces 'ns'. When dropping a db, ns i s "dbname."
Used by drop, dropIndexes, dropDatabase. Used by drop, dropIndexes, dropDatabase.
*/ */
static void invalidate(const char *nsPrefix); static void invalidate(const char *ns);
/** /**
* @param microsToSleep -1 : ask client * @param microsToSleep -1 : ask client
* >=0 : sleep for that amount * >=0 : sleep for that amount
* @param recordToLoad after yielding lock, load this record with o nly mmutex
* do a dbtemprelease * do a dbtemprelease
* note: caller should check matcher.docMatcher().atomic() first an d not yield if atomic - * note: caller should check matcher.docMatcher().atomic() first an d not yield if atomic -
* we don't do herein as this->matcher (above) is only initia lized for true queries/getmore. * we don't do herein as this->matcher (above) is only initia lized for true queries/getmore.
* (ie not set for remote/update) * (ie not set for remote/update)
* @return if the cursor is still valid. * @return if the cursor is still valid.
* if false is returned, then this ClientCursor should be c onsidered deleted - * if false is returned, then this ClientCursor should be c onsidered deleted -
* in fact, the whole database could be gone. * in fact, the whole database could be gone.
*/ */
bool yield( int microsToSleep = -1 ); bool yield( int microsToSleep = -1 , Record * recordToLoad = 0 );
enum RecordNeeds {
DontNeed = -1 , MaybeCovered = 0 , WillNeed = 100
};
/** /**
* @param needRecord whether or not the next record has to be read from disk for sure
* if this is true, will yield of next record isn 't in memory
* @param yielded true if a yield occurred, and potentially if a yi eld did not occur
* @return same as yield() * @return same as yield()
*/ */
bool yieldSometimes(); bool yieldSometimes( RecordNeeds need, bool *yielded = 0 );
static int yieldSuggest(); static int yieldSuggest();
static void staticYield( int micros ); static void staticYield( int micros , const StringData& ns , Record * rec );
struct YieldData { CursorId _id; bool _doingDeletes; }; struct YieldData { CursorId _id; bool _doingDeletes; };
bool prepareToYield( YieldData &data ); bool prepareToYield( YieldData &data );
static bool recoverFromYield( const YieldData &data ); static bool recoverFromYield( const YieldData &data );
struct YieldLock : boost::noncopyable { struct YieldLock : boost::noncopyable {
explicit YieldLock( ptr<ClientCursor> cc ) explicit YieldLock( ptr<ClientCursor> cc )
: _canYield(cc->c->supportYields()) { : _canYield(cc->_c->supportYields()) {
if ( _canYield ){ if ( _canYield ) {
cc->prepareToYield( _data ); cc->prepareToYield( _data );
_unlock.reset(new dbtempreleasecond()); _unlock.reset(new dbtempreleasecond());
} }
} }
~YieldLock(){ ~YieldLock() {
if ( _unlock ){ if ( _unlock ) {
log( LL_WARNING ) << "ClientCursor::YieldLock not close d properly" << endl; log( LL_WARNING ) << "ClientCursor::YieldLock not close d properly" << endl;
relock(); relock();
} }
} }
bool stillOk(){ bool stillOk() {
if ( ! _canYield ) if ( ! _canYield )
return true; return true;
relock(); relock();
return ClientCursor::recoverFromYield( _data ); return ClientCursor::recoverFromYield( _data );
} }
void relock(){ void relock() {
_unlock.reset(); _unlock.reset();
} }
private: private:
const bool _canYield; const bool _canYield;
YieldData _data; YieldData _data;
scoped_ptr<dbtempreleasecond> _unlock; scoped_ptr<dbtempreleasecond> _unlock;
}; };
// --- some pass through helpers for Cursor --- // --- some pass through helpers for Cursor ---
BSONObj indexKeyPattern() { return c->indexKeyPattern(); } Cursor* c() const { return _c.get(); }
bool ok() { return c->ok(); } int pos() const { return _pos; }
bool advance(){ return c->advance(); }
BSONObj current() { return c->current(); }
bool currentMatches(){ void incPos( int n ) { _pos += n; } // TODO: this is bad
if ( ! c->matcher() ) void setPos( int n ) { _pos = n; } // TODO : this is bad too
BSONObj indexKeyPattern() { return _c->indexKeyPattern(); }
bool modifiedKeys() const { return _c->modifiedKeys(); }
bool isMultiKey() const { return _c->isMultiKey(); }
bool ok() { return _c->ok(); }
bool advance() { return _c->advance(); }
BSONObj current() { return _c->current(); }
DiskLoc currLoc() { return _c->currLoc(); }
BSONObj currKey() const { return _c->currKey(); }
/**
* same as BSONObj::getFieldsDotted
* if it can be retrieved from key, it is
* @param holder keeps the currKey in scope by keeping a reference to it here. generally you'll want
* holder and ret to destruct about the same time.
* @return if this was retrieved from key
*/
bool getFieldsDotted( const string& name, BSONElementSet &ret, BSON Obj& holder );
/**
* same as BSONObj::getFieldDotted
* if it can be retrieved from key, it is
* @return if this was retrieved from key
*/
BSONElement getFieldDotted( const string& name , BSONObj& holder , bool * fromKey = 0 ) ;
/** extract items from object which match a pattern object.
* e.g., if pattern is { x : 1, y : 1 }, builds an object with
* x and y elements of this object, if they are present.
* returns elements with original field names
* NOTE: copied from BSONObj::extractFields
*/
BSONObj extractFields(const BSONObj &pattern , bool fillWithNull = false) ;
bool currentIsDup() { return _c->getsetdup( _c->currLoc() ); }
bool currentMatches() {
if ( ! _c->matcher() )
return true; return true;
return c->matcher()->matchesCurrent( c.get() ); return _c->matcher()->matchesCurrent( _c.get() );
} }
void setChunkManager( ShardChunkManagerPtr manager ){ _chunkManager = manager; }
ShardChunkManagerPtr getChunkManager(){ return _chunkManager; }
private: private:
void setLastLoc_inlock(DiskLoc); void setLastLoc_inlock(DiskLoc);
static ClientCursor* find_inlock(CursorId id, bool warn = true) { static ClientCursor* find_inlock(CursorId id, bool warn = true) {
CCById::iterator it = clientCursorsById.find(id); CCById::iterator it = clientCursorsById.find(id);
if ( it == clientCursorsById.end() ) { if ( it == clientCursorsById.end() ) {
if ( warn ) if ( warn )
OCCASIONALLY out() << "ClientCursor::find(): cursor not found in map " << id << " (ok after a drop)\n"; OCCASIONALLY out() << "ClientCursor::find(): cursor not found in map " << id << " (ok after a drop)\n";
return 0; return 0;
} }
return it->second; return it->second;
} }
public: public:
static ClientCursor* find(CursorId id, bool warn = true) { static ClientCursor* find(CursorId id, bool warn = true) {
recursive_scoped_lock lock(ccmutex); recursive_scoped_lock lock(ccmutex);
ClientCursor *c = find_inlock(id, warn); ClientCursor *c = find_inlock(id, warn);
// if this asserts, your code was not thread safe - you either need to set no timeout // if this asserts, your code was not thread safe - you either need to set no timeout
// for the cursor or keep a ClientCursor::Pointer in scope for it. // for the cursor or keep a ClientCursor::Pointer in scope for it.
massert( 12521, "internal error: use of an unlocked ClientCurso r", c == 0 || c->_pinValue ); massert( 12521, "internal error: use of an unlocked ClientCurso r", c == 0 || c->_pinValue );
return c; return c;
} }
static bool erase(CursorId id) { static bool erase(CursorId id) {
recursive_scoped_lock lock(ccmutex); recursive_scoped_lock lock(ccmutex);
ClientCursor *cc = find_inlock(id); ClientCursor *cc = find_inlock(id);
if ( cc ) { if ( cc ) {
assert( cc->_pinValue < 100 ); // you can't still have an a ctive ClientCursor::Pointer assert( cc->_pinValue < 100 ); // you can't still have an a ctive ClientCursor::Pointer
delete cc; delete cc;
return true; return true;
} }
return false; return false;
} }
/**
* @return number of cursors found
*/
static int erase( int n , long long * ids );
/* call when cursor's location changes so that we can update the /* call when cursor's location changes so that we can update the
cursorsbylocation map. if you are locked and internally iterati ng, only cursorsbylocation map. if you are locked and internally iterati ng, only
need to call when you are ready to "unlock". need to call when you are ready to "unlock".
*/ */
void updateLocation(); void updateLocation();
void mayUpgradeStorage() { void mayUpgradeStorage() {
/* if ( !ids_.get() ) /* if ( !ids_.get() )
return; return;
stringstream ss; stringstream ss;
skipping to change at line 295 skipping to change at line 343
} }
/** /**
* @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 );
void storeOpForSlave( DiskLoc last ); void storeOpForSlave( DiskLoc last );
void updateSlaveLocation( CurOp& curop ); void updateSlaveLocation( CurOp& curop );
unsigned idleTime(){ unsigned idleTime() const { return _idleAgeMillis; }
return _idleAgeMillis;
} void setDoingDeletes( bool doingDeletes ) {_doingDeletes = doingDel etes; }
void slaveReadTill( const OpTime& t ) { _slaveReadTill = t; }
public: // static methods
static void idleTimeReport(unsigned millis); static void idleTimeReport(unsigned millis);
private:
static void appendStats( BSONObjBuilder& result );
static unsigned numCursors() { return clientCursorsById.size(); }
static void informAboutToDeleteBucket(const DiskLoc& b);
static void aboutToDelete(const DiskLoc& dl);
static void find( const string& ns , set<CursorId>& all );
private: // methods
// cursors normally timeout after an inactivy period to prevent exc ess memory use // cursors normally timeout after an inactivy period to prevent exc ess memory use
// setting this prevents timeout of the cursor in question. // setting this prevents timeout of the cursor in question.
void noTimeout() { void noTimeout() { _pinValue++; }
_pinValue++;
}
multimap<DiskLoc, ClientCursor*>& byLoc() { CCByLoc& byLoc() { return _db->ccByLoc; }
return _db->ccByLoc;
}
public:
void setDoingDeletes( bool doingDeletes ){
_doingDeletes = doingDeletes;
}
static void appendStats( BSONObjBuilder& result ); Record* _recordForYield( RecordNeeds need );
static unsigned numCursors() { return clientCursorsById.size(); } private:
static void informAboutToDeleteBucket(const DiskLoc& b); CursorId _cursorid;
static void aboutToDelete(const DiskLoc& dl);
const string _ns;
Database * _db;
const shared_ptr<Cursor> _c;
map<string,int> _indexedFields; // map from indexed field to offse t in key object
int _pos; // # objects into the cursor so fa r
const BSONObj _query; // used for logging diags only; op tional in constructor
int _queryOptions; // see enum QueryOptions dbclient.h
OpTime _slaveReadTill;
DiskLoc _lastLoc; // use getter and setter n ot this (important)
unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time
/* 0 = normal
1 = no timeout allowed
100 = in use (pinned) -- see Pointer class
*/
unsigned _pinValue;
bool _doingDeletes;
ElapsedTracker _yieldSometimesTracker;
ShardChunkManagerPtr _chunkManager;
public:
shared_ptr<ParsedQuery> pq;
shared_ptr<Projection> fields; // which fields query wants returned
Message originalMessage; // this is effectively an auto ptr for dat a the matcher points to
private: // static members
static CCById clientCursorsById;
static long long numberTimedOut;
static boost::recursive_mutex& ccmutex; // must use this for all statics above!
static CursorId allocCursorId_inlock();
static void find( const string& ns , set<CursorId>& all );
}; };
class ClientCursorMonitor : public BackgroundJob { class ClientCursorMonitor : public BackgroundJob {
public: public:
string name() const { return "ClientCursorMonitor"; }
void run(); void run();
string name() { return "ClientCursorMonitor"; }
}; };
extern ClientCursorMonitor clientCursorMonitor; extern ClientCursorMonitor clientCursorMonitor;
} // namespace mongo } // namespace mongo
// ClientCursor should only be used with auto_ptr because it needs to be
// release()ed after a yield if stillOk() returns false and these pointer t ypes
// do not support releasing. This will prevent them from being used acciden tally
namespace boost{
template<> class scoped_ptr<mongo::ClientCursor> {};
template<> class shared_ptr<mongo::ClientCursor> {};
}
 End of changes. 39 change blocks. 
89 lines changed or deleted 177 lines changed or added


 cmdline.h   cmdline.h 
skipping to change at line 20 skipping to change at line 20
* 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 "../pch.h" #include "../pch.h"
#include "jsobj.h"
namespace mongo { namespace mongo {
#ifdef MONGO_SSL
class SSLManager;
#endif
/* command line options /* command line options
*/ */
/* concurrency: OK/READ */ /* concurrency: OK/READ */
struct CmdLine { struct CmdLine {
CmdLine();
string binaryName; // mongod or mongos
string cwd; // cwd of when process started
// this is suboptimal as someone could rename a binary. todo...
bool isMongos() const { return binaryName == "mongos"; }
int port; // --port int port; // --port
enum {
DefaultDBPort = 27017,
ConfigServerPort = 27019,
ShardServerPort = 27018
};
bool isDefaultPort() const { return port == DefaultDBPort; }
string bind_ip; // --bind_ip string bind_ip; // --bind_ip
bool rest; // --rest bool rest; // --rest
bool jsonp; // --jsonp bool jsonp; // --jsonp
string _replSet; // --replSet[/<seedlist>] string _replSet; // --replSet[/<seedlist>]
string ourSetName() const { string ourSetName() const {
string setname; string setname;
size_t sl = _replSet.find('/'); size_t sl = _replSet.find('/');
if( sl == string::npos ) if( sl == string::npos )
return _replSet; return _replSet;
return _replSet.substr(0, sl); return _replSet.substr(0, sl);
} }
bool usingReplSets() const { return !_replSet.empty(); }
// for master/slave replication
string source; // --source string source; // --source
string only; // --only string only; // --only
bool quiet; // --quiet bool quiet; // --quiet
bool notablescan; // --notablescan bool noTableScan; // --notablescan no table scans allowed
bool prealloc; // --noprealloc bool prealloc; // --noprealloc no preallocation of data fil es
bool smallfiles; // --smallfiles bool preallocj; // --nopreallocj no preallocation of journal files
bool smallfiles; // --smallfiles allocate smaller data files
bool configsvr; // --configsvr
bool quota; // --quota bool quota; // --quota
int quotaFiles; // --quotaFiles int quotaFiles; // --quotaFiles
bool cpu; // --cpu show cpu time periodically bool cpu; // --cpu show cpu time periodically
bool dur; // --dur durability (now --journal)
unsigned journalCommitInterval; // group/batch commit interval ms
/** --durOptions 7 dump journal and terminate without doing an ything further
--durOptions 4 recover and terminate without listening
*/
enum { // bits to be ORed
DurDumpJournal = 1, // dump diagnostics on the journal during recovery
DurScanOnly = 2, // don't do any real work, just scan and dump if dump specified
DurRecoverOnly = 4, // terminate after recovery step
DurParanoid = 8, // paranoid mode enables extra checks
DurAlwaysCommit = 16, // do a group commit every time the write lock is released
DurAlwaysRemap = 32 // remap the private view after every gro up commit (may lag to the next write lock acquisition, but will do all file s then)
};
int durOptions; // --durOptions <n> for debugging
bool objcheck; // --objcheck
long long oplogSize; // --oplogSize long long oplogSize; // --oplogSize
int defaultProfile; // --profile int defaultProfile; // --profile
int slowMS; // --time in ms that is "slow" int slowMS; // --time in ms that is "slow"
int pretouch; // --pretouch for replication application (e xperimental) int pretouch; // --pretouch for replication application (e xperimental)
bool moveParanoia; // for move chunk paranoia bool moveParanoia; // for move chunk paranoia
double syncdelay; // seconds between fsyncs
enum { bool noUnixSocket; // --nounixsocket
DefaultDBPort = 27017, string socket; // UNIX domain socket directory
ConfigServerPort = 27019,
ShardServerPort = 27018
};
CmdLine() : #ifdef MONGO_SSL
port(DefaultDBPort), rest(false), jsonp(false), quiet(false), n otablescan(false), prealloc(true), smallfiles(false), bool sslOnNormalPorts; // --sslOnNormalPorts
quota(false), quotaFiles(8), cpu(false), oplogSize(0), defaultP rofile(0), slowMS(100), pretouch(0), moveParanoia( true ) string sslPEMKeyFile; // --sslPEMKeyFile
{ } string sslPEMKeyPassword; // --sslPEMKeyPassword
SSLManager* sslServerManager; // currently leaks on close
#endif
static void addGlobalOptions( boost::program_options::options_descr iption& general , static void addGlobalOptions( boost::program_options::options_descr iption& general ,
boost::program_options::options_descr iption& hidden ); boost::program_options::options_descr iption& hidden );
static void addWindowsOptions( boost::program_options::options_desc ription& windows , static void addWindowsOptions( boost::program_options::options_desc ription& windows ,
boost::program_options::options_descr iption& hidden ); boost::program_options::options_desc ription& hidden );
static void parseConfigFile( istream &f, stringstream &ss);
/** /**
* @return true if should run program, false if should exit * @return true if should run program, false if should exit
*/ */
static bool store( int argc , char ** argv , static bool store( int argc , char ** argv ,
boost::program_options::options_description& vis ible, boost::program_options::options_description& vis ible,
boost::program_options::options_description& hid den, boost::program_options::options_description& hid den,
boost::program_options::positional_options_descr iption& positional, boost::program_options::positional_options_descr iption& positional,
boost::program_options::variables_map &output ); boost::program_options::variables_map &output );
}; };
// todo move to cmdline.cpp?
inline CmdLine::CmdLine() :
port(DefaultDBPort), rest(false), jsonp(false), quiet(false), noTab leScan(false), prealloc(true), preallocj(true), smallfiles(sizeof(int*) == 4),
configsvr(false),
quota(false), quotaFiles(8), cpu(false), durOptions(0), objcheck(fa lse), oplogSize(0), defaultProfile(0), slowMS(100), pretouch(0), moveParano ia( true ),
syncdelay(60), noUnixSocket(false), socket("/tmp")
{
journalCommitInterval = 0; // 0 means use default
dur = false;
#if defined(_DURABLEDEFAULTON)
dur = true;
#endif
if( sizeof(void*) == 8 )
dur = true;
#if defined(_DURABLEDEFAULTOFF)
dur = false;
#endif
#ifdef MONGO_SSL
sslOnNormalPorts = false;
sslServerManager = 0;
#endif
}
extern CmdLine cmdLine; extern CmdLine cmdLine;
void setupCoreSignals(); void setupCoreSignals();
string prettyHostName();
void printCommandLineOpts();
/**
* used for setParameter
* so you can write validation code that lives with code using it
* rather than all in the command place
* also lets you have mongos or mongod specific code
* without pulling it all sorts of things
*/
class ParameterValidator {
public:
ParameterValidator( const string& name );
virtual ~ParameterValidator() {}
virtual bool isValid( BSONElement e , string& errmsg ) = 0;
static ParameterValidator * get( const string& name );
private:
string _name;
// don't need to lock since this is all done in static init
static map<string,ParameterValidator*> * _all;
};
} }
 End of changes. 16 change blocks. 
14 lines changed or deleted 112 lines changed or added


 commands.h   commands.h 
skipping to change at line 22 skipping to change at line 22
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "jsobj.h" #include "jsobj.h"
#include "../util/timer.h"
#include "../client/dbclient.h"
namespace mongo { namespace mongo {
class BSONObj; class BSONObj;
class BSONObjBuilder; class BSONObjBuilder;
class BufBuilder;
class Client; class Client;
/** mongodb "commands" (sent via db.$cmd.findOne(...)) /** mongodb "commands" (sent via db.$cmd.findOne(...))
subclass to make a command. define a singleton object for it. subclass to make a command. define a singleton object for it.
*/ */
class Command { class Command {
public: public:
enum LockType { READ = -1 , NONE = 0 , WRITE = 1 }; enum LockType { READ = -1 , NONE = 0 , WRITE = 1 };
const string name; const string name;
/* run the given command /* run the given command
implement this... implement this...
fromRepl - command is being invoked as part of replication synci ng. In this situation you fromRepl - command is being invoked as part of replication synci ng. In this situation you
normally do not want to log the command to the local oplog. normally do not want to log the command to the local oplog.
return value is true if succeeded. if false, set errmsg text. return value is true if succeeded. if false, set errmsg text.
*/ */
virtual bool run(const string& db, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) = 0; virtual bool run(const string& db, BSONObj& cmdObj, int options, st ring& errmsg, BSONObjBuilder& result, bool fromRepl = false ) = 0;
/* /*
note: logTheTop() MUST be false if READ note: logTheTop() MUST be false if READ
if NONE, can't use Client::Context setup if NONE, can't use Client::Context setup
use with caution use with caution
*/ */
virtual LockType locktype() const = 0; virtual LockType locktype() const = 0;
/* Return true if only the admin ns has privileges to run this comm and. */ /* Return true if only the admin ns has privileges to run this comm and. */
virtual bool adminOnly() const { virtual bool adminOnly() const {
return false; return false;
} }
void htmlHelp(stringstream&) const; void htmlHelp(stringstream&) const;
/* Like adminOnly, but even stricter: we must either be authenticat ed for admin db, /* Like adminOnly, but even stricter: we must either be authenticat ed for admin db,
or, if running without auth, on the local interface. or, if running without auth, on the local interface.
When localHostOnlyIfNoAuth() is true, adminOnly() must also be t rue. When localHostOnlyIfNoAuth() is true, adminOnly() must also be t rue.
*/ */
virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return false; } virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return false; }
/* Return true if slaves of a replication pair are allowed to execu te the command /* Return true if slaves are allowed to execute the command
(the command directly from a client -- if fromRepl, always allow ed). (the command directly from a client -- if fromRepl, always allow ed).
*/ */
virtual bool slaveOk() const = 0; virtual bool slaveOk() const = 0;
/* Return true if the client force a command to be run on a slave b y /* Return true if the client force a command to be run on a slave b y
turning on the 'slaveok' option in the command query. turning on the 'slaveok' option in the command query.
*/ */
virtual bool slaveOverrideOk() { virtual bool slaveOverrideOk() {
return false; return false;
} }
skipping to change at line 97 skipping to change at line 98
*/ */
virtual bool logTheOp() { return false; } virtual bool logTheOp() { return false; }
virtual void help( stringstream& help ) const; virtual void help( stringstream& help ) const;
/* Return true if authentication and security applies to the comman ds. Some commands /* Return true if authentication and security applies to the comman ds. Some commands
(e.g., getnonce, authenticate) can be done by anyone even unauth orized. (e.g., getnonce, authenticate) can be done by anyone even unauth orized.
*/ */
virtual bool requiresAuth() { return true; } virtual bool requiresAuth() { return true; }
/* Return true if a replica set secondary should go into "recoverin g"
(unreadable) state while running this command.
*/
virtual bool maintenanceMode() const { return false; }
/** @param webUI expose the command in the web ui as localhost:2801 7/<name> /** @param webUI expose the command in the web ui as localhost:2801 7/<name>
@param oldName an optional old, deprecated name for the command @param oldName an optional old, deprecated name for the command
*/ */
Command(const char *_name, bool webUI = false, const char *oldName = 0); Command(const char *_name, bool webUI = false, const char *oldName = 0);
virtual ~Command() {} virtual ~Command() {}
protected: protected:
BSONObj getQuery( const BSONObj& cmdObj ){ BSONObj getQuery( const BSONObj& cmdObj ) {
if ( cmdObj["query"].type() == Object ) if ( cmdObj["query"].type() == Object )
return cmdObj["query"].embeddedObject(); return cmdObj["query"].embeddedObject();
if ( cmdObj["q"].type() == Object ) if ( cmdObj["q"].type() == Object )
return cmdObj["q"].embeddedObject(); return cmdObj["q"].embeddedObject();
return BSONObj(); return BSONObj();
} }
static void logIfSlow( const Timer& cmdTimer, const string& msg); static void logIfSlow( const Timer& cmdTimer, const string& msg);
static map<string,Command*> * _commands; static map<string,Command*> * _commands;
static map<string,Command*> * _commandsByBestName; static map<string,Command*> * _commandsByBestName;
static map<string,Command*> * _webCommands; static map<string,Command*> * _webCommands;
public: public:
static const map<string,Command*>* commandsByBestName() { return _c ommandsByBestName; } static const map<string,Command*>* commandsByBestName() { return _c ommandsByBestName; }
static const map<string,Command*>* webCommands() { return _webComma nds; } static const map<string,Command*>* webCommands() { return _webComma nds; }
/** @return if command was found and executed */ /** @return if command was found and executed */
static bool runAgainstRegistered(const char *ns, BSONObj& jsobj, BS ONObjBuilder& anObjBuilder); static bool runAgainstRegistered(const char *ns, BSONObj& jsobj, BS ONObjBuilder& anObjBuilder, int queryOptions = 0);
static LockType locktype( const string& name ); static LockType locktype( const string& name );
static Command * findCommand( const string& name ); static Command * findCommand( const string& name );
}; };
class CmdShutdown : public Command {
public:
virtual bool requiresAuth() { return true; }
virtual bool adminOnly() const { return true; }
virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return true; }
virtual bool logTheOp() {
return false;
}
virtual bool slaveOk() const {
return true;
}
virtual LockType locktype() const { return NONE; }
virtual void help( stringstream& help ) const;
CmdShutdown() : Command("shutdown") {}
bool run(const string& dbname, BSONObj& cmdObj, int options, string & errmsg, BSONObjBuilder& result, bool fromRepl);
private:
bool shutdownHelper();
};
bool _runCommands(const char *ns, BSONObj& jsobj, BufBuilder &b, BSONOb jBuilder& anObjBuilder, bool fromRepl, int queryOptions); bool _runCommands(const char *ns, BSONObj& jsobj, BufBuilder &b, BSONOb jBuilder& anObjBuilder, bool fromRepl, int queryOptions);
} // namespace mongo } // namespace mongo
 End of changes. 10 change blocks. 
7 lines changed or deleted 32 lines changed or added


 concurrency.h   concurrency.h 
// @file concurrency.h
/* /*
* Copyright (C) 2010 10gen Inc. * Copyright (C) 2010 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3, * it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/* concurrency.h /*mongod concurrency rules & notes will be placed here.
mongod concurrency rules & notes will be placed here.
Mutex heirarchy (1 = "leaf") Mutex heirarchy (1 = "leaf")
name level name level
Logstream::mutex 1 Logstream::mutex 1
ClientCursor::ccmutex 2 ClientCursor::ccmutex 2
dblock 3 dblock 3
End func name with _inlock to indicate "caller must lock before callin g". End func name with _inlock to indicate "caller must lock before callin g".
*/ */
#pragma once #pragma once
#include "../util/concurrency/rwlock.h" #include "../util/concurrency/rwlock.h"
#include "../util/mmap.h" #include "../util/mmap.h"
#include "../util/time_support.h"
namespace mongo { namespace mongo {
string sayClientState(); string sayClientState();
bool haveClient(); bool haveClient();
void curopWaitingForLock( int type ); class Client;
void curopGotLock(); Client* curopWaitingForLock( int type );
void curopGotLock(Client*);
/* mutex time stats */ /* mutex time stats */
class MutexInfo { class MutexInfo {
unsigned long long start, enter, timeLocked; // all in microseconds unsigned long long enter, timeLocked; // microseconds
int locked; int locked;
unsigned long long start; // last as we touch this least often
public: public:
MutexInfo() : timeLocked(0) , locked(0) { MutexInfo() : timeLocked(0) , locked(0) {
start = curTimeMicros64(); start = curTimeMicros64();
} }
void entered() { void entered() {
if ( locked == 0 ) if ( locked == 0 )
enter = curTimeMicros64(); enter = curTimeMicros64();
locked++; locked++;
assert( locked >= 1 ); assert( locked >= 1 );
} }
void leaving() { void leaving() {
locked--; locked--;
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 getTimingInfo(unsigned long long &s, unsigned long long &tl) c onst { void getTimingInfo(unsigned long long &s, unsigned long long &tl) c onst {
s = start; s = start;
tl = timeLocked; tl = timeLocked;
} }
unsigned long long getTimeLocked() const { unsigned long long getTimeLocked() const { return timeLocked; }
return timeLocked;
}
}; };
class MongoMutex { }
MutexInfo _minfo;
RWLock _m;
ThreadLocalValue<int> _state;
/* we use a separate TLS value for releasedEarly - that is ok as
our normal/common code path, we never even touch it.
*/
ThreadLocalValue<bool> _releasedEarly;
public:
MongoMutex(const char * name) : _m(name) { }
/**
* @return
* > 0 write lock
* = 0 no lock
* < 0 read lock
*/
int getState() { return _state.get(); }
bool isWriteLocked() { return getState() > 0; }
void assertWriteLocked() {
assert( getState() > 0 );
DEV assert( !_releasedEarly.get() );
}
bool atLeastReadLocked() { return _state.get() != 0; }
void assertAtLeastReadLocked() { assert(atLeastReadLocked()); }
bool _checkWriteLockAlready(){
//DEV cout << "LOCK" << endl;
DEV assert( haveClient() );
int s = _state.get();
if( s > 0 ) {
_state.set(s+1);
return true;
}
massert( 10293 , (string)"internal error: locks are not upgrade able: " + sayClientState() , s == 0 );
return false;
}
void lock() {
if ( _checkWriteLockAlready() )
return;
_state.set(1);
curopWaitingForLock( 1 );
_m.lock();
curopGotLock();
_minfo.entered();
MongoFile::lockAll();
}
bool lock_try( int millis ) {
if ( _checkWriteLockAlready() )
return true;
curopWaitingForLock( 1 );
bool got = _m.lock_try( millis );
curopGotLock();
if ( got ){
_minfo.entered();
_state.set(1);
MongoFile::lockAll();
}
return got;
}
void unlock() {
//DEV cout << "UNLOCK" << endl;
int s = _state.get();
if( s > 1 ) {
_state.set(s-1);
return;
}
if( s != 1 ) {
if( _releasedEarly.get() ) {
_releasedEarly.set(false);
return;
}
massert( 12599, "internal error: attempt to unlock when was n't in a write lock", false);
}
MongoFile::unlockAll();
_state.set(0);
_minfo.leaving();
_m.unlock();
}
/* unlock (write lock), and when unlock() is called later,
be smart then and don't unlock it again.
*/
void releaseEarly() {
assert( getState() == 1 ); // must not be recursive
assert( !_releasedEarly.get() );
_releasedEarly.set(true);
unlock();
}
void lock_shared() {
//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);
curopWaitingForLock( -1 );
_m.lock_shared();
curopGotLock();
}
bool lock_shared_try( int millis ) {
int s = _state.get();
if ( s ){
// we already have a lock, so no need to try
lock_shared();
return true;
}
bool got = _m.lock_shared_try( millis );
if ( got )
_state.set(-1);
return got;
}
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; }
};
extern MongoMutex &dbMutex; #include "mongomutex.h"
inline void dbunlocking_write() { } namespace mongo {
inline void dbunlocking_read() { }
struct writelock { struct writelock {
writelock(const string& ns) { writelock() { dbMutex.lock(); }
dbMutex.lock(); writelock(const string& ns) { dbMutex.lock(); }
}
~writelock() { ~writelock() {
DESTRUCTOR_GUARD( DESTRUCTOR_GUARD(
dbunlocking_write();
dbMutex.unlock(); dbMutex.unlock();
); );
} }
}; };
struct readlock { struct readlock {
readlock(const string& ns) { readlock(const string& ns) {
dbMutex.lock_shared(); dbMutex.lock_shared();
} }
readlock() { dbMutex.lock_shared(); }
~readlock() { ~readlock() {
DESTRUCTOR_GUARD( DESTRUCTOR_GUARD(
dbunlocking_read();
dbMutex.unlock_shared(); dbMutex.unlock_shared();
); );
} }
}; };
struct readlocktry { struct readlocktry {
readlocktry( const string&ns , int tryms ){ readlocktry( const string&ns , int tryms ) {
_got = dbMutex.lock_shared_try( tryms ); _got = dbMutex.lock_shared_try( tryms );
} }
~readlocktry() { ~readlocktry() {
if ( _got ){ if ( _got ) {
dbunlocking_read();
dbMutex.unlock_shared(); dbMutex.unlock_shared();
} }
} }
bool got() const { return _got; } bool got() const { return _got; }
private: private:
bool _got; bool _got;
}; };
struct writelocktry { struct writelocktry {
writelocktry( const string&ns , int tryms ){ writelocktry( const string&ns , int tryms ) {
_got = dbMutex.lock_try( tryms ); _got = dbMutex.lock_try( tryms );
} }
~writelocktry() { ~writelocktry() {
if ( _got ){ if ( _got ) {
dbunlocking_read();
dbMutex.unlock(); dbMutex.unlock();
} }
} }
bool got() const { return _got; } bool got() const { return _got; }
private: private:
bool _got; bool _got;
}; };
struct readlocktryassert : public readlocktry { struct readlocktryassert : public readlocktry {
readlocktryassert(const string& ns, int tryms) : readlocktryassert(const string& ns, int tryms) :
readlocktry(ns,tryms) { readlocktry(ns,tryms) {
uassert(13142, "timeout getting readlock", got()); uassert(13142, "timeout getting readlock", got());
} }
}; };
/** assure we have at least a read lock - they key with this being /** assure we have at least a read lock - they key with this being
if you have a write lock, that's ok too. if you have a write lock, that's ok too.
*/ */
struct atleastreadlock { struct atleastreadlock {
atleastreadlock( const string& ns ){ atleastreadlock( const string& ns ) {
_prev = dbMutex.getState(); _prev = dbMutex.getState();
if ( _prev == 0 ) if ( _prev == 0 )
dbMutex.lock_shared(); dbMutex.lock_shared();
} }
~atleastreadlock(){ ~atleastreadlock() {
if ( _prev == 0 ) if ( _prev == 0 )
dbMutex.unlock_shared(); dbMutex.unlock_shared();
} }
private: private:
int _prev; int _prev;
}; };
/* parameterized choice of read or write locking
use readlock and writelock instead of this when statically known whi ch you want
*/
class mongolock { class mongolock {
bool _writelock; bool _writelock;
public: public:
mongolock(bool write) : _writelock(write) { mongolock(bool write) : _writelock(write) {
if( _writelock ) { if( _writelock ) {
dbMutex.lock(); dbMutex.lock();
} }
else else
dbMutex.lock_shared(); dbMutex.lock_shared();
} }
~mongolock() { ~mongolock() {
DESTRUCTOR_GUARD( DESTRUCTOR_GUARD(
if( _writelock ) { if( _writelock ) {
dbunlocking_write(); dbMutex.unlock();
dbMutex.unlock(); }
} else { else {
dbunlocking_read(); dbMutex.unlock_shared();
dbMutex.unlock_shared(); }
}
); );
} }
/* this unlocks, does NOT upgrade. that works for our current usage */ /* this unlocks, does NOT upgrade. that works for our current usage */
void releaseAndWriteLock(); void releaseAndWriteLock();
}; };
/* use writelock and readlock instead */ /* deprecated - use writelock and readlock instead */
struct dblock : public writelock { struct dblock : public writelock {
dblock() : writelock("") { } dblock() : writelock("") { }
}; };
// eliminate // eliminate this - we should just type "dbMutex.assertWriteLocked();" instead
inline void assertInWriteLock() { dbMutex.assertWriteLocked(); } inline void assertInWriteLock() { dbMutex.assertWriteLocked(); }
} }
 End of changes. 26 change blocks. 
199 lines changed or deleted 36 lines changed or added


 config.h   config.h 
skipping to change at line 163 skipping to change at line 163
/* PCRE uses recursive function calls to handle backtracking while matching . /* PCRE uses recursive function calls to handle backtracking while matching .
This can sometimes be a problem on systems that have stacks of limited This can sometimes be a problem on systems that have stacks of limited
size. Define NO_RECURSE to get a version that doesn't use recursion in t he size. Define NO_RECURSE to get a version that doesn't use recursion in t he
match() function; instead it creates its own stack by steam using match() function; instead it creates its own stack by steam using
pcre_recurse_malloc() to obtain memory from the heap. For more detail, s ee pcre_recurse_malloc() to obtain memory from the heap. For more detail, s ee
the comments and other stuff just above the match() function. On systems the comments and other stuff just above the match() function. On systems
that support it, "configure" can be used to set this in the Makefile (us e that support it, "configure" can be used to set this in the Makefile (us e
--disable-stack-for-recursion). */ --disable-stack-for-recursion). */
/* #undef NO_RECURSE */ /* #undef NO_RECURSE */
/* mongodb: don't recurse, don't want to use much stack or blow stack */
#define NO_RECURSE 1
/* Name of package */ /* Name of package */
#define PACKAGE "pcre" #define PACKAGE "pcre"
/* Define to the address where bug reports for this package should be sent. */ /* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "" #define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */ /* Define to the full name of this package. */
#define PACKAGE_NAME "PCRE" #define PACKAGE_NAME "PCRE"
 End of changes. 1 change blocks. 
0 lines changed or deleted 2 lines changed or added


 connections.h   connections.h 
skipping to change at line 23 skipping to change at line 23
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <map> #include <map>
#include "../../client/dbclient.h" #include "../../client/dbclient.h"
#include "../security_common.h"
namespace mongo { namespace mongo {
/** here we keep a single connection (with reconnect) for a set of host s, /** here we keep a single connection (with reconnect) for a set of host s,
one each, and allow one user at a time per host. if in use already for that one each, and allow one user at a time per host. if in use already for that
host, we block. so this is an easy way to keep a 1-deep pool of co nnections host, we block. so this is an easy way to keep a 1-deep pool of co nnections
that many threads can share. that many threads can share.
thread-safe. thread-safe.
skipping to change at line 46 skipping to change at line 47
c->runCommand(...); c->runCommand(...);
} }
throws exception on connect error (but fine to try again later with a new throws exception on connect error (but fine to try again later with a new
scopedconn object for same host). scopedconn object for same host).
*/ */
class ScopedConn { class ScopedConn {
public: public:
/** throws assertions if connect failure etc. */ /** throws assertions if connect failure etc. */
ScopedConn(string hostport); ScopedConn(string hostport);
~ScopedConn(); ~ScopedConn() {
DBClientConnection* operator->(); // conLock releases...
}
/* If we were to run a query and not exhaust the cursor, future use of the connection would be problematic.
So here what we do is wrapper known safe methods and not allow c ursor-style queries at all. This makes
ScopedConn limited in functionality but very safe. More non-cur sor wrappers can be added here if needed.
*/
bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj & info, int options=0) {
return conn()->runCommand(dbname, cmd, info, options);
}
unsigned long long count(const string &ns) {
return conn()->count(ns);
}
BSONObj findOne(const string &ns, const Query& q, const BSONObj *fi eldsToReturn = 0, int queryOptions = 0) {
return conn()->findOne(ns, q, fieldsToReturn, queryOptions);
}
void setTimeout(double to) {
conn()->setSoTimeout(to);
}
private: private:
auto_ptr<scoped_lock> connLock; auto_ptr<scoped_lock> connLock;
static mutex mapMutex; static mongo::mutex mapMutex;
struct X { struct X {
mutex z; mongo::mutex z;
DBClientConnection cc; DBClientConnection cc;
X() : z("X"), cc(/*reconnect*/true, 0, /*timeout*/10) { X() : z("X"), cc(/*reconnect*/ true, 0, /*timeout*/ 10.0) {
cc._logLevel = 2; cc._logLevel = 2;
} }
} *x; } *x;
typedef map<string,ScopedConn::X*> M; typedef map<string,ScopedConn::X*> M;
static M& _map; static M& _map;
DBClientConnection* conn() { return &x->cc; }
}; };
inline ScopedConn::ScopedConn(string hostport) { inline ScopedConn::ScopedConn(string hostport) {
bool first = false; bool first = false;
{ {
scoped_lock lk(mapMutex); scoped_lock lk(mapMutex);
x = _map[hostport]; x = _map[hostport];
if( x == 0 ) { if( x == 0 ) {
x = _map[hostport] = new X(); x = _map[hostport] = new X();
first = true; first = true;
connLock.reset( new scoped_lock(x->z) ); connLock.reset( new scoped_lock(x->z) );
} }
} }
if( !first ) { if( !first ) {
connLock.reset( new scoped_lock(x->z) ); connLock.reset( new scoped_lock(x->z) );
return; return;
} }
// we already locked above... // we already locked above...
string err; string err;
x->cc.connect(hostport, err); if (!x->cc.connect(hostport, err)) {
} log() << "couldn't connect to " << hostport << ": " << err << r sLog;
return;
inline ScopedConn::~ScopedConn() { }
// conLock releases...
}
inline DBClientConnection* ScopedConn::operator->() { if (!noauth && !x->cc.auth("local", internalSecurity.user, internal Security.pwd, err, false)) {
return &x->cc; log() << "could not authenticate against " << conn()->toString( ) << ", " << err << rsLog;
return;
}
} }
} }
 End of changes. 8 change blocks. 
13 lines changed or deleted 34 lines changed or added


 connpool.h   connpool.h 
skipping to change at line 24 skipping to change at line 24
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <stack> #include <stack>
#include "dbclient.h" #include "dbclient.h"
#include "redef_macros.h" #include "redef_macros.h"
#include "../util/background.h"
namespace mongo { namespace mongo {
class Shard; class Shard;
class DBConnectionPool;
struct PoolForHost { /**
* not thread safe
* thread safety is handled by DBConnectionPool
*/
class PoolForHost {
public:
PoolForHost() PoolForHost()
: created(0){} : _created(0) {}
PoolForHost( const PoolForHost& other ){
assert(other.pool.size() == 0); PoolForHost( const PoolForHost& other ) {
created = other.created; assert(other._pool.size() == 0);
assert( created == 0 ); _created = other._created;
assert( _created == 0 );
} }
std::stack<DBClientBase*> pool; ~PoolForHost();
long long created;
int numAvailable() const { return (int)_pool.size(); }
void createdOne( DBClientBase * base );
long long numCreated() const { return _created; }
ConnectionString::ConnectionType type() const { assert(_created); r eturn _type; }
/**
* gets a connection or return NULL
*/
DBClientBase * get( DBConnectionPool * pool , double socketTimeout );
void done( DBConnectionPool * pool , DBClientBase * c );
void flush();
void getStaleConnections( vector<DBClientBase*>& stale );
static void setMaxPerHost( unsigned max ) { _maxPerHost = max; }
static unsigned getMaxPerHost() { return _maxPerHost; }
private:
struct StoredConnection {
StoredConnection( DBClientBase * c );
bool ok( time_t now );
DBClientBase* conn;
time_t when;
};
std::stack<StoredConnection> _pool;
long long _created;
ConnectionString::ConnectionType _type;
static unsigned _maxPerHost;
}; };
class DBConnectionHook { class DBConnectionHook {
public: public:
virtual ~DBConnectionHook(){} virtual ~DBConnectionHook() {}
virtual void onCreate( DBClientBase * conn ){} virtual void onCreate( DBClientBase * conn ) {}
virtual void onHandedOut( DBClientBase * conn ){} virtual void onHandedOut( DBClientBase * conn ) {}
virtual void onDestory( DBClientBase * conn ) {}
}; };
/** Database connection pool. /** Database connection pool.
Generally, use ScopedDbConnection and do not call these directly. Generally, use ScopedDbConnection and do not call these directly.
This class, so far, is suitable for use with unauthenticated connec tions. This class, so far, is suitable for use with unauthenticated connec tions.
Support for authenticated connections requires some adjustements: p lease Support for authenticated connections requires some adjustements: p lease
request... request...
Usage: Usage:
{ {
ScopedDbConnection c("myserver"); ScopedDbConnection c("myserver");
c.conn()... c.conn()...
} }
*/ */
class DBConnectionPool { class DBConnectionPool : public PeriodicTask {
mongo::mutex _mutex;
map<string,PoolForHost> _pools; // servername -> pool
list<DBConnectionHook*> _hooks;
DBClientBase* _get( const string& ident );
DBClientBase* _finishCreate( const string& ident , DBClientBase* co nn );
public: public:
DBConnectionPool() : _mutex("DBConnectionPool") { }
DBConnectionPool();
~DBConnectionPool(); ~DBConnectionPool();
/** right now just controls some asserts. defaults to "dbconnectio npool" */
void setName( const string& name ) { _name = name; }
void onCreate( DBClientBase * conn ); void onCreate( DBClientBase * conn );
void onHandedOut( DBClientBase * conn ); void onHandedOut( DBClientBase * conn );
void onDestory( DBClientBase * conn );
void flush(); void flush();
DBClientBase *get(const string& host); DBClientBase *get(const string& host, double socketTimeout = 0);
DBClientBase *get(const ConnectionString& host); DBClientBase *get(const ConnectionString& host, double socketTimeou t = 0);
void release(const string& host, DBClientBase *c) { void release(const string& host, DBClientBase *c);
if ( c->isFailed() ){
delete c; void addHook( DBConnectionHook * hook ); // we take ownership
return;
}
scoped_lock L(_mutex);
_pools[host].pool.push(c);
}
void addHook( DBConnectionHook * hook );
void appendInfo( BSONObjBuilder& b ); void appendInfo( BSONObjBuilder& b );
/** compares server namees, but is smart about replica set names */
struct serverNameCompare {
bool operator()( const string& a , const string& b ) const;
};
virtual string taskName() const { return "DBConnectionPool-cleaner" ; }
virtual void taskDoWork();
private:
DBConnectionPool( DBConnectionPool& p );
DBClientBase* _get( const string& ident , double socketTimeout );
DBClientBase* _finishCreate( const string& ident , double socketTim eout, DBClientBase* conn );
struct PoolKey {
PoolKey( string i , double t ) : ident( i ) , timeout( t ) {}
string ident;
double timeout;
};
struct poolKeyCompare {
bool operator()( const PoolKey& a , const PoolKey& b ) const;
};
typedef map<PoolKey,PoolForHost,poolKeyCompare> PoolMap; // servern ame -> pool
mongo::mutex _mutex;
string _name;
PoolMap _pools;
// pointers owned by me, right now they leak on shutdown
// _hooks itself also leaks because it creates a shutdown race cond ition
list<DBConnectionHook*> * _hooks;
}; };
extern DBConnectionPool pool; extern DBConnectionPool pool;
class AScopedConnection : boost::noncopyable { class AScopedConnection : boost::noncopyable {
public: public:
virtual ~AScopedConnection(){} AScopedConnection() { _numConnections++; }
virtual ~AScopedConnection() { _numConnections--; }
virtual DBClientBase* get() = 0; virtual DBClientBase* get() = 0;
virtual void done() = 0; virtual void done() = 0;
virtual string getHost() const = 0; virtual string getHost() const = 0;
/**
* @return true iff this has a connection to the db
*/
virtual bool ok() const = 0;
/**
* @return total number of current instances of AScopedConnection
*/
static int getNumConnections() { return _numConnections; }
private:
static AtomicUInt _numConnections;
}; };
/** Use to get a connection from the pool. On exceptions things /** Use to get a connection from the pool. On exceptions things
clean up nicely. clean up nicely (i.e. the socket gets closed automatically when the
scopeddbconnection goes out of scope).
*/ */
class ScopedDbConnection : public AScopedConnection { class ScopedDbConnection : public AScopedConnection {
const string _host;
DBClientBase *_conn;
public: public:
/** the main constructor you want to use
throws UserException if can't connect
*/
explicit ScopedDbConnection(const string& host, double socketTimeou t = 0) : _host(host), _conn( pool.get(host, socketTimeout) ), _socketTimeou t( socketTimeout ) {
_setSocketTimeout();
}
ScopedDbConnection() : _host( "" ) , _conn(0), _socketTimeout( 0 ) {}
/* @param conn - bind to an existing connection */
ScopedDbConnection(const string& host, DBClientBase* conn, double s ocketTimeout = 0 ) : _host( host ) , _conn( conn ), _socketTimeout( socketT imeout ) {
_setSocketTimeout();
}
/** throws UserException if can't connect */
explicit ScopedDbConnection(const ConnectionString& url, double soc ketTimeout = 0 ) : _host(url.toString()), _conn( pool.get(url, socketTimeou t) ), _socketTimeout( socketTimeout ) {
_setSocketTimeout();
}
/** throws UserException if can't connect */
explicit ScopedDbConnection(const Shard& shard, double socketTimeou t = 0 );
explicit ScopedDbConnection(const Shard* shard, double socketTimeou t = 0 );
~ScopedDbConnection();
/** get the associated connection object */ /** get the associated connection object */
DBClientBase* operator->(){ DBClientBase* operator->() {
uassert( 11004 , "did you call done already" , _conn ); uassert( 11004 , "connection was returned to the pool already" , _conn );
return _conn; return _conn;
} }
/** get the associated connection object */ /** get the associated connection object */
DBClientBase& conn() { DBClientBase& conn() {
uassert( 11005 , "did you call done already" , _conn ); uassert( 11005 , "connection was returned to the pool already" , _conn );
return *_conn; return *_conn;
} }
/** get the associated connection object */ /** get the associated connection object */
DBClientBase* get() { DBClientBase* get() {
uassert( 13102 , "did you call done already" , _conn ); uassert( 13102 , "connection was returned to the pool already" , _conn );
return _conn; return _conn;
} }
ScopedDbConnection() bool ok() const { return _conn > 0; }
: _host( "" ) , _conn(0) {
}
/** throws UserException if can't connect */
ScopedDbConnection(const string& host)
: _host(host), _conn( pool.get(host) ) {
}
ScopedDbConnection(const string& host, DBClientBase* conn )
: _host( host ) , _conn( conn ){
}
ScopedDbConnection(const Shard& shard );
ScopedDbConnection(const Shard* shard );
ScopedDbConnection(const ConnectionString& url )
: _host(url.toString()), _conn( pool.get(url) ) {
}
string getHost() const { return _host; } string getHost() const { return _host; }
/** Force closure of the connection. You should call this if you l eave it in /** Force closure of the connection. You should call this if you l eave it in
a bad state. Destructor will do this too, but it is verbose. a bad state. Destructor will do this too, but it is verbose.
*/ */
void kill() { void kill() {
delete _conn; delete _conn;
_conn = 0; _conn = 0;
} }
skipping to change at line 182 skipping to change at line 279
if ( _conn->isFailed() ) if ( _conn->isFailed() )
kill(); kill();
else else
*/ */
pool.release(_host, _conn); pool.release(_host, _conn);
_conn = 0; _conn = 0;
} }
ScopedDbConnection * steal(); ScopedDbConnection * steal();
~ScopedDbConnection(); private:
void _setSocketTimeout();
const string _host;
DBClientBase *_conn;
const double _socketTimeout;
}; };
} // namespace mongo } // namespace mongo
#include "undef_macros.h" #include "undef_macros.h"
 End of changes. 23 change blocks. 
59 lines changed or deleted 162 lines changed or added


 core.h   core.h 
skipping to change at line 34 skipping to change at line 34
#include <cmath> #include <cmath>
#ifndef M_PI #ifndef M_PI
# define M_PI 3.14159265358979323846 # define M_PI 3.14159265358979323846
#endif #endif
namespace mongo { namespace mongo {
class GeoBitSets { class GeoBitSets {
public: public:
GeoBitSets(){ GeoBitSets() {
for ( int i=0; i<32; i++ ){ for ( int i=0; i<32; i++ ) {
masks32[i] = ( 1 << ( 31 - i ) ); masks32[i] = ( 1 << ( 31 - i ) );
} }
for ( int i=0; i<64; i++ ){ for ( int i=0; i<64; i++ ) {
masks64[i] = ( 1LL << ( 63 - i ) ); masks64[i] = ( 1LL << ( 63 - i ) );
} }
for ( unsigned i=0; i<16; i++ ){ for ( unsigned i=0; i<16; i++ ) {
unsigned fixed = 0; unsigned fixed = 0;
for ( int j=0; j<4; j++ ){ for ( int j=0; j<4; j++ ) {
if ( i & ( 1 << j ) ) if ( i & ( 1 << j ) )
fixed |= ( 1 << ( j * 2 ) ); fixed |= ( 1 << ( j * 2 ) );
} }
hashedToNormal[fixed] = i; hashedToNormal[fixed] = i;
} }
} }
int masks32[32]; int masks32[32];
long long masks64[64]; long long masks64[64];
skipping to change at line 62 skipping to change at line 62
int masks32[32]; int masks32[32];
long long masks64[64]; long long masks64[64];
unsigned hashedToNormal[256]; unsigned hashedToNormal[256];
}; };
extern GeoBitSets geoBitSets; extern GeoBitSets geoBitSets;
class GeoHash { class GeoHash {
public: public:
GeoHash() GeoHash()
: _hash(0),_bits(0){ : _hash(0),_bits(0) {
} }
explicit GeoHash( const char * hash ){ explicit GeoHash( const char * hash ) {
init( hash ); init( hash );
} }
explicit GeoHash( const string& hash ){ explicit GeoHash( const string& hash ) {
init( hash ); init( hash );
} }
explicit GeoHash( const BSONElement& e , unsigned bits=32 ){ static GeoHash makeFromBinData(const char *bindata, unsigned bits) {
GeoHash h;
h._bits = bits;
h._copy( (char*)&h._hash , bindata );
h._fix();
return h;
}
explicit GeoHash( const BSONElement& e , unsigned bits=32 ) {
_bits = bits; _bits = bits;
if ( e.type() == BinData ){ if ( e.type() == BinData ) {
int len = 0; int len = 0;
_copy( (char*)&_hash , e.binData( len ) ); _copy( (char*)&_hash , e.binData( len ) );
assert( len == 8 ); assert( len == 8 );
_bits = bits; _bits = bits;
} }
else { else {
cout << "GeoHash cons e : " << e << endl; cout << "GeoHash bad element: " << e << endl;
uassert(13047,"wrong type for geo index. if you're using a pre-release version, need to rebuild index",0); uassert(13047,"wrong type for geo index. if you're using a pre-release version, need to rebuild index",0);
} }
_fix(); _fix();
} }
GeoHash( unsigned x , unsigned y , unsigned bits=32){ GeoHash( unsigned x , unsigned y , unsigned bits=32) {
init( x , y , bits ); init( x , y , bits );
} }
GeoHash( const GeoHash& old ){ GeoHash( const GeoHash& old ) {
_hash = old._hash; _hash = old._hash;
_bits = old._bits; _bits = old._bits;
} }
GeoHash( long long hash , unsigned bits ) GeoHash( long long hash , unsigned bits )
: _hash( hash ) , _bits( bits ){ : _hash( hash ) , _bits( bits ) {
_fix(); _fix();
} }
void init( unsigned x , unsigned y , unsigned bits ){ void init( unsigned x , unsigned y , unsigned bits ) {
assert( bits <= 32 ); assert( bits <= 32 );
_hash = 0; _hash = 0;
_bits = bits; _bits = bits;
for ( unsigned i=0; i<bits; i++ ){ for ( unsigned i=0; i<bits; i++ ) {
if ( isBitSet( x , i ) ) _hash |= geoBitSets.masks64[i*2]; if ( isBitSet( x , i ) ) _hash |= geoBitSets.masks64[i*2];
if ( isBitSet( y , i ) ) _hash |= geoBitSets.masks64[(i*2)+ 1]; if ( isBitSet( y , i ) ) _hash |= geoBitSets.masks64[(i*2)+ 1];
} }
} }
void unhash_fast( unsigned& x , unsigned& y ) const { void unhash_fast( unsigned& x , unsigned& y ) const {
x = 0; x = 0;
y = 0; y = 0;
char * c = (char*)(&_hash); char * c = (char*)(&_hash);
for ( int i=0; i<8; i++ ){ for ( int i=0; i<8; i++ ) {
unsigned t = (unsigned)(c[i]) & 0x55; unsigned t = (unsigned)(c[i]) & 0x55;
y |= ( geoBitSets.hashedToNormal[t] << (4*(i)) ); y |= ( geoBitSets.hashedToNormal[t] << (4*(i)) );
t = ( (unsigned)(c[i]) >> 1 ) & 0x55; t = ( (unsigned)(c[i]) >> 1 ) & 0x55;
x |= ( geoBitSets.hashedToNormal[t] << (4*(i)) ); x |= ( geoBitSets.hashedToNormal[t] << (4*(i)) );
} }
} }
void unhash_slow( unsigned& x , unsigned& y ) const { void unhash_slow( unsigned& x , unsigned& y ) const {
x = 0; x = 0;
y = 0; y = 0;
for ( unsigned i=0; i<_bits; i++ ){ for ( unsigned i=0; i<_bits; i++ ) {
if ( getBitX(i) ) if ( getBitX(i) )
x |= geoBitSets.masks32[i]; x |= geoBitSets.masks32[i];
if ( getBitY(i) ) if ( getBitY(i) )
y |= geoBitSets.masks32[i]; y |= geoBitSets.masks32[i];
} }
} }
void unhash( unsigned& x , unsigned& y ) const { void unhash( unsigned& x , unsigned& y ) const {
unhash_fast( x , y ); unhash_fast( x , y );
} }
/** /**
* @param 0 = high * @param 0 = high
*/ */
static bool isBitSet( unsigned val , unsigned bit ){ static bool isBitSet( unsigned val , unsigned bit ) {
return geoBitSets.masks32[bit] & val; return geoBitSets.masks32[bit] & val;
} }
GeoHash up() const { GeoHash up() const {
return GeoHash( _hash , _bits - 1 ); return GeoHash( _hash , _bits - 1 );
} }
bool hasPrefix( const GeoHash& other ) const { bool hasPrefix( const GeoHash& other ) const {
assert( other._bits <= _bits ); assert( other._bits <= _bits );
if ( other._bits == 0 ) if ( other._bits == 0 )
skipping to change at line 174 skipping to change at line 183
buf.append( _hash & geoBitSets.masks64[x] ? "1" : "0" ); buf.append( _hash & geoBitSets.masks64[x] ? "1" : "0" );
return buf.str(); return buf.str();
} }
string toStringHex1() const { string toStringHex1() const {
stringstream ss; stringstream ss;
ss << hex << _hash; ss << hex << _hash;
return ss.str(); return ss.str();
} }
void init( const string& s ){ void init( const string& s ) {
_hash = 0; _hash = 0;
_bits = s.size() / 2; _bits = s.size() / 2;
for ( unsigned pos=0; pos<s.size(); pos++ ) for ( unsigned pos=0; pos<s.size(); pos++ )
if ( s[pos] == '1' ) if ( s[pos] == '1' )
setBit( pos , 1 ); setBit( pos , 1 );
} }
void setBit( unsigned pos , bool one ){ void setBit( unsigned pos , bool one ) {
assert( pos < _bits * 2 ); assert( pos < _bits * 2 );
if ( one ) if ( one )
_hash |= geoBitSets.masks64[pos]; _hash |= geoBitSets.masks64[pos];
else if ( _hash & geoBitSets.masks64[pos] ) else if ( _hash & geoBitSets.masks64[pos] )
_hash &= ~geoBitSets.masks64[pos]; _hash &= ~geoBitSets.masks64[pos];
} }
bool getBit( unsigned pos ) const { bool getBit( unsigned pos ) const {
return _hash & geoBitSets.masks64[pos]; return _hash & geoBitSets.masks64[pos];
} }
skipping to change at line 216 skipping to change at line 225
append( b , "" ); append( b , "" );
BSONObj o = b.obj(); BSONObj o = b.obj();
assert( o.objsize() == 20 ); assert( o.objsize() == 20 );
return o; return o;
} }
bool constrains() const { bool constrains() const {
return _bits > 0; return _bits > 0;
} }
void move( int x , int y ){ bool canRefine() const {
return _bits < 32;
}
void move( int x , int y ) {
assert( _bits ); assert( _bits );
_move( 0 , x ); _move( 0 , x );
_move( 1 , y ); _move( 1 , y );
} }
void _move( unsigned offset , int d ){ void _move( unsigned offset , int d ) {
if ( d == 0 ) if ( d == 0 )
return; return;
assert( d <= 1 && d>= -1 ); // TEMP assert( d <= 1 && d>= -1 ); // TEMP
bool from, to; bool from, to;
if ( d > 0 ){ if ( d > 0 ) {
from = 0; from = 0;
to = 1; to = 1;
} }
else { else {
from = 1; from = 1;
to = 0; to = 0;
} }
unsigned pos = ( _bits * 2 ) - 1; unsigned pos = ( _bits * 2 ) - 1;
if ( offset == 0 ) if ( offset == 0 )
pos--; pos--;
while ( true ){ while ( true ) {
if ( getBit(pos) == from ){ if ( getBit(pos) == from ) {
setBit( pos , to ); setBit( pos , to );
return; return;
} }
if ( pos < 2 ){ if ( pos < 2 ) {
// overflow // overflow
for ( ; pos < ( _bits * 2 ) ; pos += 2 ){ for ( ; pos < ( _bits * 2 ) ; pos += 2 ) {
setBit( pos , from ); setBit( pos , from );
} }
return; return;
} }
setBit( pos , from ); setBit( pos , from );
pos -= 2; pos -= 2;
} }
assert(0); assert(0);
} }
GeoHash& operator=(const GeoHash& h) { GeoHash& operator=(const GeoHash& h) {
_hash = h._hash; _hash = h._hash;
_bits = h._bits; _bits = h._bits;
return *this; return *this;
} }
bool operator==(const GeoHash& h ){ bool operator==(const GeoHash& h ) const {
return _hash == h._hash && _bits == h._bits; return _hash == h._hash && _bits == h._bits;
} }
bool operator!=(const GeoHash& h ) const {
return !( *this == h );
}
bool operator<(const GeoHash& h ) const {
if( _hash != h._hash ) return _hash < h._hash;
return _bits < h._bits;
}
GeoHash& operator+=( const char * s ) { GeoHash& operator+=( const char * s ) {
unsigned pos = _bits * 2; unsigned pos = _bits * 2;
_bits += strlen(s) / 2; _bits += strlen(s) / 2;
assert( _bits <= 32 ); assert( _bits <= 32 );
while ( s[0] ){ while ( s[0] ) {
if ( s[0] == '1' ) if ( s[0] == '1' )
setBit( pos , 1 ); setBit( pos , 1 );
pos++; pos++;
s++; s++;
} }
return *this; return *this;
} }
GeoHash operator+( const char * s ) const { GeoHash operator+( const char * s ) const {
GeoHash n = *this; GeoHash n = *this;
n+=s; n+=s;
return n; return n;
} }
void _fix(){ GeoHash operator+( string s ) const {
return operator+( s.c_str() );
}
void _fix() {
static long long FULL = 0xFFFFFFFFFFFFFFFFLL; static long long FULL = 0xFFFFFFFFFFFFFFFFLL;
long long mask = FULL << ( 64 - ( _bits * 2 ) ); long long mask = FULL << ( 64 - ( _bits * 2 ) );
_hash &= mask; _hash &= mask;
} }
void append( BSONObjBuilder& b , const char * name ) const { void append( BSONObjBuilder& b , const char * name ) const {
char buf[8]; char buf[8];
_copy( buf , (char*)&_hash ); _copy( buf , (char*)&_hash );
b.appendBinData( name , 8 , bdtCustom , buf ); b.appendBinData( name , 8 , bdtCustom , buf );
} }
skipping to change at line 313 skipping to change at line 339
long long getHash() const { long long getHash() const {
return _hash; return _hash;
} }
unsigned getBits() const { unsigned getBits() const {
return _bits; return _bits;
} }
GeoHash commonPrefix( const GeoHash& other ) const { GeoHash commonPrefix( const GeoHash& other ) const {
unsigned i=0; unsigned i=0;
for ( ; i<_bits && i<other._bits; i++ ){ for ( ; i<_bits && i<other._bits; i++ ) {
if ( getBitX( i ) == other.getBitX( i ) && if ( getBitX( i ) == other.getBitX( i ) &&
getBitY( i ) == other.getBitY( i ) ) getBitY( i ) == other.getBitY( i ) )
continue; continue;
break; break;
} }
return GeoHash(_hash,i); return GeoHash(_hash,i);
} }
private: private:
void _copy( char * dst , const char * src ) const { static void _copy( char * dst , const char * src ) {
for ( unsigned a=0; a<8; a++ ){ for ( unsigned a=0; a<8; a++ ) {
dst[a] = src[7-a]; dst[a] = src[7-a];
} }
} }
long long _hash; long long _hash;
unsigned _bits; // bits per field, so 1 to 32 unsigned _bits; // bits per field, so 1 to 32
}; };
inline ostream& operator<<( ostream &s, const GeoHash &h ){ inline ostream& operator<<( ostream &s, const GeoHash &h ) {
s << h.toString(); s << h.toString();
return s; return s;
} }
class GeoConvert { class GeoConvert {
public: public:
virtual ~GeoConvert(){} virtual ~GeoConvert() {}
virtual void unhash( const GeoHash& h , double& x , double& y ) con st = 0; virtual void unhash( const GeoHash& h , double& x , double& y ) con st = 0;
virtual GeoHash hash( double x , double y ) const = 0; virtual GeoHash hash( double x , double y ) const = 0;
}; };
class Point { class Point {
public: public:
Point( const GeoConvert * g , const GeoHash& hash ){ Point( const GeoConvert * g , const GeoHash& hash ) {
g->unhash( hash , _x , _y ); g->unhash( hash , _x , _y );
} }
explicit Point( const BSONElement& e ){ explicit Point( const BSONElement& e ) {
BSONObjIterator i(e.Obj()); BSONObjIterator i(e.Obj());
_x = i.next().number(); _x = i.next().number();
_y = i.next().number(); _y = i.next().number();
} }
explicit Point( const BSONObj& o ){ explicit Point( const BSONObj& o ) {
BSONObjIterator i(o); BSONObjIterator i(o);
_x = i.next().number(); _x = i.next().number();
_y = i.next().number(); _y = i.next().number();
} }
Point( double x , double y ) Point( double x , double y )
: _x( x ) , _y( y ){ : _x( x ) , _y( y ) {
} }
Point() : _x(0),_y(0){ Point() : _x(0),_y(0) {
} }
GeoHash hash( const GeoConvert * g ){ GeoHash hash( const GeoConvert * g ) {
return g->hash( _x , _y ); return g->hash( _x , _y );
} }
double distance( const Point& p ) const { double distance( const Point& p ) const {
double a = _x - p._x; double a = _x - p._x;
double b = _y - p._y; double b = _y - p._y;
// Avoid numerical error if possible...
if( a == 0 ) return abs( _y - p._y );
if( b == 0 ) return abs( _x - p._x );
return sqrt( ( a * a ) + ( b * b ) ); return sqrt( ( a * a ) + ( b * b ) );
} }
/**
* Distance method that compares x or y coords when other direction is zero,
* avoids numerical error when distances are very close to radius b ut axis-aligned.
*
* An example of the problem is:
* (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0 001 in double arithmetic
* but:
* 51.9999 + 0.0001 <= 52.0
*
* This avoids some (but not all!) suprising results in $center que ries where points are
* ( radius + center.x, center.y ) or vice-versa.
*/
bool distanceWithin( const Point& p, double radius ) const {
double a = _x - p._x;
double b = _y - p._y;
if( a == 0 ) {
//
// Note: For some, unknown reason, when a 32-bit g++ optim izes this call, the sum is
// calculated imprecisely. We need to force the compiler t o always evaluate it correctly,
// hence the weirdness.
//
// On some 32-bit linux machines, removing the volatile key word or calculating the sum inline
// will make certain geo tests fail. Of course this check will force volatile for all 32-bit systems,
// not just affected systems.
if( sizeof(void*) <= 4 ){
volatile double sum = _y > p._y ? p._y + radius : _y + radius;
return _y > p._y ? sum >= _y : sum >= p._y;
}
else {
// Original math, correct for most systems
return _y > p._y ? p._y + radius >= _y : _y + radius >= p._y;
}
}
if( b == 0 ) {
if( sizeof(void*) <= 4 ){
volatile double sum = _x > p._x ? p._x + radius : _x + radius;
return _x > p._x ? sum >= _x : sum >= p._x;
}
else {
return _x > p._x ? p._x + radius >= _x : _x + radius >= p._x;
}
}
return sqrt( ( a * a ) + ( b * b ) ) <= radius;
}
string toString() const { string toString() const {
StringBuilder buf(32); StringBuilder buf(32);
buf << "(" << _x << "," << _y << ")"; buf << "(" << _x << "," << _y << ")";
return buf.str(); return buf.str();
} }
double _x; double _x;
double _y; double _y;
}; };
extern const double EARTH_RADIUS_KM; extern const double EARTH_RADIUS_KM;
extern const double EARTH_RADIUS_MILES; extern const double EARTH_RADIUS_MILES;
// Technically lat/long bounds, not really tied to earth radius.
inline void checkEarthBounds( Point p ) {
uassert( 14808, str::stream() << "point " << p.toString() << " must be in earth-like bounds of long : [-180, 180), lat : [-90, 90] ",
p._x >= -180 && p._x < 180 && p._y >= -90 && p._y <= 90 );
}
inline double deg2rad(double deg) { return deg * (M_PI/180); } inline double deg2rad(double deg) { return deg * (M_PI/180); }
inline double rad2deg(double rad) { return rad * (180/M_PI); } inline double rad2deg(double rad) { return rad * (180/M_PI); }
// WARNING: _x and _y MUST be longitude and latitude in that order // WARNING: _x and _y MUST be longitude and latitude in that order
// note: multiply by earth radius for distance // note: multiply by earth radius for distance
inline double spheredist_rad( const Point& p1, const Point& p2 ) { inline double spheredist_rad( const Point& p1, const Point& p2 ) {
// this uses the n-vector formula: http://en.wikipedia.org/wiki/N-v ector // this uses the n-vector formula: http://en.wikipedia.org/wiki/N-v ector
// If you try to match the code to the formula, note that I inline the cross-product. // If you try to match the code to the formula, note that I inline the cross-product.
// TODO: optimize with SSE // TODO: optimize with SSE
double sin_x1(sin(p1._x)), cos_x1(cos(p1._x)); double sin_x1(sin(p1._x)), cos_x1(cos(p1._x));
double sin_y1(sin(p1._y)), cos_y1(cos(p1._y)); double sin_y1(sin(p1._y)), cos_y1(cos(p1._y));
double sin_x2(sin(p2._x)), cos_x2(cos(p2._x)); double sin_x2(sin(p2._x)), cos_x2(cos(p2._x));
double sin_y2(sin(p2._y)), cos_y2(cos(p2._y)); double sin_y2(sin(p2._y)), cos_y2(cos(p2._y));
double cross_prod = double cross_prod =
(cos_y1*cos_x1 * cos_y2*cos_x2) + (cos_y1*cos_x1 * cos_y2*cos_x2) +
(cos_y1*sin_x1 * cos_y2*sin_x2) + (cos_y1*sin_x1 * cos_y2*sin_x2) +
(sin_y1 * sin_y2); (sin_y1 * sin_y2);
if (cross_prod >= 1 || cross_prod <= -1){ if (cross_prod >= 1 || cross_prod <= -1) {
// fun with floats // fun with floats
assert( fabs(cross_prod)-1 < 1e-6 ); assert( fabs(cross_prod)-1 < 1e-6 );
return cross_prod > 0 ? 0 : M_PI; return cross_prod > 0 ? 0 : M_PI;
} }
return acos(cross_prod); return acos(cross_prod);
} }
// note: return is still in radians as that can be multiplied by radius to get arc length // note: return is still in radians as that can be multiplied by radius to get arc length
inline double spheredist_deg( const Point& p1, const Point& p2 ) { inline double spheredist_deg( const Point& p1, const Point& p2 ) {
return spheredist_rad( return spheredist_rad(
Point( deg2rad(p1._x), deg2rad(p1._y) ), Point( deg2rad(p1._x), deg2rad(p1._y) ),
Point( deg2rad(p2._x), deg2rad(p2._y) ) Point( deg2rad(p2._x), deg2rad(p2._y) )
); );
} }
} }
 End of changes. 47 change blocks. 
46 lines changed or deleted 130 lines changed or added


 counters.h   counters.h 
skipping to change at line 22 skipping to change at line 22
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../../pch.h" #include "../../pch.h"
#include "../jsobj.h" #include "../jsobj.h"
#include "../../util/message.h" #include "../../util/net/message.h"
#include "../../util/processinfo.h" #include "../../util/processinfo.h"
#include "../../util/concurrency/spin_lock.h"
namespace mongo { namespace mongo {
/** /**
* for storing operation counters * for storing operation counters
* note: not thread safe. ok with that for speed * note: not thread safe. ok with that for speed
*/ */
class OpCounters { class OpCounters {
public: public:
OpCounters(); OpCounters();
AtomicUInt * getInsert(){ return _insert; } AtomicUInt * getInsert() { return _insert; }
AtomicUInt * getQuery(){ return _query; } AtomicUInt * getQuery() { return _query; }
AtomicUInt * getUpdate(){ return _update; } AtomicUInt * getUpdate() { return _update; }
AtomicUInt * getDelete(){ return _delete; } AtomicUInt * getDelete() { return _delete; }
AtomicUInt * getGetMore(){ return _getmore; } AtomicUInt * getGetMore() { return _getmore; }
AtomicUInt * getCommand(){ return _command; } AtomicUInt * getCommand() { return _command; }
void gotInsert(){ _insert[0]++; } void incInsertInWriteLock(int n) { _insert->x += n; }
void gotQuery(){ _query[0]++; } void gotInsert() { _insert[0]++; }
void gotUpdate(){ _update[0]++; } void gotQuery() { _query[0]++; }
void gotDelete(){ _delete[0]++; } void gotUpdate() { _update[0]++; }
void gotGetMore(){ _getmore[0]++; } void gotDelete() { _delete[0]++; }
void gotCommand(){ _command[0]++; } void gotGetMore() { _getmore[0]++; }
void gotCommand() { _command[0]++; }
void gotOp( int op , bool isCommand ); void gotOp( int op , bool isCommand );
BSONObj& getObj(){ return _obj; } BSONObj& getObj();
private: private:
BSONObj _obj; BSONObj _obj;
// todo: there will be a lot of cache line contention on these. ne ed to do something
// else eventually.
AtomicUInt * _insert; AtomicUInt * _insert;
AtomicUInt * _query; AtomicUInt * _query;
AtomicUInt * _update; AtomicUInt * _update;
AtomicUInt * _delete; AtomicUInt * _delete;
AtomicUInt * _getmore; AtomicUInt * _getmore;
AtomicUInt * _command; AtomicUInt * _command;
}; };
extern OpCounters globalOpCounters; extern OpCounters globalOpCounters;
extern OpCounters replOpCounters;
class IndexCounters { class IndexCounters {
public: public:
IndexCounters(); IndexCounters();
void btree( char * node ){ void btree( char * node ) {
if ( ! _memSupported ) if ( ! _memSupported )
return; return;
if ( _sampling++ % _samplingrate ) if ( _sampling++ % _samplingrate )
return; return;
btree( _pi.blockInMemory( node ) ); btree( _pi.blockInMemory( node ) );
} }
void btree( bool memHit ){ void btree( bool memHit ) {
if ( memHit ) if ( memHit )
_btreeMemHits++; _btreeMemHits++;
else else
_btreeMemMisses++; _btreeMemMisses++;
_btreeAccesses++; _btreeAccesses++;
} }
void btreeHit(){ _btreeMemHits++; _btreeAccesses++; } void btreeHit() { _btreeMemHits++; _btreeAccesses++; }
void btreeMiss(){ _btreeMemMisses++; _btreeAccesses++; } void btreeMiss() { _btreeMemMisses++; _btreeAccesses++; }
void append( BSONObjBuilder& b ); void append( BSONObjBuilder& b );
private: private:
ProcessInfo _pi; ProcessInfo _pi;
bool _memSupported; bool _memSupported;
int _sampling; int _sampling;
int _samplingrate; int _samplingrate;
skipping to change at line 132 skipping to change at line 139
class GenericCounter { class GenericCounter {
public: public:
GenericCounter() : _mutex("GenericCounter") { } GenericCounter() : _mutex("GenericCounter") { }
void hit( const string& name , int count=0 ); void hit( const string& name , int count=0 );
BSONObj getObj(); BSONObj getObj();
private: private:
map<string,long long> _counts; // TODO: replace with thread safe ma p map<string,long long> _counts; // TODO: replace with thread safe ma p
mongo::mutex _mutex; mongo::mutex _mutex;
}; };
class NetworkCounter {
public:
NetworkCounter() : _bytesIn(0), _bytesOut(0), _requests(0), _overfl ows(0) {}
void hit( long long bytesIn , long long bytesOut );
void append( BSONObjBuilder& b );
private:
long long _bytesIn;
long long _bytesOut;
long long _requests;
long long _overflows;
SpinLock _lock;
};
extern NetworkCounter networkCounter;
} }
 End of changes. 11 change blocks. 
18 lines changed or deleted 42 lines changed or added


 curop.h   curop.h 
// curop.h // @file curop.h
/* /*
* Copyright (C) 2010 10gen Inc. * Copyright (C) 2010 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3, * it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "namespace.h" #include "namespace-inl.h"
#include "client.h" #include "client.h"
#include "../bson/util/atomic_int.h" #include "../bson/util/atomic_int.h"
#include "../util/concurrency/spin_lock.h" #include "../util/concurrency/spin_lock.h"
#include "db.h" #include "../util/time_support.h"
#include "../util/net/hostandport.h"
namespace mongo { namespace mongo {
class CurOp;
/* lifespan is different than CurOp because of recursives with DBDirect Client */ /* lifespan is different than CurOp because of recursives with DBDirect Client */
class OpDebug { class OpDebug {
public: public:
StringBuilder str; OpDebug() : ns(""){ reset(); }
void reset(){ void reset();
str.reset();
} string toString() const;
void append( const CurOp& curop, BSONObjBuilder& b ) const;
// -------------------
StringBuilder extra; // weird things we need to fix later
// basic options
int op;
bool iscommand;
Namespace ns;
BSONObj query;
BSONObj updateobj;
// detailed options
long long cursorid;
int ntoreturn;
int ntoskip;
bool exhaust;
// debugging/profile info
int nscanned;
bool idhack;
bool scanAndOrder;
bool moved;
bool fastmod;
bool fastmodinsert;
bool upsert;
unsigned keyUpdates;
// error handling
ExceptionInfo exceptionInfo;
// response info
int executionTime;
int nreturned;
int responseLength;
}; };
/**
* stores a copy of a bson obj in a fixed size buffer
* if its too big for the buffer, says "too big"
* useful for keeping a copy around indefinitely without wasting a lot of space or doing malloc
*/
class CachedBSONObj { class CachedBSONObj {
public: public:
enum { TOO_BIG_SENTINEL = 1 } ;
static BSONObj _tooBig; // { $msg : "query not recording (too large )" } static BSONObj _tooBig; // { $msg : "query not recording (too large )" }
CachedBSONObj(){ CachedBSONObj() {
_size = (int*)_buf; _size = (int*)_buf;
reset(); reset();
} }
void reset( int sz = 0 ){ void reset( int sz = 0 ) {
_size[0] = sz; _lock.lock();
_reset( sz );
_lock.unlock();
} }
void set( const BSONObj& o ){ void set( const BSONObj& o ) {
_lock.lock(); _lock.lock();
try { try {
int sz = o.objsize(); int sz = o.objsize();
if ( sz > (int) sizeof(_buf) ) { if ( sz > (int) sizeof(_buf) ) {
reset(1); // flag as too big and return _reset(TOO_BIG_SENTINEL);
} }
else { else {
memcpy(_buf, o.objdata(), sz ); memcpy(_buf, o.objdata(), sz );
} }
_lock.unlock(); _lock.unlock();
} }
catch ( ... ){ catch ( ... ) {
_lock.unlock(); _lock.unlock();
throw; throw;
} }
} }
int size() const { return *_size; } int size() const { return *_size; }
bool have() const { return size() > 0; } bool have() const { return size() > 0; }
BSONObj get( bool threadSafe ){ BSONObj get() const {
_lock.lock(); _lock.lock();
BSONObj o; BSONObj o;
try { try {
o = _get( threadSafe ); o = _get();
_lock.unlock(); _lock.unlock();
} }
catch ( ... ){ catch ( ... ) {
_lock.unlock(); _lock.unlock();
throw; throw;
} }
return o; return o;
} }
void append( BSONObjBuilder& b , const StringData& name ){ void append( BSONObjBuilder& b , const StringData& name ) const {
_lock.lock(); scoped_spinlock lk(_lock);
try { BSONObj temp = _get();
b.append( name , _get( false ) ); b.append( name , temp );
_lock.unlock();
}
catch ( ... ){
_lock.unlock();
throw;
}
} }
private: private:
/** you have to be locked when you call this */ /** you have to be locked when you call this */
BSONObj _get( bool getCopy ){ BSONObj _get() const {
int sz = size(); int sz = size();
if ( sz == 0 ) if ( sz == 0 )
return BSONObj(); return BSONObj();
if ( sz == TOO_BIG_SENTINEL )
if ( sz == 1 )
return _tooBig; return _tooBig;
return BSONObj( _buf ).copy(); return BSONObj( _buf ).copy();
} }
SpinLock _lock; /** you have to be locked when you call this */
char _buf[512]; void _reset( int sz ) { _size[0] = sz; }
mutable SpinLock _lock;
int * _size; int * _size;
char _buf[512];
}; };
/* Current operation (for the current Client). /* Current operation (for the current Client).
an embedded member of Client class, and typically used from within t he mutex there. */ an embedded member of Client class, and typically used from within t he mutex there.
*/
class CurOp : boost::noncopyable { class CurOp : boost::noncopyable {
static AtomicUInt _nextOpNum;
Client * _client;
CurOp * _wrapped;
unsigned long long _start;
unsigned long long _checkpoint;
unsigned long long _end;
bool _active;
int _op;
bool _command;
int _lockType; // see concurrency.h for values
bool _waitingForLock;
int _dbprofile; // 0=off, 1=slow, 2=all
AtomicUInt _opNum;
char _ns[Namespace::MaxNsLen+2];
struct SockAddr _remote;
CachedBSONObj _query;
OpDebug _debug;
ThreadSafeString _message;
ProgressMeter _progressMeter;
void _reset(){
_command = false;
_lockType = 0;
_dbprofile = 0;
_end = 0;
_waitingForLock = false;
_message = "";
_progressMeter.finished();
}
void setNS(const char *ns) {
strncpy(_ns, ns, Namespace::MaxNsLen);
}
public: public:
CurOp( Client * client , CurOp * wrapped = 0 );
~CurOp();
bool haveQuery() const { return _query.have(); } bool haveQuery() const { return _query.have(); }
BSONObj query( bool threadSafe = false ){ return _query.get( thread Safe ); } BSONObj query() { return _query.get(); }
void appendQuery( BSONObjBuilder& b , const StringData& name ) cons t { _query.append( b , name ); }
void ensureStarted(){ void ensureStarted() {
if ( _start == 0 ) if ( _start == 0 )
_start = _checkpoint = curTimeMicros64(); _start = _checkpoint = curTimeMicros64();
} }
void enter( Client::Context * context ){ bool isStarted() const { return _start > 0; }
ensureStarted();
setNS( context->ns() );
if ( context->_db && context->_db->profile > _dbprofile )
_dbprofile = context->_db->profile;
}
void leave( Client::Context * context ){ void enter( Client::Context * context );
unsigned long long now = curTimeMicros64();
Top::global.record( _ns , _op , _lockType , now - _checkpoint , _command );
_checkpoint = now;
}
void reset(){ void leave( Client::Context * context );
void reset() {
_reset(); _reset();
_start = _checkpoint = 0; _start = _checkpoint = 0;
_active = true;
_opNum = _nextOpNum++; _opNum = _nextOpNum++;
_ns[0] = '?'; // just in case not set later _ns[0] = 0;
_debug.reset(); _debug.reset();
_query.reset(); _query.reset();
_active = true; // this should be last for ui clarity
} }
void reset( const SockAddr & remote, int op ) { void reset( const HostAndPort& remote, int op ) {
reset(); reset();
_remote = remote; _remote = remote;
_op = op; _op = op;
} }
void markCommand(){ void markCommand() { _command = true; }
_command = true;
}
void waitingForLock( int type ){ void waitingForLock( int type ) {
_waitingForLock = true; _waitingForLock = true;
if ( type > 0 ) if ( type > 0 )
_lockType = 1; _lockType = 1;
else else
_lockType = -1; _lockType = -1;
} }
void gotLock(){ void gotLock() { _waitingForLock = false; }
_waitingForLock = false; OpDebug& debug() { return _debug; }
} int profileLevel() const { return _dbprofile; }
const char * getNS() const { return _ns; }
OpDebug& debug(){
return _debug;
}
int profileLevel() const {
return _dbprofile;
}
const char * getNS() const {
return _ns;
}
bool shouldDBProfile( int ms ) const { bool shouldDBProfile( int ms ) const {
if ( _dbprofile <= 0 ) if ( _dbprofile <= 0 )
return false; return false;
return _dbprofile >= 2 || ms >= cmdLine.slowMS; return _dbprofile >= 2 || ms >= cmdLine.slowMS;
} }
AtomicUInt opNum() const { return _opNum; } AtomicUInt opNum() const { return _opNum; }
skipping to change at line 265 skipping to change at line 246
void done() { void done() {
_active = false; _active = false;
_end = curTimeMicros64(); _end = curTimeMicros64();
} }
unsigned long long totalTimeMicros() { unsigned long long totalTimeMicros() {
massert( 12601 , "CurOp not marked done yet" , ! _active ); massert( 12601 , "CurOp not marked done yet" , ! _active );
return _end - startTime(); return _end - startTime();
} }
int totalTimeMillis() { int totalTimeMillis() { return (int) (totalTimeMicros() / 1000); }
return (int) (totalTimeMicros() / 1000);
}
int elapsedMillis() { int elapsedMillis() {
unsigned long long total = curTimeMicros64() - startTime(); unsigned long long total = curTimeMicros64() - startTime();
return (int) (total / 1000); return (int) (total / 1000);
} }
int elapsedSeconds() { int elapsedSeconds() { return elapsedMillis() / 1000; }
return elapsedMillis() / 1000;
}
void setQuery(const BSONObj& query) {
_query.set( query );
}
Client * getClient() const {
return _client;
}
CurOp( Client * client , CurOp * wrapped = 0 ) { void setQuery(const BSONObj& query) { _query.set( query ); }
_client = client;
_wrapped = wrapped;
if ( _wrapped ){
_client->_curOp = this;
}
_start = _checkpoint = 0;
_active = false;
_reset();
_op = 0;
// These addresses should never be written to again. The zeroe s are
// placed here as a precaution because currentOp may be accesse d
// without the db mutex.
memset(_ns, 0, sizeof(_ns));
}
~CurOp(); Client * getClient() const { return _client; }
BSONObj info() { BSONObj info() {
if( ! cc().getAuthenticationInfo()->isAuthorized("admin") ) { if( ! cc().getAuthenticationInfo()->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();
string getRemoteString( bool includePort = true ){ string getRemoteString( bool includePort = true ) { return _remote. toString(includePort); }
return _remote.toString(includePort);
}
ProgressMeter& setMessage( const char * msg , long long progressMet erTotal = 0 , int secondsBetween = 3 ){
if ( progressMeterTotal ){ ProgressMeter& setMessage( const char * msg , unsigned long long pr ogressMeterTotal = 0 , int secondsBetween = 3 ) {
if ( _progressMeter.isActive() ){ if ( progressMeterTotal ) {
if ( _progressMeter.isActive() ) {
cout << "about to assert, old _message: " << _message < < " new message:" << msg << endl; cout << "about to assert, old _message: " << _message < < " new message:" << msg << endl;
assert( ! _progressMeter.isActive() ); assert( ! _progressMeter.isActive() );
} }
_progressMeter.reset( progressMeterTotal , secondsBetween ) ; _progressMeter.reset( progressMeterTotal , secondsBetween ) ;
} }
else { else {
_progressMeter.finished(); _progressMeter.finished();
} }
_message = msg; _message = msg;
return _progressMeter; return _progressMeter;
} }
string getMessage() const { return _message.toString(); } string getMessage() const { return _message.toString(); }
ProgressMeter& getProgressMeter() { return _progressMeter; } ProgressMeter& getProgressMeter() { return _progressMeter; }
CurOp *parent() const { return _wrapped; }
void kill() { _killed = true; }
bool killed() const { return _killed; }
void yielded() { _numYields++; }
void setNS(const char *ns) {
strncpy(_ns, ns, Namespace::MaxNsLen);
_ns[Namespace::MaxNsLen] = 0;
}
friend class Client; friend class Client;
private:
static AtomicUInt _nextOpNum;
Client * _client;
CurOp * _wrapped;
unsigned long long _start;
unsigned long long _checkpoint;
unsigned long long _end;
bool _active;
int _op;
bool _command;
int _lockType; // see concurrency.h for values
bool _waitingForLock;
int _dbprofile; // 0=off, 1=slow, 2=all
AtomicUInt _opNum;
char _ns[Namespace::MaxNsLen+2];
HostAndPort _remote;
CachedBSONObj _query;
OpDebug _debug;
ThreadSafeString _message;
ProgressMeter _progressMeter;
volatile bool _killed;
int _numYields;
void _reset() {
_command = false;
_lockType = 0;
_dbprofile = 0;
_end = 0;
_waitingForLock = false;
_message = "";
_progressMeter.finished();
_killed = false;
_numYields = 0;
}
}; };
/* 0 = ok /* _globalKill: we are shutting down
1 = kill current operation and reset this to 0 otherwise kill attribute set on specified CurOp
future: maybe use this as a "going away" thing on process terminatio n with a higher flag value this class does not handle races between interruptJs and the checkFo rInterrupt functions - those must be
handled by the client of this class
*/ */
extern class KillCurrentOp { extern class KillCurrentOp {
enum { Off, On, All } state;
AtomicUInt toKill;
public: public:
void killAll() { state = All; } void killAll();
void kill(AtomicUInt i) { toKill = i; state = On; } void kill(AtomicUInt i);
void checkForInterrupt() { /** @return true if global interrupt and should terminate the opera tion */
if( state != Off ) { bool globalInterruptCheck() const { return _globalKill; }
if( state == All )
uasserted(11600,"interrupted at shutdown"); void checkForInterrupt( bool heedMutex = true ) {
if( cc().curop()->opNum() == toKill ) { if ( heedMutex && dbMutex.isWriteLocked() )
state = Off; return;
uasserted(11601,"interrupted"); if( _globalKill )
} uasserted(11600,"interrupted at shutdown");
} if( cc().curop()->killed() )
uasserted(11601,"interrupted");
} }
/** @return "" if not interrupted. otherwise, you should stop. */
const char *checkForInterruptNoAssert( bool heedMutex = true ) {
if ( heedMutex && dbMutex.isWriteLocked() )
return "";
if( _globalKill )
return "interrupted at shutdown";
if( cc().curop()->killed() )
return "interrupted";
return "";
}
private:
void interruptJs( AtomicUInt *op );
volatile bool _globalKill;
} killCurrentOp; } killCurrentOp;
} }
 End of changes. 57 change blocks. 
169 lines changed or deleted 179 lines changed or added


 cursor.h   cursor.h 
skipping to change at line 27 skipping to change at line 27
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "jsobj.h" #include "jsobj.h"
#include "diskloc.h" #include "diskloc.h"
#include "matcher.h" #include "matcher.h"
namespace mongo { namespace mongo {
class NamespaceDetails;
class Record; class Record;
class CoveredIndexMatcher; class CoveredIndexMatcher;
/* Query cursors, base class. This is for our internal cursors. "Clie ntCursor" is a separate /* Query cursors, base class. This is for our internal cursors. "Clie ntCursor" is a separate
concept and is for the user's cursor. concept and is for the user's cursor.
WARNING concurrency: the vfunctions below are called back from withi n a WARNING concurrency: the vfunctions below are called back from withi n a
ClientCursor::ccmutex. Don't cause a deadlock, you've been warned. ClientCursor::ccmutex. Don't cause a deadlock, you've been warned.
*/ */
class Cursor : boost::noncopyable { class Cursor : boost::noncopyable {
skipping to change at line 72 skipping to change at line 73
/* optional to implement. if implemented, means 'this' is a protot ype */ /* optional to implement. if implemented, means 'this' is a protot ype */
virtual Cursor* clone() { virtual Cursor* clone() {
return 0; return 0;
} }
virtual BSONObj indexKeyPattern() { virtual BSONObj indexKeyPattern() {
return BSONObj(); return BSONObj();
} }
virtual bool supportGetMore() = 0;
/* called after every query block is iterated -- i.e. between getMo re() blocks /* called after every query block is iterated -- i.e. between getMo re() blocks
so you can note where we are, if necessary. so you can note where we are, if necessary.
*/ */
virtual void noteLocation() { } virtual void noteLocation() { }
/* called before query getmore block is iterated */ /* called before query getmore block is iterated */
virtual void checkLocation() { } virtual void checkLocation() { }
virtual bool supportGetMore() = 0;
virtual bool supportYields() = 0; virtual bool supportYields() = 0;
/** Called before a ClientCursor yield. */
virtual bool prepareToYield() { noteLocation(); return supportYield s(); }
/** Called after a ClientCursor yield. */
virtual void recoverFromYield() { checkLocation(); }
virtual string toString() { return "abstract?"; } virtual string toString() { return "abstract?"; }
/* used for multikey index traversal to avoid sending back dups. se e Matcher::matches(). /* used for multikey index traversal to avoid sending back dups. se e Matcher::matches().
if a multikey index traversal: if a multikey index traversal:
if loc has already been sent, returns true. if loc has already been sent, returns true.
otherwise, marks loc as sent. otherwise, marks loc as sent.
@param deep - match was against an array, so we know it is multi key. this is legacy and kept
for backwards datafile compatibility. 'deep' can be eliminated next time we
force a data file conversion. 7Jul09
*/ */
virtual bool getsetdup(DiskLoc loc) = 0; virtual bool getsetdup(DiskLoc loc) = 0;
virtual bool isMultiKey() const = 0;
/**
* return true if the keys in the index have been modified from the main doc
* if you have { a : 1 , b : [ 1 , 2 ] }
* an index on { a : 1 } would not be modified
* an index on { b : 1 } would be since the values of the array are put in the index
* not the array
*/
virtual bool modifiedKeys() const = 0;
virtual BSONObj prettyIndexBounds() const { return BSONArray(); } virtual BSONObj prettyIndexBounds() const { return BSONArray(); }
virtual bool capped() const { return false; } virtual bool capped() const { return false; }
virtual long long nscanned() = 0; virtual long long nscanned() = 0;
// The implementation may return different matchers depending on th e // The implementation may return different matchers depending on th e
// position of the cursor. If matcher() is nonzero at the start, // position of the cursor. If matcher() is nonzero at the start,
// matcher() should be checked each time advance() is called. // matcher() should be checked each time advance() is called.
// Implementations which generate their own matcher should return t his
// to avoid a matcher being set manually.
// Note that the return values differ subtly here
// Used when we want fast matcher lookup
virtual CoveredIndexMatcher *matcher() const { return 0; } virtual CoveredIndexMatcher *matcher() const { return 0; }
// Used when we need to share this matcher with someone else
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn shared_ptr< CoveredIndexMatcher >(); }
// A convenience function for setting the value of matcher() manual ly // A convenience function for setting the value of matcher() manual ly
// so it may accessed later. Implementations which must generate // so it may accessed later. Implementations which must generate
// their own matcher() should assert here. // their own matcher() should assert here.
virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) {
massert( 13285, "manual matcher config not allowed", false ); massert( 13285, "manual matcher config not allowed", false );
} }
virtual void explainDetails( BSONObjBuilder& b ) { return; }
}; };
// strategy object implementing direction of traversal. // strategy object implementing direction of traversal.
class AdvanceStrategy { class AdvanceStrategy {
public: public:
virtual ~AdvanceStrategy() { } virtual ~AdvanceStrategy() { }
virtual DiskLoc next( const DiskLoc &prev ) const = 0; virtual DiskLoc next( const DiskLoc &prev ) const = 0;
}; };
const AdvanceStrategy *forward(); const AdvanceStrategy *forward();
const AdvanceStrategy *reverse(); const AdvanceStrategy *reverse();
/* table-scan style cursor */ /* table-scan style cursor */
class BasicCursor : public Cursor { class BasicCursor : public Cursor {
protected:
DiskLoc curr, last;
const AdvanceStrategy *s;
void incNscanned() { if ( !curr.isNull() ) { ++_nscanned; } }
private:
bool tailable_;
shared_ptr< CoveredIndexMatcher > _matcher;
long long _nscanned;
void init() {
tailable_ = false;
}
public: public:
bool ok() { BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : cu rr(dl), s( _s ), _nscanned() {
return !curr.isNull(); incNscanned();
init();
}
BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nsca nned() {
init();
} }
bool ok() { return !curr.isNull(); }
Record* _current() { Record* _current() {
assert( ok() ); assert( ok() );
return curr.rec(); return curr.rec();
} }
BSONObj current() { BSONObj current() {
Record *r = _current(); Record *r = _current();
BSONObj j(r); BSONObj j(r);
return j; return j;
} }
virtual DiskLoc currLoc() { virtual DiskLoc currLoc() { return curr; }
return curr; virtual DiskLoc refLoc() { return curr.isNull() ? last : curr; }
}
virtual DiskLoc refLoc() {
return curr.isNull() ? last : curr;
}
bool advance(); bool advance();
virtual string toString() { return "BasicCursor"; }
BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : cu rr(dl), s( _s ), _nscanned() {
incNscanned();
init();
}
BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nsca nned() {
init();
}
virtual string toString() {
return "BasicCursor";
}
virtual void setTailable() { virtual void setTailable() {
if ( !curr.isNull() || !last.isNull() ) if ( !curr.isNull() || !last.isNull() )
tailable_ = true; tailable_ = true;
} }
virtual bool tailable() { virtual bool tailable() { return tailable_; }
return tailable_;
}
virtual bool getsetdup(DiskLoc loc) { return false; } virtual bool getsetdup(DiskLoc loc) { return false; }
virtual bool isMultiKey() const { return false; }
virtual bool modifiedKeys() const { return false; }
virtual bool supportGetMore() { return true; } virtual bool supportGetMore() { return true; }
virtual bool supportYields() { return true; } virtual bool supportYields() { return true; }
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); } virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); }
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; }
virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher ) { _matcher = matcher; }
_matcher = matcher;
}
virtual long long nscanned() { return _nscanned; } virtual long long nscanned() { return _nscanned; }
protected:
DiskLoc curr, last;
const AdvanceStrategy *s;
void incNscanned() { if ( !curr.isNull() ) { ++_nscanned; } }
private:
bool tailable_;
shared_ptr< CoveredIndexMatcher > _matcher;
long long _nscanned;
void init() { tailable_ = false; }
}; };
/* used for order { $natural: -1 } */ /* used for order { $natural: -1 } */
class ReverseCursor : public BasicCursor { class ReverseCursor : public BasicCursor {
public: public:
ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { } ReverseCursor(DiskLoc dl) : BasicCursor( dl, reverse() ) { }
ReverseCursor() : BasicCursor( reverse() ) { } ReverseCursor() : BasicCursor( reverse() ) { }
virtual string toString() { virtual string toString() { return "ReverseCursor"; }
return "ReverseCursor";
}
}; };
class NamespaceDetails;
class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy { class ForwardCappedCursor : public BasicCursor, public AdvanceStrategy {
public: public:
ForwardCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &star tLoc = DiskLoc() ); ForwardCappedCursor( NamespaceDetails *nsd = 0, const DiskLoc &star tLoc = DiskLoc() );
virtual string toString() { virtual string toString() {
return "ForwardCappedCursor"; return "ForwardCappedCursor";
} }
virtual DiskLoc next( const DiskLoc &prev ) const; virtual DiskLoc next( const DiskLoc &prev ) const;
virtual bool capped() const { return true; } virtual bool capped() const { return true; }
private: private:
NamespaceDetails *nsd; NamespaceDetails *nsd;
 End of changes. 21 change blocks. 
51 lines changed or deleted 54 lines changed or added


 cursors.h   cursors.h 
skipping to change at line 41 skipping to change at line 41
class ShardedClientCursor : boost::noncopyable { class ShardedClientCursor : boost::noncopyable {
public: public:
ShardedClientCursor( QueryMessage& q , ClusteredCursor * cursor ); ShardedClientCursor( QueryMessage& q , ClusteredCursor * cursor );
virtual ~ShardedClientCursor(); virtual ~ShardedClientCursor();
long long getId(); long long getId();
/** /**
* @return whether there is more data left * @return whether there is more data left
*/ */
bool sendNextBatch( Request& r ){ return sendNextBatch( r , _ntoret urn ); } bool sendNextBatch( Request& r ) { return sendNextBatch( r , _ntore turn ); }
bool sendNextBatch( Request& r , int ntoreturn ); bool sendNextBatch( Request& r , int ntoreturn );
void accessed(); void accessed();
/** @return idle time in ms */ /** @return idle time in ms */
long long idleTime( long long now ); long long idleTime( long long now );
protected: protected:
ClusteredCursor * _cursor; ClusteredCursor * _cursor;
skipping to change at line 76 skipping to change at line 76
public: public:
static long long TIMEOUT; static long long TIMEOUT;
typedef map<long long,ShardedClientCursorPtr> MapSharded; typedef map<long long,ShardedClientCursorPtr> MapSharded;
typedef map<long long,string> MapNormal; typedef map<long long,string> MapNormal;
CursorCache(); CursorCache();
~CursorCache(); ~CursorCache();
ShardedClientCursorPtr get( long long id ); ShardedClientCursorPtr get( long long id ) const;
void store( ShardedClientCursorPtr cursor ); void store( ShardedClientCursorPtr cursor );
void remove( long long id ); void remove( long long id );
void storeRef( const string& server , long long id ); void storeRef( const string& server , long long id );
/** @return the server for id or "" */
string getRef( long long id ) const ;
void gotKillCursors(Message& m ); void gotKillCursors(Message& m );
void appendInfo( BSONObjBuilder& result ); void appendInfo( BSONObjBuilder& result ) const ;
long long genId(); long long genId();
void doTimeouts(); void doTimeouts();
void startTimeoutThread(); void startTimeoutThread();
private: private:
mutex _mutex; mutable mongo::mutex _mutex;
MapSharded _cursors; MapSharded _cursors;
MapNormal _refs; MapNormal _refs;
long long _shardedTotal; long long _shardedTotal;
static int _myLogLevel;
}; };
extern CursorCache cursorCache; extern CursorCache cursorCache;
} }
 End of changes. 6 change blocks. 
4 lines changed or deleted 9 lines changed or added


 d_logic.h   d_logic.h 
// d_logic.h // @file d_logic.h
/* /*
* Copyright (C) 2010 10gen Inc. * Copyright (C) 2010 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3, * it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
skipping to change at line 21 skipping to change at line 21
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../db/jsobj.h" #include "../db/jsobj.h"
#include "d_chunk_manager.h"
#include "util.h" #include "util.h"
namespace mongo { namespace mongo {
class ShardingState; class Database;
class DiskLoc;
typedef ShardChunkVersion ConfigVersion; typedef ShardChunkVersion ConfigVersion;
typedef map<string,ConfigVersion> NSVersionMap; typedef map<string,ConfigVersion> NSVersionMap;
// -----------
class ChunkMatcher {
typedef map<BSONObj,pair<BSONObj,BSONObj>,BSONObjCmp> MyMap;
public:
bool belongsToMe( const BSONObj& key , const DiskLoc& loc ) const;
private:
ChunkMatcher( ConfigVersion version );
void gotRange( const BSONObj& min , const BSONObj& max );
ConfigVersion _version;
BSONObj _key;
MyMap _map;
friend class ShardingState;
};
typedef shared_ptr<ChunkMatcher> ChunkMatcherPtr;
// -------------- // --------------
// --- global state --- // --- global state ---
// -------------- // --------------
class ShardingState { class ShardingState {
public: public:
ShardingState(); ShardingState();
bool enabled() const { return _enabled; } bool enabled() const { return _enabled; }
const string& getConfigServer() const { return _configServer; } const string& getConfigServer() const { return _configServer; }
void enable( const string& server ); void enable( const string& server );
void gotShardName( const string& name ); void gotShardName( const string& name );
void gotShardHost( const string& host ); void gotShardHost( string host );
/** Reverts back to a state where this mongod is not sharded. */
void resetShardingState();
// versioning support
bool hasVersion( const string& ns ); bool hasVersion( const string& ns );
bool hasVersion( const string& ns , ConfigVersion& version ); bool hasVersion( const string& ns , ConfigVersion& version );
ConfigVersion& getVersion( const string& ns ); // TODO: this is dan geroues const ConfigVersion getVersion( const string& ns ) const;
void setVersion( const string& ns , const ConfigVersion& version );
/**
* Uninstalls the manager for a given collection. This should be us ed when the collection is dropped.
*
* NOTE:
* An existing collection with no chunks on this shard will have a manager on version 0, which is different than a
* a dropped collection, which will not have a manager.
*
* TODO
* When sharding state is enabled, absolutely all collections sho uld have a manager. (The non-sharded ones are
* a be degenerate case of one-chunk collections).
* For now, a dropped collection and an non-sharded one are indis tinguishable (SERVER-1849)
*
* @param ns the collection to be dropped
*/
void resetVersion( const string& ns );
/**
* Requests to access a collection at a certain version. If the col lection's manager is not at that version it
* will try to update itself to the newest version. The request is only granted if the version is the current or
* the newest one.
*
* @param ns collection to be accessed
* @param version (IN) the client belive this collection is on and (OUT) the version the manager is actually in
* @return true if the access can be allowed at the provided versio n
*/
bool trySetVersion( const string& ns , ConfigVersion& version );
void appendInfo( BSONObjBuilder& b ); void appendInfo( BSONObjBuilder& b );
ChunkMatcherPtr getChunkMatcher( const string& ns ); // querying support
bool needShardChunkManager( const string& ns ) const;
ShardChunkManagerPtr getShardChunkManager( const string& ns );
// chunk migrate and split support
/**
* Creates and installs a new chunk manager for a given collection by "forgetting" about one of its chunks.
* The new manager uses the provided version, which has to be highe r than the current manager's.
* One exception: if the forgotten chunk is the last one in this sh ard for the collection, version has to be 0.
*
* If it runs successfully, clients need to grab the new version to access the collection.
*
* @param ns the collection
* @param min max the chunk to eliminate from the current manager
* @param version at which the new manager should be at
*/
void donateChunk( const string& ns , const BSONObj& min , const BSO NObj& max , ShardChunkVersion version );
/**
* Creates and installs a new chunk manager for a given collection by reclaiming a previously donated chunk.
* The previous manager's version has to be provided.
*
* If it runs successfully, clients that became stale by the previo us donateChunk will be able to access the
* collection again.
*
* @param ns the collection
* @param min max the chunk to reclaim and add to the current manag er
* @param version at which the new manager should be at
*/
void undoDonateChunk( const string& ns , const BSONObj& min , const BSONObj& max , ShardChunkVersion version );
/**
* Creates and installs a new chunk manager for a given collection by splitting one of its chunks in two or more.
* The version for the first split chunk should be provided. The su bsequent chunks' version would be the latter with the
* minor portion incremented.
*
* The effect on clients will depend on the version used. If the ma jor portion is the same as the current shards,
* clients shouldn't perceive the split.
*
* @param ns the collection
* @param min max the chunk that should be split
* @param splitKeys point in which to split
* @param version at which the new manager should be at
*/
void splitChunk( const string& ns , const BSONObj& min , const BSON Obj& max , const vector<BSONObj>& splitKeys ,
ShardChunkVersion version );
bool inCriticalMigrateSection(); bool inCriticalMigrateSection();
private:
private:
bool _enabled; bool _enabled;
string _configServer; string _configServer;
string _shardName; string _shardName;
string _shardHost; string _shardHost;
mongo::mutex _mutex; // protects state below
NSVersionMap _versions; mutable mongo::mutex _mutex;
map<string,ChunkMatcherPtr> _chunks;
// map from a namespace into the ensemble of chunk ranges that are stored in this mongod
// a ShardChunkManager carries all state we need for a collection a t this shard, including its version information
typedef map<string,ShardChunkManagerPtr> ChunkManagersMap;
ChunkManagersMap _chunks;
}; };
extern ShardingState shardingState; extern ShardingState shardingState;
// -------------- /**
// --- per connection --- * one per connection from mongos
// -------------- * holds version state for each namesapce
*/
class ShardedConnectionInfo { class ShardedConnectionInfo {
public: public:
ShardedConnectionInfo(); ShardedConnectionInfo();
const OID& getID() const { return _id; } const OID& getID() const { return _id; }
bool hasID() const { return _id.isSet(); } bool hasID() const { return _id.isSet(); }
void setID( const OID& id ); void setID( const OID& id );
ConfigVersion& getVersion( const string& ns ); // TODO: this is dan geroues const ConfigVersion getVersion( const string& ns ) const;
void setVersion( const string& ns , const ConfigVersion& version ); void setVersion( const string& ns , const ConfigVersion& version );
static ShardedConnectionInfo* get( bool create ); static ShardedConnectionInfo* get( bool create );
static void reset(); static void reset();
static void addHook();
bool inForceMode() const { bool inForceVersionOkMode() const {
return _forceMode; return _forceVersionOk;
} }
void enterForceMode(){ _forceMode = true; } void enterForceVersionOkMode() { _forceVersionOk = true; }
void leaveForceMode(){ _forceMode = false; } void leaveForceVersionOkMode() { _forceVersionOk = false; }
private: private:
OID _id; OID _id;
NSVersionMap _versions; NSVersionMap _versions;
bool _forceMode; bool _forceVersionOk; // if this is true, then chunk version #s are n't check, and all ops are allowed
static boost::thread_specific_ptr<ShardedConnectionInfo> _tl; static boost::thread_specific_ptr<ShardedConnectionInfo> _tl;
}; };
struct ShardForceModeBlock { struct ShardForceVersionOkModeBlock {
ShardForceModeBlock(){ ShardForceVersionOkModeBlock() {
info = ShardedConnectionInfo::get( false ); info = ShardedConnectionInfo::get( false );
if ( info ) if ( info )
info->enterForceMode(); info->enterForceVersionOkMode();
} }
~ShardForceModeBlock(){ ~ShardForceVersionOkModeBlock() {
if ( info ) if ( info )
info->leaveForceMode(); info->leaveForceVersionOkMode();
} }
ShardedConnectionInfo * info; ShardedConnectionInfo * info;
}; };
// ----------------- // -----------------
// --- core --- // --- core ---
// ----------------- // -----------------
unsigned long long extractVersion( BSONElement e , string& errmsg ); unsigned long long extractVersion( BSONElement e , string& errmsg );
/** /**
* @return true if we have any shard info for the ns * @return true if we have any shard info for the ns
*/ */
bool haveLocalShardingInfo( const string& ns ); bool haveLocalShardingInfo( const string& ns );
/** /**
* @return true if the current threads shard version is ok, or not in s harded version * @return true if the current threads shard version is ok, or not in s harded version
*/ */
bool shardVersionOk( const string& ns , bool write , string& errmsg ); bool shardVersionOk( const string& ns , string& errmsg );
/** /**
* @return true if we took care of the message and nothing else should be done * @return true if we took care of the message and nothing else should be done
*/ */
bool handlePossibleShardedMessage( Message &m, DbResponse * dbresponse ); struct DbResponse;
void logOpForSharding( const char * opstr , const char * ns , const BSO NObj& obj , BSONObj * patt ); bool _handlePossibleShardedMessage( Message &m, DbResponse * dbresponse );
// ----------------- /** What does this do? document please? */
// --- writeback --- inline bool handlePossibleShardedMessage( Message &m, DbResponse * dbre sponse ) {
// ----------------- if( !shardingState.enabled() )
return false;
return _handlePossibleShardedMessage(m, dbresponse);
}
/* queue a write back on a remote server for a failed write */ void logOpForSharding( const char * opstr , const char * ns , const BSO NObj& obj , BSONObj * patt );
void queueWriteBack( const string& remote , const BSONObj& o ); void aboutToDeleteForSharding( const Database* db , const DiskLoc& dl ) ;
} }
 End of changes. 26 change blocks. 
55 lines changed or deleted 123 lines changed or added


 database.h   database.h 
skipping to change at line 26 skipping to change at line 26
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "cmdline.h" #include "cmdline.h"
namespace mongo { namespace mongo {
class ClientCursor; class ClientCursor;
struct ByLocKey;
typedef map<ByLocKey, ClientCursor*> CCByLoc;
/** /**
* 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, ...
* NOT memory mapped * NOT memory mapped
*/ */
class Database { class Database {
public: public:
static bool _openAllFiles; static bool _openAllFiles;
Database(const char *nm, /*out*/ bool& newDb, const string& _path = dbpath); Database(const char *nm, /*out*/ bool& newDb, const string& _path = dbpath);
private: private:
~Database(); ~Database();
public: public:
/* you must use this to close - there is essential code in this met hod that is not in the ~Database destructor. /* you must use this to close - there is essential code in this met hod that is not in the ~Database destructor.
thus the destructor is private. this could be cleaned up one da y... thus the destructor is private. this could be cleaned up one da y...
*/ */
static void closeDatabase( const char *db, const string& path ); static void closeDatabase( const char *db, const string& path );
void openAllFiles();
/** /**
* tries to make sure that this hasn't been deleted * tries to make sure that this hasn't been deleted
*/ */
bool isOk() const { return magic == 781231; } bool isOk() const { return magic == 781231; }
bool isEmpty(){ bool isEmpty() { return ! namespaceIndex.allocated(); }
return ! namespaceIndex.allocated();
}
boost::filesystem::path fileName( int n ) const { /**
stringstream ss; * total file size of Database in bytes
ss << name << '.' << n; */
boost::filesystem::path fullName; long long fileSize() const;
fullName = boost::filesystem::path(path);
if ( directoryperdb )
fullName /= name;
fullName /= ss.str();
return fullName;
}
bool exists(int n) const { int numFiles() const { return (int)files.size(); }
return boost::filesystem::exists( fileName( n ) );
}
void openAllFiles() { /**
int n = 0; * returns file valid for file number n
while( exists(n) ) { */
getFile(n); boost::filesystem::path fileName( int n ) const;
n++;
}
// If last file is empty, consider it preallocated and make sur e it's not mapped
// until a write is requested
if ( n > 1 && getFile( n - 1 )->getHeader()->isEmpty() ) {
delete files[ n - 1 ];
files.pop_back();
}
}
MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate Only = false ) { bool exists(int n) const { return boost::filesystem::exists( fileNa me( n ) ); }
assert(this);
namespaceIndex.init(); /**
if ( n < 0 || n >= DiskLoc::MaxFiles ) { * return file n. if it doesn't exist, create it
out() << "getFile(): n=" << n << endl; */
#if 0 MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate Only = false );
if( n >= RecCache::Base && n <= RecCache::Base+1000 )
massert( 10294 , "getFile(): bad file number - using re cstore db w/nonrecstore db build?", false);
#endif
massert( 10295 , "getFile(): bad file number value (corrupt db?): run repair", false);
}
DEV {
if ( n > 100 )
out() << "getFile(): n=" << n << "?" << endl;
}
MongoDataFile* p = 0;
if ( !preallocateOnly ) {
while ( n >= (int) files.size() )
files.push_back(0);
p = files[n];
}
if ( p == 0 ) {
boost::filesystem::path fullName = fileName( n );
string fullNameString = fullName.string();
p = new MongoDataFile(n);
int minSize = 0;
if ( n != 0 && files[ n - 1 ] )
minSize = files[ n - 1 ]->getHeader()->fileLength;
if ( sizeNeeded + DataFileHeader::HeaderSize > minSize )
minSize = sizeNeeded + DataFileHeader::HeaderSize;
try {
p->open( fullNameString.c_str(), minSize, preallocateOn ly );
}
catch ( AssertionException& ) {
delete p;
throw;
}
if ( preallocateOnly )
delete p;
else
files[n] = p;
}
return preallocateOnly ? 0 : p;
}
MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile ) { MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile ) ;
int n = (int) files.size();
MongoDataFile *ret = getFile( n, sizeNeeded );
if ( preallocateNextFile )
preallocateAFile();
return ret;
}
// safe to call this multiple times - the implementation will only preallocate one file /**
void preallocateAFile() { * makes sure we have an extra file at the end that is empty
int n = (int) files.size(); * safe to call this multiple times - the implementation will only preallocate one file
getFile( n, 0, true ); */
} void preallocateAFile() { getFile( numFiles() , 0, true ); }
MongoDataFile* suitableFile( int sizeNeeded, bool preallocate ) { MongoDataFile* suitableFile( const char *ns, int sizeNeeded, bool p reallocate, bool enforceQuota );
MongoDataFile* f = newestFile();
if ( !f ) {
f = addAFile( sizeNeeded, preallocate );
}
for ( int i = 0; i < 8; i++ ) {
if ( f->getHeader()->unusedLength >= sizeNeeded )
break;
f = addAFile( sizeNeeded, preallocate );
if ( f->getHeader()->fileLength >= MongoDataFile::maxSize() ) // this is as big as they get so might as well stop
break;
}
return f;
}
Extent* allocExtent( const char *ns, int size, bool capped ) { Extent* allocExtent( const char *ns, int size, bool capped, bool en forceQuota );
Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped );
if( e ) return e;
return suitableFile( size, !capped )->createExtent( ns, size, c apped );
}
MongoDataFile* newestFile() { MongoDataFile* newestFile();
int n = (int) files.size();
if ( n > 0 ) {
n--;
} else {
return 0;
}
return getFile(n);
}
/** /**
* @return true if success. false if bad level or error creating p rofile ns * @return true if success. false if bad level or error creating p rofile ns
*/ */
bool setProfilingLevel( int newLevel , string& errmsg ); bool setProfilingLevel( int newLevel , string& errmsg );
void finishInit(); void flushFiles( bool sync ) const;
/**
* @return true if ns is part of the database
* ns=foo.bar, db=foo returns true
*/
bool ownsNS( const string& ns ) const {
if ( ! startsWith( ns , name ) )
return false;
return ns[name.size()] == '.';
}
static bool validDBName( const string& ns ); static bool validDBName( const string& ns );
long long fileSize(){ /**
long long size=0; * @throws DatabaseDifferCaseCode if the name is a duplicate based on
for (int n=0; exists(n); n++) * case insensitive matching.
size += boost::filesystem::file_size( fileName(n) ); */
return size; void checkDuplicateUncasedNames() const;
}
void flushFiles( bool sync ); /**
* @return name of an existing database with same text name but dif ferent
* casing, if one exists. Otherwise the empty string is returned. If
* 'duplicates' is specified, it is filled with all duplicate names .
*/
static string duplicateUncasedName( const string &name, const strin g &path, set< string > *duplicates = 0 );
public: // this should be private later
vector<MongoDataFile*> files; vector<MongoDataFile*> files;
const string name; // "alleyinsider" const string name; // "alleyinsider"
const string path; const string path;
NamespaceIndex namespaceIndex; NamespaceIndex namespaceIndex;
int profile; // 0=off. int profile; // 0=off.
const string profileName; // "alleyinsider.system.profile" const string profileName; // "alleyinsider.system.profile"
multimap<DiskLoc, ClientCursor*> ccByLoc; CCByLoc ccByLoc;
int magic; // used for making sure the object is still loaded in me mory int magic; // used for making sure the object is still loaded in me mory
}; };
} // namespace mongo } // namespace mongo
 End of changes. 17 change blocks. 
122 lines changed or deleted 53 lines changed or added


 db.h   db.h 
skipping to change at line 20 skipping to change at line 20
* 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 "../pch.h" #include "../pch.h"
#include "../util/message.h" #include "../util/net/message.h"
#include "concurrency.h" #include "concurrency.h"
#include "pdfile.h" #include "pdfile.h"
#include "curop.h"
#include "client.h" #include "client.h"
namespace mongo { namespace mongo {
// void jniCallback(Message& m, Message& out); // void jniCallback(Message& m, Message& out);
/* Note the limit here is rather arbitrary and is simply a standard. ge nerally the code works
with any object that fits in ram.
Also note that the server has some basic checks to enforce this limi t but those checks are not exhaustive
for example need to check for size too big after
update $push (append) operation
various db.eval() type operations
Note also we sometimes do work with objects slightly larger - an obj ect in the replication local.oplog
could be slightly larger.
*/
const int MaxBSONObjectSize = 4 * 1024 * 1024;
/** /**
* class to hold path + dbname -> Database * class to hold path + dbname -> Database
* might be able to optimizer further * might be able to optimizer further
*/ */
class DatabaseHolder { class DatabaseHolder {
public: public:
typedef map<string,Database*> DBs; typedef map<string,Database*> DBs;
typedef map<string,DBs> Paths; typedef map<string,DBs> Paths;
DatabaseHolder() : _size(0){ DatabaseHolder() : _size(0) { }
}
bool isLoaded( const string& ns , const string& path ) const { bool isLoaded( const string& ns , const string& path ) const {
dbMutex.assertAtLeastReadLocked(); dbMutex.assertAtLeastReadLocked();
Paths::const_iterator x = _paths.find( path ); Paths::const_iterator x = _paths.find( path );
if ( x == _paths.end() ) if ( x == _paths.end() )
return false; return false;
const DBs& m = x->second; const DBs& m = x->second;
string db = _todb( ns ); string db = _todb( ns );
skipping to change at line 82 skipping to change at line 69
const DBs& m = x->second; const DBs& m = x->second;
string db = _todb( ns ); string db = _todb( ns );
DBs::const_iterator it = m.find(db); DBs::const_iterator it = m.find(db);
if ( it != m.end() ) if ( it != m.end() )
return it->second; return it->second;
return 0; return 0;
} }
void put( const string& ns , const string& path , Database * db ){ void put( const string& ns , const string& path , Database * db ) {
dbMutex.assertWriteLocked(); dbMutex.assertWriteLocked();
DBs& m = _paths[path]; DBs& m = _paths[path];
Database*& d = m[_todb(ns)]; Database*& d = m[_todb(ns)];
if ( ! d ) if ( ! d )
_size++; _size++;
d = db; d = db;
} }
Database* getOrCreate( const string& ns , const string& path , bool & justCreated ){ Database* getOrCreate( const string& ns , const string& path , bool & justCreated );
dbMutex.assertWriteLocked();
DBs& m = _paths[path];
string dbname = _todb( ns );
Database* & db = m[dbname];
if ( db ){
justCreated = false;
return db;
}
log(1) << "Accessing: " << dbname << " for the first time" << e ndl;
try {
db = new Database( dbname.c_str() , justCreated , path );
}
catch ( ... ){
m.erase( dbname );
throw;
}
_size++;
return db;
}
void erase( const string& ns , const string& path ){ void erase( const string& ns , const string& path ) {
dbMutex.assertWriteLocked(); dbMutex.assertWriteLocked();
DBs& m = _paths[path]; DBs& m = _paths[path];
_size -= (int)m.erase( _todb( ns ) ); _size -= (int)m.erase( _todb( ns ) );
} }
/* force - force close even if something underway - use at shutdown */ /* force - force close even if something underway - use at shutdown */
bool closeAll( const string& path , BSONObjBuilder& result, bool fo rce ); bool closeAll( const string& path , BSONObjBuilder& result, bool fo rce );
int size(){ int size() {
return _size; return _size;
} }
void forEach(boost::function<void(Database *)> f) const { void forEach(boost::function<void(Database *)> f) const {
dbMutex.assertAtLeastReadLocked(); dbMutex.assertAtLeastReadLocked();
for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ){ for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ) {
DBs m = i->second; DBs m = i->second;
for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ){ for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) {
f(j->second); f(j->second);
} }
} }
} }
/** /**
* gets all unique db names, ignoring paths * gets all unique db names, ignoring paths
*/ */
void getAllShortNames( set<string>& all ) const { void getAllShortNames( set<string>& all ) const {
dbMutex.assertAtLeastReadLocked(); dbMutex.assertAtLeastReadLocked();
for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ){ for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); i++ ) {
DBs m = i->second; DBs m = i->second;
for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ){ for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) {
all.insert( j->first ); all.insert( j->first );
} }
} }
} }
private: private:
string _todb( const string& ns ) const { string _todb( const string& ns ) const {
string d = __todb( ns ); string d = __todb( ns );
uassert( 13280 , (string)"invalid db name: " + ns , Database::v alidDBName( d ) ); uassert( 13280 , (string)"invalid db name: " + ns , Database::v alidDBName( d ) );
return d; return d;
} }
string __todb( const string& ns ) const { string __todb( const string& ns ) const {
size_t i = ns.find( '.' ); size_t i = ns.find( '.' );
if ( i == string::npos ){ if ( i == string::npos ) {
uassert( 13074 , "db name can't be empty" , ns.size() ); uassert( 13074 , "db name can't be empty" , ns.size() );
return ns; return ns;
} }
uassert( 13075 , "db name can't be empty" , i > 0 ); uassert( 13075 , "db name can't be empty" , i > 0 );
return ns.substr( 0 , i ); return ns.substr( 0 , i );
} }
Paths _paths; Paths _paths;
int _size; int _size;
}; };
extern DatabaseHolder dbHolder; extern DatabaseHolder dbHolder;
struct dbtemprelease { struct dbtemprelease {
Client::Context * _context; Client::Context * _context;
int _locktype; int _locktype;
dbtemprelease() { dbtemprelease() {
_context = cc().getContext(); const Client& c = cc();
_context = c.getContext();
_locktype = dbMutex.getState(); _locktype = dbMutex.getState();
assert( _locktype ); assert( _locktype );
if ( _locktype > 0 ) { if ( _locktype > 0 ) {
massert( 10298 , "can't temprelease nested w rite lock", _locktype == 1); massert( 10298 , "can't temprelease nested write lock", _lo cktype == 1);
if ( _context ) _context->unlocked(); if ( _context ) _context->unlocked();
dbMutex.unlock(); dbMutex.unlock();
} }
else { else {
massert( 10299 , "can't temprelease nested r ead lock", _locktype == -1); massert( 10299 , "can't temprelease nested read lock", _loc ktype == -1);
if ( _context ) _context->unlocked(); if ( _context ) _context->unlocked();
dbMutex.unlock_shared(); dbMutex.unlock_shared();
} }
verify( 14814 , c.curop() );
c.curop()->yielded();
} }
~dbtemprelease() { ~dbtemprelease() {
if ( _locktype > 0 ) if ( _locktype > 0 )
dbMutex.lock(); dbMutex.lock();
else else
dbMutex.lock_shared(); dbMutex.lock_shared();
if ( _context ) _context->relocked(); if ( _context ) _context->relocked();
} }
}; };
/** must be write locked
no assert (and no release) if nested write lock
a lot like dbtempreleasecond but no malloc so should be a tiny bit faster
*/
struct dbtempreleasewritelock {
Client::Context * _context;
int _locktype;
dbtempreleasewritelock() {
const Client& c = cc();
_context = c.getContext();
_locktype = dbMutex.getState();
assert( _locktype >= 1 );
if( _locktype > 1 )
return; // nested
if ( _context )
_context->unlocked();
dbMutex.unlock();
verify( 14845 , c.curop() );
c.curop()->yielded();
}
~dbtempreleasewritelock() {
if ( _locktype == 1 )
dbMutex.lock();
if ( _context )
_context->relocked();
}
};
/** /**
only does a temp release if we're not nested and have a lock only does a temp release if we're not nested and have a lock
*/ */
struct dbtempreleasecond { struct dbtempreleasecond {
dbtemprelease * real; dbtemprelease * real;
int locktype; int locktype;
dbtempreleasecond(){ dbtempreleasecond() {
real = 0; real = 0;
locktype = dbMutex.getState(); locktype = dbMutex.getState();
if ( locktype == 1 || locktype == -1 ) if ( locktype == 1 || locktype == -1 )
real = new dbtemprelease(); real = new dbtemprelease();
} }
~dbtempreleasecond(){ ~dbtempreleasecond() {
if ( real ){ if ( real ) {
delete real; delete real;
real = 0; real = 0;
} }
} }
bool unlocked(){ bool unlocked() {
return real > 0; return real > 0;
} }
}; };
} // namespace mongo } // namespace mongo
//#include "dbinfo.h" //#include "dbinfo.h"
#include "concurrency.h" #include "concurrency.h"
 End of changes. 22 change blocks. 
56 lines changed or deleted 53 lines changed or added


 dbclient.h   dbclient.h 
/** @file dbclient.h - connect to a Mongo database as a database, from C++ */ /** @file dbclient.h
Core MongoDB C++ driver interfaces are defined here.
*/
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/message.h" #include "../util/net/message.h"
#include "../util/net/message_port.h"
#include "../db/jsobj.h" #include "../db/jsobj.h"
#include "../db/json.h" #include "../db/json.h"
#include <stack> #include <stack>
namespace mongo { namespace mongo {
/** the query field 'options' can have these bits set: */ /** the query field 'options' can have these bits set: */
enum QueryOptions { enum QueryOptions {
/** Tailable means cursor is not closed when the last data is retri eved. rather, the cursor marks /** Tailable means cursor is not closed when the last data is retri eved. rather, the cursor marks
the final object's position. you can resume using the cursor la ter, from where it was located, the final object's position. you can resume using the cursor la ter, from where it was located,
skipping to change at line 73 skipping to change at line 77
/** Stream the data down full blast in multiple "more" packages, on the assumption that the client /** Stream the data down full blast in multiple "more" packages, on the assumption that the client
will fully read all data queried. Faster when you are pulling a lot of data and know you want to will fully read all data queried. Faster when you are pulling a lot of data and know you want to
pull it all down. Note: it is not allowed to not read all the data unless you close the connection. pull it all down. Note: it is not allowed to not read all the data unless you close the connection.
Use the query( boost::function<void(const BSONObj&)> f, ... ) v ersion of the connection's query() Use the query( boost::function<void(const BSONObj&)> f, ... ) v ersion of the connection's query()
method, and it will take care of all the details for you. method, and it will take care of all the details for you.
*/ */
QueryOption_Exhaust = 1 << 6, QueryOption_Exhaust = 1 << 6,
QueryOption_AllSupported = QueryOption_CursorTailable | QueryOption _SlaveOk | QueryOption_OplogReplay | QueryOption_NoCursorTimeout | QueryOpt ion_AwaitData | QueryOption_Exhaust /** When sharded, this means its ok to return partial results
Usually we will fail a query if all required shards aren't up
If this is set, it'll be a partial result set
*/
QueryOption_PartialResults = 1 << 7 ,
QueryOption_AllSupported = QueryOption_CursorTailable | QueryOption _SlaveOk | QueryOption_OplogReplay | QueryOption_NoCursorTimeout | QueryOpt ion_AwaitData | QueryOption_Exhaust | QueryOption_PartialResults
}; };
enum UpdateOptions { enum UpdateOptions {
/** Upsert - that is, insert the item if no matching item is found. */ /** Upsert - that is, insert the item if no matching item is found. */
UpdateOption_Upsert = 1 << 0, UpdateOption_Upsert = 1 << 0,
/** Update multiple documents (if multiple documents match query ex pression). /** Update multiple documents (if multiple documents match query ex pression).
(Default is update a single document and stop.) */ (Default is update a single document and stop.) */
UpdateOption_Multi = 1 << 1, UpdateOption_Multi = 1 << 1,
skipping to change at line 97 skipping to change at line 107
}; };
enum RemoveOptions { enum RemoveOptions {
/** only delete one option */ /** only delete one option */
RemoveOption_JustOne = 1 << 0, RemoveOption_JustOne = 1 << 0,
/** flag from mongo saying this update went everywhere */ /** flag from mongo saying this update went everywhere */
RemoveOption_Broadcast = 1 << 1 RemoveOption_Broadcast = 1 << 1
}; };
/**
* need to put in DbMesssage::ReservedOptions as well
*/
enum InsertOptions {
/** With muli-insert keep processing inserts if one fails */
InsertOption_ContinueOnError = 1 << 0
};
class DBClientBase; class DBClientBase;
/**
* ConnectionString handles parsing different ways to connect to mongo and determining method
* samples:
* server
* server:port
* foo/server:port,server:port SET
* server,server,server SYNC
*
* tyipcal use
* string errmsg,
* ConnectionString cs = ConnectionString::parse( url , errmsg );
* if ( ! cs.isValid() ) throw "bad: " + errmsg;
* DBClientBase * conn = cs.connect( errmsg );
*/
class ConnectionString { class ConnectionString {
public: public:
enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC }; enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC };
ConnectionString( const HostAndPort& server ){ ConnectionString() {
_type = INVALID;
}
ConnectionString( const HostAndPort& server ) {
_type = MASTER; _type = MASTER;
_servers.push_back( server ); _servers.push_back( server );
_finishInit(); _finishInit();
} }
// TODO Delete if nobody is using ConnectionString( ConnectionType type , const string& s , const str ing& setName = "" ) {
//ConnectionString( ConnectionType type , const vector<HostAndPort> & servers )
// : _type( type ) , _servers( servers ){
// _finishInit();
//}
ConnectionString( ConnectionType type , const string& s , const str ing& setName = "" ){
_type = type; _type = type;
_setName = setName; _setName = setName;
_fillServers( s ); _fillServers( s );
switch ( _type ){ switch ( _type ) {
case MASTER: case MASTER:
assert( _servers.size() == 1 ); assert( _servers.size() == 1 );
break; break;
case SET: case SET:
assert( _setName.size() ); assert( _setName.size() );
assert( _servers.size() >= 1 ); // 1 is ok since we can der ive assert( _servers.size() >= 1 ); // 1 is ok since we can der ive
break; break;
case PAIR: case PAIR:
assert( _servers.size() == 2 ); assert( _servers.size() == 2 );
break; break;
default: default:
assert( _servers.size() > 0 ); assert( _servers.size() > 0 );
} }
_finishInit(); _finishInit();
} }
ConnectionString( const string& s , ConnectionType favoredMultipleT ype ){ ConnectionString( const string& s , ConnectionType favoredMultipleT ype ) {
_type = INVALID;
_fillServers( s ); _fillServers( s );
if ( _servers.size() == 1 ){ if ( _type != INVALID ) {
// set already
}
else if ( _servers.size() == 1 ) {
_type = MASTER; _type = MASTER;
} }
else { else {
_type = favoredMultipleType; _type = favoredMultipleType;
assert( _type != MASTER ); assert( _type == SET || _type == SYNC );
} }
_finishInit(); _finishInit();
} }
bool isValid() const { return _type != INVALID; } bool isValid() const { return _type != INVALID; }
string toString() const { string toString() const { return _string; }
return _string;
}
DBClientBase* connect( string& errmsg ) const; DBClientBase* connect( string& errmsg, double socketTimeout = 0 ) c onst;
static ConnectionString parse( const string& url , string& errmsg ) ; string getSetName() const { return _setName; }
string getSetName() const{ vector<HostAndPort> getServers() const { return _servers; }
return _setName;
}
vector<HostAndPort> getServers() const { ConnectionType type() const { return _type; }
return _servers;
}
private: static ConnectionString parse( const string& url , string& errmsg ) ;
ConnectionString(){ static string typeToString( ConnectionType type );
_type = INVALID;
}
void _fillServers( string s ){ private:
string::size_type idx;
while ( ( idx = s.find( ',' ) ) != string::npos ){
_servers.push_back( s.substr( 0 , idx ) );
s = s.substr( idx + 1 );
}
_servers.push_back( s );
}
void _finishInit(){ void _fillServers( string s );
stringstream ss; void _finishInit();
if ( _type == SET )
ss << _setName << "/";
for ( unsigned i=0; i<_servers.size(); i++ ){
if ( i > 0 )
ss << ",";
ss << _servers[i].toString();
}
_string = ss.str();
}
ConnectionType _type; ConnectionType _type;
vector<HostAndPort> _servers; vector<HostAndPort> _servers;
string _string; string _string;
string _setName; string _setName;
}; };
/** /**
* controls how much a clients cares about writes * controls how much a clients cares about writes
* default is NORMAL * default is NORMAL
skipping to change at line 300 skipping to change at line 311
of these as "bind variables". of these as "bind variables".
Examples: Examples:
conn.findOne("test.coll", Query("{a:3}").where("this.b == 2 | | this.c == 3")); conn.findOne("test.coll", Query("{a:3}").where("this.b == 2 | | this.c == 3"));
Query badBalance = Query().where("this.debits - this.credits < 0"); Query badBalance = Query().where("this.debits - this.credits < 0");
*/ */
Query& where(const string &jscode, BSONObj scope); Query& where(const string &jscode, BSONObj scope);
Query& where(const string &jscode) { return where(jscode, BSONObj() ); } Query& where(const string &jscode) { return where(jscode, BSONObj() ); }
/** /**
* if this query has an orderby, hint, or some other field * @return true if this query has an orderby, hint, or some other f ield
*/ */
bool isComplex( bool * hasDollar = 0 ) const; bool isComplex( bool * hasDollar = 0 ) const;
BSONObj getFilter() const; BSONObj getFilter() const;
BSONObj getSort() const; BSONObj getSort() const;
BSONObj getHint() const; BSONObj getHint() const;
bool isExplain() const; bool isExplain() const;
string toString() const; string toString() const;
operator string() const { return toString(); } operator string() const { return toString(); }
skipping to change at line 323 skipping to change at line 334
template< class T > template< class T >
void appendComplex( const char *fieldName, const T& val ) { void appendComplex( const char *fieldName, const T& val ) {
makeComplex(); makeComplex();
BSONObjBuilder b; BSONObjBuilder b;
b.appendElements(obj); b.appendElements(obj);
b.append(fieldName, val); b.append(fieldName, val);
obj = b.obj(); obj = b.obj();
} }
}; };
/** Typically one uses the QUERY(...) macro to construct a Query object. /** Typically one uses the QUERY(...) macro to construct a Query object .
Example: QUERY( "age" << 33 << "school" << "UCLA" ) Example: QUERY( "age" << 33 << "school" << "UCLA" )
*/ */
#define QUERY(x) mongo::Query( BSON(x) ) #define QUERY(x) mongo::Query( BSON(x) )
/** /**
interface that handles communication with the db interface that handles communication with the db
*/ */
class DBConnector { class DBConnector {
public: public:
virtual ~DBConnector() {} virtual ~DBConnector() {}
virtual bool call( Message &toSend, Message &response, bool assertO k=true ) = 0; /** actualServer is set to the actual server where they call went i f there was a choice (SlaveOk) */
virtual void say( Message &toSend ) = 0; virtual bool call( Message &toSend, Message &response, bool assertO k=true , string * actualServer = 0 ) = 0;
virtual void say( Message &toSend, bool isRetry = false ) = 0;
virtual void sayPiggyBack( Message &toSend ) = 0; virtual void sayPiggyBack( Message &toSend ) = 0;
virtual void checkResponse( const char* data, int nReturned ) {}
/* used by QueryOption_Exhaust. To use that your subclass must imp lement this. */ /* used by QueryOption_Exhaust. To use that your subclass must imp lement this. */
virtual void recv( Message& m ) { assert(false); } virtual bool recv( Message& m ) { assert(false); return false; }
// In general, for lazy queries, we'll need to say, recv, then chec kResponse
virtual string getServerAddress() const = 0; virtual void checkResponse( const char* data, int nReturned, bool* retry = NULL, string* targetHost = NULL ) {
if( retry ) *retry = false; if( targetHost ) *targetHost = "";
}
virtual bool lazySupported() const = 0;
}; };
/** /**
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 , int batchSize = 0 ) = 0; const BSONObj *fieldsToRetur n = 0, int queryOptions = 0 , int batchSize = 0 ) = 0;
/** don't use this - called automatically by DBClientCursor for you */ virtual void insert( const string &ns, BSONObj obj , int flags=0) = 0;
virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo ng cursorId, int nToReturn = 0, int options = 0 ) = 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 , int flags=0) = 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 @return a single object that matches the query. if none do, the n the object is empty
@throws AssertionException @throws AssertionException
*/ */
virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0); virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
/** query N objects from the database into an array. makes sense m ostly when you want a small number of results. if a huge number, use
query() and iterate the cursor.
*/
void findN(vector<BSONObj>& out, const string&ns, Query query, int nToReturn, int nToSkip = 0, const BSONObj *fieldsToReturn = 0, int queryOpt ions = 0);
virtual string getServerAddress() const = 0;
/** don't use this - called automatically by DBClientCursor for you */
virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo ng cursorId, int nToReturn = 0, int options = 0 ) = 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 {
set<string> _seenIndexes; set<string> _seenIndexes;
public: public:
/** controls how chatty the client is about network errors & such. See log.h */ /** controls how chatty the client is about network errors & such. See log.h */
skipping to change at line 399 skipping to change at line 418
@param command -- command name @param command -- command name
@return true if the command returned "ok". @return true if the command returned "ok".
*/ */
bool simpleCommand(const string &dbname, BSONObj *info, const strin g &command); bool simpleCommand(const string &dbname, BSONObj *info, const strin g &command);
/** Run a database command. Database commands are represented as B SON objects. Common database /** Run a database command. Database commands are represented as B SON objects. Common database
commands have prebuilt helper functions -- see below. If a hel per is not available you can commands have prebuilt helper functions -- see below. If a hel per is not available you can
directly call runCommand. directly call runCommand.
@param dbname database name. Use "admin" for global administra tive commands. @param dbname database name. Use "admin" for global administra tive commands.
@param cmd the command object to execute. For exam ple, { ismaster : 1 } @param cmd the command object to execute. For example, { isma ster : 1 }
@param info the result object the database returns. Typically has { ok : ..., errmsg : ... } fields @param info the result object the database returns. Typically h as { ok : ..., errmsg : ... } fields
set. set.
@param options see enum QueryOptions - normally not needed to r un a command @param options see enum QueryOptions - normally not needed to r un a command
@return true if the command returned "ok". @return true if the command returned "ok".
*/ */
virtual bool runCommand(const string &dbname, const BSONObj& cmd, B SONObj &info, int options=0); virtual bool runCommand(const string &dbname, const BSONObj& cmd, B SONObj &info, int options=0);
/** Authorize access to a particular database. /** Authorize access to a particular database.
Authentication is separate for each database on the server -- y ou may authenticate for any Authentication is separate for each database on the server -- y ou may authenticate for any
number of databases on a single connection. number of databases on a single connection.
The "admin" database is special and once authenticated provides access to all databases on the The "admin" database is special and once authenticated provides access to all databases on the
server. server.
@param digestPassword if password is plain text, set this to tr ue. otherwise assumed to be pre-digested @param digestPassword if password is plain text, set this to tr ue. otherwise assumed to be pre-digested
@return true if successful @return true if successful
*/ */
virtual bool auth(const string &dbname, const string &username, con st string &pwd, string& errmsg, bool digestPassword = true); virtual bool auth(const string &dbname, const string &username, con st string &pwd, string& errmsg, bool digestPassword = true);
/** count number of objects in collection ns that match the query c riteria specified /** count number of objects in collection ns that match the query c riteria specified
throws UserAssertion if database returns an error throws UserAssertion if database returns an error
*/ */
unsigned long long count(const string &ns, const BSONObj& query = B SONObj(), int options=0 ); virtual unsigned long long count(const string &ns, const BSONObj& q uery = BSONObj(), int options=0, int limit=0, int skip=0 );
string createPasswordDigest( const string &username , const string &clearTextPassword ); string createPasswordDigest( const string &username , const string &clearTextPassword );
/** returns true in isMaster parm if this db is the current master /** returns true in isMaster parm if this db is the current master
of a replica pair. of a replica pair.
pass in info for more details e.g.: pass in info for more details e.g.:
{ "ismaster" : 1.0 , "msg" : "not paired" , "ok" : 1.0 } { "ismaster" : 1.0 , "msg" : "not paired" , "ok" : 1.0 }
returns true if command invoked successfully. returns true if command invoked successfully.
skipping to change at line 452 skipping to change at line 471
Must be <= 1000000000 for normal collections. Must be <= 1000000000 for normal collections.
For fixed size (capped) collections, this size is the total/max size of the For fixed size (capped) collections, this size is the total/max size of the
collection. collection.
@param capped if true, this is a fixed size collection (where ol d data rolls out). @param capped if true, this is a fixed size collection (where ol d data rolls out).
@param max maximum number of objects if capped (optional). @param max maximum number of objects if capped (optional).
returns true if successful. returns true if successful.
*/ */
bool createCollection(const string &ns, long long size = 0, bool ca pped = false, int max = 0, BSONObj *info = 0); bool createCollection(const string &ns, long long size = 0, bool ca pped = false, int max = 0, BSONObj *info = 0);
/** Get error result from the last operation on this connection. /** Get error result from the last write operation (insert/update/d elete) on this connection.
@return error message text, or empty string if no error. @return error message text, or empty string if no error.
*/ */
string getLastError(); string getLastError();
/** Get error result from the last operation on this connect ion.
@return full error object.
*/
virtual BSONObj getLastErrorDetailed();
/** Get error result from the last write operation (insert/update/d elete) on this connection.
@return full error object.
*/
virtual BSONObj getLastErrorDetailed();
/** Can be called with the returned value from getLastErrorDetailed to extract an error string.
If all you need is the string, just call getLastError() instead .
*/
static string getLastErrorString( const BSONObj& res ); static string getLastErrorString( const BSONObj& res );
/** Return the last error which has occurred, even if not the very last operation. /** Return the last error which has occurred, even if not the very last operation.
@return { err : <error message>, nPrev : <how_many_ops_back_occu rred>, ok : 1 } @return { err : <error message>, nPrev : <how_many_ops_back_occu rred>, ok : 1 }
result.err will be null if no error has occurred. result.err will be null if no error has occurred.
*/ */
BSONObj getPrevError(); BSONObj getPrevError();
/** 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( 10011 , "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;
} }
skipping to change at line 531 skipping to change at line 554
*/ */
enum ProfilingLevel { enum ProfilingLevel {
ProfileOff = 0, ProfileOff = 0,
ProfileSlow = 1, // log very slow (>100ms) operations ProfileSlow = 1, // log very slow (>100ms) operations
ProfileAll = 2 ProfileAll = 2
}; };
bool setDbProfilingLevel(const string &dbname, ProfilingLevel level , BSONObj *info = 0); bool setDbProfilingLevel(const string &dbname, ProfilingLevel level , BSONObj *info = 0);
bool getDbProfilingLevel(const string &dbname, ProfilingLevel& leve l, BSONObj *info = 0); bool getDbProfilingLevel(const string &dbname, ProfilingLevel& leve l, BSONObj *info = 0);
/** This implicitly converts from char*, string, and BSONObj to be an argument to mapreduce
You shouldn't need to explicitly construct this
*/
struct MROutput {
MROutput(const char* collection) : out(BSON("replace" << collec tion)) {}
MROutput(const string& collection) : out(BSON("replace" << coll ection)) {}
MROutput(const BSONObj& obj) : out(obj) {}
BSONObj out;
};
static MROutput MRInline;
/** Run a map/reduce job on the server. /** Run a map/reduce job on the server.
See http://www.mongodb.org/display/DOCS/MapReduce See http://www.mongodb.org/display/DOCS/MapReduce
ns namespace (db+collection name) of input data ns namespace (db+collection name) of input data
jsmapf javascript map function code jsmapf javascript map function code
jsreducef javascript reduce function code. jsreducef javascript reduce function code.
query optional query filter for the input query optional query filter for the input
output optional permanent output collection name. if not sp ecified server will output either a string collection name or an object represen ting output type
generate a temporary collection and return its name. if not specified uses inline output type
returns a result object which contains: returns a result object which contains:
{ result : <collection_name>, { result : <collection_name>,
numObjects : <number_of_objects_scanned>, numObjects : <number_of_objects_scanned>,
timeMillis : <job_time>, timeMillis : <job_time>,
ok : <1_if_ok>, ok : <1_if_ok>,
[, err : <errmsg_if_error>] [, err : <errmsg_if_error>]
} }
For example one might call: For example one might call:
result.getField("ok").trueValue() result.getField("ok").trueValue()
on the result to check if ok. on the result to check if ok.
*/ */
BSONObj mapreduce(const string &ns, const string &jsmapf, const str ing &jsreducef, BSONObj query = BSONObj(), const string& output = ""); BSONObj mapreduce(const string &ns, const string &jsmapf, const str ing &jsreducef, BSONObj query = BSONObj(), MROutput output = MRInline);
/** Run javascript code on the database server. /** Run javascript code on the database server.
dbname database SavedContext in which the code runs. The java script variable 'db' will be assigned dbname database SavedContext in which the code runs. The java script variable 'db' will be assigned
to this database when the function is invoked. to this database when the function is invoked.
jscode source code for a javascript function. jscode source code for a javascript function.
info the command object which contains any information on t he invocation result including info the command object which contains any information on t he invocation result including
the return value and other information. If an error occurs running the jscode, error the return value and other information. If an error occurs running the jscode, error
information will be in info. (try "out() < < info.toString()") information will be in info. (try "out() << info.toSt ring()")
retValue return value from the jscode function. retValue return value from the jscode function.
args args to pass to the jscode function. when invoked, th e 'args' variable will be defined args args to pass to the jscode function. when invoked, th e 'args' variable will be defined
for use by the jscode. for use by the jscode.
returns true if runs ok. returns true if runs ok.
See testDbEval() in dbclient.cpp for an example of usage. See testDbEval() in dbclient.cpp for an example of usage.
*/ */
bool eval(const string &dbname, const string &jscode, BSONObj& info , BSONElement& retValue, BSONObj *args = 0); bool eval(const string &dbname, const string &jscode, BSONObj& info , BSONElement& retValue, BSONObj *args = 0);
/** /** validate a collection, checking for errors and reporting back s tatistics.
this operation is slow and blocking.
*/ */
bool validate( const string &ns , bool scandata=true ){ bool validate( const string &ns , bool scandata=true ) {
BSONObj cmd = BSON( "validate" << nsGetCollection( ns ) << "sca ndata" << scandata ); BSONObj cmd = BSON( "validate" << nsGetCollection( ns ) << "sca ndata" << scandata );
BSONObj info; BSONObj info;
return runCommand( nsGetDB( ns ).c_str() , cmd , info ); return runCommand( nsGetDB( ns ).c_str() , cmd , info );
} }
/* The following helpers are simply more convenient forms of eval() for certain common cases */ /* The following helpers are simply more convenient forms of eval() for certain common cases */
/* invocation with no return value of interest -- with or without o ne simple parameter */ /* invocation with no return value of interest -- with or without o ne simple parameter */
bool eval(const string &dbname, const string &jscode); bool eval(const string &dbname, const string &jscode);
template< class T > template< class T >
skipping to change at line 630 skipping to change at line 665
list<string> getCollectionNames( const string& db ); list<string> getCollectionNames( const string& db );
bool exists( const string& ns ); bool exists( const string& ns );
/** Create an index if it does not already exist. /** Create an index if it does not already exist.
ensureIndex calls are remembered so it is safe/fast to call thi s function many ensureIndex calls are remembered so it is safe/fast to call thi s function many
times in your code. times in your code.
@param ns collection to be indexed @param ns collection to be indexed
@param keys the "key pattern" for the index. e.g., { name : 1 } @param keys the "key pattern" for the index. e.g., { name : 1 }
@param unique if true, indicates that key uniqueness should be e nforced for this index @param unique if true, indicates that key uniqueness should be e nforced for this index
@param name if not isn't specified, it will be created from the keys (recommended) @param name if not specified, it will be created from the keys a utomatically (which is recommended)
@param cache if set to false, the index cache for the connection won't remember this call
@param background build index in the background (see mongodb doc s/wiki for details)
@param v index version. leave at default value. (unit tests set this parameter.)
@return whether or not sent message to db. @return whether or not sent message to db.
should be true on first call, false on subsequent unless reset IndexCache was called should be true on first call, false on subsequent unless reset IndexCache was called
*/ */
virtual bool ensureIndex( const string &ns , BSONObj keys , bool un ique = false, const string &name = "" ); virtual bool ensureIndex( const string &ns , BSONObj keys , bool un ique = false, const string &name = "",
bool cache = true, bool background = fals e, int v = -1 );
/** /**
clears the index cache, so the subsequent call to ensureIndex fo r any index will go to the server clears the index cache, so the subsequent call to ensureIndex fo r any index will go to the server
*/ */
virtual void resetIndexCache(); virtual void resetIndexCache();
virtual auto_ptr<DBClientCursor> getIndexes( const string &ns ); virtual auto_ptr<DBClientCursor> getIndexes( const string &ns );
virtual void dropIndex( const string& ns , BSONObj keys ); virtual void dropIndex( const string& ns , BSONObj keys );
virtual void dropIndex( const string& ns , const string& indexName ); virtual void dropIndex( const string& ns , const string& indexName );
skipping to change at line 665 skipping to change at line 704
/** Erase / drop an entire database */ /** Erase / drop an entire database */
virtual bool dropDatabase(const string &dbname, BSONObj *info = 0) { virtual bool dropDatabase(const string &dbname, BSONObj *info = 0) {
bool ret = simpleCommand(dbname, info, "dropDatabase"); bool ret = simpleCommand(dbname, info, "dropDatabase");
resetIndexCache(); resetIndexCache();
return ret; return ret;
} }
virtual string toString() = 0; virtual string toString() = 0;
/** @return the database name portion of an ns string */ /** @return the database name portion of an ns string */
string nsGetDB( const string &ns ){ string nsGetDB( const string &ns ) {
string::size_type pos = ns.find( "." ); string::size_type pos = ns.find( "." );
if ( pos == string::npos ) if ( pos == string::npos )
return ns; return ns;
return ns.substr( 0 , pos ); return ns.substr( 0 , pos );
} }
/** @return the collection name portion of an ns string */ /** @return the collection name portion of an ns string */
string nsGetCollection( const string &ns ){ string nsGetCollection( const string &ns ) {
string::size_type pos = ns.find( "." ); string::size_type pos = ns.find( "." );
if ( pos == string::npos ) if ( pos == string::npos )
return ""; return "";
return ns.substr( pos + 1 ); return ns.substr( pos + 1 );
} }
protected: protected:
bool isOk(const BSONObj&); bool isOk(const BSONObj&);
BSONObj _countCmd(const string &ns, const BSONObj& query, int optio ns, int limit, int skip );
enum QueryOptions availableOptions(); enum QueryOptions availableOptions();
private: private:
enum QueryOptions _cachedAvailableOptions; enum QueryOptions _cachedAvailableOptions;
bool _haveCachedAvailableOptions; bool _haveCachedAvailableOptions;
}; };
/** /**
abstract class that implements the core db operations abstract class that implements the core db operations
*/ */
class DBClientBase : public DBClientWithCommands, public DBConnector { class DBClientBase : public DBClientWithCommands, public DBConnector {
protected: protected:
WriteConcern _writeConcern; WriteConcern _writeConcern;
public: public:
DBClientBase(){ DBClientBase() {
_writeConcern = W_NORMAL; _writeConcern = W_NORMAL;
} }
WriteConcern getWriteConcern() const { return _writeConcern; } WriteConcern getWriteConcern() const { return _writeConcern; }
void setWriteConcern( WriteConcern w ){ _writeConcern = w; } void setWriteConcern( WriteConcern w ) { _writeConcern = w; }
/** send a query to the database. /** send a query to the database.
@param ns namespace to query, format is <dbname>.<collectname>[.<c ollectname>]* @param ns namespace to query, format is <dbname>.<collectname>[.<c ollectname>]*
@param query query to perform on the collection. this is a BSONOb j (binary JSON) @param query query to perform on the collection. this is a BSONOb j (binary JSON)
You may format as You may format as
{ query: { ... }, orderby: { ... } } { query: { ... }, orderby: { ... } }
to specify a sort order. to specify a sort order.
@param nToReturn n to return. 0 = unlimited @param nToReturn n to return (i.e., limit). 0 = unlimited
@param nToSkip start with the nth item @param nToSkip start with the nth item
@param fieldsToReturn optional template of which fields to select. if unspecified, returns all fields @param fieldsToReturn optional template of which fields to select. if unspecified, returns all fields
@param queryOptions see options enum at top of this file @param queryOptions see options enum at top of this file
@return cursor. 0 if error (connection failure) @return cursor. 0 if error (connection failure)
@throws AssertionException @throws AssertionException
*/ */
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 , int batchSize = 0 ); const BSONObj *fieldsToRetur n = 0, int queryOptions = 0 , int batchSize = 0 );
/** don't use this - called automatically by DBClientCursor for you /** don't use this - called automatically by DBClientCursor for you
@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 );
/** /**
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 , int flags=0);
/** /**
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 , int flags=0);
/** /**
remove matching objects from the database remove matching objects from the database
@param justOne if this true, then once a single match is found w ill stop @param justOne if this true, then once a single match is found w ill stop
*/ */
virtual void remove( const string &ns , Query q , bool justOne = 0 ); virtual void remove( const string &ns , Query q , bool justOne = 0 );
/** /**
updates objects matching query updates objects matching query
*/ */
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = false , bool multi = false ); virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = false , bool multi = false );
virtual bool isFailed() const = 0; virtual bool isFailed() const = 0;
virtual void killCursor( long long cursorID ) = 0; virtual void killCursor( long long cursorID ) = 0;
static int countCommas( const string& s ){
int n = 0;
for ( unsigned i=0; i<s.size(); i++ )
if ( s[i] == ',' )
n++;
return n;
}
virtual bool callRead( Message& toSend , Message& response ) = 0; virtual bool callRead( Message& toSend , Message& response ) = 0;
// virtual bool callWrite( Message& toSend , Message& response ) = 0; // TODO: add this if needed // virtual bool callWrite( Message& toSend , Message& response ) = 0; // TODO: add this if needed
virtual void say( Message& toSend ) = 0;
virtual ConnectionString::ConnectionType type() const = 0; virtual ConnectionString::ConnectionType type() const = 0;
/** @return true if conn is either equal to or contained in this co nnection */ virtual double getSoTimeout() const = 0;
virtual bool isMember( const DBConnector * conn ) const = 0;
}; // DBClientBase }; // DBClientBase
class DBClientReplicaSet; class DBClientReplicaSet;
class ConnectException : public UserException { class ConnectException : public UserException {
public: public:
ConnectException(string msg) : UserException(9000,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 {
DBClientReplicaSet *clientSet;
boost::scoped_ptr<MessagingPort> p;
boost::scoped_ptr<SockAddr> server;
bool failed; // true if some sort of fatal error has ever happened
bool autoReconnect;
time_t lastReconnectTry;
HostAndPort _server; // remember for reconnects
string _serverString;
int _port;
void _checkConnection();
void checkConnection() { if( failed ) _checkConnection(); }
map< string, pair<string,string> > authCache;
int _timeout;
bool _connect( string& errmsg );
public: public:
/** /**
@param _autoReconnect if true, automatically reconnect on a conn ection failure @param _autoReconnect if true, automatically reconnect on a conn ection failure
@param cp used by DBClientReplicaSet. You do not need to specif y this parameter @param cp used by DBClientReplicaSet. You do not need to specif y this parameter
@param timeout tcp timeout in seconds - this is for read/write, not connect. @param timeout tcp timeout in seconds - this is for read/write, not connect.
Connect timeout is fixed, but short, at 5 seconds. Connect timeout is fixed, but short, at 5 seconds.
*/ */
DBClientConnection(bool _autoReconnect=false, DBClientReplicaSet* c p=0, int timeout=0) : DBClientConnection(bool _autoReconnect=false, DBClientReplicaSet* c p=0, double so_timeout=0) :
clientSet(cp), failed(false), autoReconnect(_autoReconnect) , lastReconnectTry(0), _timeout(timeout) { } clientSet(cp), _failed(false), autoReconnect(_autoReconnect), l astReconnectTry(0), _so_timeout(so_timeout) {
_numConnections++;
}
virtual ~DBClientConnection() {
_numConnections--;
}
/** Connect to a Mongo database server. /** Connect to a Mongo database server.
If autoReconnect is true, you can try to use the DBClientConnect ion even when If autoReconnect is true, you can try to use the DBClientConnect ion even when
false was returned -- it will try to connect again. false was returned -- it will try to connect again.
@param serverHostname host to connect to. can include port numb er ( 127.0.0.1 , 127.0.0.1:5555 ) @param serverHostname host to connect to. can include port numb er ( 127.0.0.1 , 127.0.0.1:5555 )
If you use IPv6 you must add a port number ( ::1:27017 ) If you use IPv6 you must add a port number ( ::1:27017 )
@param errmsg any relevant error message will appended to the st ring @param errmsg any relevant error message will appended to the st ring
@deprecated please use HostAndPort @deprecated please use HostAndPort
@return false if fails to connect. @return false if fails to connect.
*/ */
virtual bool connect(const char * hostname, string& errmsg){ virtual bool connect(const char * hostname, string& errmsg) {
// TODO: remove this method // TODO: remove this method
HostAndPort t( hostname ); HostAndPort t( hostname );
return connect( t , errmsg ); return connect( t , errmsg );
} }
/** Connect to a Mongo database server. /** Connect to a Mongo database server.
If autoReconnect is true, you can try to use the DBClientConnect ion even when If autoReconnect is true, you can try to use the DBClientConnect ion even when
false was returned -- it will try to connect again. false was returned -- it will try to connect again.
skipping to change at line 862 skipping to change at line 884
} }
virtual bool auth(const string &dbname, const string &username, con st string &pwd, string& errmsg, bool digestPassword = true); virtual bool auth(const string &dbname, const string &username, con st string &pwd, string& errmsg, bool digestPassword = true);
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y=Query(), int nToReturn = 0, int nToSkip = 0, virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y=Query(), int nToReturn = 0, int nToSkip = 0,
const BSONObj *fieldsToRetur n = 0, int queryOptions = 0 , int batchSize = 0 ) { const BSONObj *fieldsToRetur n = 0, int queryOptions = 0 , int batchSize = 0 ) {
checkConnection(); checkConnection();
return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel dsToReturn, queryOptions , batchSize ); return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel dsToReturn, queryOptions , batchSize );
} }
/** uses QueryOption_Exhaust /** Uses QueryOption_Exhaust
use DBClientCursorBatchIterator if you want to do items in larg e blocks, perhpas to avoid granular locking and such. Exhaust mode sends back all data queries as fast as possible, w ith no back-and-for for OP_GETMORE. If you are certain
you will exhaust the query, it could be useful.
Use DBClientCursorBatchIterator version if you want to do items in large blocks, perhaps to avoid granular locking and such.
*/ */
unsigned long long query( boost::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int query Options = 0); unsigned long long query( boost::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int query Options = 0);
unsigned long long query( boost::function<void(DBClientCursorBatchI terator&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0); unsigned long long query( boost::function<void(DBClientCursorBatchI terator&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
/** /**
@return true if this connection is currently in a failed state. When autoreconnect is on, @return true if this connection is currently in a failed state. When autoreconnect is on,
a connection will transition back to an ok state after r econnecting. a connection will transition back to an ok state after r econnecting.
*/ */
bool isFailed() const { bool isFailed() const { return _failed; }
return failed;
}
MessagingPort& port() { MessagingPort& port() { assert(p); return *p; }
return *p;
}
string toStringLong() const { string toStringLong() const {
stringstream ss; stringstream ss;
ss << _serverString; ss << _serverString;
if ( failed ) ss << " failed"; if ( _failed ) ss << " failed";
return ss.str(); return ss.str();
} }
/** Returns the address of the server */ /** Returns the address of the server */
string toString() { string toString() { return _serverString; }
return _serverString;
}
string getServerAddress() const { string getServerAddress() const { return _serverString; }
return _serverString;
}
virtual void killCursor( long long cursorID ); virtual void killCursor( long long cursorID );
virtual bool callRead( Message& toSend , Message& response ) { retu rn call( toSend , response ); }
virtual bool callRead( Message& toSend , Message& response ){ virtual void say( Message &toSend, bool isRetry = false );
return call( toSend , response ); virtual bool recv( Message& m );
} virtual void checkResponse( const char *data, int nReturned, bool* retry = NULL, string* host = NULL );
virtual bool call( Message &toSend, Message &response, bool assertO k = true , string * actualServer = 0 );
virtual void say( Message &toSend );
virtual bool call( Message &toSend, Message &response, bool assertO k = true );
virtual ConnectionString::ConnectionType type() const { return Conn ectionString::MASTER; } virtual ConnectionString::ConnectionType type() const { return Conn ectionString::MASTER; }
void setSoTimeout(double to) { _so_timeout = to; }
double getSoTimeout() const { return _so_timeout; }
virtual bool isMember( const DBConnector * conn ) const { return th is == conn; }; virtual bool lazySupported() const { return true; }
virtual void checkResponse( const char *data, int nReturned ); static int getNumConnections() {
return _numConnections;
}
static void setLazyKillCursor( bool lazy ) { _lazyKillCursor = lazy ; }
static bool getLazyKillCursor() { return _lazyKillCursor; }
protected: protected:
friend class SyncClusterConnection; friend class SyncClusterConnection;
virtual void recv( Message& m );
virtual void sayPiggyBack( Message &toSend ); virtual void sayPiggyBack( Message &toSend );
}; DBClientReplicaSet *clientSet;
boost::scoped_ptr<MessagingPort> p;
/** Use this class to connect to a replica set of servers. The class w ill manage boost::scoped_ptr<SockAddr> server;
checking for which server in a replica set is master, and do failove r automatically. bool _failed;
const bool autoReconnect;
This can also be used to connect to replica pairs since pairs are a subset of sets time_t lastReconnectTry;
HostAndPort _server; // remember for reconnects
On a failover situation, expect at least one operation to return an error (throw string _serverString;
an exception) before the failover is complete. Operations are no t retried. void _checkConnection();
*/
class DBClientReplicaSet : public DBClientBase {
string _name;
DBClientConnection * _currentMaster;
vector<HostAndPort> _servers;
vector<DBClientConnection*> _conns;
void _checkMaster();
DBClientConnection * checkMaster();
public:
/** Call connect() after constructing. autoReconnect is always on f or DBClientReplicaSet connections. */
DBClientReplicaSet( const string& name , const vector<HostAndPort>& servers );
virtual ~DBClientReplicaSet();
/** Returns false if nomember of the set were reachable, or neither is
master, although,
when false returned, you can still try to use this connection ob ject, it will
try reconnects.
*/
bool connect();
/** Authorize. Authorizes all nodes as needed
*/
virtual bool auth(const string &dbname, const string &username, con st string &pwd, string& errmsg, bool digestPassword = true );
/** throws userassertion "no master found" */
virtual
auto_ptr<DBClientCursor> query(const string &ns, Query query, int n ToReturn = 0, int nToSkip = 0,
const BSONObj *fieldsToReturn = 0, i nt queryOptions = 0 , int batchSize = 0 );
/** throws userassertion "no master found" */
virtual
BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
/** insert */
virtual void insert( const string &ns , BSONObj obj ) {
checkMaster()->insert(ns, obj);
}
/** insert multiple objects. Note that single object insert is asy nchronous, so this version
is only nominally faster and not worth a special effort to try to use. */
virtual void insert( const string &ns, const vector< BSONObj >& v ) {
checkMaster()->insert(ns, v);
}
/** remove */
virtual void remove( const string &ns , Query obj , bool justOne = 0 ) {
checkMaster()->remove(ns, obj, justOne);
}
/** update */
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert = 0 , bool multi = 0 ) {
return checkMaster()->update(ns, query, obj, upsert,multi);
}
virtual void killCursor( long long cursorID ){
checkMaster()->killCursor( cursorID );
}
string toString();
/* this is the callback from our underlying connections to notify u s that we got a "not master" error.
*/
void isntMaster() {
_currentMaster = 0;
}
string getServerAddress() const;
DBClientConnection& masterConn();
DBClientConnection& slaveConn();
virtual bool call( Message &toSend, Message &response, bool assertO k=true ) { return checkMaster()->call( toSend , response , assertOk ); }
virtual void say( Message &toSend ) { checkMaster()->say( toSend ); }
virtual bool callRead( Message& toSend , Message& response ){ retur n checkMaster()->callRead( toSend , response ); }
virtual ConnectionString::ConnectionType type() const { return Conn ectionString::SET; }
virtual bool isMember( const DBConnector * conn ) const; // throws SocketException if in failed state and not reconnecting o r if waiting to reconnect
void checkConnection() { if( _failed ) _checkConnection(); }
virtual void checkResponse( const char *data, int nReturned ) { che ckMaster()->checkResponse( data , nReturned ); } map< string, pair<string,string> > authCache;
double _so_timeout;
bool _connect( string& errmsg );
protected: static AtomicUInt _numConnections;
virtual void sayPiggyBack( Message &toSend ) { checkMaster()->say( toSend ); } static bool _lazyKillCursor; // lazy means we piggy back kill curso rs on next op
bool isFailed() const { #ifdef MONGO_SSL
return _currentMaster == 0 || _currentMaster->isFailed(); static SSLManager* sslManager();
} static SSLManager* _sslManager;
#endif
}; };
/** pings server to check if it's up /** pings server to check if it's up
*/ */
bool serverAlive( const string &uri ); bool serverAlive( const string &uri );
DBClientBase * createDirectClient(); DBClientBase * createDirectClient();
BSONElement getErrField( const BSONObj& result );
bool hasErrField( const BSONObj& result );
} // namespace mongo } // namespace mongo
#include "dbclientcursor.h" #include "dbclientcursor.h"
#include "dbclient_rs.h"
#include "undef_macros.h" #include "undef_macros.h"
 End of changes. 75 change blocks. 
241 lines changed or deleted 191 lines changed or added


 dbclientcursor.h   dbclientcursor.h 
skipping to change at line 21 skipping to change at line 21
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/message.h" #include "../util/net/message.h"
#include "../db/jsobj.h" #include "../db/jsobj.h"
#include "../db/json.h" #include "../db/json.h"
#include <stack> #include <stack>
namespace mongo { namespace mongo {
class AScopedConnection; class AScopedConnection;
/** Queries return a cursor object */ /** for mock purposes only -- do not create variants of DBClientCursor, nor hang code here */
class DBClientCursor : boost::noncopyable { class DBClientCursorInterface {
public: public:
/** If true, safe to call next(). Requests more from server if necessary. */ virtual ~DBClientCursorInterface() {}
virtual bool more() = 0;
virtual BSONObj next() = 0;
// TODO bring more of the DBClientCursor interface to here
protected:
DBClientCursorInterface() {}
};
/** Queries return a cursor object */
class DBClientCursor : public DBClientCursorInterface {
public:
/** If true, safe to call next(). Requests more from server if nec essary. */
bool more(); bool more();
/** If true, there is more in our local buffers to be fetched via n ext(). Returns /** If true, there is more in our local buffers to be fetched via n ext(). Returns
false when a getMore request back to server would be required. You can use this false when a getMore request back to server would be required. You can use this
if you want to exhaust whatever data has been fetched to the cl ient already but if you want to exhaust whatever data has been fetched to the cl ient already but
then perhaps stop. then perhaps stop.
*/ */
int objsLeftInBatch() const { _assertIfNull(); return _putBack.size () + nReturned - pos; } int objsLeftInBatch() const { _assertIfNull(); return _putBack.size () + b.nReturned - b.pos; }
bool moreInCurrentBatch() { return objsLeftInBatch() > 0; } bool moreInCurrentBatch() { return objsLeftInBatch() > 0; }
/** next /** next
@return next object in the result cursor. @return next object in the result cursor.
on an error at the remote server, you will get back: on an error at the remote server, you will get back:
{ $err: <string> } { $err: <string> }
if you do not want to handle that yourself, call nextSafe(). if you do not want to handle that yourself, call nextSafe().
*/ */
BSONObj next(); BSONObj next();
/** /**
restore an object previously returned by next() to the cursor restore an object previously returned by next() to the cursor
*/ */
void putBack( const BSONObj &o ) { _putBack.push( o.getOwned() ); } void putBack( const BSONObj &o ) { _putBack.push( o.getOwned() ); }
/** throws AssertionException if get back { $err : ... } */ /** throws AssertionException if get back { $err : ... } */
BSONObj nextSafe() { BSONObj nextSafe() {
BSONObj o = next(); BSONObj o = next();
BSONElement e = o.firstElement(); if( strcmp(o.firstElementFieldName(), "$err") == 0 ) {
if( strcmp(e.fieldName(), "$err") == 0 ) { string s = "nextSafe(): " + o.toString();
if( logLevel >= 5 ) if( logLevel >= 5 )
log() << "nextSafe() error " << o.toString() << endl; log() << s << endl;
uassert(13106, "nextSafe(): " + o.toString(), false); uasserted(13106, s);
} }
return o; return o;
} }
/** peek ahead at items buffered for future next() calls. /** peek ahead at items buffered for future next() calls.
never requests new data from the server. so peek only effectiv e never requests new data from the server. so peek only effectiv e
with what is already buffered. with what is already buffered.
WARNING: no support for _putBack yet! WARNING: no support for _putBack yet!
*/ */
void peek(vector<BSONObj>&, int atMost); void peek(vector<BSONObj>&, int atMost);
/** /**
* peek ahead and see if an error occurred, and get the error if so .
*/
bool peekError(BSONObj* error = NULL);
/**
iterate the rest of the cursor and return the number if items iterate the rest of the cursor and return the number if items
*/ */
int itcount(){ int itcount() {
int c = 0; int c = 0;
while ( more() ){ while ( more() ) {
next(); next();
c++; c++;
} }
return c; return c;
} }
/** cursor no longer valid -- use with tailable cursors. /** cursor no longer valid -- use with tailable cursors.
note you should only rely on this once more() returns false; note you should only rely on this once more() returns false;
'dead' may be preset yet some data still queued and locally 'dead' may be preset yet some data still queued and locally
available from the dbclientcursor. available from the dbclientcursor.
*/ */
bool isDead() const { bool isDead() const { return !this || cursorId == 0; }
return !this || cursorId == 0;
}
bool tailable() const { bool tailable() const { return (opts & QueryOption_CursorTailable) != 0; }
return (opts & QueryOption_CursorTailable) != 0;
}
/** see ResultFlagType (constants.h) for flag values /** see ResultFlagType (constants.h) for flag values
mostly these flags are for internal purposes - mostly these flags are for internal purposes -
ResultFlag_ErrSet is the possible exception to that ResultFlag_ErrSet is the possible exception to that
*/ */
bool hasResultFlag( int flag ){ bool hasResultFlag( int flag ) {
_assertIfNull(); _assertIfNull();
return (resultFlags & flag) != 0; return (resultFlags & flag) != 0;
} }
DBClientCursor( DBConnector *_connector, const string &_ns, BSONObj _query, int _nToReturn, DBClientCursor( DBClientBase* client, const string &_ns, BSONObj _q uery, int _nToReturn,
int _nToSkip, const BSONObj *_fieldsToReturn, int q ueryOptions , int bs ) : int _nToSkip, const BSONObj *_fieldsToReturn, int q ueryOptions , int bs ) :
connector(_connector), _client(client),
ns(_ns), ns(_ns),
query(_query), query(_query),
nToReturn(_nToReturn), nToReturn(_nToReturn),
haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_C ursorTailable)), haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_Curso rTailable)),
nToSkip(_nToSkip), nToSkip(_nToSkip),
fieldsToReturn(_fieldsToReturn), fieldsToReturn(_fieldsToReturn),
opts(queryOptions), opts(queryOptions),
batchSize(bs==1?2:bs), batchSize(bs==1?2:bs),
m(new Message()), cursorId(),
cursorId(), _ownCursor( true ),
nReturned(), wasError( false ) {
pos(),
data(),
_ownCursor( true ){
} }
DBClientCursor( DBConnector *_connector, const string &_ns, long lo ng _cursorId, int _nToReturn, int options ) : DBClientCursor( DBClientBase* client, const string &_ns, long long _cursorId, int _nToReturn, int options ) :
connector(_connector), _client(client),
ns(_ns), ns(_ns),
nToReturn( _nToReturn ), nToReturn( _nToReturn ),
haveLimit( _nToReturn > 0 && !(options & QueryOption_Cursor Tailable)), haveLimit( _nToReturn > 0 && !(options & QueryOption_CursorTail able)),
opts( options ), opts( options ),
m(new Message()), cursorId(_cursorId),
cursorId( _cursorId ), _ownCursor( true ) {
nReturned(),
pos(),
data(),
_ownCursor( true ){
} }
virtual ~DBClientCursor(); virtual ~DBClientCursor();
long long getCursorId() const { return cursorId; } long long getCursorId() const { return cursorId; }
/** by default we "own" the cursor and will send the server a KillC ursor /** by default we "own" the cursor and will send the server a KillC ursor
message when ~DBClientCursor() is called. This function overrid es that. message when ~DBClientCursor() is called. This function overrid es that.
*/ */
void decouple() { _ownCursor = false; } void decouple() { _ownCursor = false; }
void attach( AScopedConnection * conn ); void attach( AScopedConnection * conn );
/**
* actually does the query
*/
bool init();
void initLazy( bool isRetry = false );
bool initLazyFinish( bool& retry );
class Batch : boost::noncopyable {
friend class DBClientCursor;
auto_ptr<Message> m;
int nReturned;
int pos;
const char *data;
public:
Batch() : m( new Message() ), nReturned(), pos(), data() { }
};
private: private:
friend class DBClientBase; friend class DBClientBase;
friend class DBClientConnection; friend class DBClientConnection;
bool init();
int nextBatchSize(); int nextBatchSize();
DBConnector *connector;
Batch b;
DBClientBase* _client;
string ns; string ns;
BSONObj query; BSONObj query;
int nToReturn; int nToReturn;
bool haveLimit; bool haveLimit;
int nToSkip; int nToSkip;
const BSONObj *fieldsToReturn; const BSONObj *fieldsToReturn;
int opts; int opts;
int batchSize; int batchSize;
auto_ptr<Message> m;
stack< BSONObj > _putBack; stack< BSONObj > _putBack;
int resultFlags; int resultFlags;
long long cursorId; long long cursorId;
int nReturned;
int pos;
const char *data;
void dataReceived();
void requestMore();
void exhaustReceiveMore(); // for exhaust
bool _ownCursor; // see decouple() bool _ownCursor; // see decouple()
string _scopedHost; string _scopedHost;
string _lazyHost;
bool wasError;
void dataReceived() { bool retry; string lazyHost; dataReceived( re try, lazyHost ); }
void dataReceived( bool& retry, string& lazyHost );
void requestMore();
void exhaustReceiveMore(); // for exhaust
// Don't call from a virtual function // Don't call from a virtual function
void _assertIfNull() const { uassert(13348, "connection died", this ); } void _assertIfNull() const { uassert(13348, "connection died", this ); }
// non-copyable , non-assignable
DBClientCursor( const DBClientCursor& );
DBClientCursor& operator=( const DBClientCursor& );
// init pieces
void _assembleInit( Message& toSend );
}; };
/** iterate over objects in current batch only - will not cause a netwo rk call /** iterate over objects in current batch only - will not cause a netwo rk call
*/ */
class DBClientCursorBatchIterator { class DBClientCursorBatchIterator {
public: public:
DBClientCursorBatchIterator( DBClientCursor &c ) : _c( c ), _n() {} DBClientCursorBatchIterator( DBClientCursor &c ) : _c( c ), _n() {}
bool moreInCurrentBatch() { return _c.moreInCurrentBatch(); } bool moreInCurrentBatch() { return _c.moreInCurrentBatch(); }
BSONObj nextSafe() { BSONObj nextSafe() {
massert( 13383, "BatchIterator empty", moreInCurrentBatch() ); massert( 13383, "BatchIterator empty", moreInCurrentBatch() );
 End of changes. 24 change blocks. 
57 lines changed or deleted 92 lines changed or added


 dbhelpers.h   dbhelpers.h 
skipping to change at line 36 skipping to change at line 36
#include "client.h" #include "client.h"
#include "db.h" #include "db.h"
namespace mongo { namespace mongo {
const BSONObj reverseNaturalObj = BSON( "$natural" << -1 ); const BSONObj reverseNaturalObj = BSON( "$natural" << -1 );
class Cursor; class Cursor;
class CoveredIndexMatcher; class CoveredIndexMatcher;
class CursorIterator {
public:
CursorIterator( shared_ptr<Cursor> c , BSONObj filter = BSONObj() ) ;
BSONObj next();
bool hasNext();
private:
void _advance();
shared_ptr<Cursor> _cursor;
auto_ptr<CoveredIndexMatcher> _matcher;
BSONObj _o;
};
/** /**
all helpers assume locking is handled above them 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"
skipping to change at line 93 skipping to change at line 79
* @param foundIndex if passed in will be set to 1 if ns and index found * @param foundIndex if passed in will be set to 1 if ns and index found
* @return true if object found * @return true if object found
*/ */
static bool findById(Client&, const char *ns, BSONObj query, BSONOb j& result , static bool findById(Client&, const char *ns, BSONObj query, BSONOb j& result ,
bool * nsFound = 0 , bool * indexFound = 0 ); bool * nsFound = 0 , bool * indexFound = 0 );
/* uasserts if no _id index. /* uasserts if no _id index.
@return null loc if not found */ @return null loc if not found */
static DiskLoc findById(NamespaceDetails *d, BSONObj query); static DiskLoc findById(NamespaceDetails *d, BSONObj query);
static auto_ptr<CursorIterator> find( const char *ns , BSONObj quer y = BSONObj() , bool requireIndex = false );
/** Get/put the first (or last) object from a collection. Generall y only useful if the collection /** Get/put the first (or last) object from a collection. Generall y only useful if the collection
only ever has a single object -- which is a "singleton collecti on". only ever has a single object -- which is a "singleton collecti on".
You do not need to set the database (Context) before calling. You do not need to set the database (Context) before calling.
@return true if object exists. @return 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);
static void putSingletonGod(const char *ns, BSONObj obj, bool logTh eOp); static void putSingletonGod(const char *ns, BSONObj obj, bool logTh eOp);
skipping to change at line 118 skipping to change at line 102
/** /**
* you have to lock * you have to lock
* you do not have to have Context set * you do not have to have Context set
* o has to have an _id field or will assert * o has to have an _id field or will assert
*/ */
static void upsert( const string& ns , const BSONObj& o ); static void upsert( const string& ns , const BSONObj& o );
/** You do not need to set the database before calling. /** You do not need to set the database before calling.
@return true if collection is empty. @return true if collection is empty.
*/ */
static bool isEmpty(const char *ns); static bool isEmpty(const char *ns, bool doAuth=true);
// TODO: this should be somewhere else probably // TODO: this should be somewhere else probably
static BSONObj toKeyFormat( const BSONObj& o , BSONObj& key ); static BSONObj toKeyFormat( const BSONObj& o , BSONObj& key );
class RemoveCallback { class RemoveCallback {
public: public:
virtual ~RemoveCallback(){} virtual ~RemoveCallback() {}
virtual void goingToDelete( const BSONObj& o ) = 0; virtual void goingToDelete( const BSONObj& o ) = 0;
}; };
/* removeRange: operation is oplog'd */ /* removeRange: operation is oplog'd */
static long long removeRange( const string& ns , const BSONObj& min , const BSONObj& max , bool yield = false , bool maxInclusive = false , Re moveCallback * callback = 0 ); static long long removeRange( const string& ns , const BSONObj& min , const BSONObj& max , bool yield = false , bool maxInclusive = false , Re moveCallback * callback = 0 );
/* Remove all objects from a collection. /* Remove all objects from a collection.
You do not need to set the database before calling. You do not need to set the database before calling.
*/ */
static void emptyCollection(const char *ns); static void emptyCollection(const char *ns);
 End of changes. 4 change blocks. 
18 lines changed or deleted 2 lines changed or added


 dblogger.h   dblogger.h 
skipping to change at line 28 skipping to change at line 28
#pragma once #pragma once
namespace mongo { namespace mongo {
/** helper to log (and read log) of a capped collection in the database */ /** helper to log (and read log) of a capped collection in the database */
class DBLogger { class DBLogger {
bool _inited; bool _inited;
public: public:
const string _ns; const string _ns;
DBLogger(string ns) : _inited(false), _ns(ns){ } DBLogger(string ns) : _inited(false), _ns(ns) { }
}; };
} }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 dbmessage.h   dbmessage.h 
// dbmessage.h
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 "diskloc.h" #include "diskloc.h"
#include "jsobj.h" #include "jsobj.h"
#include "namespace.h" #include "namespace-inl.h"
#include "../util/message.h" #include "../util/net/message.h"
#include "../client/constants.h" #include "../client/constants.h"
#include "instance.h"
namespace mongo { namespace mongo {
/* db response format /* db response format
Query or GetMore: // see struct QueryResult Query or GetMore: // see struct QueryResult
int resultFlags; int resultFlags;
int64 cursorID; int64 cursorID;
int startingFrom; int startingFrom;
int nReturned; int nReturned;
list of marshalled JSObjects; list of marshalled JSObjects;
*/ */
extern bool objcheck; /* db request message format
unsigned opid; // arbitary; will be echoed back
byte operation;
int options;
then for:
dbInsert:
string collection;
a series of JSObjects
dbDelete:
string collection;
int flags=0; // 1=DeleteSingle
JSObject query;
dbUpdate:
string collection;
int flags; // 1=upsert
JSObject query;
JSObject objectToUpdate;
objectToUpdate may include { $inc: <field> } or { $set: ... }, see struct Mod.
dbQuery:
string collection;
int nToSkip;
int nToReturn; // how many you want back as the beginning of the curs or data (0=no limit)
// greater than zero is simply a hint on how many obje cts to send back per "cursor batch".
// a negative number indicates a hard limit.
JSObject query;
[JSObject fieldsToReturn]
dbGetMore:
string collection; // redundant, might use for security.
int nToReturn;
int64 cursorID;
dbKillCursors=2007:
int n;
int64 cursorIDs[n];
Note that on Update, there is only one object, which is different
from insert where you can pass a list of objects to insert in the db.
Note that the update field layout is very similar layout to Query.
*/
#pragma pack(1) #pragma pack(1)
struct QueryResult : public MsgData { struct QueryResult : public MsgData {
long long cursorId; long long cursorId;
int startingFrom; int startingFrom;
int nReturned; int nReturned;
const char *data() { const char *data() {
return (char *) (((int *)&nReturned)+1); return (char *) (((int *)&nReturned)+1);
} }
int resultFlags() { int resultFlags() {
return dataAsInt(); return dataAsInt();
} }
int& _resultFlags() { int& _resultFlags() {
return dataAsInt(); return dataAsInt();
} }
void setResultFlagsToOk() { void setResultFlagsToOk() {
_resultFlags() = ResultFlag_AwaitCapable; _resultFlags() = ResultFlag_AwaitCapable;
} }
void initializeResultFlags() {
_resultFlags() = 0;
}
}; };
#pragma pack() #pragma pack()
/* For the database/server protocol, these objects and functions encaps ulate /* For the database/server protocol, these objects and functions encaps ulate
the various messages transmitted over the connection. the various messages transmitted over the connection.
See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol See http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
*/ */
class DbMessage { class DbMessage {
public: public:
DbMessage(const Message& _m) : m(_m) DbMessage(const Message& _m) : m(_m) , mark(0) {
{
// for received messages, Message has only one buffer // for received messages, Message has only one buffer
theEnd = _m.singleData()->_data + _m.header()->dataLen(); theEnd = _m.singleData()->_data + _m.header()->dataLen();
char *r = _m.singleData()->_data; char *r = _m.singleData()->_data;
reserved = (int *) r; reserved = (int *) r;
data = r + 4; data = r + 4;
nextjsobj = data; nextjsobj = data;
} }
/** the 32 bit field before the ns */ /** the 32 bit field before the ns
* track all bit usage here as its cross op
* 0: InsertOption_ContinueOnError
* 1: fromWriteback
*/
int& reservedField() { return *reserved; } int& reservedField() { return *reserved; }
const char * getns() const { const char * getns() const {
return data; return data;
} }
void getns(Namespace& ns) const { void getns(Namespace& ns) const {
ns = data; ns = data;
} }
const char * afterNS() const { const char * afterNS() const {
skipping to change at line 99 skipping to change at line 149
int getInt( int num ) const { int getInt( int num ) const {
const int * foo = (const int*)afterNS(); const int * foo = (const int*)afterNS();
return foo[num]; return foo[num];
} }
int getQueryNToReturn() const { int getQueryNToReturn() const {
return getInt( 1 ); return getInt( 1 );
} }
void resetPull(){ nextjsobj = data; } /**
* get an int64 at specified offsetBytes after ns
*/
long long getInt64( int offsetBytes ) const {
const char * x = afterNS();
x += offsetBytes;
const long long * ll = (const long long*)x;
return ll[0];
}
void resetPull() { nextjsobj = data; }
int pullInt() const { return pullInt(); } int pullInt() const { return pullInt(); }
int& pullInt() { int& pullInt() {
if ( nextjsobj == data ) if ( nextjsobj == data )
nextjsobj += strlen(data) + 1; // skip namespace nextjsobj += strlen(data) + 1; // skip namespace
int& i = *((int *)nextjsobj); int& i = *((int *)nextjsobj);
nextjsobj += 4; nextjsobj += 4;
return i; return i;
} }
long long pullInt64() const { long long pullInt64() const {
return pullInt64(); return pullInt64();
skipping to change at line 143 skipping to change at line 203
} }
BSONObj nextJsObj() { BSONObj nextJsObj() {
if ( nextjsobj == data ) { if ( nextjsobj == data ) {
nextjsobj += strlen(data) + 1; // skip namespace nextjsobj += strlen(data) + 1; // skip namespace
massert( 13066 , "Message contains no documents", theEnd > nextjsobj ); massert( 13066 , "Message contains no documents", theEnd > nextjsobj );
} }
massert( 10304 , "Client Error: Remaining data too small for B SON object", theEnd - nextjsobj > 3 ); massert( 10304 , "Client Error: Remaining data too small for B SON object", theEnd - nextjsobj > 3 );
BSONObj js(nextjsobj); BSONObj js(nextjsobj);
massert( 10305 , "Client Error: Invalid object size", js.objsi ze() > 3 ); massert( 10305 , "Client Error: Invalid object size", js.objsi ze() > 3 );
massert( 10306 , "Client Error: Next object larger than space left in message", massert( 10306 , "Client Error: Next object larger than space left in message",
js.objsize() < ( theEnd - data ) ); js.objsize() < ( theEnd - data ) );
if ( objcheck && !js.valid() ) { if ( cmdLine.objcheck && !js.valid() ) {
massert( 10307 , "Client Error: bad object in message", fal se); massert( 10307 , "Client Error: bad object in message", fal se);
} }
nextjsobj += js.objsize(); nextjsobj += js.objsize();
if ( nextjsobj >= theEnd ) if ( nextjsobj >= theEnd )
nextjsobj = 0; nextjsobj = 0;
return js; return js;
} }
const Message& msg() const { return m; } const Message& msg() const { return m; }
void markSet(){ void markSet() {
mark = nextjsobj; mark = nextjsobj;
} }
void markReset(){ void markReset() {
assert( mark );
nextjsobj = mark; nextjsobj = mark;
} }
private: private:
const Message& m; const Message& m;
int* reserved; int* reserved;
const char *data; const char *data;
const char *nextjsobj; const char *nextjsobj;
const char *theEnd; const char *theEnd;
const char * mark; const char * mark;
public:
enum ReservedOptions {
Reserved_InsertOption_ContinueOnError = 1 << 0 ,
Reserved_FromWriteback = 1 << 1
};
}; };
/* a request to run a query, received from the database */ /* a request to run a query, received from the database */
class QueryMessage { class QueryMessage {
public: public:
const char *ns; const char *ns;
int ntoskip; int ntoskip;
int ntoreturn; int ntoreturn;
int queryOptions; int queryOptions;
BSONObj query; BSONObj query;
skipping to change at line 196 skipping to change at line 263
ntoskip = d.pullInt(); ntoskip = d.pullInt();
ntoreturn = d.pullInt(); ntoreturn = d.pullInt();
query = d.nextJsObj(); query = d.nextJsObj();
if ( d.moreJSObjs() ) { if ( d.moreJSObjs() ) {
fields = d.nextJsObj(); fields = d.nextJsObj();
} }
queryOptions = d.msg().header()->dataAsInt(); queryOptions = d.msg().header()->dataAsInt();
} }
}; };
} // namespace mongo void replyToQuery(int queryResultFlags,
AbstractMessagingPort* p, Message& requestMsg,
#include "../client/dbclient.h" void *data, int size,
int nReturned, int startingFrom = 0,
namespace mongo { long long cursorId = 0
);
inline void replyToQuery(int queryResultFlags,
AbstractMessagingPort* p, Message& requestMsg,
void *data, int size,
int nReturned, int startingFrom = 0,
long long cursorId = 0
) {
BufBuilder b(32768);
b.skip(sizeof(QueryResult));
b.appendBuf(data, size);
QueryResult *qr = (QueryResult *) b.buf();
qr->_resultFlags() = queryResultFlags;
qr->len = b.len();
qr->setOperation(opReply);
qr->cursorId = cursorId;
qr->startingFrom = startingFrom;
qr->nReturned = nReturned;
b.decouple();
Message resp(qr, true);
p->reply(requestMsg, resp, requestMsg.header()->id);
}
} // namespace mongo
//#include "bsonobj.h"
#include "instance.h"
namespace mongo {
/* object reply helper. */ /* object reply helper. */
inline void replyToQuery(int queryResultFlags, void replyToQuery(int queryResultFlags,
AbstractMessagingPort* p, Message& requestMsg, AbstractMessagingPort* p, Message& requestMsg,
BSONObj& responseObj) BSONObj& responseObj);
{
replyToQuery(queryResultFlags,
p, requestMsg,
(void *) responseObj.objdata(), responseObj.objsize(), 1);
}
/* helper to do a reply using a DbResponse object */ /* helper to do a reply using a DbResponse object */
inline void replyToQuery(int queryResultFlags, Message &m, DbResponse & dbresponse, BSONObj obj) { void replyToQuery(int queryResultFlags, Message &m, DbResponse &dbrespo nse, BSONObj obj);
BufBuilder b;
b.skip(sizeof(QueryResult));
b.appendBuf((void*) obj.objdata(), obj.objsize());
QueryResult* msgdata = (QueryResult *) b.buf();
b.decouple();
QueryResult *qr = msgdata;
qr->_resultFlags() = queryResultFlags;
qr->len = b.len();
qr->setOperation(opReply);
qr->cursorId = 0;
qr->startingFrom = 0;
qr->nReturned = 1;
Message *resp = new Message();
resp->setData(msgdata, true); // transport will free
dbresponse.response = resp;
dbresponse.responseTo = m.header()->id;
}
string debugString( Message& m );
} // namespace mongo } // namespace mongo
 End of changes. 16 change blocks. 
73 lines changed or deleted 88 lines changed or added


 dbwebserver.h   dbwebserver.h 
skipping to change at line 20 skipping to change at line 20
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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/>.
*/ */
#include "../util/admin_access.h"
namespace mongo { namespace mongo {
class Prioritizable { class Prioritizable {
public: public:
Prioritizable( double p ) : _priority(p){} Prioritizable( double p ) : _priority(p) {}
double priority() const { return _priority; } double priority() const { return _priority; }
private: private:
double _priority; double _priority;
}; };
class DbWebHandler : public Prioritizable { class DbWebHandler : public Prioritizable {
public: public:
DbWebHandler( const string& name , double priority , bool requiresR EST ); DbWebHandler( const string& name , double priority , bool requiresR EST );
virtual ~DbWebHandler(){} virtual ~DbWebHandler() {}
virtual bool handles( const string& url ) const { return url == _de faultUrl; } virtual bool handles( const string& url ) const { return url == _de faultUrl; }
virtual bool requiresREST( const string& url ) const { return _requ iresREST; } virtual bool requiresREST( const string& url ) const { return _requ iresREST; }
virtual void handle( const char *rq, // the full request virtual void handle( const char *rq, // the full request
string url, string url,
BSONObj params, BSONObj params,
// set these and return them: // set these and return them:
string& responseMsg, string& responseMsg,
int& responseCode, int& responseCode,
vector<string>& headers, // if completely empt y, content-type: text/html will be added vector<string>& headers, // if completely empt y, content-type: text/html will be added
const SockAddr &from const SockAddr &from
) = 0; ) = 0;
string toString() const { return _toString; } string toString() const { return _toString; }
static DbWebHandler * findHandler( const string& url ); static DbWebHandler * findHandler( const string& url );
private: private:
string _name; string _name;
bool _requiresREST; bool _requiresREST;
string _defaultUrl; string _defaultUrl;
string _toString; string _toString;
static vector<DbWebHandler*> * _handlers; static vector<DbWebHandler*> * _handlers;
}; };
class WebStatusPlugin : public Prioritizable { class WebStatusPlugin : public Prioritizable {
public: public:
WebStatusPlugin( const string& secionName , double priority , const string& subheader = "" ); WebStatusPlugin( const string& secionName , double priority , const string& subheader = "" );
virtual ~WebStatusPlugin(){} virtual ~WebStatusPlugin() {}
virtual void run( stringstream& ss ) = 0; virtual void run( stringstream& ss ) = 0;
/** called when web server stats up */ /** called when web server stats up */
virtual void init() = 0; virtual void init() = 0;
static void initAll(); static void initAll();
static void runAll( stringstream& ss ); static void runAll( stringstream& ss );
private: private:
string _name; string _name;
string _subHeading; string _subHeading;
static vector<WebStatusPlugin*> * _plugins; static vector<WebStatusPlugin*> * _plugins;
}; };
void webServerThread(); void webServerThread( const AdminAccess* admins );
string prettyHostName(); string prettyHostName();
/** @return if there are any admin users. this should not block for lo ng and throw if can't get a lock if needed */
bool webHaveAdminUsers();
/** @return admin user with this name. this should not block for long and throw if can't get a lock if needed */
BSONObj webGetAdminUser( const string& username );
}; };
 End of changes. 7 change blocks. 
11 lines changed or deleted 7 lines changed or added


 debug_util.h   debug_util.h 
skipping to change at line 78 skipping to change at line 78
#if defined(_WIN32) #if defined(_WIN32)
inline int strcasecmp(const char* s1, const char* s2) {return _stricmp( s1, s2);} inline int strcasecmp(const char* s1, const char* s2) {return _stricmp( s1, s2);}
#endif #endif
// Sets SIGTRAP handler to launch GDB // Sets SIGTRAP handler to launch GDB
// Noop unless on *NIX and compiled with _DEBUG // Noop unless on *NIX and compiled with _DEBUG
void setupSIGTRAPforGDB(); void setupSIGTRAPforGDB();
extern int tlogLevel; extern int tlogLevel;
inline void breakpoint(){ inline void breakpoint() {
if ( tlogLevel < 0 ) if ( tlogLevel < 0 )
return; return;
#ifdef _WIN32 #ifdef _WIN32
DEV DebugBreak(); //DEV DebugBreak();
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
// code to raise a breakpoint in GDB // code to raise a breakpoint in GDB
ONCE { ONCE {
//prevent SIGTRAP from crashing the program if default action i s specified and we are not in gdb //prevent SIGTRAP from crashing the program if default action i s specified and we are not in gdb
struct sigaction current; struct sigaction current;
sigaction(SIGTRAP, NULL, &current); sigaction(SIGTRAP, NULL, &current);
if (current.sa_handler == SIG_DFL){ if (current.sa_handler == SIG_DFL) {
signal(SIGTRAP, SIG_IGN); signal(SIGTRAP, SIG_IGN);
} }
} }
raise(SIGTRAP); raise(SIGTRAP);
#endif #endif
} }
// conditional breakpoint // conditional breakpoint
inline void breakif(bool test){ inline void breakif(bool test) {
if (test) if (test)
breakpoint(); breakpoint();
} }
} // namespace mongo } // namespace mongo
 End of changes. 4 change blocks. 
4 lines changed or deleted 4 lines changed or added


 diskloc.h   diskloc.h 
skipping to change at line 17 skipping to change at line 17
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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/>.
*/ */
/* storage.h /* @file diskloc.h
Storage subsystem management. Storage subsystem management.
Lays out our datafiles on disk, manages disk space. Lays out our datafiles on disk, manages disk space.
*/ */
#pragma once #pragma once
#include "jsobj.h" #include "jsobj.h"
namespace mongo { namespace mongo {
class Record; class Record;
class DeletedRecord; class DeletedRecord;
class Extent; class Extent;
class BtreeBucket;
class MongoDataFile; class MongoDataFile;
template< class Version > class BtreeBucket;
#pragma pack(1) #pragma pack(1)
/** represents a disk location/offset on disk in a database. 64 bits.
it is assumed these will be passed around by value a lot so don't d o anything to make them large
(such as adding a virtual function)
*/
class DiskLoc { class DiskLoc {
int fileNo; /* this will be volume, file #, etc. */ int _a; // this will be volume, file #, etsc. but is a logical value could be anything depending on storage engine
int ofs; int ofs;
public: public:
// Note: MaxFiles imposes a limit of about 32TB of data per process
enum SentinelValues { MaxFiles=16000, NullOfs = -1 };
int a() const { enum SentinelValues {
return fileNo; /* note NullOfs is different. todo clean up. see refs to NullO fs in code - use is valid but outside DiskLoc context so confusing as-is. * /
} NullOfs = -1,
MaxFiles=16000 // thus a limit of about 32TB of data per db
};
DiskLoc(int a, int b) : fileNo(a), ofs(b) { DiskLoc(int a, int Ofs) : _a(a), ofs(Ofs) { }
//assert(ofs!=0);
}
DiskLoc() { Null(); } DiskLoc() { Null(); }
DiskLoc(const DiskLoc& l) { DiskLoc(const DiskLoc& l) {
fileNo=l.fileNo; _a=l._a;
ofs=l.ofs; ofs=l.ofs;
} }
bool questionable() { bool questionable() const {
return ofs < -1 || return ofs < -1 ||
fileNo < -1 || _a < -1 ||
fileNo > 524288; _a > 524288;
} }
bool isNull() const { bool isNull() const { return _a == -1; }
return fileNo == -1;
// return ofs == NullOfs;
}
void Null() { void Null() {
fileNo = -1; _a = -1;
ofs = 0; ofs = 0; /* note NullOfs is different. todo clean up. see refs to NullOfs in code - use is valid but outside DiskLoc context so confusing as-is. */
}
void assertOk() {
assert(!isNull());
} }
void assertOk() { assert(!isNull()); }
void setInvalid() { void setInvalid() {
fileNo = -2; _a = -2;
ofs = 0; ofs = 0;
} }
bool isValid() const { bool isValid() const { return _a != -2; }
return fileNo != -2;
}
string toString() const { string toString() const {
if ( isNull() ) if ( isNull() )
return "null"; return "null";
stringstream ss; stringstream ss;
ss << hex << fileNo << ':' << ofs; ss << hex << _a << ':' << ofs;
return ss.str(); return ss.str();
} }
BSONObj toBSONObj() const { BSONObj toBSONObj() const { return BSON( "file" << _a << "offset" < < ofs ); }
return BSON( "file" << fileNo << "offset" << ofs );
}
int& GETOFS() { int a() const { return _a; }
return ofs;
} int& GETOFS() { return ofs; }
int getOfs() const { int getOfs() const { return ofs; }
return ofs;
}
void set(int a, int b) { void set(int a, int b) {
fileNo=a; _a=a;
ofs=b; ofs=b;
} }
void setOfs(int _fileNo, int _ofs) {
fileNo = _fileNo;
ofs = _ofs;
}
void inc(int amt) { void inc(int amt) {
assert( !isNull() ); assert( !isNull() );
ofs += amt; ofs += amt;
} }
bool sameFile(DiskLoc b) { bool sameFile(DiskLoc b) {
return fileNo == b.fileNo; return _a== b._a;
} }
bool operator==(const DiskLoc& b) const { bool operator==(const DiskLoc& b) const {
return fileNo==b.fileNo && ofs == b.ofs; return _a==b._a&& ofs == b.ofs;
} }
bool operator!=(const DiskLoc& b) const { bool operator!=(const DiskLoc& b) const {
return !(*this==b); return !(*this==b);
} }
const DiskLoc& operator=(const DiskLoc& b) { const DiskLoc& operator=(const DiskLoc& b) {
fileNo=b.fileNo; _a=b._a;
ofs = b.ofs; ofs = b.ofs;
//assert(ofs!=0); //assert(ofs!=0);
return *this; return *this;
} }
int compare(const DiskLoc& b) const { int compare(const DiskLoc& b) const {
int x = fileNo - b.fileNo; int x = _a - b._a;
if ( x ) if ( x )
return x; return x;
return ofs - b.ofs; return ofs - b.ofs;
} }
bool operator<(const DiskLoc& b) const { bool operator<(const DiskLoc& b) const {
return compare(b) < 0; return compare(b) < 0;
} }
/* get the "thing" associated with this disk location. /**
it is assumed the object is what it is -- you must asure that: * Marks this disk loc for writing
think of this as an unchecked type cast. * @returns a non const reference to this disk loc
* This function explicitly signals we are writing and casts away c onst
*/
DiskLoc& writing() const; // see dur.h
/* Get the "thing" associated with this disk location.
it is assumed the object is what you say it is -- you must assur e that
(think of this as an unchecked type cast)
Note: set your Context first so that the database to which the d iskloc applies is known.
*/ */
BSONObj obj() const; BSONObj obj() const;
Record* rec() const; Record* rec() const;
DeletedRecord* drec() const; DeletedRecord* drec() const;
Extent* ext() const; Extent* ext() const;
BtreeBucket* btree() const;
BtreeBucket* btreemod() const; // marks modified / dirty
MongoDataFile& pdf() const; template< class V >
const BtreeBucket<V> * btree() const;
// Explicitly signals we are writing and casts away const
template< class V >
BtreeBucket<V> * btreemod() const;
/*MongoDataFile& pdf() const;*/
}; };
#pragma pack() #pragma pack()
const DiskLoc minDiskLoc(0, 1); const DiskLoc minDiskLoc(0, 1);
const DiskLoc maxDiskLoc(0x7fffffff, 0x7fffffff); const DiskLoc maxDiskLoc(0x7fffffff, 0x7fffffff);
} // namespace mongo } // namespace mongo
 End of changes. 29 change blocks. 
53 lines changed or deleted 55 lines changed or added


 distlock.h   distlock.h 
skipping to change at line 18 skipping to change at line 18
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/** #pragma once
* distributed locking mechanism
*/
#include "../pch.h" #include "../pch.h"
#include "dbclient.h" #include "dbclient.h"
#include "connpool.h" #include "connpool.h"
#include "redef_macros.h" #include "redef_macros.h"
#include "syncclusterconnection.h" #include "syncclusterconnection.h"
#define LOCK_TIMEOUT (15 * 60 * 1000)
#define LOCK_SKEW_FACTOR (30)
#define LOCK_PING (LOCK_TIMEOUT / LOCK_SKEW_FACTOR)
#define MAX_LOCK_NET_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR)
#define MAX_LOCK_CLOCK_SKEW (LOCK_TIMEOUT / LOCK_SKEW_FACTOR)
#define NUM_LOCK_SKEW_CHECKS (3)
// The maximum clock skew we need to handle between config servers is
// 2 * MAX_LOCK_NET_SKEW + MAX_LOCK_CLOCK_SKEW.
// Net effect of *this* clock being slow is effectively a multiplier on the max net skew
// and a linear increase or decrease of the max clock skew.
namespace mongo { namespace mongo {
/**
* Exception class to encapsulate exceptions while managing distributed locks
*/
class LockException : public DBException {
public:
LockException( const char * msg , int code ) : DBException( msg, cod e ) {}
LockException( const string& msg, int code ) : DBException( msg, cod e ) {}
virtual ~LockException() throw() { }
};
/**
* Indicates an error in retrieving time values from remote servers.
*/
class TimeNotFoundException : public LockException {
public:
TimeNotFoundException( const char * msg , int code ) : LockExceptio n( msg, code ) {}
TimeNotFoundException( const string& msg, int code ) : LockExceptio n( msg, code ) {}
virtual ~TimeNotFoundException() throw() { }
};
/**
* The distributed lock is a configdb backed way of synchronizing syste m-wide tasks. A task must be identified by a
* unique name across the system (e.g., "balancer"). A lock is taken by writing a document in the configdb's locks
* collection with that name.
*
* To be maintained, each taken lock needs to be revalidaded ("pinged") within a pre-established amount of time. This
* class does this maintenance automatically once a DistributedLock obj ect was constructed.
*/
class DistributedLock { class DistributedLock {
public: public:
static LabeledLevel logLvl;
/** /**
* @param takeoverMinutes how long before we steal lock in minutes * The constructor does not connect to the configdb yet and constru cting does not mean the lock was acquired.
* Construction does trigger a lock "pinging" mechanism, though.
*
* @param conn address of config(s) server(s)
* @param name identifier for the lock
* @param lockTimeout how long can the log go "unpinged" before a n ew attempt to lock steals it (in minutes).
* @param lockPing how long to wait between lock pings
* @param legacy use legacy logic
*
*/ */
DistributedLock( const ConnectionString& conn , const string& name , unsigned takeoverMinutes = 10 ); DistributedLock( const ConnectionString& conn , const string& name , unsigned long long lockTimeout = 0, bool asProcess = false );
~DistributedLock(){};
bool lock_try( string why , BSONObj * other = 0 ); /**
void unlock(); * Attempts to acquire 'this' lock, checking if it could or should be stolen from the previous holder. Please
* consider using the dist_lock_try construct to acquire this lock in an exception safe way.
*
* @param why human readable description of why the lock is being t aken (used to log)
* @param whether this is a lock re-entry or a new lock
* @param other configdb's lock document that is currently holding the lock, if lock is taken, or our own lock
* details if not
* @return true if it managed to grab the lock
*/
bool lock_try( const string& why , bool reenter = false, BSONObj * other = 0 );
/**
* Releases a previously taken lock.
*/
void unlock( BSONObj* oldLockPtr = NULL );
Date_t getRemoteTime();
bool isRemoteTimeSkewed();
const string& getProcessId();
const ConnectionString& getRemoteConnection();
/**
* Check the skew between a cluster of servers
*/
static bool checkSkew( const ConnectionString& cluster, unsigned sk ewChecks = NUM_LOCK_SKEW_CHECKS, unsigned long long maxClockSkew = MAX_LOCK _CLOCK_SKEW, unsigned long long maxNetSkew = MAX_LOCK_NET_SKEW );
/**
* Get the remote time from a server or cluster
*/
static Date_t remoteTime( const ConnectionString& cluster, unsigned long long maxNetSkew = MAX_LOCK_NET_SKEW );
static bool killPinger( DistributedLock& lock );
/**
* Namespace for lock pings
*/
static const string lockPingNS;
/**
* Namespace for locks
*/
static const string locksNS;
const ConnectionString _conn;
const string _name;
const BSONObj _id;
const string _processId;
// Timeout for lock, usually LOCK_TIMEOUT
const unsigned long long _lockTimeout;
const unsigned long long _maxClockSkew;
const unsigned long long _maxNetSkew;
const unsigned long long _lockPing;
private: private:
ConnectionString _conn;
string _name;
unsigned _takeoverMinutes;
string _ns; void resetLastPing(){
BSONObj _id; scoped_lock lk( _mutex );
_lastPingCheck = boost::tuple<string, Date_t, Date_t, OID>();
}
mongo::mutex _mutex;
// Data from last check of process with ping time
boost::tuple<string, Date_t, Date_t, OID> _lastPingCheck;
// May or may not exist, depending on startup
string _threadId;
}; };
class dist_lock_try { class dist_lock_try {
public: public:
dist_lock_try() : _lock(NULL), _got(false) {}
dist_lock_try( const dist_lock_try& that ) : _lock(that._lock), _got (that._got), _other(that._other) {
_other.getOwned();
// Make sure the lock ownership passes to this object,
// so we only unlock once.
((dist_lock_try&) that)._got = false;
((dist_lock_try&) that)._lock = NULL;
((dist_lock_try&) that)._other = BSONObj();
}
// Needed so we can handle lock exceptions in context of lock try.
dist_lock_try& operator=( const dist_lock_try& that ){
if( this == &that ) return *this;
_lock = that._lock;
_got = that._got;
_other = that._other;
_other.getOwned();
_why = that._why;
// Make sure the lock ownership passes to this object,
// so we only unlock once.
((dist_lock_try&) that)._got = false;
((dist_lock_try&) that)._lock = NULL;
((dist_lock_try&) that)._other = BSONObj();
return *this;
}
dist_lock_try( DistributedLock * lock , string why ) dist_lock_try( DistributedLock * lock , string why )
: _lock(lock){ : _lock(lock), _why(why) {
_got = _lock->lock_try( why , &_other ); _got = _lock->lock_try( why , false , &_other );
} }
~dist_lock_try(){ ~dist_lock_try() {
if ( _got ){ if ( _got ) {
_lock->unlock(); assert( ! _other.isEmpty() );
_lock->unlock( &_other );
} }
} }
bool got() const { bool reestablish(){
return _got; return retry();
} }
BSONObj other() const { bool retry() {
return _other; assert( _lock );
assert( _got );
assert( ! _other.isEmpty() );
return _got = _lock->lock_try( _why , true, &_other );
} }
bool got() const { return _got; }
BSONObj other() const { return _other; }
private: private:
DistributedLock * _lock; DistributedLock * _lock;
bool _got; bool _got;
BSONObj _other; BSONObj _other;
string _why;
}; };
} }
 End of changes. 16 change blocks. 
22 lines changed or deleted 174 lines changed or added


 embedded_builder.h   embedded_builder.h 
skipping to change at line 33 skipping to change at line 33
class EmbeddedBuilder { class EmbeddedBuilder {
public: public:
EmbeddedBuilder( BSONObjBuilder *b ) { EmbeddedBuilder( BSONObjBuilder *b ) {
_builders.push_back( make_pair( "", b ) ); _builders.push_back( make_pair( "", b ) );
} }
// It is assumed that the calls to prepareContext will be made with the 'name' // It is assumed that the calls to prepareContext will be made with the 'name'
// parameter in lex ascending order. // parameter in lex ascending order.
void prepareContext( string &name ) { void prepareContext( string &name ) {
int i = 1, n = _builders.size(); int i = 1, n = _builders.size();
while( i < n && while( i < n &&
name.substr( 0, _builders[ i ].first.length() ) == _buil ders[ i ].first && name.substr( 0, _builders[ i ].first.length() ) == _bui lders[ i ].first &&
( name[ _builders[i].first.length() ] == '.' || name[ _b uilders[i].first.length() ] == 0 ) ( name[ _builders[i].first.length() ] == '.' || name[ _ builders[i].first.length() ] == 0 )
){ ) {
name = name.substr( _builders[ i ].first.length() + 1 ); name = name.substr( _builders[ i ].first.length() + 1 );
++i; ++i;
} }
for( int j = n - 1; j >= i; --j ) { for( int j = n - 1; j >= i; --j ) {
popBuilder(); popBuilder();
} }
for( string next = splitDot( name ); !next.empty(); next = spli tDot( name ) ) { for( string next = splitDot( name ); !next.empty(); next = spli tDot( name ) ) {
addBuilder( next ); addBuilder( next );
} }
} }
 End of changes. 1 change blocks. 
3 lines changed or deleted 3 lines changed or added


 engine.h   engine.h 
skipping to change at line 30 skipping to change at line 30
#include "../pch.h" #include "../pch.h"
#include "../db/jsobj.h" #include "../db/jsobj.h"
namespace mongo { namespace mongo {
struct JSFile { struct JSFile {
const char* name; const char* name;
const StringData& source; const StringData& source;
}; };
namespace JSFiles {
extern const JSFile collection;
extern const JSFile db;
extern const JSFile mongo;
extern const JSFile mr;
extern const JSFile query;
extern const JSFile servers;
extern const JSFile utils;
}
typedef unsigned long long ScriptingFunction; typedef unsigned long long ScriptingFunction;
typedef BSONObj (*NativeFunction) ( const BSONObj &args ); typedef BSONObj (*NativeFunction) ( const BSONObj &args, void* data );
class Scope : boost::noncopyable { class Scope : boost::noncopyable {
public: public:
Scope(); Scope();
virtual ~Scope(); virtual ~Scope();
virtual void reset() = 0; virtual void reset() = 0;
virtual void init( BSONObj * data ) = 0; virtual void init( const BSONObj * data ) = 0;
void init( const char * data ){ void init( const char * data ) {
BSONObj o( data , 0 ); BSONObj o( data );
init( &o ); init( &o );
} }
virtual void localConnect( const char * dbName ) = 0; virtual void localConnect( const char * dbName ) = 0;
virtual void externalSetup() = 0; virtual void externalSetup() = 0;
class NoDBAccess { class NoDBAccess {
Scope * _s; Scope * _s;
public: public:
NoDBAccess( Scope * s ){ NoDBAccess( Scope * s ) {
_s = s; _s = s;
} }
~NoDBAccess(){ ~NoDBAccess() {
_s->rename( "____db____" , "db" ); _s->rename( "____db____" , "db" );
} }
}; };
NoDBAccess disableDBAccess( const char * why ){ NoDBAccess disableDBAccess( const char * why ) {
rename( "db" , "____db____" ); rename( "db" , "____db____" );
return NoDBAccess( this ); return NoDBAccess( this );
} }
virtual double getNumber( const char *field ) = 0; virtual double getNumber( const char *field ) = 0;
virtual int getNumberInt( const char *field ){ return (int)getNumbe r( field ); } virtual int getNumberInt( const char *field ) { return (int)getNumb er( field ); }
virtual long long getNumberLongLong( const char *field ){ return (l ong long)getNumber( field ); } virtual long long getNumberLongLong( const char *field ) { return ( long long)getNumber( field ); }
virtual string getString( const char *field ) = 0; virtual string getString( const char *field ) = 0;
virtual bool getBoolean( const char *field ) = 0; virtual bool getBoolean( const char *field ) = 0;
virtual BSONObj getObject( const char *field ) = 0; virtual BSONObj getObject( const char *field ) = 0;
virtual int type( const char *field ) = 0; virtual int type( const char *field ) = 0;
void append( BSONObjBuilder & builder , const char * fieldName , co nst char * scopeName ); virtual void append( BSONObjBuilder & builder , const char * fieldN ame , const char * scopeName );
virtual void setElement( const char *field , const BSONElement& e ) = 0; virtual void setElement( const char *field , const BSONElement& e ) = 0;
virtual void setNumber( const char *field , double val ) = 0; virtual void setNumber( const char *field , double val ) = 0;
virtual void setString( const char *field , const char * val ) = 0; virtual void setString( const char *field , const char * val ) = 0;
virtual void setObject( const char *field , const BSONObj& obj , bo ol readOnly=true ) = 0; virtual void setObject( const char *field , const BSONObj& obj , bo ol readOnly=true ) = 0;
virtual void setBoolean( const char *field , bool val ) = 0; virtual void setBoolean( const char *field , bool val ) = 0;
virtual void setThis( const BSONObj * obj ) = 0; virtual void setFunction( const char *field , const char * code ) = 0;
// virtual void setThis( const BSONObj * obj ) = 0;
virtual ScriptingFunction createFunction( const char * code ); virtual ScriptingFunction createFunction( const char * code );
virtual void rename( const char * from , const char * to ) = 0; virtual void rename( const char * from , const char * to ) = 0;
/** /**
* @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, c onst BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool rea dOnlyArgs = false, bool readOnlyRecv = false ) = 0;
void invokeSafe( ScriptingFunction func , const BSONObj& args, int timeoutMs = 0 ){ void invokeSafe( ScriptingFunction func , const BSONObj* args, cons t BSONObj* recv, int timeoutMs = 0, bool readOnlyArgs = false, bool readOnl yRecv = false ) {
int res = invoke( func , args , timeoutMs ); int res = invoke( func , args , recv, timeoutMs, readOnlyArgs, readOnlyRecv );
if ( res == 0 ) if ( res == 0 )
return; return;
throw UserException( 9004 , (string)"invoke failed: " + getErro r() ); 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, const BSONObj* recv, int timeoutMs = 0 );
void invokeSafe( const char* code , const BSONObj& args, int timeou tMs = 0 ){ void invokeSafe( const char* code , const BSONObj* args, const BSON Obj* recv, int timeoutMs = 0 ) {
if ( invoke( code , args , timeoutMs ) == 0 ) if ( invoke( code , args , recv, timeoutMs ) == 0 )
return; return;
throw UserException( 9005 , (string)"invoke failed: " + getErro r() ); throw UserException( 9005 , (string)"invoke failed: " + getErro r() );
} }
virtual bool exec( const StringData& code , const string& name , bo ol printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0; virtual bool exec( const StringData& code , const string& name , bo ol printResult , bool reportError , bool assertOnError, int timeoutMs = 0 ) = 0;
virtual void execSetup( const StringData& code , const string& name = "setup" ){ virtual void execSetup( const StringData& code , const string& name = "setup" ) {
exec( code , name , false , true , true , 0 ); exec( code , name , false , true , true , 0 );
} }
void execSetup( const JSFile& file){ void execSetup( const JSFile& file) {
execSetup(file.source, file.name); execSetup(file.source, file.name);
} }
void execCoreFiles(){ void execCoreFiles();
// keeping same order as in SConstruct
execSetup(JSFiles::utils);
execSetup(JSFiles::db);
execSetup(JSFiles::mongo);
execSetup(JSFiles::mr);
execSetup(JSFiles::query);
execSetup(JSFiles::collection);
}
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, void* data = 0 ) = 0;
virtual void gc() = 0; virtual void gc() = 0;
void loadStored( bool ignoreNotConnected = false ); void loadStored( bool ignoreNotConnected = false );
/** /**
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 ); 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;
skipping to change at line 201 skipping to change at line 184
struct Unlocker { virtual ~Unlocker() {} }; struct Unlocker { virtual ~Unlocker() {} };
virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U nlocker >( new Unlocker ); } virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U nlocker >( new Unlocker ); }
void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInit Callback = func; } void setScopeInitCallback( void ( *func )( Scope & ) ) { _scopeInit Callback = func; }
static void setConnectCallback( void ( *func )( DBClientWithCommand s& ) ) { _connectCallback = func; } static void setConnectCallback( void ( *func )( DBClientWithCommand s& ) ) { _connectCallback = func; }
static void runConnectCallback( DBClientWithCommands &c ) { static void runConnectCallback( DBClientWithCommands &c ) {
if ( _connectCallback ) if ( _connectCallback )
_connectCallback( c ); _connectCallback( c );
} }
// engine implementation may either respond to interrupt events or
// poll for interrupts
// the interrupt functions must not wait indefinitely on a lock
virtual void interrupt( unsigned opSpec ) {}
virtual void interruptAll() {}
static void setGetInterruptSpecCallback( unsigned ( *func )() ) { _ getInterruptSpecCallback = func; }
static bool haveGetInterruptSpecCallback() { return _getInterruptSp ecCallback; }
static unsigned getInterruptSpec() {
massert( 13474, "no _getInterruptSpecCallback", _getInterruptSp ecCallback );
return _getInterruptSpecCallback();
}
static void setCheckInterruptCallback( const char * ( *func )() ) { _checkInterruptCallback = func; }
static bool haveCheckInterruptCallback() { return _checkInterruptCa llback; }
static const char * checkInterrupt() {
return _checkInterruptCallback ? _checkInterruptCallback() : "" ;
}
static bool interrupted() {
const char *r = checkInterrupt();
return r && r[ 0 ];
}
protected: protected:
virtual Scope * createScope() = 0; virtual Scope * createScope() = 0;
private: private:
void ( *_scopeInitCallback )( Scope & ); void ( *_scopeInitCallback )( Scope & );
static void ( *_connectCallback )( DBClientWithCommands & ); static void ( *_connectCallback )( DBClientWithCommands & );
static const char * ( *_checkInterruptCallback )();
static unsigned ( *_getInterruptSpecCallback )();
}; };
bool hasJSReturn( const string& s ); bool hasJSReturn( const string& s );
const char * jsSkipWhiteSpace( const char * raw );
extern ScriptEngine * globalScriptEngine; extern ScriptEngine * globalScriptEngine;
} }
 End of changes. 19 change blocks. 
40 lines changed or deleted 51 lines changed or added


 engine_java.h   engine_java.h 
skipping to change at line 165 skipping to change at line 165
s = 0; s = 0;
} }
void reset() { void reset() {
JavaJS->scopeReset(s); JavaJS->scopeReset(s);
} }
void init( BSONObj * o ) { void init( BSONObj * o ) {
JavaJS->scopeInit( s , o ); JavaJS->scopeInit( s , o );
} }
void localConnect( const char * dbName ){ void localConnect( const char * dbName ) {
setString("$client", dbName ); setString("$client", dbName );
} }
double getNumber(const char *field) { double getNumber(const char *field) {
return JavaJS->scopeGetNumber(s,field); return JavaJS->scopeGetNumber(s,field);
} }
string getString(const char *field) { string getString(const char *field) {
return JavaJS->scopeGetString(s,field); return JavaJS->scopeGetString(s,field);
} }
bool getBoolean(const char *field) { bool getBoolean(const char *field) {
return JavaJS->scopeGetBoolean(s,field); return JavaJS->scopeGetBoolean(s,field);
} }
BSONObj getObject(const char *field ) { BSONObj getObject(const char *field ) {
return JavaJS->scopeGetObject(s,field); return JavaJS->scopeGetObject(s,field);
} }
int type(const char *field ) { int type(const char *field ) {
return JavaJS->scopeGetType(s,field); return JavaJS->scopeGetType(s,field);
} }
void setThis( const BSONObj * obj ){ void setThis( const BSONObj * obj ) {
JavaJS->scopeSetThis( s , obj ); JavaJS->scopeSetThis( s , obj );
} }
void setNumber(const char *field, double val ) { void setNumber(const char *field, double val ) {
JavaJS->scopeSetNumber(s,field,val); JavaJS->scopeSetNumber(s,field,val);
} }
void setString(const char *field, const char * val ) { void setString(const char *field, const char * val ) {
JavaJS->scopeSetString(s,field,val); JavaJS->scopeSetString(s,field,val);
} }
void setObject(const char *field, const BSONObj& obj , bool readOnl y ) { void setObject(const char *field, const BSONObj& obj , bool readOnl y ) {
uassert( 10211 , "only readOnly setObject supported in java" , readOnly ); uassert( 10211 , "only readOnly setObject supported in java" , readOnly );
JavaJS->scopeSetObject(s,field,&obj); JavaJS->scopeSetObject(s,field,&obj);
} }
void setBoolean(const char *field, bool val ) { void setBoolean(const char *field, bool val ) {
JavaJS->scopeSetBoolean(s,field,val); JavaJS->scopeSetBoolean(s,field,val);
} }
ScriptingFunction createFunction( const char * code ){ ScriptingFunction createFunction( const char * code ) {
return JavaJS->functionCreate( code ); return JavaJS->functionCreate( code );
} }
int invoke( ScriptingFunction function , const BSONObj& args ){ int invoke( ScriptingFunction function , const BSONObj& args ) {
setObject( "args" , args , true ); setObject( "args" , args , true );
return JavaJS->invoke(s,function); return JavaJS->invoke(s,function);
} }
string getError(){ string getError() {
return getString( "error" ); return getString( "error" );
} }
jlong s; jlong s;
}; };
JNIEXPORT void JNICALL java_native_say(JNIEnv *, jclass, jobject outBuf fer ); JNIEXPORT void JNICALL java_native_say(JNIEnv *, jclass, jobject outBuf fer );
JNIEXPORT jint JNICALL java_native_call(JNIEnv *, jclass, jobject outBu ffer , jobject inBuffer ); JNIEXPORT jint JNICALL java_native_call(JNIEnv *, jclass, jobject outBu ffer , jobject inBuffer );
} // namespace mongo } // namespace mongo
 End of changes. 5 change blocks. 
5 lines changed or deleted 5 lines changed or added


 engine_spidermonkey.h   engine_spidermonkey.h 
skipping to change at line 67 skipping to change at line 67
// END inc hacking // END inc hacking
// -- SM 1.6 hacks --- // -- SM 1.6 hacks ---
#ifndef JSCLASS_GLOBAL_FLAGS #ifndef JSCLASS_GLOBAL_FLAGS
#warning old version of spider monkey ( probably 1.6 ) you should upgrade t o at least 1.7 #warning old version of spider monkey ( probably 1.6 ) you should upgrade t o at least 1.7
#define JSCLASS_GLOBAL_FLAGS 0 #define JSCLASS_GLOBAL_FLAGS 0
JSBool JS_CStringsAreUTF8(){ JSBool JS_CStringsAreUTF8() {
return false; return false;
} }
#define SM16 #define SM16
#endif #endif
// -- END SM 1.6 hacks --- // -- END SM 1.6 hacks ---
#ifdef JSVAL_IS_TRACEABLE #ifdef JSVAL_IS_TRACEABLE
#define SM18 #define SM18
skipping to change at line 98 skipping to change at line 98
extern JSClass bson_class; extern JSClass bson_class;
extern JSClass bson_ro_class; extern JSClass bson_ro_class;
extern JSClass object_id_class; extern JSClass object_id_class;
extern JSClass dbpointer_class; extern JSClass dbpointer_class;
extern JSClass dbref_class; extern JSClass dbref_class;
extern JSClass bindata_class; extern JSClass bindata_class;
extern JSClass timestamp_class; extern JSClass timestamp_class;
extern JSClass numberlong_class; extern JSClass numberlong_class;
extern JSClass numberint_class;
extern JSClass minkey_class; extern JSClass minkey_class;
extern JSClass maxkey_class; extern JSClass maxkey_class;
// internal things // internal things
void dontDeleteScope( SMScope * s ){} void dontDeleteScope( SMScope * s ) {}
void errorReporter( JSContext *cx, const char *message, JSErrorReport * report ); void errorReporter( JSContext *cx, const char *message, JSErrorReport * report );
extern boost::thread_specific_ptr<SMScope> currentScope; extern boost::thread_specific_ptr<SMScope> currentScope;
// bson // bson
JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ); JSBool resolveBSONField( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp );
// mongo // mongo
void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ); void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local );
bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const s tring& name , jsval val , JSObject * o ); bool appendSpecialDBObject( Convertor * c , BSONObjBuilder& b , const s tring& name , jsval val , JSObject * o );
#define JSVAL_IS_OID(v) ( JSVAL_IS_OBJECT( v ) && JS_InstanceOf( cx , JSVAL _TO_OBJECT( v ) , &object_id_class , 0 ) ) #define JSVAL_IS_OID(v) ( JSVAL_IS_OBJECT( v ) && JS_InstanceOf( cx , JSVAL _TO_OBJECT( v ) , &object_id_class , 0 ) )
bool isDate( JSContext * cx , JSObject * o ); bool isDate( JSContext * cx , JSObject * o );
// JS private data must be 2byte aligned, so we use a holder to refer t o an unaligned pointer. // JS private data must be 2byte aligned, so we use a holder to refer t o an unaligned pointer.
struct BinDataHolder { struct BinDataHolder {
BinDataHolder( const char *c, int copyLen = -1 ) : BinDataHolder( const char *c, int copyLen = -1 ) :
c_( const_cast< char * >( c ) ), c_( const_cast< char * >( c ) ),
iFree_( copyLen != -1 ) { iFree_( copyLen != -1 ) {
if ( copyLen != -1 ) { if ( copyLen != -1 ) {
c_ = (char*)malloc( copyLen ); c_ = (char*)malloc( copyLen );
memcpy( c_, c, copyLen ); memcpy( c_, c, copyLen );
} }
} }
~BinDataHolder() { ~BinDataHolder() {
if ( iFree_ ) if ( iFree_ )
free( c_ ); free( c_ );
} }
char *c_; char *c_;
 End of changes. 4 change blocks. 
4 lines changed or deleted 5 lines changed or added


 engine_v8.h   engine_v8.h 
skipping to change at line 29 skipping to change at line 29
#include <vector> #include <vector>
#include "engine.h" #include "engine.h"
#include <v8.h> #include <v8.h>
using namespace v8; using namespace v8;
namespace mongo { namespace mongo {
class V8ScriptEngine; class V8ScriptEngine;
class V8Scope;
typedef Handle< Value > (*v8Function) ( V8Scope* scope, const v8::Argum ents& args );
// Preemption is going to be allowed for the v8 mutex, and some of our v8
// usage is not preemption safe. So we are using an additional mutex t hat
// will not be preempted. The V8Lock should be used in place of v8::Lo cker
// except in certain special cases involving interrupts.
namespace v8Locks {
// the implementations are quite simple - objects must be destroyed in
// reverse of the order created, and should not be shared between t hreads
struct RecursiveLock {
RecursiveLock();
~RecursiveLock();
bool _unlock;
};
struct RecursiveUnlock {
RecursiveUnlock();
~RecursiveUnlock();
bool _lock;
};
} // namespace v8Locks
class V8Lock {
v8Locks::RecursiveLock _noPreemptionLock;
v8::Locker _preemptionLock;
};
struct V8Unlock {
v8::Unlocker _preemptionUnlock;
v8Locks::RecursiveUnlock _noPreemptionUnlock;
};
class V8Scope : public Scope { class V8Scope : public Scope {
public: public:
V8Scope( V8ScriptEngine * engine ); V8Scope( V8ScriptEngine * engine );
~V8Scope(); ~V8Scope();
virtual void reset(); virtual void reset();
virtual void init( BSONObj * data ); virtual void init( const 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 ); // caller must cre ate context and handle scopes 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 );
Handle<v8::Object> getGlobalObject() { return _global; };
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 setFunction( const char *field , const char * code );
// virtual void setThis( const BSONObj * obj );
virtual void rename( const char * from , const char * to ); virtual void rename( const char * from , const char * to );
virtual ScriptingFunction _createFunction( const char * code ); virtual ScriptingFunction _createFunction( const char * code );
Local< v8::Function > __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, c onst BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool rea dOnlyArgs = false, bool readOnlyRecv = false );
virtual bool exec( const StringData& code , const string& name , bo ol printResult , bool reportError , bool assertOnError, int timeoutMs ); virtual bool exec( const StringData& code , const string& name , bo ol printResult , 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, void* data = 0 );
void injectNative( const char *field, NativeFunction func, Handle<v 8::Object>& obj, void* data = 0 );
void injectV8Function( const char *field, v8Function func );
void injectV8Function( const char *field, v8Function func, Handle<v 8::Object>& obj );
void injectV8Function( const char *field, v8Function func, Handle<v 8::Template>& t );
Handle<v8::FunctionTemplate> createV8Function( v8Function func );
void gc(); void gc();
Handle< Context > context() const { return _context; } Handle< Context > context() const { return _context; }
v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool ar ray = 0 , bool readOnly = false );
v8::Handle<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false );
mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0 );
void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name ,
const string sname , v8::Handle<v8::Value> v alue , int depth = 0, BSONObj* originalParent=0 );
v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool readOnly = false );
virtual void append( BSONObjBuilder & builder , const char * fieldN ame , const char * scopeName );
v8::Function * getNamedCons( const char * name );
v8::Function * getObjectIdCons();
Local< v8::Value > newId( const OID &id );
Persistent<v8::Object> wrapBSONObject(Local<v8::Object> obj, BSONOb j* data);
Persistent<v8::Object> wrapArrayObject(Local<v8::Object> obj, char* data);
v8::Handle<v8::String> getV8Str(string str);
// inline v8::Handle<v8::String> getV8Str(string str) { return v8::S tring::New(str.c_str()); }
inline v8::Handle<v8::String> getLocalV8Str(string str) { return v8 ::String::New(str.c_str()); }
Handle<v8::String> V8STR_CONN;
Handle<v8::String> V8STR_ID;
Handle<v8::String> V8STR_LENGTH;
Handle<v8::String> V8STR_LEN;
Handle<v8::String> V8STR_TYPE;
Handle<v8::String> V8STR_ISOBJECTID;
Handle<v8::String> V8STR_NATIVE_FUNC;
Handle<v8::String> V8STR_NATIVE_DATA;
Handle<v8::String> V8STR_V8_FUNC;
Handle<v8::String> V8STR_RETURN;
Handle<v8::String> V8STR_ARGS;
Handle<v8::String> V8STR_T;
Handle<v8::String> V8STR_I;
Handle<v8::String> V8STR_EMPTY;
Handle<v8::String> V8STR_MINKEY;
Handle<v8::String> V8STR_MAXKEY;
Handle<v8::String> V8STR_NUMBERLONG;
Handle<v8::String> V8STR_NUMBERINT;
Handle<v8::String> V8STR_DBPTR;
Handle<v8::String> V8STR_BINDATA;
Handle<v8::String> V8STR_WRAPPER;
Handle<v8::String> V8STR_RO;
Handle<v8::String> V8STR_MODIFIED;
private: private:
void _startCall(); void _startCall();
static Handle< Value > nativeCallback( const Arguments &args ); static Handle< Value > nativeCallback( V8Scope* scope, const Argume nts &args );
static v8::Handle< v8::Value > v8Callback( const v8::Arguments &arg s );
static Handle< Value > loadCallback( const Arguments &args ); static Handle< Value > load( V8Scope* scope, const Arguments &args );
static Handle< Value > Print(V8Scope* scope, const v8::Arguments& a rgs);
static Handle< Value > Version(V8Scope* scope, const v8::Arguments& args);
static Handle< Value > GCV8(V8Scope* scope, const v8::Arguments& ar gs);
V8ScriptEngine * _engine; V8ScriptEngine * _engine;
Persistent<Context> _context; Persistent<Context> _context;
Persistent<v8::Object> _global; Persistent<v8::Object> _global;
string _error; string _error;
vector< Persistent<Value> > _funcs; vector< Persistent<Value> > _funcs;
v8::Persistent<v8::Object> _this; v8::Persistent<v8::Object> _emptyObj;
v8::Persistent<v8::Function> _wrapper; v8::Persistent<v8::Function> _wrapper;
enum ConnectState { NOT , LOCAL , EXTERNAL }; enum ConnectState { NOT , LOCAL , EXTERNAL };
ConnectState _connectState; ConnectState _connectState;
std::map <string, v8::Persistent <v8::String> > _strCache;
Persistent<v8::ObjectTemplate> lzObjectTemplate;
Persistent<v8::ObjectTemplate> roObjectTemplate;
Persistent<v8::ObjectTemplate> lzArrayTemplate;
Persistent<v8::ObjectTemplate> internalFieldObjects;
}; };
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; }
class V8Unlocker : public Unlocker { class V8UnlockForClient : public Unlocker {
v8::Unlocker u_; V8Unlock u_;
}; };
virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U nlocker >( new V8Unlocker ); } virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U nlocker >( new V8UnlockForClient ); }
virtual void interrupt( unsigned opSpec );
virtual void interruptAll();
private: private:
friend class V8Scope; friend class V8Scope;
}; };
class ExternalString : public v8::String::ExternalAsciiStringResource {
public:
ExternalString(std::string str) : _data(str) {
}
~ExternalString() {
}
const char* data () const { return _data.c_str(); }
size_t length () const { return _data.length(); }
private:
// string _str;
// const char* _data;
std::string _data;
// size_t _len;
};
extern ScriptEngine * globalScriptEngine; extern ScriptEngine * globalScriptEngine;
extern map< unsigned, int > __interruptSpecToThreadId;
} }
 End of changes. 17 change blocks. 
14 lines changed or deleted 127 lines changed or added


 extsort.h   extsort.h 
skipping to change at line 23 skipping to change at line 23
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "jsobj.h" #include "jsobj.h"
#include "namespace.h" #include "namespace-inl.h"
#include "curop.h" #include "curop-inl.h"
#include "../util/array.h" #include "../util/array.h"
namespace mongo { namespace mongo {
/** /**
for sorting by BSONObj and attaching a value for external (disk) sorting by BSONObj and attaching a value
*/ */
class BSONObjExternalSorter : boost::noncopyable { class BSONObjExternalSorter : boost::noncopyable {
public: public:
BSONObjExternalSorter( IndexInterface &i, const BSONObj & order = B SONObj() , long maxFileSize = 1024 * 1024 * 100 );
~BSONObjExternalSorter();
typedef pair<BSONObj,DiskLoc> Data; typedef pair<BSONObj,DiskLoc> Data;
private: private:
static BSONObj extSortOrder; IndexInterface& _idxi;
static int extSortComp( const void *lv, const void *rv ){ static int _compare(IndexInterface& i, const Data& l, const Data& r , const Ordering& order) {
RARELY killCurrentOp.checkForInterrupt(); RARELY killCurrentOp.checkForInterrupt();
_compares++; _compares++;
int x = i.keyCompare(l.first, r.first, order);
if ( x )
return x;
return l.second.compare( r.second );
}
class MyCmp {
public:
MyCmp( IndexInterface& i, BSONObj order = BSONObj() ) : _i(i), _order( Ordering::make(order) ) {}
bool operator()( const Data &l, const Data &r ) const {
return _compare(_i, l, r, _order) < 0;
};
private:
IndexInterface& _i;
const Ordering _order;
};
static IndexInterface *extSortIdxInterface;
static Ordering extSortOrder;
static int extSortComp( const void *lv, const void *rv ) {
DEV RARELY {
dbMutex.assertWriteLocked(); // must be as we use a global var
}
Data * l = (Data*)lv; Data * l = (Data*)lv;
Data * r = (Data*)rv; Data * r = (Data*)rv;
int cmp = l->first.woCompare( r->first , extSortOrder ); return _compare(*extSortIdxInterface, *l, *r, extSortOrder);
if ( cmp )
return cmp;
return l->second.compare( r->second );
}; };
class FileIterator : boost::noncopyable { class FileIterator : boost::noncopyable {
public: public:
FileIterator( string file ); FileIterator( string file );
~FileIterator(); ~FileIterator();
bool more(); bool more();
Data next(); Data next();
private: private:
MemoryMappedFile _file; MemoryMappedFile _file;
char * _buf; char * _buf;
char * _end; char * _end;
}; };
class MyCmp {
public:
MyCmp( const BSONObj & order = BSONObj() ) : _order( order ){}
bool operator()( const Data &l, const Data &r ) const {
RARELY killCurrentOp.checkForInterrupt();
_compares++;
int x = l.first.woCompare( r.first , _order );
if ( x )
return x < 0;
return l.second.compare( r.second ) < 0;
};
private:
BSONObj _order;
};
public: public:
typedef FastArray<Data> InMemory; typedef FastArray<Data> InMemory;
class Iterator : boost::noncopyable { class Iterator : boost::noncopyable {
public: public:
Iterator( BSONObjExternalSorter * sorter ); Iterator( BSONObjExternalSorter * sorter );
~Iterator(); ~Iterator();
bool more(); bool more();
skipping to change at line 101 skipping to change at line 106
private: private:
MyCmp _cmp; MyCmp _cmp;
vector<FileIterator*> _files; vector<FileIterator*> _files;
vector< pair<Data,bool> > _stash; vector< pair<Data,bool> > _stash;
InMemory * _in; InMemory * _in;
InMemory::iterator _it; InMemory::iterator _it;
}; };
BSONObjExternalSorter( const BSONObj & order = BSONObj() , long max FileSize = 1024 * 1024 * 100 );
~BSONObjExternalSorter();
void add( const BSONObj& o , const DiskLoc & loc ); void add( const BSONObj& o , const DiskLoc & loc );
void add( const BSONObj& o , int a , int b ){ void add( const BSONObj& o , int a , int b ) {
add( o , DiskLoc( a , b ) ); add( o , DiskLoc( a , b ) );
} }
/* call after adding values, and before fetching the iterator */ /* call after adding values, and before fetching the iterator */
void sort(); void sort();
auto_ptr<Iterator> iterator(){ auto_ptr<Iterator> iterator() {
uassert( 10052 , "not sorted" , _sorted ); uassert( 10052 , "not sorted" , _sorted );
return auto_ptr<Iterator>( new Iterator( this ) ); return auto_ptr<Iterator>( new Iterator( this ) );
} }
int numFiles(){ int numFiles() {
return _files.size(); return _files.size();
} }
long getCurSizeSoFar(){ return _curSizeSoFar; } long getCurSizeSoFar() { return _curSizeSoFar; }
void hintNumObjects( long long numObjects ){ void hintNumObjects( long long numObjects ) {
if ( numObjects < _arraySize ) if ( numObjects < _arraySize )
_arraySize = (int)(numObjects + 100); _arraySize = (int)(numObjects + 100);
} }
private: private:
void _sortInMem(); void _sortInMem();
void sort( string file ); void sort( string file );
void finishMap(); void finishMap();
 End of changes. 14 change blocks. 
34 lines changed or deleted 36 lines changed or added


 file.h   file.h 
// file.h // file.h cross platform basic file class. supports 64 bit offsets and such .
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
skipping to change at line 26 skipping to change at line 26
*/ */
#pragma once #pragma once
#if !defined(_WIN32) #if !defined(_WIN32)
#include "errno.h" #include "errno.h"
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#else #include <sys/statvfs.h>
#include <windows.h>
#endif #endif
#include "text.h" #include "text.h"
namespace mongo { namespace mongo {
#ifndef __sunos__ #ifndef __sunos__
typedef uint64_t fileofs; typedef uint64_t fileofs;
#else #else
typedef boost::uint64_t fileofs; typedef boost::uint64_t fileofs;
#endif #endif
class FileInterface { /* NOTE: not thread-safe. (at least the windows implementation isn't. * /
public:
void open(const char *fn) {} class FileInterface {
void write(fileofs o, const char *data, unsigned len) {} public:
void read(fileofs o, char *data, unsigned len) {} void open(const char *fn) {}
bool bad() {return false;} void write(fileofs o, const char *data, unsigned len) {}
bool is_open() {return false;} void read(fileofs o, char *data, unsigned len) {}
fileofs len() { return 0; } bool bad() {return false;}
}; bool is_open() {return false;}
fileofs len() { return 0; }
void fsync() { assert(false); }
// shrink file to size bytes. No-op if file already smaller.
void truncate(fileofs size);
/** @return -1 if error or unavailable */
static boost::intmax_t freeSpace(const string &path) { assert(false ); return -1; }
};
#if defined(_WIN32) #if defined(_WIN32)
#include <io.h> #include <io.h>
class File : public FileInterface { class File : public FileInterface {
HANDLE fd; HANDLE fd;
bool _bad; bool _bad;
void err(BOOL b=false) { /* false = error happened */ string _name;
if( !b && !_bad ) { void err(BOOL b=false) { /* false = error happened */
if( !b && !_bad ) {
_bad = true;
log() << "File " << _name << "I/O error " << GetLastError() << '\n';
}
}
public:
File() {
fd = INVALID_HANDLE_VALUE;
_bad = true; _bad = true;
log() << "File I/O error " << GetLastError() << '\n';
} }
} ~File() {
public: if( is_open() ) CloseHandle(fd);
File() { fd = INVALID_HANDLE_VALUE;
fd = INVALID_HANDLE_VALUE;
_bad = true;
}
~File() {
if( is_open() ) CloseHandle(fd);
fd = INVALID_HANDLE_VALUE;
}
void open(const char *filename, bool readOnly=false ) {
fd = CreateFile(
toNativeString(filename).c_str(),
( readOnly ? 0 : GENERIC_WRITE ) | GENERIC_READ, FILE_SHAR E_READ,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if( !is_open() ) {
out() << "CreateFile failed " << filename << endl;
} }
else void open(const char *filename, bool readOnly=false , bool direct=f alse) {
_bad = false; _name = filename;
} fd = CreateFile(
void write(fileofs o, const char *data, unsigned len) { toNativeString(filename).c_str(),
LARGE_INTEGER li; ( readOnly ? 0 : GENERIC_WRITE ) | GENERIC_READ, FILE_ SHARE_WRITE|FILE_SHARE_READ,
li.QuadPart = o; NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointerEx(fd, li, NULL, FILE_BEGIN); if( !is_open() ) {
DWORD written; DWORD e = GetLastError();
err( WriteFile(fd, data, len, &written, NULL) ); log() << "Create/Open File failed " << filename << ' ' << e rrnoWithDescription(e) << endl;
} }
void read(fileofs o, char *data, unsigned len) { else
DWORD read; _bad = false;
LARGE_INTEGER li;
li.QuadPart = o;
SetFilePointerEx(fd, li, NULL, FILE_BEGIN);
int ok = ReadFile(fd, data, len, &read, 0);
if( !ok )
err(ok);
else
massert( 10438 , "ReadFile error - truncated file?", read == le n);
}
bool bad() { return _bad; }
bool is_open() { return fd != INVALID_HANDLE_VALUE; }
fileofs len() {
LARGE_INTEGER li;
li.LowPart = GetFileSize(fd, (DWORD *) &li.HighPart);
if( li.HighPart == 0 && li.LowPart == INVALID_FILE_SIZE ) {
err( false );
return 0;
} }
return li.QuadPart; static boost::intmax_t freeSpace(const string &path) {
} ULARGE_INTEGER avail;
void fsync() { FlushFileBuffers(fd); } if( GetDiskFreeSpaceEx(toNativeString(path.c_str()).c_str(), &a vail, NULL, NULL) ) {
}; return avail.QuadPart;
}
DWORD e = GetLastError();
log() << "GetDiskFreeSpaceEx fails errno: " << e << endl;
return -1;
}
void write(fileofs o, const char *data, unsigned len) {
LARGE_INTEGER li;
li.QuadPart = o;
SetFilePointerEx(fd, li, NULL, FILE_BEGIN);
DWORD written;
err( WriteFile(fd, data, len, &written, NULL) );
}
void read(fileofs o, char *data, unsigned len) {
DWORD read;
LARGE_INTEGER li;
li.QuadPart = o;
SetFilePointerEx(fd, li, NULL, FILE_BEGIN);
int ok = ReadFile(fd, data, len, &read, 0);
if( !ok )
err(ok);
else
massert( 10438 , "ReadFile error - truncated file?", read = = len);
}
bool bad() { return _bad; }
bool is_open() { return fd != INVALID_HANDLE_VALUE; }
fileofs len() {
LARGE_INTEGER li;
li.LowPart = GetFileSize(fd, (DWORD *) &li.HighPart);
if( li.HighPart == 0 && li.LowPart == INVALID_FILE_SIZE ) {
err( false );
return 0;
}
return li.QuadPart;
}
void fsync() { FlushFileBuffers(fd); }
void truncate(fileofs size) {
if (len() <= size)
return;
LARGE_INTEGER li;
li.QuadPart = size;
if (SetFilePointerEx(fd, li, NULL, FILE_BEGIN) == 0){
err(false);
return; //couldn't seek
}
err(SetEndOfFile(fd));
}
};
#else #else
class File : public FileInterface { class File : public FileInterface {
int fd; public:
bool _bad; int fd;
void err(bool ok) { private:
if( !ok && !_bad ) { bool _bad;
void err(bool ok) {
if( !ok && !_bad ) {
_bad = true;
log() << "File I/O " << errnoWithDescription() << '\n';
}
}
public:
File() {
fd = -1;
_bad = true; _bad = true;
log() << "File I/O " << errnoWithDescription() << '\n';
} }
} ~File() {
public: if( is_open() ) ::close(fd);
File() { fd = -1;
fd = -1; }
_bad = true;
}
~File() {
if( is_open() ) ::close(fd);
fd = -1;
}
#ifndef O_NOATIME #ifndef O_NOATIME
#define O_NOATIME 0 #define O_NOATIME 0
#define lseek64 lseek
#endif #endif
void open(const char *filename, bool readOnly=false ) { void open(const char *filename, bool readOnly=false , bool direct=f alse) {
fd = ::open(filename, fd = ::open(filename,
O_CREAT | ( readOnly ? 0 : ( O_RDWR | O_NOATIME ) ) , O_CREAT | ( readOnly ? 0 : ( O_RDWR | O_NOATIME ) )
S_IRUSR | S_IWUSR); #if defined(O_DIRECT)
if ( fd <= 0 ) { | ( direct ? O_DIRECT : 0 )
out() << "couldn't open " << filename << ' ' << errnoWithDescri ption() << endl; #endif
return; ,
S_IRUSR | S_IWUSR);
if ( fd <= 0 ) {
out() << "couldn't open " << filename << ' ' << errnoWithDe scription() << endl;
return;
}
_bad = false;
} }
_bad = false; void write(fileofs o, const char *data, unsigned len) {
} err( ::pwrite(fd, data, len, o) == (int) len );
void write(fileofs o, const char *data, unsigned len) { }
lseek64(fd, o, SEEK_SET); void read(fileofs o, char *data, unsigned len) {
err( ::write(fd, data, len) == (int) len ); ssize_t s = ::pread(fd, data, len, o);
} if( s == -1 ) {
void read(fileofs o, char *data, unsigned len) { err(false);
lseek(fd, o, SEEK_SET); }
err( ::read(fd, data, len) == (int) len ); else if( s != (int) len ) {
} _bad = true;
bool bad() { return _bad; } log() << "File error read:" << s << " bytes, wanted:" << le n << " ofs:" << o << endl;
bool is_open() { return fd > 0; } }
fileofs len() { }
return lseek(fd, 0, SEEK_END); bool bad() { return _bad; }
} bool is_open() { return fd > 0; }
void fsync() { ::fsync(fd); } fileofs len() {
}; off_t o = lseek(fd, 0, SEEK_END);
if( o != (off_t) -1 )
return o;
err(false);
return 0;
}
void fsync() { ::fsync(fd); }
static boost::intmax_t freeSpace ( const string &path ) {
struct statvfs info;
assert( !statvfs( path.c_str() , &info ) );
return boost::intmax_t( info.f_bavail ) * info.f_frsize;
}
void truncate(fileofs size) {
if (len() <= size)
return;
err(ftruncate(fd, size) == 0);
}
};
#endif #endif
} }
 End of changes. 17 change blocks. 
112 lines changed or deleted 171 lines changed or added


 file_allocator.h   file_allocator.h 
skipping to change at line 19 skipping to change at line 19
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include "../pch.h" #include "../pch.h"
#include <fcntl.h>
#include <errno.h>
#if defined(__freebsd__) || defined(__openbsd__)
#include <sys/stat.h>
#endif
#ifndef O_NOATIME
#define O_NOATIME 0
#endif
namespace mongo { namespace mongo {
/* Handles allocation of contiguous files on disk. Allocation may be /*
requested asynchronously or synchronously. * Handles allocation of contiguous files on disk. Allocation may be
*/ * requested asynchronously or synchronously.
class FileAllocator { * singleton
/* The public functions may not be called concurrently. The alloca tion */
functions may be called multiple times per file, but only the fi rst class FileAllocator : boost::noncopyable {
size specified per file will be used. /*
* The public functions may not be called concurrently. The alloca tion
* functions may be called multiple times per file, but only the fi rst
* size specified per file will be used.
*/ */
public: public:
#if !defined(_WIN32) void start();
FileAllocator() : pendingMutex_("FileAllocator"), failed_() {}
#endif
void start() {
#if !defined(_WIN32)
Runner r( *this );
boost::thread t( r );
#endif
}
// May be called if file exists. If file exists, or its allocation has
// been requested, size is updated to match existing file 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)
scoped_lock lk( pendingMutex_ );
if ( failed_ )
return;
long oldSize = prevSize( name );
if ( oldSize != -1 ) {
size = oldSize;
return;
}
pending_.push_back( name );
pendingSize_[ name ] = size;
pendingUpdated_.notify_all();
#endif
}
// Returns when file has been allocated. If file exists, size is
// updated to match existing file size.
void allocateAsap( const string &name, long &size ) {
#if !defined(_WIN32)
scoped_lock lk( pendingMutex_ );
long oldSize = prevSize( name );
if ( oldSize != -1 ) {
size = oldSize;
if ( !inProgress( name ) )
return;
}
checkFailure();
pendingSize_[ name ] = size;
if ( pending_.size() == 0 )
pending_.push_back( name );
else if ( pending_.front() != name ) {
pending_.remove( name );
list< string >::iterator i = pending_.begin();
++i;
pending_.insert( i, name );
}
pendingUpdated_.notify_all();
while( inProgress( name ) ) {
checkFailure();
pendingUpdated_.wait( lk.boost() );
}
#endif
}
void waitUntilFinished() const { /**
#if !defined(_WIN32) * May be called if file exists. If file exists, or its allocation has
if ( failed_ ) * been requested, size is updated to match existing file size.
return; */
scoped_lock lk( pendingMutex_ ); void requestAllocation( const string &name, long &size );
while( pending_.size() != 0 )
pendingUpdated_.wait( lk.boost() );
#endif
}
static void ensureLength( int fd , long size ){ /**
* Returns when file has been allocated. If file exists, size is
* updated to match existing file size.
*/
void allocateAsap( const string &name, unsigned long long &size );
#if defined(_WIN32) void waitUntilFinished() const;
// we don't zero on windows
// TODO : we should to avoid fragmentation
#else
#if defined(__linux__) bool hasFailed() const;
int ret = posix_fallocate(fd,0,size);
if ( ret == 0 )
return;
log() << "posix_fallocate failed: " << errnoWithDescription( re t ) << " falling back" << endl; static void ensureLength(int fd , long size);
#endif
off_t filelen = lseek(fd, 0, SEEK_END); /** @return the singletone */
if ( filelen < size ) { static FileAllocator * get();
if (filelen != 0) {
stringstream ss;
ss << "failure creating new datafile; lseek failed for fd " << fd << " with errno: " << errnoWithDescription();
massert( 10440 , ss.str(), filelen == 0 );
}
// Check for end of disk.
massert( 10441 , "Unable to allocate file of desired size" ,
size - 1 == lseek(fd, size - 1, SEEK_SET) );
massert( 10442 , "Unable to allocate file of desired size" ,
1 == write(fd, "", 1) );
lseek(fd, 0, SEEK_SET);
const long z = 256 * 1024; private:
const boost::scoped_array<char> buf_holder (new char[z]);
char* buf = buf_holder.get();
memset(buf, 0, z);
long left = size;
while ( left > 0 ) {
long towrite = left;
if ( towrite > z )
towrite = z;
int written = write( fd , buf , towrite ); FileAllocator();
massert( 10443 , errnoWithPrefix("write failed" ), writ ten > 0 );
left -= written;
}
}
#endif
}
private:
#if !defined(_WIN32) #if !defined(_WIN32)
void checkFailure() { void checkFailure();
massert( 12520, "file allocation failure", !failed_ );
}
// caller must hold pendingMutex_ lock. Returns size if allocated or // caller must hold pendingMutex_ lock. Returns size if allocated or
// allocation requested, -1 otherwise. // allocation requested, -1 otherwise.
long prevSize( const string &name ) const { long prevSize( const string &name ) const;
if ( pendingSize_.count( name ) > 0 )
return pendingSize_[ name ];
if ( boost::filesystem::exists( name ) )
return boost::filesystem::file_size( name );
return -1;
}
// caller must hold pendingMutex_ lock. // caller must hold pendingMutex_ lock.
bool inProgress( const string &name ) const { bool inProgress( const string &name ) const;
for( list< string >::const_iterator i = pending_.begin(); i != pending_.end(); ++i )
if ( *i == name )
return true;
return false;
}
mutable mongo::mutex pendingMutex_;
mutable boost::condition pendingUpdated_;
list< string > pending_;
mutable map< string, long > pendingSize_;
bool failed_;
struct Runner {
Runner( FileAllocator &allocator ) : a_( allocator ) {}
FileAllocator &a_;
void operator()() {
while( 1 ) {
{
scoped_lock lk( a_.pendingMutex_ );
if ( a_.pending_.size() == 0 )
a_.pendingUpdated_.wait( lk.boost() );
}
while( 1 ) {
string name;
long size;
{
scoped_lock lk( a_.pendingMutex_ );
if ( a_.pending_.size() == 0 )
break;
name = a_.pending_.front();
size = a_.pendingSize_[ name ];
}
try {
log() << "allocating new datafile " << name << ", filling with zeroes..." << endl;
long fd = open(name.c_str(), O_CREAT | O_RDWR | O_NOATIME, S_IRUSR | S_IWUSR);
if ( fd <= 0 ) {
stringstream ss;
ss << "couldn't open " << name << ' ' << er rnoWithDescription();
massert( 10439 , ss.str(), fd <= 0 );
}
#if defined(POSIX_FADV_DONTNEED)
if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTN EED) ) {
log() << "warning: posix_fadvise fails " << name << ' ' << errnoWithDescription() << endl;
}
#endif
Timer t; /** called from the worked thread */
static void run( FileAllocator * fa );
/* make sure the file is the full desired lengt h */ mutable mongo::mutex _pendingMutex;
ensureLength( fd , size ); mutable boost::condition _pendingUpdated;
log() << "done allocating datafile " << name << ", " list< string > _pending;
<< "size: " << size/1024/1024 << "MB, " mutable map< string, long > _pendingSize;
<< " took " << ((double)t.millis())/1000. 0 << " secs"
<< endl;
close( fd ); bool _failed;
#endif
} catch ( ... ) { static FileAllocator* _instance;
problem() << "Failed to allocate new file: " << name
<< ", size: " << size << ", aborting. " << endl;
try {
BOOST_CHECK_EXCEPTION( boost::filesystem::r emove( name ) );
} catch ( ... ) {
}
scoped_lock lk( a_.pendingMutex_ );
a_.failed_ = true;
// not erasing from pending
a_.pendingUpdated_.notify_all();
return; // no more allocation
}
{
scoped_lock lk( a_.pendingMutex_ );
a_.pendingSize_.erase( name );
a_.pending_.pop_front();
a_.pendingUpdated_.notify_all();
}
}
}
}
};
#endif
}; };
FileAllocator &theFileAllocator(); /** like "mkdir -p" but on parent dir of p rather than p itself */
boost::filesystem::path ensureParentDirCreated(const boost::filesystem: :path& p);
} // namespace mongo } // namespace mongo
 End of changes. 22 change blocks. 
214 lines changed or deleted 43 lines changed or added


 filever.h   filever.h 
skipping to change at line 23 skipping to change at line 23
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
namespace mongo { namespace mongo {
inline void checkDataFileVersion(NamespaceDetails& d) { inline void checkDataFileVersion(NamespaceDetails& d) {
} }
inline void checkIndexFileVersion(NamespaceDetails& d) { inline void checkIndexFileVersion(NamespaceDetails& d) {
} }
} }
 End of changes. 2 change blocks. 
4 lines changed or deleted 4 lines changed or added


 fine_clock.h   fine_clock.h 
skipping to change at line 39 skipping to change at line 39
* does not involve a trip to the OS (VDSO). * does not involve a trip to the OS (VDSO).
* *
* We're exporting a type WallTime that is and should remain * We're exporting a type WallTime that is and should remain
* opaque. The business of getting accurate time is still ongoing * opaque. The business of getting accurate time is still ongoing
* and we may change the internal representation of this class. * and we may change the internal representation of this class.
* (http://lwn.net/Articles/388188/) * (http://lwn.net/Articles/388188/)
* *
* Really, you shouldn't be using this class in hot code paths for * Really, you shouldn't be using this class in hot code paths for
* platforms you're not sure whether the overhead is low. * platforms you're not sure whether the overhead is low.
*/ */
class FineClock{ class FineClock {
public: public:
typedef timespec WallTime; typedef timespec WallTime;
static WallTime now(){ static WallTime now() {
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
return ts; return ts;
} }
static uint64_t diffInNanos( WallTime end, WallTime start ){ static uint64_t diffInNanos( WallTime end, WallTime start ) {
uint64_t diff; uint64_t diff;
if ( end.tv_nsec < start.tv_nsec ){ if ( end.tv_nsec < start.tv_nsec ) {
diff = 1000000000 * ( end.tv_sec - start.tv_sec - 1); diff = 1000000000 * ( end.tv_sec - start.tv_sec - 1);
diff += 1000000000 + end.tv_nsec - start.tv_nsec; diff += 1000000000 + end.tv_nsec - start.tv_nsec;
} else { }
else {
diff = 1000000000 * ( end.tv_sec - start.tv_sec ); diff = 1000000000 * ( end.tv_sec - start.tv_sec );
diff += end.tv_nsec - start.tv_nsec; diff += end.tv_nsec - start.tv_nsec;
} }
return diff; return diff;
} }
}; };
} }
#endif // DB_STATS_FINE_CLOCK_HEADER #endif // DB_STATS_FINE_CLOCK_HEADER
 End of changes. 5 change blocks. 
5 lines changed or deleted 6 lines changed or added


 framework.h   framework.h 
skipping to change at line 50 skipping to change at line 50
#include "../db/instance.h" #include "../db/instance.h"
namespace mongo { namespace mongo {
namespace regression { namespace regression {
class Result; class Result;
class TestCase { class TestCase {
public: public:
virtual ~TestCase(){} virtual ~TestCase() {}
virtual void run() = 0; virtual void run() = 0;
virtual string getName() = 0; virtual string getName() = 0;
}; };
template< class T > template< class T >
class TestHolderBase : public TestCase { class TestHolderBase : public TestCase {
public: public:
TestHolderBase(){} TestHolderBase() {}
virtual ~TestHolderBase(){} virtual ~TestHolderBase() {}
virtual void run(){ virtual void run() {
auto_ptr<T> t; auto_ptr<T> t;
t.reset( create() ); t.reset( create() );
t->run(); t->run();
} }
virtual T * create() = 0; virtual T * create() = 0;
virtual string getName(){ virtual string getName() {
return demangleName( typeid(T) ); return demangleName( typeid(T) );
} }
}; };
template< class T > template< class T >
class TestHolder0 : public TestHolderBase<T> { class TestHolder0 : public TestHolderBase<T> {
public: public:
virtual T * create(){ virtual T * create() {
return new T(); return new T();
} }
}; };
template< class T , typename A > template< class T , typename A >
class TestHolder1 : public TestHolderBase<T> { class TestHolder1 : public TestHolderBase<T> {
public: public:
TestHolder1( const A& a ) : _a(a){} TestHolder1( const A& a ) : _a(a) {}
virtual T * create(){ virtual T * create() {
return new T( _a ); return new T( _a );
} }
const A& _a; const A& _a;
}; };
class Suite { class Suite {
public: public:
Suite( string name ) : _name( name ){ Suite( string name ) : _name( name ) {
registerSuite( name , this ); registerSuite( name , this );
_ran = 0; _ran = 0;
} }
virtual ~Suite() { virtual ~Suite() {
if ( _ran ){ if ( _ran ) {
DBDirectClient c; DBDirectClient c;
c.dropDatabase( "unittests" ); c.dropDatabase( "unittests" );
} }
} }
template<class T> template<class T>
void add(){ void add() {
_tests.push_back( new TestHolder0<T>() ); _tests.push_back( new TestHolder0<T>() );
} }
template<class T , typename A > template<class T , typename A >
void add( const A& a ){ void add( const A& a ) {
_tests.push_back( new TestHolder1<T,A>(a) ); _tests.push_back( new TestHolder1<T,A>(a) );
} }
Result * run( const string& filter ); Result * run( const string& filter );
static int run( vector<string> suites , const string& filter ); static int run( vector<string> suites , const string& filter );
static int run( int argc , char ** argv , string default_dbpath ); static int run( int argc , char ** argv , string default_dbpath );
protected: protected:
virtual void setupTests() = 0; virtual void setupTests() = 0;
skipping to change at line 137 skipping to change at line 137
void registerSuite( string name , Suite * s ); void registerSuite( string name , Suite * s );
}; };
void assert_pass(); void assert_pass();
void assert_fail( const char * exp , const char * file , unsigned l ine ); void assert_fail( const char * exp , const char * file , unsigned l ine );
void fail( const char * exp , const char * file , unsigned line ); void fail( const char * exp , const char * file , unsigned line );
class MyAssertionException : boost::noncopyable { class MyAssertionException : boost::noncopyable {
public: public:
MyAssertionException(){ MyAssertionException() {
ss << "assertion: "; ss << "assertion: ";
} }
stringstream ss; stringstream ss;
}; };
class MyAsserts { class MyAsserts {
public: public:
MyAsserts( const char * aexp , const char * bexp , const char * file , unsigned line ) MyAsserts( const char * aexp , const char * bexp , const char * file , unsigned line )
: _aexp( aexp ) , _bexp( bexp ) , _file( file ) , _line( li ne ){ : _aexp( aexp ) , _bexp( bexp ) , _file( file ) , _line( li ne ) {
} }
template<typename A,typename B> template<typename A,typename B>
void ae( A a , B b ){ void ae( A a , B b ) {
_gotAssert(); _gotAssert();
if ( a == b ) if ( a == b )
return; return;
printLocation(); printLocation();
MyAssertionException * e = getBase(); MyAssertionException * e = getBase();
e->ss << a << " != " << b << endl; e->ss << a << " != " << b << endl;
log() << e->ss.str() << endl; log() << e->ss.str() << endl;
throw e; throw e;
} }
template<typename A,typename B> template<typename A,typename B>
void nae( A a , B b ){ void nae( A a , B b ) {
_gotAssert(); _gotAssert();
if ( a != b ) if ( a != b )
return; return;
printLocation(); printLocation();
MyAssertionException * e = getBase(); MyAssertionException * e = getBase();
e->ss << a << " == " << b << endl; e->ss << a << " == " << b << endl;
log() << e->ss.str() << endl; log() << e->ss.str() << endl;
throw e; throw e;
 End of changes. 13 change blocks. 
16 lines changed or deleted 16 lines changed or added


 goodies.h   goodies.h 
// @file goodies.h // @file goodies.h
// miscellaneous junk // miscellaneous
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
skipping to change at line 26 skipping to change at line 26
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../bson/util/misc.h" #include "../bson/util/misc.h"
#include "concurrency/mutex.h" #include "concurrency/mutex.h"
namespace mongo { namespace mongo {
void setThreadName(const char * name); /* @return a dump of the buffer as hex byte ascii output */
string hexdump(const char *data, unsigned len);
/**
* @return if this name has an increasing counter associated, return th e value
* otherwise 0
*/
unsigned setThreadName(const char * name);
string getThreadName(); string getThreadName();
template<class T> template<class T>
inline string ToString(const T& t) { inline string ToString(const T& t) {
stringstream s; stringstream s;
s << t; s << t;
return s.str(); return s.str();
} }
#if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d efined(__openbsd__) && !defined(__sun__) #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d efined(__openbsd__) && !defined(__sun__)
skipping to change at line 52 skipping to change at line 59
namespace mongo { namespace mongo {
inline pthread_t GetCurrentThreadId() { inline pthread_t GetCurrentThreadId() {
return pthread_self(); return pthread_self();
} }
/* use "addr2line -CFe <exe>" to parse. */ /* use "addr2line -CFe <exe>" to parse. */
inline void printStackTrace( ostream &o = cout ) { inline void printStackTrace( ostream &o = cout ) {
void *b[20]; void *b[20];
size_t size;
size_t i;
size = backtrace(b, 20); int size = backtrace(b, 20);
for (i = 0; i < size; i++) for (int i = 0; i < size; i++)
o << hex << b[i] << dec << ' '; o << hex << b[i] << dec << ' ';
o << endl; o << endl;
char **strings; char **strings;
strings = backtrace_symbols(b, size); strings = backtrace_symbols(b, size);
for (i = 0; i < size; i++) for (int i = 0; i < size; i++)
o << ' ' << strings[i] << '\n'; o << ' ' << strings[i] << '\n';
o.flush(); o.flush();
free (strings); free (strings);
} }
#else #else
inline void printStackTrace( ostream &o = cout ) { } inline void printStackTrace( ostream &o = cout ) { }
#endif #endif
/* set to TRUE if we are exiting */
extern bool goingAway;
/* find the multimap member which matches a particular key and value.
note this can be slow if there are a lot with the same key.
*/
template<class C,class K,class V> inline typename C::iterator kv_find(C & c, const K& k,const V& v) {
pair<typename C::iterator,typename C::iterator> p = c.equal_range(k );
for ( typename C::iterator it=p.first; it!=p.second; ++it)
if ( it->second == v )
return it;
return c.end();
}
bool isPrime(int n); bool isPrime(int n);
int nextPrime(int n); int nextPrime(int n);
inline void dumpmemory(const char *data, int len) { inline void dumpmemory(const char *data, int len) {
if ( len > 1024 ) if ( len > 1024 )
len = 1024; len = 1024;
try { try {
const char *q = data; const char *q = data;
const char *p = q; const char *p = q;
while ( len > 0 ) { while ( len > 0 ) {
skipping to change at line 113 skipping to change at line 101
cout << '.'; cout << '.';
p++; p++;
} }
cout << " "; cout << " ";
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" // PRINT(2+2); prints "2+2: 4"
#define MONGO_PRINT(x) cout << #x ": " << (x) << endl #define MONGO_PRINT(x) cout << #x ": " << (x) << endl
#define PRINT MONGO_PRINT #define PRINT MONGO_PRINT
// PRINTFL; prints file:line // PRINTFL; prints file:line
#define MONGO_PRINTFL cout << __FILE__ ":" << __LINE__ << endl #define MONGO_PRINTFL cout << __FILE__ ":" << __LINE__ << endl
#define PRINTFL MONGO_PRINTFL #define PRINTFL MONGO_PRINTFL
#define MONGO_FLOG log() << __FILE__ ":" << __LINE__ << endl
#define FLOG MONGO_FLOG
#undef assert #undef assert
#define assert MONGO_assert #define assert MONGO_assert
struct WrappingInt {
WrappingInt() {
x = 0;
}
WrappingInt(unsigned z) : x(z) { }
unsigned x;
operator unsigned() const {
return x;
}
static int diff(unsigned a, unsigned b) {
return a-b;
}
bool operator<=(WrappingInt r) {
// platform dependent
int df = (r.x - x);
return df >= 0;
}
bool operator>(WrappingInt r) {
return !(r<=*this);
}
};
inline void time_t_to_Struct(time_t t, struct tm * buf , bool local = f alse ) {
#if defined(_WIN32)
if ( local )
localtime_s( buf , &t );
else
gmtime_s(buf, &t);
#else
if ( local )
localtime_r(&t, buf);
else
gmtime_r(&t, buf);
#endif
}
// uses ISO 8601 dates without trailing Z
// colonsOk should be false when creating filenames
inline string terseCurrentTime(bool colonsOk=true){
struct tm t;
time_t_to_Struct( time(0) , &t );
const char* fmt = (colonsOk ? "%Y-%m-%dT%H:%M:%S" : "%Y-%m-%dT%H-%M -%S");
char buf[32];
assert(strftime(buf, sizeof(buf), fmt, &t) == 19);
return buf;
}
#define MONGO_asctime _asctime_not_threadsafe_
#define asctime MONGO_asctime
#define MONGO_gmtime _gmtime_not_threadsafe_
#define gmtime MONGO_gmtime
#define MONGO_localtime _localtime_not_threadsafe_
#define localtime MONGO_localtime
#define MONGO_ctime _ctime_is_not_threadsafe_
#define ctime MONGO_ctime
#if defined(_WIN32) || defined(__sunos__)
inline void sleepsecs(int s) {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += s;
boost::thread::sleep(xt);
}
inline void sleepmillis(long long s) {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += (int)( s / 1000 );
xt.nsec += (int)(( s % 1000 ) * 1000000);
if ( xt.nsec >= 1000000000 ) {
xt.nsec -= 1000000000;
xt.sec++;
}
boost::thread::sleep(xt);
}
inline void sleepmicros(long long s) {
if ( s <= 0 )
return;
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += (int)( s / 1000000 );
xt.nsec += (int)(( s % 1000000 ) * 1000);
if ( xt.nsec >= 1000000000 ) {
xt.nsec -= 1000000000;
xt.sec++;
}
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(long long s) {
if ( s <= 0 )
return;
struct timespec t;
t.tv_sec = (int)(s / 1000000);
t.tv_nsec = 1000 * ( s % 1000000 );
struct timespec out;
if ( nanosleep( &t , &out ) ){
cout << "nanosleep failed" << endl;
}
}
inline void sleepmillis(long long s) {
sleepmicros( s * 1000 );
}
#endif
// note this wraps
inline int tdiff(unsigned told, unsigned tnew) {
return WrappingInt::diff(tnew, told);
}
inline unsigned curTimeMillis() {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
unsigned t = xt.nsec / 1000000;
return (xt.sec & 0xfffff) * 1000 + t;
}
inline Date_t jsTime() {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
unsigned long long t = xt.nsec / 1000000;
return ((unsigned long long) xt.sec * 1000) + t;
}
inline unsigned long long curTimeMicros64() {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
unsigned long long t = xt.nsec / 1000;
return (((unsigned long long) xt.sec) * 1000000) + t;
}
// measures up to 1024 seconds. or, 512 seconds with tdiff that is...
inline unsigned curTimeMicros() {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
unsigned t = xt.nsec / 1000;
unsigned secs = xt.sec % 1024;
return secs*1000000 + t;
}
// simple scoped timer
class Timer {
public:
Timer() {
reset();
}
Timer( unsigned long long start ) {
old = start;
}
int seconds() const {
return (int)(micros() / 1000000);
}
int millis() const {
return (long)(micros() / 1000);
}
unsigned long long micros() const {
unsigned long long n = curTimeMicros64();
return n - old;
}
unsigned long long micros(unsigned long long & n) const { // return s cur time in addition to timer result
n = curTimeMicros64();
return n - old;
}
unsigned long long startTime(){
return old;
}
void reset() {
old = curTimeMicros64();
}
private:
unsigned long long old;
};
/*
class DebugMutex : boost::noncopyable {
friend class lock;
mongo::mutex m;
int locked;
public:
DebugMutex() : locked(0); { }
bool isLocked() { return locked; }
};
*/
//typedef scoped_lock lock;
inline bool startsWith(const char *str, const char *prefix) { inline bool startsWith(const char *str, const char *prefix) {
size_t l = strlen(prefix); size_t l = strlen(prefix);
if ( strlen(str) < l ) return false; if ( strlen(str) < l ) return false;
return strncmp(str, prefix, l) == 0; return strncmp(str, prefix, l) == 0;
} }
inline bool startsWith(string s, string p) { return startsWith(s.c_str( ), p.c_str()); } inline bool startsWith(string s, string p) { return startsWith(s.c_str( ), p.c_str()); }
inline bool endsWith(const char *p, const char *suffix) { inline bool endsWith(const char *p, const char *suffix) {
size_t a = strlen(p); size_t a = strlen(p);
size_t b = strlen(suffix); size_t b = strlen(suffix);
skipping to change at line 358 skipping to change at line 153
} }
#else #else
inline unsigned long fixEndian(unsigned long x) { inline unsigned long fixEndian(unsigned long x) {
return swapEndian(x); return swapEndian(x);
} }
#endif #endif
#if !defined(_WIN32) #if !defined(_WIN32)
typedef int HANDLE; typedef int HANDLE;
inline void strcpy_s(char *dst, unsigned len, const char *src) { inline void strcpy_s(char *dst, unsigned len, const char *src) {
assert( strlen(src) < len );
strcpy(dst, src); strcpy(dst, src);
} }
#else #else
typedef void *HANDLE; typedef void *HANDLE;
#endif #endif
/* 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 ) { }
T get() { T get() const {
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;
boost::thread_specific_ptr<T> _val; boost::thread_specific_ptr<T> _val;
const T _default;
}; };
class ProgressMeter : boost::noncopyable { class ProgressMeter : boost::noncopyable {
public: public:
ProgressMeter( long long total , int secondsBetween = 3 , int check Interval = 100 ){ ProgressMeter( unsigned long long total , int secondsBetween = 3 , int checkInterval = 100 ) {
reset( total , secondsBetween , checkInterval ); reset( total , secondsBetween , checkInterval );
} }
ProgressMeter(){ ProgressMeter() {
_active = 0; _active = 0;
} }
void reset( long long total , int secondsBetween = 3 , int checkInt erval = 100 ){ // typically you do ProgressMeterHolder
void reset( unsigned long long total , int secondsBetween = 3 , int checkInterval = 100 ) {
_total = total; _total = total;
_secondsBetween = secondsBetween; _secondsBetween = secondsBetween;
_checkInterval = checkInterval; _checkInterval = checkInterval;
_done = 0; _done = 0;
_hits = 0; _hits = 0;
_lastTime = (int)time(0); _lastTime = (int)time(0);
_active = 1; _active = 1;
} }
void finished(){ void finished() {
_active = 0; _active = 0;
} }
bool isActive(){ bool isActive() {
return _active; return _active;
} }
bool hit( int n = 1 ){ /**
if ( ! _active ){ * @param n how far along we are relative to the total # we set in CurOp::setMessage
* @return if row was printed
*/
bool hit( int n = 1 ) {
if ( ! _active ) {
cout << "warning: hit on in-active ProgressMeter" << endl; cout << "warning: hit on in-active ProgressMeter" << endl;
return false;
} }
_done += n; _done += n;
_hits++; _hits++;
if ( _hits % _checkInterval ) if ( _hits % _checkInterval )
return false; return false;
int t = (int) time(0); int t = (int) time(0);
if ( t - _lastTime < _secondsBetween ) if ( t - _lastTime < _secondsBetween )
return false; return false;
if ( _total > 0 ){ if ( _total > 0 ) {
int per = (int)( ( (double)_done * 100.0 ) / (double)_total ); int per = (int)( ( (double)_done * 100.0 ) / (double)_total );
cout << "\t\t" << _done << "/" << _total << "\t" << per << "%" << endl; cout << "\t\t" << _done << "/" << _total << "\t" << per << "%" << endl;
} }
_lastTime = t; _lastTime = t;
return true; return true;
} }
long long done(){ void setTotalWhileRunning( unsigned long long total ) {
return _done; _total = total;
} }
long long hits(){ unsigned long long done() const { return _done; }
return _hits;
} unsigned long long hits() const { return _hits; }
unsigned long long total() const { return _total; }
string toString() const { string toString() const {
if ( ! _active ) if ( ! _active )
return ""; return "";
stringstream buf; stringstream buf;
buf << _done << "/" << _total << " " << (_done*100)/_total << " %"; buf << _done << "/" << _total << " " << (_done*100)/_total << " %";
return buf.str(); return buf.str();
} }
bool operator==( const ProgressMeter& other ) const { bool operator==( const ProgressMeter& other ) const {
return this == &other; return this == &other;
} }
private: private:
bool _active; bool _active;
long long _total; unsigned long long _total;
int _secondsBetween; int _secondsBetween;
int _checkInterval; int _checkInterval;
long long _done; unsigned long long _done;
long long _hits; unsigned long long _hits;
int _lastTime; int _lastTime;
}; };
// e.g.:
// CurOp * op = cc().curop();
// ProgressMeterHolder pm( op->setMessage( "index: (1/3) external sort" , d->stats.nrecords , 10 ) );
// loop { pm.hit(); }
class ProgressMeterHolder : boost::noncopyable { class ProgressMeterHolder : boost::noncopyable {
public: public:
ProgressMeterHolder( ProgressMeter& pm ) ProgressMeterHolder( ProgressMeter& pm )
: _pm( pm ){ : _pm( pm ) {
} }
~ProgressMeterHolder(){ ~ProgressMeterHolder() {
_pm.finished(); _pm.finished();
} }
ProgressMeter* operator->(){ ProgressMeter* operator->() {
return &_pm; return &_pm;
} }
bool hit( int n = 1 ){ bool hit( int n = 1 ) {
return _pm.hit( n ); return _pm.hit( n );
} }
void finished(){ void finished() {
_pm.finished(); _pm.finished();
} }
bool operator==( const ProgressMeter& other ){ bool operator==( const ProgressMeter& other ) {
return _pm == other; return _pm == other;
} }
private: private:
ProgressMeter& _pm; ProgressMeter& _pm;
}; };
class TicketHolder { class TicketHolder {
public: public:
TicketHolder( int num ) : _mutex("TicketHolder") { TicketHolder( int num ) : _mutex("TicketHolder") {
_outof = num; _outof = num;
_num = num; _num = num;
} }
bool tryAcquire(){ bool tryAcquire() {
scoped_lock lk( _mutex ); scoped_lock lk( _mutex );
if ( _num <= 0 ){ if ( _num <= 0 ) {
if ( _num < 0 ){ if ( _num < 0 ) {
cerr << "DISASTER! in TicketHolder" << endl; cerr << "DISASTER! in TicketHolder" << endl;
} }
return false; return false;
} }
_num--; _num--;
return true; return true;
} }
void release(){ void release() {
scoped_lock lk( _mutex ); scoped_lock lk( _mutex );
_num++; _num++;
} }
void resize( int newSize ){ void resize( int newSize ) {
scoped_lock lk( _mutex ); scoped_lock lk( _mutex );
int used = _outof - _num; int used = _outof - _num;
if ( used > newSize ){ if ( used > newSize ) {
cout << "ERROR: can't resize since we're using (" << used < < ") more than newSize(" << newSize << ")" << endl; cout << "ERROR: can't resize since we're using (" << used < < ") more than newSize(" << newSize << ")" << endl;
return; return;
} }
_outof = newSize; _outof = newSize;
_num = _outof - used; _num = _outof - used;
} }
int available() const { int available() const {
return _num; return _num;
skipping to change at line 564 skipping to change at line 372
int outof() const { return _outof; } int outof() const { return _outof; }
private: private:
int _outof; int _outof;
int _num; int _num;
mongo::mutex _mutex; mongo::mutex _mutex;
}; };
class TicketHolderReleaser { class TicketHolderReleaser {
public: public:
TicketHolderReleaser( TicketHolder * holder ){ TicketHolderReleaser( TicketHolder * holder ) {
_holder = holder; _holder = holder;
} }
~TicketHolderReleaser(){ ~TicketHolderReleaser() {
_holder->release(); _holder->release();
} }
private: private:
TicketHolder * _holder; TicketHolder * _holder;
}; };
/** /**
* this is a thread safe string * this is a thread safe string
* you will never get a bad pointer, though data may be mungedd * you will never get a bad pointer, though data may be mungedd
*/ */
class ThreadSafeString { class ThreadSafeString {
public: public:
ThreadSafeString( size_t size=256 ) ThreadSafeString( size_t size=256 )
: _size( 256 ) , _buf( new char[256] ){ : _size( size ) , _buf( new char[size] ) {
memset( _buf , 0 , _size ); memset( _buf , 0 , _size );
} }
ThreadSafeString( const ThreadSafeString& other ) ThreadSafeString( const ThreadSafeString& other )
: _size( other._size ) , _buf( new char[_size] ){ : _size( other._size ) , _buf( new char[_size] ) {
strncpy( _buf , other._buf , _size ); strncpy( _buf , other._buf , _size );
} }
~ThreadSafeString(){ ~ThreadSafeString() {
delete[] _buf; delete[] _buf;
_buf = 0; _buf = 0;
} }
string toString() const { string toString() const {
string s = _buf; string s = _buf;
return s; return s;
} }
ThreadSafeString& operator=( const char * str ){ ThreadSafeString& operator=( const char * str ) {
size_t s = strlen(str); size_t s = strlen(str);
if ( s >= _size - 2 ) if ( s >= _size - 2 )
s = _size - 2; s = _size - 2;
strncpy( _buf , str , s ); strncpy( _buf , str , s );
_buf[s] = 0; _buf[s] = 0;
return *this; return *this;
} }
bool operator==( const ThreadSafeString& other ) const { bool operator==( const ThreadSafeString& other ) const {
return strcmp( _buf , other._buf ) == 0; return strcmp( _buf , other._buf ) == 0;
skipping to change at line 633 skipping to change at line 441
return _buf == 0 || _buf[0] == 0; return _buf == 0 || _buf[0] == 0;
} }
private: private:
size_t _size; size_t _size;
char * _buf; char * _buf;
}; };
ostream& operator<<( ostream &s, const ThreadSafeString &o ); ostream& operator<<( ostream &s, const ThreadSafeString &o );
inline bool isNumber( char c ) {
return c >= '0' && c <= '9';
}
inline unsigned stringToNum(const char *str) {
unsigned x = 0;
const char *p = str;
while( 1 ) {
if( !isNumber(*p) ) {
if( *p == 0 && p != str )
break;
throw 0;
}
x = x * 10 + *p++ - '0';
}
return x;
}
// for convenience, '{' is greater than anything and stops number parsi ng
inline int lexNumCmp( const char *s1, const char *s2 ) {
//cout << "START : " << s1 << "\t" << s2 << endl;
while( *s1 && *s2 ) {
bool p1 = ( *s1 == (char)255 );
bool p2 = ( *s2 == (char)255 );
//cout << "\t\t " << p1 << "\t" << p2 << endl;
if ( p1 && !p2 )
return 1;
if ( p2 && !p1 )
return -1;
bool n1 = isNumber( *s1 );
bool n2 = isNumber( *s2 );
if ( n1 && n2 ) {
// get rid of leading 0s
while ( *s1 == '0' ) s1++;
while ( *s2 == '0' ) s2++;
char * e1 = (char*)s1;
char * e2 = (char*)s2;
// find length
// if end of string, will break immediately ('\0')
while ( isNumber (*e1) ) e1++;
while ( isNumber (*e2) ) e2++;
int len1 = e1-s1;
int len2 = e2-s2;
int result;
// if one is longer than the other, return
if ( len1 > len2 ) {
return 1;
}
else if ( len2 > len1 ) {
return -1;
}
// if the lengths are equal, just strcmp
else if ( (result = strncmp(s1, s2, len1)) != 0 ) {
return result;
}
// otherwise, the numbers are equal
s1 = e1;
s2 = e2;
continue;
}
if ( n1 )
return 1;
if ( n2 )
return -1;
if ( *s1 > *s2 )
return 1;
if ( *s2 > *s1 )
return -1;
s1++; s2++;
}
if ( *s1 )
return 1;
if ( *s2 )
return -1;
return 0;
}
/** A generic pointer type for function arguments. /** A generic pointer type for function arguments.
* It will convert from any pointer type except auto_ptr. * It will convert from any pointer type except auto_ptr.
* Semantics are the same as passing the pointer returned from get() * Semantics are the same as passing the pointer returned from get()
* const ptr<T> => T * const * const ptr<T> => T * const
* ptr<const T> => T const * or const T* * ptr<const T> => T const * or const T*
*/ */
template <typename T> template <typename T>
struct ptr{ struct ptr {
ptr() : _p(NULL) {} ptr() : _p(NULL) {}
// convert to ptr<T> // convert to ptr<T>
ptr(T* p) : _p(p) {} // needed for NULL ptr(T* p) : _p(p) {} // needed for NULL
template<typename U> ptr(U* p) : _p(p) {} template<typename U> ptr(U* p) : _p(p) {}
template<typename U> ptr(const ptr<U>& p) : _p(p) {} template<typename U> ptr(const ptr<U>& p) : _p(p) {}
template<typename U> ptr(const boost::shared_ptr<U>& p) : _p(p.get( )) {} template<typename U> ptr(const boost::shared_ptr<U>& p) : _p(p.get( )) {}
template<typename U> ptr(const boost::scoped_ptr<U>& p) : _p(p.get( )) {} template<typename U> ptr(const boost::scoped_ptr<U>& p) : _p(p.get( )) {}
//template<typename U> ptr(const auto_ptr<U>& p) : _p(p.get()) {} //template<typename U> ptr(const auto_ptr<U>& p) : _p(p.get()) {}
 End of changes. 45 change blocks. 
349 lines changed or deleted 66 lines changed or added


 grid.h   grid.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 <boost/date_time/posix_time/posix_time.hpp>
#include "../util/time_support.h"
#include "../util/concurrency/mutex.h" #include "../util/concurrency/mutex.h"
#include "config.h" // DBConfigPtr #include "config.h" // DBConfigPtr
namespace mongo { namespace mongo {
/** /**
* stores meta-information about the grid * stores meta-information about the grid
* TODO: used shard_ptr for DBConfig pointers * TODO: used shard_ptr for DBConfig pointers
*/ */
skipping to change at line 63 skipping to change at line 66
* @param whether to allow shards and config servers to use 'localh ost' in address * @param whether to allow shards and config servers to use 'localh ost' in address
*/ */
void setAllowLocalHost( bool allow ); void setAllowLocalHost( bool allow );
/** /**
* *
* addShard will create a new shard in the grid. It expects a mongo d process to be runing * addShard will create a new shard in the grid. It expects a mongo d process to be runing
* on the provided address. Adding a shard that is a replica set is supported. * on the provided address. Adding a shard that is a replica set is supported.
* *
* @param name is an optional string with the name of the shard. if ommited, grid will * @param name is an optional string with the name of the shard. if ommited, grid will
* generate one and update the parameter. * generate one and update the parameter.
* @param servers is the connection string of the shard being added * @param servers is the connection string of the shard being added
* @param maxSize is the optional space quota in bytes. Zeros means there's no limitation to * @param maxSize is the optional space quota in bytes. Zeros means there's no limitation to
* space usage * space usage
* @param errMsg is the error description in case the operation fai led. * @param errMsg is the error description in case the operation fai led.
* @return true if shard was successfully added. * @return true if shard was successfully added.
*/ */
bool addShard( string* name , const ConnectionString& servers , lon g long maxSize , string& errMsg ); bool addShard( string* name , const ConnectionString& servers , lon g long maxSize , string& errMsg );
/** /**
* @return true if the config database knows about a host 'name' * @return true if the config database knows about a host 'name'
*/ */
bool knowAboutShard( const string& name ) const; bool knowAboutShard( const string& name ) const;
/** /**
* @return true if the chunk balancing functionality is enabled * @return true if the chunk balancing functionality is enabled
*/ */
bool shouldBalance() const; bool shouldBalance() const;
unsigned long long getNextOpTime() const; unsigned long long getNextOpTime() const;
void flushConfig();
// exposed methods below are for testing only
/**
* @param balancerDoc bson that may contain a window of time for th e balancer to work
* format { ... , activeWindow: { start: "8:30" , stop: "19: 00" } , ... }
* @return true if there is no window of time specified for the bal ancer or it we're currently in it
*/
static bool _inBalancingWindow( const BSONObj& balancerDoc , const boost::posix_time::ptime& now );
private: private:
mongo::mutex _lock; // protects _databases; TODO: change to r/w lock ?? mongo::mutex _lock; // protects _databases; TODO: change to r/w lock ??
map<string, DBConfigPtr > _databases; // maps ns to DBConfig' s map<string, DBConfigPtr > _databases; // maps ns to DBConfig' s
bool _allowLocalShard; // can 'localhost' be u sed in shard addresses? bool _allowLocalShard; // can 'localhost' be u sed in shard addresses?
/** /**
* @param name is the chose name for the shard. Parameter is mandat ory. * @param name is the chose name for the shard. Parameter is mandat ory.
* @return true if it managed to generate a shard name. May return false if (currently) * @return true if it managed to generate a shard name. May return false if (currently)
* 10000 shard * 10000 shard
*/ */
bool _getNewShardName( string* name ) const; bool _getNewShardName( string* name ) const;
/** /**
* @return whether a give dbname is used for shard "local" database s (e.g., admin or local) * @return whether a give dbname is used for shard "local" database s (e.g., admin or local)
*/ */
static bool _isSpecialLocalDB( const string& dbName ); static bool _isSpecialLocalDB( const string& dbName );
/**
* @param balancerDoc bson that may contain a marker to stop the ba lancer
* format { ... , stopped: [ "true" | "false" ] , ... }
* @return true if the marker is present and is set to true
*/
static bool _balancerStopped( const BSONObj& balancerDoc );
}; };
extern Grid grid; extern Grid grid;
} // namespace mongo } // namespace mongo
 End of changes. 5 change blocks. 
2 lines changed or deleted 23 lines changed or added


 gridfs.h   gridfs.h 
skipping to change at line 35 skipping to change at line 35
typedef unsigned long long gridfs_offset; typedef unsigned long long gridfs_offset;
class GridFS; class GridFS;
class GridFile; class GridFile;
class GridFSChunk { class GridFSChunk {
public: public:
GridFSChunk( BSONObj data ); GridFSChunk( BSONObj data );
GridFSChunk( BSONObj fileId , int chunkNumber , const char * data , int len ); GridFSChunk( BSONObj fileId , int chunkNumber , const char * data , int len );
int len(){ int len() {
int len; int len;
_data["data"].binDataClean( len ); _data["data"].binDataClean( len );
return len; return len;
} }
const char * data( int & len ){ const char * data( int & len ) {
return _data["data"].binDataClean( len ); return _data["data"].binDataClean( len );
} }
private: private:
BSONObj _data; BSONObj _data;
friend class GridFS; friend class GridFS;
}; };
/** /**
this is the main entry point into the mongo grid fs GridFS is for storing large file-style objects in MongoDB.
@see http://www.mongodb.org/display/DOCS/GridFS+Specification
*/ */
class GridFS{ class GridFS {
public: public:
/** /**
* @param client - db connection * @param client - db connection
* @param dbName - root database name * @param dbName - root database name
* @param prefix - if you want your data somewhere besides <dbname> .fs * @param prefix - if you want your data somewhere besides <dbname> .fs
*/ */
GridFS( DBClientBase& client , const string& dbName , const string& prefix="fs" ); GridFS( DBClientBase& client , const string& dbName , const string& prefix="fs" );
~GridFS(); ~GridFS();
/** /**
skipping to change at line 140 skipping to change at line 142
/** /**
wrapper for a file stored in the Mongo database wrapper for a file stored in the Mongo database
*/ */
class GridFile { class GridFile {
public: public:
/** /**
* @return whether or not this file exists * @return whether or not this file exists
* findFile will always return a GriFile, so need to check this * findFile will always return a GriFile, so need to check this
*/ */
bool exists(){ bool exists() {
return ! _obj.isEmpty(); return ! _obj.isEmpty();
} }
string getFilename(){ string getFilename() {
return _obj["filename"].str(); return _obj["filename"].str();
} }
int getChunkSize(){ int getChunkSize() {
return (int)(_obj["chunkSize"].number()); return (int)(_obj["chunkSize"].number());
} }
gridfs_offset getContentLength(){ gridfs_offset getContentLength() {
return (gridfs_offset)(_obj["length"].number()); return (gridfs_offset)(_obj["length"].number());
} }
string getContentType(){ string getContentType() {
return _obj["contentType"].valuestr(); return _obj["contentType"].valuestr();
} }
Date_t getUploadDate(){ Date_t getUploadDate() {
return _obj["uploadDate"].date(); return _obj["uploadDate"].date();
} }
string getMD5(){ string getMD5() {
return _obj["md5"].str(); return _obj["md5"].str();
} }
BSONElement getFileField( const string& name ){ BSONElement getFileField( const string& name ) {
return _obj[name]; return _obj[name];
} }
BSONObj getMetadata(); BSONObj getMetadata();
int getNumChunks(){ int getNumChunks() {
return (int) ceil( (double)getContentLength() / (double)getChun kSize() ); return (int) ceil( (double)getContentLength() / (double)getChun kSize() );
} }
GridFSChunk getChunk( int n ); GridFSChunk getChunk( int n );
/** /**
write the file to the output stream write the file to the output stream
*/ */
gridfs_offset write( ostream & out ); gridfs_offset write( ostream & out );
 End of changes. 13 change blocks. 
13 lines changed or deleted 14 lines changed or added


 hashtab.h   hashtab.h 
skipping to change at line 27 skipping to change at line 27
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include <map> #include <map>
#include "../db/dur.h"
namespace mongo { namespace mongo {
#pragma pack(1) #pragma pack(1)
/* you should define: /* you should define:
int Key::hash() return > 0 always. int Key::hash() return > 0 always.
*/ */
template < template <
class Key, class Key,
class Type, class Type
class PTR >
>
class HashTable : boost::noncopyable { class HashTable : boost::noncopyable {
public: public:
const char *name; const char *name;
struct Node { struct Node {
int hash; int hash;
Key k; Key k;
Type value; Type value;
bool inUse() { bool inUse() {
return hash != 0; return hash != 0;
} }
void setUnused() { void setUnused() {
hash = 0; hash = 0;
} }
}; };
PTR _buf; void* _buf;
int n; int n; // number of hashtable buckets
int maxChain; int maxChain;
Node& nodes(int i) { Node& nodes(int i) {
return *((Node*) _buf.at(i * sizeof(Node), sizeof(Node))); Node *nodes = (Node *) _buf;
return nodes[i];
} }
int _find(const Key& k, bool& found) { int _find(const Key& k, bool& found) {
found = false; found = false;
int h = k.hash(); int h = k.hash();
int i = h % n; int i = h % n;
int start = i; int start = i;
int chain = 0; int chain = 0;
int firstNonUsed = -1; int firstNonUsed = -1;
while ( 1 ) { while ( 1 ) {
skipping to change at line 93 skipping to change at line 94
chain++; chain++;
i = (i+1) % n; i = (i+1) % n;
if ( i == start ) { if ( i == start ) {
// shouldn't get here / defensive for infinite loops // shouldn't get here / defensive for infinite loops
out() << "error: hashtable " << name << " is full n:" < < n << endl; out() << "error: hashtable " << name << " is full n:" < < n << endl;
return -1; return -1;
} }
if( chain >= maxChain ) { if( chain >= maxChain ) {
if ( firstNonUsed >= 0 ) if ( firstNonUsed >= 0 )
return firstNonUsed; return firstNonUsed;
out() << "error: hashtable " << name << " max chain n:" << n << endl; out() << "error: hashtable " << name << " max chain rea ched:" << maxChain << endl;
return -1; return -1;
} }
} }
} }
public: public:
/* buf must be all zeroes on initialization. */ /* buf must be all zeroes on initialization. */
HashTable(PTR buf, int buflen, const char *_name) : name(_name) { HashTable(void* buf, int buflen, const char *_name) : name(_name) {
int m = sizeof(Node); int m = sizeof(Node);
// out() << "hashtab init, buflen:" << buflen << " m:" << m << endl; // out() << "hashtab init, buflen:" << buflen << " m:" << m << endl;
n = buflen / m; n = buflen / m;
if ( (n & 1) == 0 ) if ( (n & 1) == 0 )
n--; n--;
maxChain = (int) (n * 0.05); maxChain = (int) (n * 0.05);
_buf = buf; _buf = buf;
//nodes = (Node *) buf; //nodes = (Node *) buf;
if ( sizeof(Node) != 628 ){ if ( sizeof(Node) != 628 ) {
out() << "HashTable() " << _name << " sizeof(node):" << siz eof(Node) << " n:" << n << " sizeof(Key): " << sizeof(Key) << " sizeof(Type ):" << sizeof(Type) << endl; out() << "HashTable() " << _name << " sizeof(node):" << siz eof(Node) << " n:" << n << " sizeof(Key): " << sizeof(Key) << " sizeof(Type ):" << sizeof(Type) << endl;
assert( sizeof(Node) == 628 ); assert( sizeof(Node) == 628 );
} }
} }
Type* get(const Key& k) { Type* get(const Key& k) {
bool found; bool found;
int i = _find(k, found); int i = _find(k, found);
if ( found ) if ( found )
return &nodes(i).value; return &nodes(i).value;
return 0; return 0;
} }
void kill(const Key& k) { void kill(const Key& k) {
bool found; bool found;
int i = _find(k, found); int i = _find(k, found);
if ( i >= 0 && found ) { if ( i >= 0 && found ) {
Node& n = nodes(i); Node* n = &nodes(i);
n.k.kill(); n = getDur().writing(n);
n.setUnused(); n->k.kill();
} n->setUnused();
}
/*
void drop(const Key& k) {
bool found;
int i = _find(k, found);
if ( i >= 0 && found ) {
nodes[i].setUnused();
} }
} }
*/
/** returns false if too full */ /** returns false if too full */
bool put(const Key& k, const Type& value) { bool put(const Key& k, const Type& value) {
bool found; bool found;
int i = _find(k, found); int i = _find(k, found);
if ( i < 0 ) if ( i < 0 )
return false; return false;
Node& n = nodes(i); Node* n = getDur().writing( &nodes(i) );
if ( !found ) { if ( !found ) {
n.k = k; n->k = k;
n.hash = k.hash(); n->hash = k.hash();
} }
else { else {
assert( n.hash == k.hash() ); assert( n->hash == k.hash() );
} }
n.value = value; n->value = value;
return true; return true;
} }
typedef void (*IteratorCallback)( const Key& k , Type& v ); typedef void (*IteratorCallback)( const Key& k , Type& v );
void iterAll( IteratorCallback callback ){ void iterAll( IteratorCallback callback ) {
for ( int i=0; i<n; i++ ){ for ( int i=0; i<n; i++ ) {
if ( ! nodes(i).inUse() ) if ( nodes(i).inUse() ) {
continue; callback( nodes(i).k , nodes(i).value );
callback( nodes(i).k , nodes(i).value ); }
} }
} }
// TODO: should probably use boost::bind for this, but didn't want to look at it // TODO: should probably use boost::bind for this, but didn't want to look at it
typedef void (*IteratorCallback2)( const Key& k , Type& v , void * extra ); typedef void (*IteratorCallback2)( const Key& k , Type& v , void * extra );
void iterAll( IteratorCallback2 callback , void * extra ){ void iterAll( IteratorCallback2 callback , void * extra ) {
for ( int i=0; i<n; i++ ){ for ( int i=0; i<n; i++ ) {
if ( ! nodes(i).inUse() ) if ( nodes(i).inUse() ) {
continue; callback( nodes(i).k , nodes(i).value , extra );
callback( nodes(i).k , nodes(i).value , extra ); }
} }
} }
}; };
#pragma pack() #pragma pack()
} // namespace mongo } // namespace mongo
 End of changes. 15 change blocks. 
36 lines changed or deleted 30 lines changed or added


 health.h   health.h 
skipping to change at line 27 skipping to change at line 27
*/ */
#pragma once #pragma once
namespace mongo { namespace mongo {
/* throws */ /* throws */
bool requestHeartbeat(string setname, string fromHost, string memberFul lName, BSONObj& result, int myConfigVersion, int& theirConfigVersion, bool checkEmpty = false); bool requestHeartbeat(string setname, string fromHost, string memberFul lName, BSONObj& result, int myConfigVersion, int& theirConfigVersion, bool checkEmpty = false);
struct HealthOptions { struct HealthOptions {
HealthOptions() { HealthOptions() :
heartbeatSleepMillis = 2000; heartbeatSleepMillis(2000),
heartbeatTimeoutMillis = 10000; heartbeatTimeoutMillis( 10000 ),
heartbeatConnRetries = 2; heartbeatConnRetries(2)
} { }
bool isDefault() const { return *this == HealthOptions(); } bool isDefault() const { return *this == HealthOptions(); }
// see http://www.mongodb.org/display/DOCS/Replica+Set+Internals // see http://www.mongodb.org/display/DOCS/Replica+Set+Internals
unsigned heartbeatSleepMillis; unsigned heartbeatSleepMillis;
unsigned heartbeatTimeoutMillis; unsigned heartbeatTimeoutMillis;
unsigned heartbeatConnRetries ; unsigned heartbeatConnRetries ;
void check() { void check() {
uassert(13112, "bad replset heartbeat option", heartbeatSleepMi llis >= 10); uassert(13112, "bad replset heartbeat option", heartbeatSleepMi llis >= 10);
uassert(13113, "bad replset heartbeat option", heartbeatTimeout Millis >= 10); uassert(13113, "bad replset heartbeat option", heartbeatTimeout Millis >= 10);
} }
bool operator==(const HealthOptions& r) const { bool operator==(const HealthOptions& r) const {
return heartbeatSleepMillis==r.heartbeatSleepMillis && heartbea tTimeoutMillis==r.heartbeatTimeoutMillis && heartbeatConnRetries==heartbeat ConnRetries; return heartbeatSleepMillis==r.heartbeatSleepMillis && heartbea tTimeoutMillis==r.heartbeatTimeoutMillis && heartbeatConnRetries==r.heartbe atConnRetries;
} }
}; };
} }
 End of changes. 2 change blocks. 
6 lines changed or deleted 6 lines changed or added


 heapcheck.h   heapcheck.h 
// inline.h // @file heapcheck.h
/** /**
* Copyright (C) 2010 10gen Inc. * Copyright (C) 2010 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 hex.h   hex.h 
skipping to change at line 33 skipping to change at line 33
if ( '0' <= c && c <= '9' ) if ( '0' <= c && c <= '9' )
return c - '0'; return c - '0';
if ( 'a' <= c && c <= 'f' ) if ( 'a' <= c && c <= 'f' )
return c - 'a' + 10; return c - 'a' + 10;
if ( 'A' <= c && c <= 'F' ) if ( 'A' <= c && c <= 'F' )
return c - 'A' + 10; return c - 'A' + 10;
assert( false ); assert( false );
return 0xff; return 0xff;
} }
inline char fromHex( const char *c ) { inline char fromHex( const char *c ) {
return ( fromHex( c[ 0 ] ) << 4 ) | fromHex( c[ 1 ] ); return (char)(( fromHex( c[ 0 ] ) << 4 ) | fromHex( c[ 1 ] ));
} }
inline string toHex(const void* inRaw, int len){ inline string toHex(const void* inRaw, int len) {
static const char hexchars[] = "0123456789ABCDEF"; static const char hexchars[] = "0123456789ABCDEF";
StringBuilder out; StringBuilder out;
const char* in = reinterpret_cast<const char*>(inRaw); const char* in = reinterpret_cast<const char*>(inRaw);
for (int i=0; i<len; ++i){ for (int i=0; i<len; ++i) {
char c = in[i]; char c = in[i];
char hi = hexchars[(c & 0xF0) >> 4]; char hi = hexchars[(c & 0xF0) >> 4];
char lo = hexchars[(c & 0x0F)]; char lo = hexchars[(c & 0x0F)];
out << hi << lo; out << hi << lo;
} }
return out.str(); return out.str();
} }
inline string toHexLower(const void* inRaw, int len){ inline string toHexLower(const void* inRaw, int len) {
static const char hexchars[] = "0123456789abcdef"; static const char hexchars[] = "0123456789abcdef";
StringBuilder out; StringBuilder out;
const char* in = reinterpret_cast<const char*>(inRaw); const char* in = reinterpret_cast<const char*>(inRaw);
for (int i=0; i<len; ++i){ for (int i=0; i<len; ++i) {
char c = in[i]; char c = in[i];
char hi = hexchars[(c & 0xF0) >> 4]; char hi = hexchars[(c & 0xF0) >> 4];
char lo = hexchars[(c & 0x0F)]; char lo = hexchars[(c & 0x0F)];
out << hi << lo; out << hi << lo;
} }
return out.str(); return out.str();
} }
} }
 End of changes. 5 change blocks. 
5 lines changed or deleted 5 lines changed or added


 histogram.h   histogram.h 
skipping to change at line 73 skipping to change at line 73
boost::uint32_t bucketSize; boost::uint32_t bucketSize;
boost::uint32_t initialValue; boost::uint32_t initialValue;
// use exponential buckets? // use exponential buckets?
bool exponential; bool exponential;
Options() Options()
: numBuckets(0) : numBuckets(0)
, bucketSize(0) , bucketSize(0)
, initialValue(0) , initialValue(0)
, exponential(false){} , exponential(false) {}
}; };
explicit Histogram( const Options& opts ); explicit Histogram( const Options& opts );
~Histogram(); ~Histogram();
/** /**
* Find the bucket that 'element' falls into and increment its coun t. * Find the bucket that 'element' falls into and increment its coun t.
*/ */
void insert( boost::uint32_t element ); void insert( boost::uint32_t element );
/** /**
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 hostandport.h   hostandport.h 
skipping to change at line 21 skipping to change at line 21
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "sock.h" #include "sock.h"
#include "../db/cmdline.h" #include "../../db/cmdline.h"
#include "mongoutils/str.h" #include "../mongoutils/str.h"
namespace mongo { namespace mongo {
using namespace mongoutils; using namespace mongoutils;
/** helper for manipulating host:port connection endpoints. /** helper for manipulating host:port connection endpoints.
*/ */
struct HostAndPort { struct HostAndPort {
HostAndPort() : _port(-1) { } HostAndPort() : _port(-1) { }
/** From a string hostname[:portnumber] /** From a string hostname[:portnumber]
Throws user assertion if bad config string or bad port #. Throws user assertion if bad config string or bad port #.
*/ */
HostAndPort(string s); HostAndPort(string s);
/** @param p port number. -1 is ok to use default. */ /** @param p port number. -1 is ok to use default. */
HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) { } HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) { }
HostAndPort(const SockAddr& sock ) HostAndPort(const SockAddr& sock )
: _host( sock.getAddr() ) , _port( sock.getPort() ){ : _host( sock.getAddr() ) , _port( sock.getPort() ) {
} }
static HostAndPort me() { static HostAndPort me() {
return HostAndPort("localhost", cmdLine.port); return HostAndPort("localhost", cmdLine.port);
} }
/* uses real hostname instead of localhost */ /* uses real hostname instead of localhost */
static HostAndPort Me(); static HostAndPort Me();
bool operator<(const HostAndPort& r) const { bool operator<(const HostAndPort& r) const {
skipping to change at line 64 skipping to change at line 64
return true; return true;
if( _host == r._host ) if( _host == r._host )
return port() < r.port(); return port() < r.port();
return false; return false;
} }
bool operator==(const HostAndPort& r) const { bool operator==(const HostAndPort& r) const {
return _host == r._host && port() == r.port(); return _host == r._host && port() == r.port();
} }
bool operator!=(const HostAndPort& r) const {
return _host != r._host || port() != r.port();
}
/* returns true if the host/port combo identifies this process inst ance. */ /* returns true if the host/port combo identifies this process inst ance. */
bool isSelf() const; // defined in message.cpp bool isSelf() const; // defined in message.cpp
bool isLocalHost() const; bool isLocalHost() const;
// @returns host:port /**
string toString() const; * @param includePort host:port if true, host otherwise
*/
string toString( bool includePort=true ) const;
operator string() const { return toString(); } operator string() const { return toString(); }
string host() const { return _host; } string host() const { return _host; }
int port() const { return _port >= 0 ? _port : CmdLine::DefaultDBPo rt; } int port() const { return _port >= 0 ? _port : CmdLine::DefaultDBPo rt; }
bool hasPort() const { return _port >= 0; } bool hasPort() const { return _port >= 0; }
void setPort( int port ) { _port = port; } void setPort( int port ) { _port = port; }
private: private:
// invariant (except full obj assignment): // invariant (except full obj assignment):
string _host; string _host;
int _port; // -1 indicates unspecified int _port; // -1 indicates unspecified
}; };
/** returns true if strings seem to be the same hostname.
"nyc1" and "nyc1.acme.com" are treated as the same.
in fact "nyc1.foo.com" and "nyc1.acme.com" are treated the same -
we oly look up to the first period.
*/
inline bool sameHostname(const string& a, const string& b) {
return str::before(a, '.') == str::before(b, '.');
}
inline HostAndPort HostAndPort::Me() { inline HostAndPort HostAndPort::Me() {
const char* ips = cmdLine.bind_ip.c_str();
while(*ips) {
string ip;
const char * comma = strchr(ips, ',');
if (comma) {
ip = string(ips, comma - ips);
ips = comma + 1;
}
else {
ip = string(ips);
ips = "";
}
HostAndPort h = HostAndPort(ip, cmdLine.port);
if (!h.isLocalHost()) {
return h;
}
}
string h = getHostName(); string h = getHostName();
assert( !h.empty() ); assert( !h.empty() );
assert( h != "localhost" ); assert( h != "localhost" );
return HostAndPort(h, cmdLine.port); return HostAndPort(h, cmdLine.port);
} }
inline string HostAndPort::toString() const { inline string HostAndPort::toString( bool includePort ) const {
if ( ! includePort )
return _host;
stringstream ss; stringstream ss;
ss << _host; ss << _host;
if ( _port != -1 ){ if ( _port != -1 ) {
ss << ':'; ss << ':';
#if defined(_DEBUG) #if defined(_DEBUG)
if( _port >= 44000 && _port < 44100 ) { if( _port >= 44000 && _port < 44100 ) {
log() << "warning: special debug port 44xxx used" << endl; log() << "warning: special debug port 44xxx used" << endl;
ss << _port+1; ss << _port+1;
} }
else else
ss << _port; ss << _port;
#else #else
ss << _port; ss << _port;
#endif #endif
} }
return ss.str(); return ss.str();
} }
inline bool HostAndPort::isLocalHost() const { inline bool HostAndPort::isLocalHost() const {
return _host == "localhost" || startsWith(_host.c_str(), "127.") || _host == "::1"; return ( _host == "localhost"
|| startsWith(_host.c_str(), "127.")
|| _host == "::1"
|| _host == "anonymous unix socket"
|| _host.c_str()[0] == '/' // unix socket
);
} }
inline HostAndPort::HostAndPort(string s) { inline HostAndPort::HostAndPort(string s) {
const char *p = s.c_str(); const char *p = s.c_str();
uassert(13110, "HostAndPort: bad config string", *p); uassert(13110, "HostAndPort: bad config string", *p);
const char *colon = strrchr(p, ':'); const char *colon = strrchr(p, ':');
if( colon ) { if( colon ) {
int port = atoi(colon+1); int port = atoi(colon+1);
uassert(13095, "HostAndPort: bad port #", port > 0); uassert(13095, "HostAndPort: bad port #", port > 0);
_host = string(p,colon-p); _host = string(p,colon-p);
 End of changes. 9 change blocks. 
17 lines changed or deleted 40 lines changed or added


 html.h   html.h 
skipping to change at line 58 skipping to change at line 58
ss << "<td>" << x << "</td>"; ss << "<td>" << x << "</td>";
return ss.str(); return ss.str();
} }
inline string td(string x) { inline string td(string x) {
return "<td>" + x + "</td>"; return "<td>" + x + "</td>";
} }
inline string th(string x) { inline string th(string x) {
return "<th>" + x + "</th>"; return "<th>" + x + "</th>";
} }
inline void tablecell( stringstream& ss , bool b ){ inline void tablecell( stringstream& ss , bool b ) {
ss << "<td>" << (b ? "<b>X</b>" : "") << "</td>"; ss << "<td>" << (b ? "<b>X</b>" : "") << "</td>";
} }
template< typename T> template< typename T>
inline void tablecell( stringstream& ss , const T& t ){ inline void tablecell( stringstream& ss , const T& t ) {
ss << "<td>" << t << "</td>"; ss << "<td>" << t << "</td>";
} }
inline string table(const char *headers[] = 0, bool border = true) { inline string table(const char *headers[] = 0, bool border = true) {
stringstream ss; stringstream ss;
ss << "\n<table " ss << "\n<table "
<< (border?"border=1 ":"") << (border?"border=1 ":"")
<< "cellpadding=2 cellspacing=0>\n"; << "cellpadding=2 cellspacing=0>\n";
if( headers ) { if( headers ) {
ss << "<tr>"; ss << "<tr>";
while( *headers ) { while( *headers ) {
ss << "<th>" << *headers << "</th>"; ss << "<th>" << *headers << "</th>";
headers++; headers++;
} }
ss << "</tr>\n"; ss << "</tr>\n";
} }
return ss.str(); return ss.str();
} }
inline string start(string title) { inline string start(string title) {
stringstream ss; stringstream ss;
ss << "<html><head>\n<title>"; ss << "<html><head>\n<title>";
ss << title; ss << title;
ss << "</title>\n"; ss << "</title>\n";
ss << "<style type=\"text/css\" media=\"screen\">" ss << "<style type=\"text/css\" media=\"screen\">"
"body { font-family: helvetica, arial, san-serif }\n" "body { font-family: helvetica, arial, san-serif }\n"
"table { border-collapse:collapse; border-color:#999; margi n-top:.5em }\n" "table { border-collapse:collapse; border-color:#999; margin -top:.5em }\n"
"th { background-color:#bbb; color:#000 }\n" "th { background-color:#bbb; color:#000 }\n"
"td,th { padding:.25em }\n" "td,th { padding:.25em }\n"
"</style>\n"; "</style>\n";
ss << "</head>\n<body>\n"; ss << "</head>\n<body>\n";
return ss.str(); return ss.str();
} }
inline string red(string contentHtml, bool color=true) { inline string red(string contentHtml, bool color=true) {
if( !color ) return contentHtml; if( !color ) return contentHtml;
stringstream ss; stringstream ss;
ss << "<span style=\"color:#A00;\">" << contentHtml << "</span> "; ss << "<span style=\"color:#A00;\">" << contentHtml << "</span> ";
return ss.str(); return ss.str();
 End of changes. 4 change blocks. 
9 lines changed or deleted 9 lines changed or added


 httpclient.h   httpclient.h 
skipping to change at line 20 skipping to change at line 20
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../../pch.h"
#include "sock.h"
namespace mongo { namespace mongo {
class HttpClient { class HttpClient : boost::noncopyable {
public: public:
typedef map<string,string> Headers;
class Result { class Result {
public: public:
Result(){} Result() {}
const string& getEntireResponse() const { const string& getEntireResponse() const {
return _entireResponse; return _entireResponse;
} }
const map<string,string> getHeaders() const { const Headers getHeaders() const {
return _headers; return _headers;
} }
const string& getBody() const { const string& getBody() const {
return _body; return _body;
} }
private: private:
void _init( int code , string entire ); void _init( int code , string entire );
int _code; int _code;
string _entireResponse; string _entireResponse;
map<string,string> _headers; Headers _headers;
string _body; string _body;
friend class HttpClient; friend class HttpClient;
}; };
/** /**
* @return response code * @return response code
*/ */
int get( string url , Result * result = 0 ); int get( string url , Result * result = 0 );
/** /**
* @return response code * @return response code
*/ */
int post( string url , string body , Result * result = 0 ); int post( string url , string body , Result * result = 0 );
private: private:
int _go( const char * command , string url , const char * body , Re sult * result ); int _go( const char * command , string url , const char * body , Re sult * result );
#ifdef MONGO_SSL
void _checkSSLManager();
scoped_ptr<SSLManager> _sslManager;
#endif
}; };
} }
 End of changes. 7 change blocks. 
5 lines changed or deleted 13 lines changed or added


 index.h   index.h 
skipping to change at line 25 skipping to change at line 25
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "diskloc.h" #include "diskloc.h"
#include "jsobj.h" #include "jsobj.h"
#include "indexkey.h" #include "indexkey.h"
#include "key.h"
namespace mongo { namespace mongo {
/* Details about a particular index. There is one of these effective ly for each object in class IndexInterface {
system.namespaces (although this also includes the head pointer, which is not in that protected:
collection). virtual ~IndexInterface() { }
public:
virtual int keyCompare(const BSONObj& l,const BSONObj& r, const Ord ering &ordering) = 0;
virtual long long fullValidate(const DiskLoc& thisLoc, const BSONOb j &order) = 0;
virtual DiskLoc findSingle(const IndexDetails &indexdetails , const DiskLoc& thisLoc, const BSONObj& key) const = 0;
virtual bool unindex(const DiskLoc thisLoc, IndexDetails& id, const BSONObj& key, const DiskLoc recordLoc) const = 0;
virtual int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLo c,
const BSONObj& key, const Ordering &order, bool dupsAllowed,
IndexDetails& idx, bool toplevel = true) const = 0;
virtual DiskLoc addBucket(const IndexDetails&) = 0;
virtual void uassertIfDups(IndexDetails& idx, vector<BSONObj*>& add edKeys, DiskLoc head,
DiskLoc self, const Ordering& ordering) = 0;
// these are for geo
virtual bool isUsed(DiskLoc thisLoc, int pos) = 0;
virtual void keyAt(DiskLoc thisLoc, int pos, BSONObj&, DiskLoc& rec ordLoc) = 0;
virtual BSONObj keyAt(DiskLoc thisLoc, int pos) = 0;
virtual DiskLoc locate(const IndexDetails &idx , const DiskLoc& thi sLoc, const BSONObj& key, const Ordering &order,
int& pos, bool& found, const DiskLoc &record Loc, int direction=1) = 0;
virtual DiskLoc advance(const DiskLoc& thisLoc, int& keyOfs, int di rection, const char *caller) = 0;
};
/* Details about a particular index. There is one of these effectively for each object in
system.namespaces (although this also includes the head pointer, whi ch is not in that
collection).
** MemoryMapped Record ** (i.e., this is on disk data) ** MemoryMapped Record ** (i.e., this is on disk data)
*/ */
class IndexDetails { class IndexDetails {
public: public:
DiskLoc head; /* btree head disk location */ /**
* btree head disk location
* TODO We should make this variable private, since btree operation s
* may change its value and we don't want clients to rely on an old
* value. If we create a btree class, we can provide a btree objec t
* to clients instead of 'head'.
*/
DiskLoc head;
/* Location of index info object. Format: /* Location of index info object. Format:
{ name:"nameofindex", ns:"parentnsname", key: {keypattobject} { name:"nameofindex", ns:"parentnsname", key: {keypattobject}
[, unique: <bool>, background: <bool>] [, unique: <bool>, background: <bool>, v:<version>]
} }
This object is in the system.indexes collection. Note that sinc e we This object is in the system.indexes collection. Note that sinc e we
have a pointer to the object here, the object in system.indexes MUST NEVER MOVE. have a pointer to the object here, the object in system.indexes MUST NEVER MOVE.
*/ */
DiskLoc info; DiskLoc info;
/* extract key value from the query object /* extract key value from the query object
e.g., if key() == { x : 1 }, e.g., if key() == { x : 1 },
{ x : 70, y : 3 } -> { x : 70 } { x : 70, y : 3 } -> { x : 70 }
skipping to change at line 64 skipping to change at line 96
BSONObj k = keyPattern(); BSONObj k = keyPattern();
BSONObj res = query.extractFieldsUnDotted(k); BSONObj res = query.extractFieldsUnDotted(k);
return res; return res;
} }
/* pull out the relevant key objects from obj, so we /* pull out the relevant key objects from obj, so we
can index them. Note that the set is multiple elements can index them. Note that the set is multiple elements
only when it's a "multikey" array. only when it's a "multikey" array.
keys will be left empty if key not found in the object. keys will be left empty if key not found in the object.
*/ */
void getKeysFromObject( const BSONObj& obj, BSONObjSetDefaultOrder& keys) const; void getKeysFromObject( const BSONObj& obj, BSONObjSet& keys) const ;
/* get the key pattern for this object. /* get the key pattern for this object.
e.g., { lastname:1, firstname:1 } e.g., { lastname:1, firstname:1 }
*/ */
BSONObj keyPattern() const { BSONObj keyPattern() const {
return info.obj().getObjectField("key"); return info.obj().getObjectField("key");
} }
/**
* @return offset into keyPattern for key
-1 if doesn't exist
*/
int keyPatternOffset( const string& key ) const;
bool inKeyPattern( const string& key ) const { return keyPatternOff set( key ) >= 0; }
/* true if the specified key is in the index */ /* true if the specified key is in the index */
bool hasKey(const BSONObj& key); bool hasKey(const BSONObj& key);
bool wouldCreateDup(const BSONObj& key, DiskLoc self);
// returns name of this index's storage area // returns name of this index's storage area
// database.table.$index // database.table.$index
string indexNamespace() const { string indexNamespace() const {
BSONObj io = info.obj(); BSONObj io = info.obj();
string s; string s;
s.reserve(Namespace::MaxNsLen); s.reserve(Namespace::MaxNsLen);
s = io.getStringField("ns"); s = io.getStringField("ns");
assert( !s.empty() ); assert( !s.empty() );
s += ".$"; s += ".$";
skipping to change at line 115 skipping to change at line 153
} }
/* gets not our namespace name (indexNamespace for that), /* gets not our namespace name (indexNamespace for that),
but the collection we index, its name. but the collection we index, its name.
*/ */
string parentNS() const { string parentNS() const {
BSONObj io = info.obj(); BSONObj io = info.obj();
return io.getStringField("ns"); return io.getStringField("ns");
} }
static int versionForIndexObj( const BSONObj &obj ) {
BSONElement e = obj["v"];
if( e.type() == NumberInt )
return e._numberInt();
// should normally be an int. this is for backward compatibili ty
int v = e.numberInt();
uassert(14802, "index v field should be Integer type", v == 0);
return v;
}
int version() const {
return versionForIndexObj( info.obj() );
}
/** @return true if index has unique constraint */
bool unique() const { bool unique() const {
BSONObj io = info.obj(); BSONObj io = info.obj();
return io["unique"].trueValue() || return io["unique"].trueValue() ||
/* temp: can we juse make unique:true always be there for _ id and get rid of this? */ /* temp: can we juse make unique:true always be there fo r _id and get rid of this? */
isIdIndex(); isIdIndex();
} }
/* if set, when building index, if any duplicates, drop the duplica ting object */ /** return true if dropDups was set when building index (if any dup licates, dropdups drops the duplicating objects) */
bool dropDups() const { bool dropDups() const {
return info.obj().getBoolField( "dropDups" ); return info.obj().getBoolField( "dropDups" );
} }
/* delete this index. does NOT clean up the system catalog /** delete this index. does NOT clean up the system catalog
(system.indexes or system.namespaces) -- only NamespaceIndex. (system.indexes or system.namespaces) -- only NamespaceIndex.
*/ */
void kill_idx(); void kill_idx();
const IndexSpec& getSpec() const; const IndexSpec& getSpec() const;
string toString() const { string toString() const {
return info.obj().toString(); return info.obj().toString();
} }
/** @return true if supported. supported means we can use the inde x, including adding new keys.
it may not mean we can build the index version in quest ion: we may not maintain building
of indexes in old formats in the future.
*/
static bool isASupportedIndexVersionNumber(int v) { return (v&1)==v ; } // v == 0 || v == 1
/** @return the interface for this interface, which varies with the index version.
used for backward compatibility of index versions/formats.
*/
IndexInterface& idxInterface() const {
int v = version();
dassert( isASupportedIndexVersionNumber(v) );
return *iis[v&1];
}
static IndexInterface *iis[];
}; };
struct IndexChanges/*on an update*/ { struct IndexChanges { /*on an update*/
BSONObjSetDefaultOrder oldkeys; BSONObjSet oldkeys;
BSONObjSetDefaultOrder newkeys; BSONObjSet newkeys;
vector<BSONObj*> removed; // these keys were removed as part of the change vector<BSONObj*> removed; // these keys were removed as part of the change
vector<BSONObj*> added; // these keys were added as part of the c hange vector<BSONObj*> added; // these keys were added as part of the c hange
/** @curObjLoc - the object we want to add's location. if it is al ready in the /** @curObjLoc - the object we want to add's location. if it is al ready in the
index, that is allowed here (for bg indexing case) . index, that is allowed here (for bg indexing case) .
*/ */
void dupCheck(IndexDetails& idx, DiskLoc curObjLoc) { void dupCheck(IndexDetails& idx, DiskLoc curObjLoc) {
if( added.empty() || !idx.unique() ) if( added.empty() || !idx.unique() )
return; return;
for( vector<BSONObj*>::iterator i = added.begin(); i != added.e nd(); i++ ) { const Ordering ordering = Ordering::make(idx.keyPattern());
bool dup = idx.wouldCreateDup(**i, curObjLoc); idx.idxInterface().uassertIfDups(idx, added, idx.head, curObjLo c, ordering); // "E11001 duplicate key on update"
uassert( 11001 , "E11001 duplicate key on update", !dup);
}
} }
}; };
class NamespaceDetails; class NamespaceDetails;
// changedId should be initialized to false // changedId should be initialized to false
void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON Obj newObj, BSONObj oldObj, bool &cangedId); void getIndexChanges(vector<IndexChanges>& v, NamespaceDetails& d, BSON Obj newObj, BSONObj oldObj, bool &cangedId);
void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d, DiskLoc cur ObjLoc); void dupCheck(vector<IndexChanges>& v, NamespaceDetails& d, DiskLoc cur ObjLoc);
} // namespace mongo } // namespace mongo
 End of changes. 15 change blocks. 
20 lines changed or deleted 88 lines changed or added


 indexkey.h   indexkey.h 
skipping to change at line 28 skipping to change at line 28
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "diskloc.h" #include "diskloc.h"
#include "jsobj.h" #include "jsobj.h"
#include <map> #include <map>
namespace mongo { namespace mongo {
extern const int DefaultIndexVersionNumber;
class Cursor; class Cursor;
class IndexSpec; class IndexSpec;
class IndexType; // TODO: this name sucks class IndexType; // TODO: this name sucks
class IndexPlugin; class IndexPlugin;
class IndexDetails; class IndexDetails;
enum IndexSuitability { USELESS = 0 , HELPFUL = 1 , OPTIMAL = 2 }; enum IndexSuitability { USELESS = 0 , HELPFUL = 1 , OPTIMAL = 2 };
/** /**
* this represents an instance of a index plugin * this represents an instance of a index plugin
* done this way so parsing, etc... can be cached * done this way so parsing, etc... can be cached
* so if there is a FTS IndexPlugin, for each index using FTS * so if there is a FTS IndexPlugin, for each index using FTS
* there will be 1 of these, and it can have things pre-parsed, etc... * there will be 1 of these, and it can have things pre-parsed, etc...
*/ */
class IndexType : boost::noncopyable { class IndexType : boost::noncopyable {
public: public:
IndexType( const IndexPlugin * plugin , const IndexSpec * spec ); IndexType( const IndexPlugin * plugin , const IndexSpec * spec );
virtual ~IndexType(); virtual ~IndexType();
virtual void getKeys( const BSONObj &obj, BSONObjSetDefaultOrder &k eys ) const = 0; virtual void getKeys( const BSONObj &obj, BSONObjSet &keys ) const = 0;
virtual shared_ptr<Cursor> newCursor( const BSONObj& query , const BSONObj& order , int numWanted ) const = 0; virtual shared_ptr<Cursor> newCursor( const BSONObj& query , const BSONObj& order , int numWanted ) const = 0;
/** optional op : changes query to match what's in the index */ /** optional op : changes query to match what's in the index */
virtual BSONObj fixKey( const BSONObj& in ) { return in; } virtual BSONObj fixKey( const BSONObj& in ) { return in; }
/** optional op : compare 2 objects with regards to this index */ /** optional op : compare 2 objects with regards to this index */
virtual int compare( const BSONObj& l , const BSONObj& r ) const; virtual int compare( const BSONObj& l , const BSONObj& r ) const;
/** @return plugin */ /** @return plugin */
const IndexPlugin * getPlugin() const { return _plugin; } const IndexPlugin * getPlugin() const { return _plugin; }
skipping to change at line 79 skipping to change at line 81
/** /**
* this represents a plugin * this represents a plugin
* a plugin could be something like full text search, sparse index, etc ... * a plugin could be something like full text search, sparse index, etc ...
* 1 of these exists per type of index per server * 1 of these exists per type of index per server
* 1 IndexType is created per index using this plugin * 1 IndexType is created per index using this plugin
*/ */
class IndexPlugin : boost::noncopyable { class IndexPlugin : boost::noncopyable {
public: public:
IndexPlugin( const string& name ); IndexPlugin( const string& name );
virtual ~IndexPlugin(){} virtual ~IndexPlugin() {}
virtual IndexType* generate( const IndexSpec * spec ) const = 0; virtual IndexType* generate( const IndexSpec * spec ) const = 0;
static IndexPlugin* get( const string& name ){ string getName() const { return _name; }
/**
* @return new keyPattern
* if nothing changes, should return keyPattern
*/
virtual BSONObj adjustIndexSpec( const BSONObj& spec ) const { retu rn spec; }
// ------- static below -------
static IndexPlugin* get( const string& name ) {
if ( ! _plugins ) if ( ! _plugins )
return 0; return 0;
map<string,IndexPlugin*>::iterator i = _plugins->find( name ); map<string,IndexPlugin*>::iterator i = _plugins->find( name );
if ( i == _plugins->end() ) if ( i == _plugins->end() )
return 0; return 0;
return i->second; return i->second;
} }
string getName() const { return _name; } /**
* @param keyPattern { x : "fts" }
* @return "" or the name
*/
static string findPluginName( const BSONObj& keyPattern );
private: private:
string _name; string _name;
static map<string,IndexPlugin*> * _plugins; static map<string,IndexPlugin*> * _plugins;
}; };
/* precomputed details about an index, used for inserting keys on updat es /* precomputed details about an index, used for inserting keys on updat es
stored/cached in NamespaceDetailsTransient, or can be used standalon e stored/cached in NamespaceDetailsTransient, or can be used standalon e
*/ */
class IndexSpec { class IndexSpec {
public: public:
BSONObj keyPattern; // e.g., { name : 1 } BSONObj keyPattern; // e.g., { name : 1 }
BSONObj info; // this is the same as IndexDetails::info.obj() BSONObj info; // this is the same as IndexDetails::info.obj()
IndexSpec() IndexSpec()
: _details(0) , _finishedInit(false){ : _details(0) , _finishedInit(false) {
} }
IndexSpec( const BSONObj& k , const BSONObj& m = BSONObj() ) explicit IndexSpec( const BSONObj& k , const BSONObj& m = BSONObj() )
: keyPattern(k) , info(m) , _details(0) , _finishedInit(false){ : keyPattern(k) , info(m) , _details(0) , _finishedInit(false) {
_init(); _init();
} }
/** /**
this is a DiscLoc of an IndexDetails info this is a DiscLoc of an IndexDetails info
should have a key field should have a key field
*/ */
IndexSpec( const DiskLoc& loc ){ explicit IndexSpec( const DiskLoc& loc ) {
reset( loc ); reset( loc );
} }
void reset( const DiskLoc& loc ); void reset( const BSONObj& info );
void reset( const DiskLoc& infoLoc ) { reset(infoLoc.obj()); }
void reset( const IndexDetails * details ); void reset( const IndexDetails * details );
void getKeys( const BSONObj &obj, BSONObjSetDefaultOrder &keys ) co nst; void getKeys( const BSONObj &obj, BSONObjSet &keys ) const;
BSONElement missingField() const { return _nullElt; } BSONElement missingField() const { return _nullElt; }
string getTypeName() const { string getTypeName() const {
if ( _indexType.get() ) if ( _indexType.get() )
return _indexType->getPlugin()->getName(); return _indexType->getPlugin()->getName();
return ""; return "";
} }
IndexType* getType() const { IndexType* getType() const {
skipping to change at line 148 skipping to change at line 166
} }
const IndexDetails * getDetails() const { const IndexDetails * getDetails() const {
return _details; return _details;
} }
IndexSuitability suitability( const BSONObj& query , const BSONObj& order ) const ; IndexSuitability suitability( const BSONObj& query , const BSONObj& order ) const ;
protected: protected:
IndexSuitability _suitability( const BSONObj& query , const BSONObj & order ) const ; int indexVersion() const;
void _getKeys( vector<const char*> fieldNames , vector<BSONElement> fixed , const BSONObj &obj, BSONObjSetDefaultOrder &keys ) const; IndexSuitability _suitability( const BSONObj& query , const BSONObj & order ) const ;
BSONSizeTracker _sizeTracker; BSONSizeTracker _sizeTracker;
vector<const char*> _fieldNames; vector<const char*> _fieldNames;
vector<BSONElement> _fixed; vector<BSONElement> _fixed;
BSONObj _nullKey;
BSONObj _nullObj; BSONObj _nullKey; // a full key with all fields null
BSONElement _nullElt; BSONObj _nullObj; // only used for _nullElt
BSONElement _nullElt; // jstNull
shared_ptr<IndexType> _indexType; BSONObj _undefinedObj; // only used for _undefinedElt
BSONElement _undefinedElt; // undefined
int _nFields; // number of fields in the index
bool _sparse; // if the index is sparse
shared_ptr<IndexType> _indexType;
const IndexDetails * _details; const IndexDetails * _details;
void _init(); void _init();
friend class IndexType;
friend class KeyGeneratorV0;
friend class KeyGeneratorV1;
public: public:
bool _finishedInit; bool _finishedInit;
friend class IndexType;
}; };
} // namespace mongo } // namespace mongo
 End of changes. 19 change blocks. 
19 lines changed or deleted 41 lines changed or added


 inline_decls.h   inline_decls.h 
// inline.h // inline_decls.h
/** /* Copyright 2010 10gen Inc.
* Copyright (C) 2010 10gen Inc. *
* * Licensed under the Apache License, Version 2.0 (the "License");
* This program is free software: you can redistribute it and/or modify * you may not use this file except in compliance with the License.
* it under the terms of the GNU Affero General Public License, version 3 , * You may obtain a copy of the License at
* as published by the Free Software Foundation. *
* * http://www.apache.org/licenses/LICENSE-2.0
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * Unless required by applicable law or agreed to in writing, software
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * distributed under the License is distributed on an "AS IS" BASIS,
* GNU Affero General Public License for more details. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* * See the License for the specific language governing permissions and
* You should have received a copy of the GNU Affero General Public Licen se * limitations under the License.
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
*/
#pragma once #pragma once
#if defined(__GNUC__) #if defined(__GNUC__)
#define NOINLINE_DECL __attribute__((noinline)) #define NOINLINE_DECL __attribute__((noinline))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define NOINLINE_DECL __declspec(noinline) #define NOINLINE_DECL __declspec(noinline)
#else #else
#define NOINLINE_DECL #define NOINLINE_DECL
#endif #endif
namespace mongo {
/* Note: do not clutter code with these -- ONLY use in hot spots / signific ant loops. */
#if !defined(__GNUC__)
// branch prediction. indicate we expect to be true
# define MONGO_likely(x) ((bool)(x))
// branch prediction. indicate we expect to be false
# define MONGO_unlikely(x) ((bool)(x))
# if defined(_WIN32)
// prefetch data from memory
inline void prefetch(const void *p) {
#if defined(_MM_HINT_T0)
_mm_prefetch((char *) p, _MM_HINT_T0);
#endif
}
#else
inline void prefetch(void *p) { }
#endif
#else
# define MONGO_likely(x) ( __builtin_expect((bool)(x), 1) )
# define MONGO_unlikely(x) ( __builtin_expect((bool)(x), 0) )
inline void prefetch(void *p) {
__builtin_prefetch(p);
}
#endif
}
 End of changes. 3 change blocks. 
16 lines changed or deleted 15 lines changed or added


 instance.h   instance.h 
skipping to change at line 23 skipping to change at line 23
* 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 "../client/dbclient.h" #include "../client/dbclient.h"
#include "curop.h" #include "curop-inl.h"
#include "security.h" #include "security.h"
#include "cmdline.h" #include "cmdline.h"
#include "client.h" #include "client.h"
namespace mongo { namespace mongo {
extern string dbExecCommand; extern string dbExecCommand;
struct DiagLog { struct DiagLog {
ofstream *f; ofstream *f;
/* 0 = off; 1 = writes, 2 = reads, 3 = both /* 0 = off; 1 = writes, 2 = reads, 3 = both
7 = log a few reads, and all writes. 7 = log a few reads, and all writes.
*/ */
int level; int level;
mongo::mutex mutex; mongo::mutex mutex;
DiagLog() : f(0) , level(0), mutex("DiagLog") { } DiagLog() : f(0) , level(0), mutex("DiagLog") { }
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 << dbpath << "/diaglog." << hex << time(0);
string name = ss.str(); string name = ss.str();
f = new ofstream(name.c_str(), ios::out | ios::binary); f = new ofstream(name.c_str(), ios::out | ios::binary);
if ( ! f->good() ) { if ( ! f->good() ) {
problem() << "couldn't open log stream" << endl; problem() << "couldn't open log stream" << endl;
throw 1717; throw 1717;
} }
} }
} }
/** /**
* @return old * @return old
*/ */
int setLevel( int newLevel ){ int setLevel( int newLevel ) {
int old = level; int old = level;
level = newLevel; level = newLevel;
init(); init();
return old; return old;
} }
void flush() { void flush() {
if ( level ){ if ( level ) {
scoped_lock lk(mutex); scoped_lock lk(mutex);
f->flush(); f->flush();
} }
} }
void write(char *data,int len) { void write(char *data,int len) {
if ( level & 1 ){ if ( level & 1 ) {
scoped_lock lk(mutex); scoped_lock lk(mutex);
f->write(data,len); f->write(data,len);
} }
} }
void readop(char *data, int len) { void readop(char *data, int len) {
if ( level & 2 ) { if ( level & 2 ) {
bool log = (level & 4) == 0; bool log = (level & 4) == 0;
OCCASIONALLY log = true; OCCASIONALLY log = true;
if ( log ){ if ( log ) {
scoped_lock lk(mutex); scoped_lock lk(mutex);
assert( f ); assert( f );
f->write(data,len); f->write(data,len);
} }
} }
} }
}; };
extern DiagLog _diaglog; extern DiagLog _diaglog;
skipping to change at line 105 skipping to change at line 105
MSGID responseTo; MSGID responseTo;
const char *exhaust; /* points to ns if exhaust mode. 0=normal mode */ const char *exhaust; /* points to ns if exhaust mode. 0=normal mode */
DbResponse(Message *r, MSGID rt) : response(r), responseTo(rt), exh aust(0) { } DbResponse(Message *r, MSGID rt) : response(r), responseTo(rt), exh aust(0) { }
DbResponse() { DbResponse() {
response = 0; response = 0;
exhaust = 0; exhaust = 0;
} }
~DbResponse() { delete response; } ~DbResponse() { delete response; }
}; };
bool assembleResponse( Message &m, DbResponse &dbresponse, const SockAd dr &client = unknownAddress ); void assembleResponse( Message &m, DbResponse &dbresponse, const HostAn dPort &client );
void getDatabaseNames( vector< string > &names , const string& usePath = dbpath ); void getDatabaseNames( vector< string > &names , const string& usePath = dbpath );
/* returns true if there is no data on this server. useful when starti ng replication. /* returns true if there is no data on this server. useful when starti ng replication.
local database does NOT count. local database does NOT count.
*/ */
bool replHasDatabases(); bool replHasDatabases();
// --- local client --- /** "embedded" calls to the local server directly.
Caller does not need to lock, that is handled within.
*/
class DBDirectClient : public DBClientBase { class DBDirectClient : public DBClientBase {
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); 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";
} }
skipping to change at line 128 skipping to change at line 128
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); 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 , string * actualServer = 0 );
virtual void say( Message &toSend ); virtual void say( Message &toSend, bool isRetry = false );
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 );
} }
virtual void killCursor( long long cursorID ); virtual void killCursor( long long cursorID );
virtual bool callRead( Message& toSend , Message& response ){ virtual bool callRead( Message& toSend , Message& response ) {
return call( toSend , response ); return call( toSend , response );
} }
virtual unsigned long long count(const string &ns, const BSONObj& q uery = BSONObj(), int options=0, int limit=0, int skip=0 );
virtual ConnectionString::ConnectionType type() const { return Conn ectionString::MASTER; } virtual ConnectionString::ConnectionType type() const { return Conn ectionString::MASTER; }
virtual bool isMember( const DBConnector * conn ) const { return th is == conn; };
double getSoTimeout() const { return 0; }
virtual bool lazySupported() const { return true; }
private:
static HostAndPort _clientHost;
}; };
extern int lockFile; extern int lockFile;
void acquirePathLock(); #ifdef _WIN32
extern HANDLE lockFileHandle;
#endif
void acquirePathLock(bool doingRepair=false); // if doingRepair=true do n't consider unclean shutdown an error
void maybeCreatePidFile(); void maybeCreatePidFile();
} // namespace mongo } // namespace mongo
 End of changes. 15 change blocks. 
16 lines changed or deleted 26 lines changed or added


 introspect.h   introspect.h 
skipping to change at line 32 skipping to change at line 32
#include "../pch.h" #include "../pch.h"
#include "jsobj.h" #include "jsobj.h"
#include "pdfile.h" #include "pdfile.h"
namespace mongo { namespace mongo {
/* --- profiling -------------------------------------------- /* --- profiling --------------------------------------------
do when database->profile is set do when database->profile is set
*/ */
void profile(const char *str, void profile( const Client& c , CurOp& currentOp );
int millis);
} // namespace mongo } // namespace mongo
 End of changes. 1 change blocks. 
2 lines changed or deleted 1 lines changed or added


 jsobj.h   jsobj.h 
skipping to change at line 43 skipping to change at line 43
#include "../util/optime.h" #include "../util/optime.h"
#include "boost/utility.hpp" #include "boost/utility.hpp"
#include <set> #include <set>
#include "../bson/bsontypes.h" #include "../bson/bsontypes.h"
#include "../bson/oid.h" #include "../bson/oid.h"
#include "../bson/bsonelement.h" #include "../bson/bsonelement.h"
#include "../bson/bsonobj.h" #include "../bson/bsonobj.h"
#include "../bson/bsonmisc.h" #include "../bson/bsonmisc.h"
#include "../bson/bsonobjbuilder.h" #include "../bson/bsonobjbuilder.h"
#include "../bson/bsonobjiterator.h" #include "../bson/bsonobjiterator.h"
#include "../bson/bsoninlines.h" #include "../bson/bson-inl.h"
#include "../bson/ordering.h" #include "../bson/ordering.h"
#include "../bson/stringdata.h" #include "../bson/stringdata.h"
#include "../bson/bson_db.h" #include "../bson/bson_db.h"
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 jsobjmanipulator.h   jsobjmanipulator.h 
skipping to change at line 22 skipping to change at line 22
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "jsobj.h" #include "jsobj.h"
#include "dur.h"
namespace mongo { namespace mongo {
/** Manipulate the binary representation of a BSONElement in-place. /** Manipulate the binary representation of a BSONElement in-place.
Careful, this casts away const. Careful, this casts away const.
*/ */
class BSONElementManipulator { class BSONElementManipulator {
public: public:
BSONElementManipulator( const BSONElement &element ) : BSONElementManipulator( const BSONElement &element ) :
_element( element ) { _element( element ) {
skipping to change at line 45 skipping to change at line 46
OpTime::now().asDate() OpTime::now().asDate()
*/ */
void initTimestamp(); void initTimestamp();
/** Change the value, in place, of the number. */ /** Change the value, in place, of the number. */
void setNumber(double d) { void setNumber(double d) {
if ( _element.type() == NumberDouble ) *reinterpret_cast< doubl e * >( value() ) = d; if ( _element.type() == NumberDouble ) *reinterpret_cast< doubl e * >( value() ) = d;
else if ( _element.type() == NumberInt ) *reinterpret_cast< int * >( value() ) = (int) d; else if ( _element.type() == NumberInt ) *reinterpret_cast< int * >( value() ) = (int) d;
else assert(0); else assert(0);
} }
void SetNumber(double d) {
if ( _element.type() == NumberDouble )
*getDur().writing( reinterpret_cast< double * >( value() ) ) = d;
else if ( _element.type() == NumberInt )
*getDur().writing( reinterpret_cast< int * >( value() ) ) = (int) d;
else assert(0);
}
void setLong(long long n) { void setLong(long long n) {
assert( _element.type() == NumberLong ); assert( _element.type() == NumberLong );
*reinterpret_cast< long long * >( value() ) = n; *reinterpret_cast< long long * >( value() ) = n;
} }
void SetLong(long long n) {
assert( _element.type() == NumberLong );
*getDur().writing( reinterpret_cast< long long * >(value()) ) = n;
}
void setInt(int n) { void setInt(int n) {
assert( _element.type() == NumberInt ); assert( _element.type() == NumberInt );
*reinterpret_cast< int * >( value() ) = n; *reinterpret_cast< int * >( value() ) = n;
} }
void SetInt(int n) {
assert( _element.type() == NumberInt );
getDur().writingInt( *reinterpret_cast< int * >( value() ) ) = n;
}
/** Replace the type and value of the element with the type and val ue of e, /** Replace the type and value of the element with the type and val ue of e,
preserving the original fieldName */ preserving the original fieldName */
void replaceTypeAndValue( const BSONElement &e ) { void replaceTypeAndValue( const BSONElement &e ) {
*data() = e.type(); *data() = e.type();
memcpy( value(), e.value(), e.valuesize() ); memcpy( value(), e.value(), e.valuesize() );
} }
static void lookForTimestamps( const BSONObj& obj ){ /* dur:: version */
void ReplaceTypeAndValue( const BSONElement &e ) {
char *d = data();
char *v = value();
int valsize = e.valuesize();
int ofs = (int) (v-d);
dassert( ofs > 0 );
char *p = (char *) getDur().writingPtr(d, valsize + ofs);
*p = e.type();
memcpy( p + ofs, e.value(), valsize );
}
static void lookForTimestamps( const BSONObj& obj ) {
// If have a Timestamp field as the first or second element, // If have a Timestamp field as the first or second element,
// update it to a Date field set to OpTime::now().asDate(). Th e // update it to a Date field set to OpTime::now().asDate(). Th e
// replacement policy is a work in progress. // replacement policy is a work in progress.
BSONObjIterator i( obj ); BSONObjIterator i( obj );
for( int j = 0; i.moreWithEOO() && j < 2; ++j ) { for( int j = 0; i.moreWithEOO() && j < 2; ++j ) {
BSONElement e = i.next(); BSONElement e = i.next();
if ( e.eoo() ) if ( e.eoo() )
break; break;
if ( e.type() == Timestamp ){ if ( e.type() == Timestamp ) {
BSONElementManipulator( e ).initTimestamp(); BSONElementManipulator( e ).initTimestamp();
break; break;
} }
} }
} }
private: private:
char *data() { return nonConst( _element.rawdata() ); } char *data() { return nonConst( _element.rawdata() ); }
char *value() { return nonConst( _element.value() ); } char *value() { return nonConst( _element.value() ); }
static char *nonConst( const char *s ) { return const_cast< char * >( s ); } static char *nonConst( const char *s ) { return const_cast< char * >( s ); }
 End of changes. 6 change blocks. 
2 lines changed or deleted 30 lines changed or added


 lasterror.h   lasterror.h 
skipping to change at line 36 skipping to change at line 36
struct LastError { struct LastError {
int code; int code;
string msg; string msg;
enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ; enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ;
OID upsertedId; OID upsertedId;
OID writebackId; OID writebackId;
long long nObjects; long long nObjects;
int nPrev; int nPrev;
bool valid; bool valid;
bool disabled; bool disabled;
void writeback( OID& oid ){ void writeback( OID& oid ) {
reset( true ); reset( true );
writebackId = oid; writebackId = oid;
} }
void raiseError(int _code , const char *_msg) { void raiseError(int _code , const char *_msg) {
reset( true ); reset( true );
code = _code; code = _code;
msg = _msg; msg = _msg;
} }
void recordUpdate( bool _updateObjects , long long _nObjects , OID _upsertedId ){ void recordUpdate( bool _updateObjects , long long _nObjects , OID _upsertedId ) {
reset( true ); reset( true );
nObjects = _nObjects; nObjects = _nObjects;
updatedExisting = _updateObjects ? True : False; updatedExisting = _updateObjects ? True : False;
if ( _upsertedId.isSet() ) if ( _upsertedId.isSet() )
upsertedId = _upsertedId; upsertedId = _upsertedId;
} }
void recordDelete( long long nDeleted ) { void recordDelete( long long nDeleted ) {
reset( true ); reset( true );
nObjects = nDeleted; nObjects = nDeleted;
skipping to change at line 71 skipping to change at line 71
code = 0; code = 0;
msg.clear(); msg.clear();
updatedExisting = NotUpdate; updatedExisting = NotUpdate;
nObjects = 0; nObjects = 0;
nPrev = 1; nPrev = 1;
valid = _valid; valid = _valid;
disabled = false; disabled = false;
upsertedId.clear(); upsertedId.clear();
writebackId.clear(); writebackId.clear();
} }
void appendSelf( BSONObjBuilder &b );
/**
* @return if there is an err
*/
bool appendSelf( BSONObjBuilder &b , bool blankErr = true );
struct Disabled : boost::noncopyable { struct Disabled : boost::noncopyable {
Disabled( LastError * le ){ Disabled( LastError * le ) {
_le = le; _le = le;
if ( _le ){ if ( _le ) {
_prev = _le->disabled; _prev = _le->disabled;
_le->disabled = true; _le->disabled = true;
} else { }
else {
_prev = false; _prev = false;
} }
} }
~Disabled(){ ~Disabled() {
if ( _le ) if ( _le )
_le->disabled = _prev; _le->disabled = _prev;
} }
LastError * _le; LastError * _le;
bool _prev; bool _prev;
}; };
static LastError noError; static LastError noError;
}; };
extern class LastErrorHolder { extern class LastErrorHolder {
public: public:
LastErrorHolder() : _id( 0 ) {} LastErrorHolder(){}
~LastErrorHolder(); ~LastErrorHolder();
LastError * get( bool create = false ); LastError * get( bool create = false );
LastError * getSafe(){ LastError * getSafe() {
LastError * le = get(false); LastError * le = get(false);
if ( ! le ){ if ( ! le ) {
log( LL_ERROR ) << " no LastError! id: " << getID() << end l; error() << " no LastError!" << endl;
assert( le ); assert( le );
} }
return le; return le;
} }
LastError * _get( bool create = false ); // may return a disabled L astError LastError * _get( bool create = false ); // may return a disabled L astError
void reset( LastError * le ); void reset( LastError * le );
/** ok to call more than once. */ /** ok to call more than once. */
void initThread(); void initThread();
/**
* id of 0 means should use thread local management
*/
void setID( int id );
int getID(); int getID();
void remove( int id );
void release(); void release();
/** when db receives a message/request, call this */ /** when db receives a message/request, call this */
void startRequest( Message& m , LastError * connectionOwned ); LastError * startRequest( Message& m , LastError * connectionOwned );
LastError * startRequest( Message& m , int clientId );
void disconnect( int clientId ); void disconnect( int clientId );
// used to disable lastError reporting while processing a killCurso rs message // used to disable lastError reporting while processing a killCurso rs message
// disable causes get() to return 0. // disable causes get() to return 0.
LastError *disableForCommand(); // only call once per command invoc ation! LastError *disableForCommand(); // only call once per command invoc ation!
private: private:
ThreadLocalValue<int> _id;
boost::thread_specific_ptr<LastError> _tl; boost::thread_specific_ptr<LastError> _tl;
struct Status { struct Status {
time_t time; time_t time;
LastError *lerr; LastError *lerr;
}; };
typedef map<int,Status> IDMap;
static mongo::mutex _idsmutex;
IDMap _ids;
} lastError; } lastError;
void raiseError(int code , const char *msg); void raiseError(int code , const char *msg);
} // namespace mongo } // namespace mongo
 End of changes. 15 change blocks. 
23 lines changed or deleted 17 lines changed or added


 list.h   list.h 
skipping to change at line 23 skipping to change at line 23
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
namespace mongo { namespace mongo {
/* this class uses a mutex for writes, but not for reads. /* this class uses a mutex for writes, but not for reads.
we can get fancier later... we can get fancier later...
struct Member : public List1<Member>::Base { struct Member : public List1<Member>::Base {
const char *host; const char *host;
int port; int port;
};
List1<Member> _members;
_members.head()->next();
*/
template<typename T>
class List1 : boost::noncopyable {
public:
/* next() and head() return 0 at end of list */
List1() : _head(0), _m("List1"), _orphans(0) { }
class Base {
friend class List1;
T *_next;
public:
Base() : _next(0){}
~Base() { wassert(false); } // we never want this to happen
T* next() const { return _next; }
}; };
List1<Member> _members;
_members.head()->next();
*/ /** note this is safe:
template<typename T>
class List1 : boost::noncopyable {
public:
/* next() and head() return 0 at end of list */
List1() : _head(0), _m("List1"), _orphans(0) { } T* p = mylist.head();
if( p )
use(p);
class Base { and this is not:
friend class List1;
T *_next;
public:
T* next() const { return _next; }
};
T* head() const { return _head; } if( mylist.head() )
use( mylist.head() ); // could become 0
*/
T* head() const { return (T*) _head; }
void push(T* t) { void push(T* t) {
scoped_lock lk(_m); assert( t->_next == 0 );
t->_next = _head; scoped_lock lk(_m);
_head = t; t->_next = (T*) _head;
} _head = t;
}
// intentionally leak. // intentionally leaks.
void orphanAll() { void orphanAll() {
_head = 0; scoped_lock lk(_m);
} _head = 0;
}
/* t is not deleted, but is removed from the list. (orphaned) */ /* t is not deleted, but is removed from the list. (orphaned) */
void orphan(T* t) { void orphan(T* t) {
scoped_lock lk(_m); scoped_lock lk(_m);
T *&prev = _head; T *&prev = (T*&) _head;
T *n = prev; T *n = prev;
while( n != t ) { while( n != t ) {
prev = n->_next; uassert( 14050 , "List1: item to orphan not in list", n );
n = prev; prev = n->_next;
n = prev;
}
prev = t->_next;
if( ++_orphans > 500 )
log() << "warning List1 orphans=" << _orphans << '\n';
} }
prev = t->_next;
if( ++_orphans > 500 )
log() << "warning orphans=" << _orphans << '\n';
}
private: private:
T *_head; volatile T *_head;
mutex _m; mongo::mutex _m;
int _orphans; int _orphans;
}; };
}; };
 End of changes. 12 change blocks. 
46 lines changed or deleted 62 lines changed or added


 log.h   log.h 
skipping to change at line 32 skipping to change at line 32
#include "../bson/util/builder.h" #include "../bson/util/builder.h"
#ifndef _WIN32 #ifndef _WIN32
//#include <syslog.h> //#include <syslog.h>
#endif #endif
namespace mongo { namespace mongo {
enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR , LL_SEVERE }; enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR , LL_SEVERE };
inline const char * logLevelToString( LogLevel l ){ inline const char * logLevelToString( LogLevel l ) {
switch ( l ){ switch ( l ) {
case LL_DEBUG: case LL_DEBUG:
case LL_INFO: case LL_INFO:
case LL_NOTICE: case LL_NOTICE:
return ""; return "";
case LL_WARNING: case LL_WARNING:
return "warning" ; return "warning" ;
case LL_ERROR: case LL_ERROR:
return "ERROR"; return "ERROR";
case LL_SEVERE: case LL_SEVERE:
return "SEVERE"; return "SEVERE";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
} }
class LabeledLevel {
public:
LabeledLevel( int level ) : _level( level ) {}
LabeledLevel( const char* label, int level ) : _label( label ), _lev el( level ) {}
LabeledLevel( const string& label, int level ) : _label( label ), _l evel( level ) {}
LabeledLevel operator+( int i ) const {
return LabeledLevel( _label, _level + i );
}
LabeledLevel operator+( const char* label ) const {
if( _label == "" )
return LabeledLevel( label, _level );
return LabeledLevel( _label + string("::") + label, _level );
}
LabeledLevel operator+( string& label ) const {
return LabeledLevel( _label + string("::") + label, _level );
}
LabeledLevel operator-( int i ) const {
return LabeledLevel( _label, _level - i );
}
const string& getLabel() const { return _label; }
int getLevel() const { return _level; }
private:
string _label;
int _level;
};
class LazyString { class LazyString {
public: public:
virtual ~LazyString() {} virtual ~LazyString() {}
virtual string val() const = 0; virtual string val() const = 0;
}; };
// Utility class for stringifying object only when val() called. // Utility class for stringifying object only when val() called.
template< class T > template< class T >
class LazyStringImpl : public LazyString { class LazyStringImpl : public LazyString {
public: public:
LazyStringImpl( const T &t ) : t_( t ) {} LazyStringImpl( const T &t ) : t_( t ) {}
virtual string val() const { return t_.toString(); } virtual string val() const { return t_.toString(); }
private: private:
const T& t_; const T& t_;
}; };
class Tee { class Tee {
public: public:
virtual ~Tee(){} virtual ~Tee() {}
virtual void write(LogLevel level , const string& str) = 0; virtual void write(LogLevel level , const string& str) = 0;
}; };
class Nullstream { class Nullstream {
public: public:
virtual Nullstream& operator<< (Tee* tee) { virtual Nullstream& operator<< (Tee* tee) {
return *this; return *this;
} }
virtual ~Nullstream() {} virtual ~Nullstream() {}
virtual Nullstream& operator<<(const char *) { virtual Nullstream& operator<<(const char *) {
skipping to change at line 107 skipping to change at line 140
} }
virtual Nullstream& operator<<(unsigned long) { virtual Nullstream& operator<<(unsigned long) {
return *this; return *this;
} }
virtual Nullstream& operator<<(long) { virtual Nullstream& operator<<(long) {
return *this; return *this;
} }
virtual Nullstream& operator<<(unsigned) { virtual Nullstream& operator<<(unsigned) {
return *this; return *this;
} }
virtual Nullstream& operator<<(unsigned short) {
return *this;
}
virtual Nullstream& operator<<(double) { virtual Nullstream& operator<<(double) {
return *this; return *this;
} }
virtual Nullstream& operator<<(void *) { virtual Nullstream& operator<<(void *) {
return *this; return *this;
} }
virtual Nullstream& operator<<(const void *) { virtual Nullstream& operator<<(const void *) {
return *this; return *this;
} }
virtual Nullstream& operator<<(long long) { virtual Nullstream& operator<<(long long) {
skipping to change at line 137 skipping to change at line 173
} }
template< class T > template< class T >
Nullstream& operator<<(T *t) { Nullstream& operator<<(T *t) {
return operator<<( static_cast<void*>( t ) ); return operator<<( static_cast<void*>( t ) );
} }
template< class T > template< class T >
Nullstream& operator<<(const T *t) { Nullstream& operator<<(const T *t) {
return operator<<( static_cast<const void*>( t ) ); return operator<<( static_cast<const void*>( t ) );
} }
template< class T > template< class T >
Nullstream& operator<<(const shared_ptr<T> p ){ Nullstream& operator<<(const shared_ptr<T> p ) {
T * t = p.get();
if ( ! t )
*this << "null";
else
*this << *t;
return *this; return *this;
} }
template< class T > template< class T >
Nullstream& operator<<(const T &t) { Nullstream& operator<<(const T &t) {
return operator<<( static_cast<const LazyString&>( LazyStringIm pl< T >( t ) ) ); return operator<<( static_cast<const LazyString&>( LazyStringIm pl< T >( t ) ) );
} }
virtual Nullstream& operator<< (ostream& ( *endl )(ostream&)) { virtual Nullstream& operator<< (ostream& ( *endl )(ostream&)) {
return *this; return *this;
} }
virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) { virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) {
return *this; return *this;
} }
virtual void flush(Tee *t = 0) {} virtual void flush(Tee *t = 0) {}
}; };
extern Nullstream nullstream; extern Nullstream nullstream;
class Logstream : public Nullstream { class Logstream : public Nullstream {
static mongo::mutex mutex; static mongo::mutex mutex;
static int doneSetup; static int doneSetup;
stringstream ss; stringstream ss;
int indent;
LogLevel logLevel; LogLevel logLevel;
static FILE* logfile; static FILE* logfile;
static boost::scoped_ptr<ostream> stream; static boost::scoped_ptr<ostream> stream;
static vector<Tee*> * globalTees; static vector<Tee*> * globalTees;
public: public:
inline static void logLockless( const StringData& s ); inline static void logLockless( const StringData& s );
static void setLogFile(FILE* f){ static void setLogFile(FILE* f) {
scoped_lock lk(mutex); scoped_lock lk(mutex);
logfile = f; logfile = f;
} }
static int magicNumber(){ static int magicNumber() {
return 1717; return 1717;
} }
static int getLogDesc() { static int getLogDesc() {
int fd = -1; int fd = -1;
if (logfile != NULL) if (logfile != NULL)
#if defined(_WIN32)
// the ISO C++ conformant name is _fileno
fd = _fileno( logfile );
#else
fd = fileno( logfile ); fd = fileno( logfile );
#endif
return fd; return fd;
} }
inline void flush(Tee *t = 0); inline void flush(Tee *t = 0);
inline Nullstream& setLogLevel(LogLevel l){ inline Nullstream& setLogLevel(LogLevel l) {
logLevel = l; logLevel = l;
return *this; return *this;
} }
/** note these are virtual */ /** note these are virtual */
Logstream& operator<<(const char *x) { ss << x; return *this; } Logstream& operator<<(const char *x) { ss << x; return *this; }
Logstream& operator<<(const string& x) { ss << x; return *this; } Logstream& operator<<(const string& x) { ss << x; return *this; }
Logstream& operator<<(const StringData& x) { ss << x.data(); return *this; } Logstream& operator<<(const StringData& x) { ss << x.data(); return *this; }
Logstream& operator<<(char *x) { ss << x; return *this; } Logstream& operator<<(char *x) { ss << x; return *this; }
Logstream& operator<<(char x) { ss << x; return *this; } Logstream& operator<<(char x) { ss << x; return *this; }
Logstream& operator<<(int x) { ss << x; return *this; } Logstream& operator<<(int x) { ss << x; return *this; }
Logstream& operator<<(ExitCode x) { ss << x; return *this; } Logstream& operator<<(ExitCode x) { ss << x; return *this; }
Logstream& operator<<(long x) { ss << x; return *this; } Logstream& operator<<(long x) { ss << x; return *this; }
Logstream& operator<<(unsigned long x) { ss << x; return *this; } Logstream& operator<<(unsigned long x) { ss << x; return *this; }
Logstream& operator<<(unsigned x) { ss << x; return *this; } Logstream& operator<<(unsigned x) { ss << x; return *this; }
Logstream& operator<<(unsigned short x){ ss << x; return *this; }
Logstream& operator<<(double x) { ss << x; return *this; } Logstream& operator<<(double x) { ss << x; return *this; }
Logstream& operator<<(void *x) { ss << x; return *this; } Logstream& operator<<(void *x) { ss << x; return *this; }
Logstream& operator<<(const void *x) { ss << x; return *this; } Logstream& operator<<(const void *x) { ss << x; return *this; }
Logstream& operator<<(long long x) { ss << x; return *this; } Logstream& operator<<(long long x) { ss << x; return *this; }
Logstream& operator<<(unsigned long long x) { ss << x; return *this ; } Logstream& operator<<(unsigned long long x) { ss << x; return *this ; }
Logstream& operator<<(bool x) { ss << x; return *this ; } Logstream& operator<<(bool x) { ss << x; return *this ; }
Logstream& operator<<(const LazyString& x) { Logstream& operator<<(const LazyString& x) {
ss << x.val(); ss << x.val();
return *this; return *this;
skipping to change at line 226 skipping to change at line 275
Logstream& operator<< (ostream& ( *_endl )(ostream&)) { Logstream& operator<< (ostream& ( *_endl )(ostream&)) {
ss << '\n'; ss << '\n';
flush(0); flush(0);
return *this; return *this;
} }
Logstream& operator<< (ios_base& (*_hex)(ios_base&)) { Logstream& operator<< (ios_base& (*_hex)(ios_base&)) {
ss << _hex; ss << _hex;
return *this; return *this;
} }
template< class T >
Nullstream& operator<<(const shared_ptr<T> p ){
T * t = p.get();
if ( ! t )
*this << "null";
else
*this << *t;
return *this;
}
Logstream& prolog() { Logstream& prolog() {
return *this; return *this;
} }
void addGlobalTee( Tee * t ){ void addGlobalTee( Tee * t ) {
if ( ! globalTees ) if ( ! globalTees )
globalTees = new vector<Tee*>(); globalTees = new vector<Tee*>();
globalTees->push_back( t ); globalTees->push_back( t );
} }
void indentInc(){ indent++; }
void indentDec(){ indent--; }
int getIndent() const { return indent; }
private: private:
static thread_specific_ptr<Logstream> tsp; static thread_specific_ptr<Logstream> tsp;
Logstream(){ Logstream() {
indent = 0;
_init(); _init();
} }
void _init(){ void _init() {
ss.str(""); ss.str("");
logLevel = LL_INFO; logLevel = LL_INFO;
} }
public: public:
static Logstream& get() { static Logstream& get() {
if ( StaticObserver::_destroyingStatics ) {
cout << "Logstream::get called in uninitialized state" << e ndl;
}
Logstream *p = tsp.get(); Logstream *p = tsp.get();
if( p == 0 ) if( p == 0 )
tsp.reset( p = new Logstream() ); tsp.reset( p = new Logstream() );
return *p; return *p;
} }
}; };
extern int logLevel; extern int logLevel;
extern int tlogLevel; extern int tlogLevel;
skipping to change at line 281 skipping to change at line 328
} }
/* flush the log stream if the log level is /* flush the log stream if the log level is
at the specified level or higher. */ at the specified level or higher. */
inline void logflush(int level = 0) { inline void logflush(int level = 0) {
if( level > logLevel ) if( level > logLevel )
Logstream::get().flush(0); Logstream::get().flush(0);
} }
/* without prolog */ /* without prolog */
inline Nullstream& _log( int level = 0 ){ inline Nullstream& _log( int level = 0 ) {
if ( level > logLevel ) if ( level > logLevel )
return nullstream; return nullstream;
return Logstream::get(); return Logstream::get();
} }
/** logging which we may not want during unit tests runs. /** logging which we may not want during unit tests (dbtests) runs.
set tlogLevel to -1 to suppress tlog() output in a test program. */ set tlogLevel to -1 to suppress tlog() output in a test program. */
inline Nullstream& tlog( int level = 0 ) { inline Nullstream& tlog( int level = 0 ) {
if ( level > tlogLevel || level > logLevel ) if ( level > tlogLevel || level > logLevel )
return nullstream; return nullstream;
return Logstream::get().prolog(); return Logstream::get().prolog();
} }
inline Nullstream& log( int level ) { inline Nullstream& log( int level ) {
if ( level > logLevel ) if ( level > logLevel )
return nullstream; return nullstream;
return Logstream::get().prolog(); return Logstream::get().prolog();
} }
#define MONGO_LOG(level) if ( MONGO_unlikely(logLevel >= (level)) ) log( le vel )
#define LOG MONGO_LOG
inline Nullstream& log( LogLevel l ) { inline Nullstream& log( LogLevel l ) {
return Logstream::get().prolog().setLogLevel( l ); return Logstream::get().prolog().setLogLevel( l );
} }
inline Nullstream& log( const LabeledLevel& ll ) {
Nullstream& stream = log( ll.getLevel() );
if( ll.getLabel() != "" )
stream << "[" << ll.getLabel() << "] ";
return stream;
}
inline Nullstream& log() { inline Nullstream& log() {
return Logstream::get().prolog(); return Logstream::get().prolog();
} }
inline Nullstream& error() { inline Nullstream& error() {
return log( LL_ERROR ); return log( LL_ERROR );
} }
inline Nullstream& warning() { inline Nullstream& warning() {
return log( LL_WARNING ); return log( LL_WARNING );
skipping to change at line 383 skipping to change at line 440
s << strerror(x); s << strerror(x);
#endif #endif
return s.str(); return s.str();
} }
/** output the error # and error message with prefix. /** output the error # and error message with prefix.
handy for use as parm in uassert/massert. handy for use as parm in uassert/massert.
*/ */
string errnoWithPrefix( const char * prefix ); string errnoWithPrefix( const char * prefix );
void Logstream::logLockless( const StringData& s ){ void Logstream::logLockless( const StringData& s ) {
if ( doneSetup == 1717 ){ if ( s.size() == 0 )
if(fwrite(s.data(), s.size(), 1, logfile)){ return;
if ( doneSetup == 1717 ) {
if (fwrite(s.data(), s.size(), 1, logfile)) {
fflush(logfile); fflush(logfile);
}else{ }
else {
int x = errno; int x = errno;
cout << "Failed to write to logfile: " << errnoWithDescript ion(x) << endl; cout << "Failed to write to logfile: " << errnoWithDescript ion(x) << endl;
} }
} }
else { else {
cout << s.data(); cout << s.data();
cout.flush(); cout.flush();
} }
} }
void Logstream::flush(Tee *t) { void Logstream::flush(Tee *t) {
// this ensures things are sane // this ensures things are sane
if ( doneSetup == 1717 ) { if ( doneSetup == 1717 ) {
string msg = ss.str(); string msg = ss.str();
string threadName = getThreadName(); string threadName = getThreadName();
const char * type = logLevelToString(logLevel); const char * type = logLevelToString(logLevel);
int spaceNeeded = msg.size() + 64 + threadName.size(); int spaceNeeded = (int)(msg.size() + 64 + threadName.size());
int bufSize = 128; int bufSize = 128;
while ( bufSize < spaceNeeded ) while ( bufSize < spaceNeeded )
bufSize += 128; bufSize += 128;
BufBuilder b(bufSize); BufBuilder b(bufSize);
time_t_to_String( time(0) , b.grow(20) ); time_t_to_String( time(0) , b.grow(20) );
if (!threadName.empty()){ if (!threadName.empty()) {
b.appendChar( '[' ); b.appendChar( '[' );
b.appendStr( threadName , false ); b.appendStr( threadName , false );
b.appendChar( ']' ); b.appendChar( ']' );
b.appendChar( ' ' ); b.appendChar( ' ' );
} }
if ( type[0] ){
for ( int i=0; i<indent; i++ )
b.appendChar( '\t' );
if ( type[0] ) {
b.appendStr( type , false ); b.appendStr( type , false );
b.appendStr( ": " , false ); b.appendStr( ": " , false );
} }
b.appendStr( msg ); b.appendStr( msg );
string out( b.buf() , b.len() - 1); string out( b.buf() , b.len() - 1);
scoped_lock lk(mutex); scoped_lock lk(mutex);
if( t ) t->write(logLevel,out); if( t ) t->write(logLevel,out);
if ( globalTees ){ if ( globalTees ) {
for ( unsigned i=0; i<globalTees->size(); i++ ) for ( unsigned i=0; i<globalTees->size(); i++ )
(*globalTees)[i]->write(logLevel,out); (*globalTees)[i]->write(logLevel,out);
} }
#ifndef _WIN32 #ifndef _WIN32
//syslog( LOG_INFO , "%s" , cc ); //syslog( LOG_INFO , "%s" , cc );
#endif #endif
if(fwrite(out.data(), out.size(), 1, logfile)){ if(fwrite(out.data(), out.size(), 1, logfile)) {
fflush(logfile); fflush(logfile);
}else{ }
else {
int x = errno; int x = errno;
cout << "Failed to write to logfile: " << errnoWithDescript ion(x) << ": " << out << endl; cout << "Failed to write to logfile: " << errnoWithDescript ion(x) << ": " << out << endl;
} }
} }
_init(); _init();
} }
struct LogIndentLevel {
LogIndentLevel(){
Logstream::get().indentInc();
}
~LogIndentLevel(){
Logstream::get().indentDec();
}
};
extern Tee* const warnings; // Things put here go in serverStatus
} // namespace mongo } // namespace mongo
 End of changes. 35 change blocks. 
33 lines changed or deleted 111 lines changed or added


 lruishmap.h   lruishmap.h 
skipping to change at line 26 skipping to change at line 26
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/goodies.h" #include "../util/goodies.h"
namespace mongo { namespace mongo {
/* Your K object must define: /* Your K object must define:
int hash() - must always return > 0. int hash() - must always return > 0.
operator== operator==
*/ */
template <class K, class V, int MaxChain> template <class K, class V, int MaxChain>
class LRUishMap { class LRUishMap {
public: public:
LRUishMap(int _n) { LRUishMap(int _n) {
n = nextPrime(_n); n = nextPrime(_n);
keys = new K[n]; keys = new K[n];
hashes = new int[n]; hashes = new int[n];
for ( int i = 0; i < n; i++ ) hashes[i] = 0; for ( int i = 0; i < n; i++ ) hashes[i] = 0;
 End of changes. 1 change blocks. 
2 lines changed or deleted 2 lines changed or added


 matcher.h   matcher.h 
skipping to change at line 35 skipping to change at line 35
namespace mongo { namespace mongo {
class Cursor; class Cursor;
class CoveredIndexMatcher; class CoveredIndexMatcher;
class Matcher; class Matcher;
class FieldRangeVector; class FieldRangeVector;
class RegexMatcher { class RegexMatcher {
public: public:
const char *fieldName; const char *_fieldName;
const char *regex; const char *_regex;
const char *flags; const char *_flags;
string prefix; string _prefix;
shared_ptr< pcrecpp::RE > re; shared_ptr< pcrecpp::RE > _re;
bool isNot; bool _isNot;
RegexMatcher() : isNot() {} RegexMatcher() : _isNot() {}
}; };
struct element_lt struct element_lt {
{ bool operator()(const BSONElement& l, const BSONElement& r) const {
bool operator()(const BSONElement& l, const BSONElement& r) const
{
int x = (int) l.canonicalType() - (int) r.canonicalType(); int x = (int) l.canonicalType() - (int) r.canonicalType();
if ( x < 0 ) return true; if ( x < 0 ) return true;
else if ( x > 0 ) return false; else if ( x > 0 ) return false;
return compareElementValues(l,r) < 0; return compareElementValues(l,r) < 0;
} }
}; };
class ElementMatcher { class ElementMatcher {
public: public:
ElementMatcher() { ElementMatcher() {
} }
ElementMatcher( BSONElement _e , int _op, bool _isNot ); ElementMatcher( BSONElement e , int op, bool isNot );
ElementMatcher( BSONElement _e , int _op , const BSONObj& array, bo ol _isNot ); ElementMatcher( BSONElement e , int op , const BSONObj& array, bool isNot );
~ElementMatcher() { } ~ElementMatcher() { }
BSONElement toMatch; BSONElement _toMatch;
int compareOp; int _compareOp;
bool isNot; bool _isNot;
shared_ptr< set<BSONElement,element_lt> > myset; shared_ptr< set<BSONElement,element_lt> > _myset;
shared_ptr< vector<RegexMatcher> > myregex; shared_ptr< vector<RegexMatcher> > _myregex;
// these are for specific operators // these are for specific operators
int mod; int _mod;
int modm; int _modm;
BSONType type; BSONType _type;
shared_ptr<Matcher> subMatcher; shared_ptr<Matcher> _subMatcher;
bool _subMatcherOnPrimitives ;
vector< shared_ptr<Matcher> > allMatchers; vector< shared_ptr<Matcher> > _allMatchers;
}; };
class Where; // used for $where javascript eval class Where; // used for $where javascript eval
class DiskLoc; class DiskLoc;
struct MatchDetails { struct MatchDetails {
MatchDetails(){ MatchDetails() {
reset(); reset();
} }
void reset(){ void reset() {
loadedObject = false; _loadedObject = false;
elemMatchKey = 0; _elemMatchKey = 0;
} }
string toString() const { string toString() const {
stringstream ss; stringstream ss;
ss << "loadedObject: " << loadedObject << " "; ss << "loadedObject: " << _loadedObject << " ";
ss << "elemMatchKey: " << ( elemMatchKey ? elemMatchKey : "NULL " ) << " "; ss << "elemMatchKey: " << ( _elemMatchKey ? _elemMatchKey : "NU LL" ) << " ";
return ss.str(); return ss.str();
} }
bool loadedObject; bool _loadedObject;
const char * elemMatchKey; // warning, this may go out of scope if matched object does const char * _elemMatchKey; // warning, this may go out of scope if matched object does
}; };
/* Match BSON objects against a query pattern. /* Match BSON objects against a query pattern.
e.g. e.g.
db.foo.find( { a : 3 } ); db.foo.find( { a : 3 } );
{ a : 3 } is the pattern object. See wiki documentation for full in fo. { a : 3 } is the pattern object. See wiki documentation for full in fo.
GT/LT: GT/LT:
skipping to change at line 137 skipping to change at line 136
int matchesNe( int matchesNe(
const char *fieldName, const char *fieldName,
const BSONElement &toMatch, const BSONObj &obj, const BSONElement &toMatch, const BSONObj &obj,
const ElementMatcher&bm, MatchDetails * details ); const ElementMatcher&bm, MatchDetails * details );
public: public:
static int opDirection(int op) { static int opDirection(int op) {
return op <= BSONObj::LTE ? -1 : 1; return op <= BSONObj::LTE ? -1 : 1;
} }
Matcher(const BSONObj &pattern, bool subMatcher = false); Matcher(const BSONObj &pattern, bool nested=false);
~Matcher(); ~Matcher();
bool matches(const BSONObj& j, MatchDetails * details = 0 ); bool matches(const BSONObj& j, MatchDetails * details = 0 );
// fast rough check to see if we must load the real doc - we also
// compare field counts against covereed index matcher; for $or cla uses
// we just compare field counts
bool keyMatch() const { return !all && !haveSize && !hasArray && !h aveNeg; }
bool atomic() const { return _atomic; } bool atomic() const { return _atomic; }
bool hasType( BSONObj::MatchType type ) const;
string toString() const { string toString() const {
return jsobj.toString(); return _jsobj.toString();
} }
void addOrConstraint( const shared_ptr< FieldRangeVector > &frv ) { void addOrDedupConstraint( const shared_ptr< FieldRangeVector > &fr v ) {
_orConstraints.push_back( frv ); _orDedupConstraints.push_back( frv );
} }
void popOrClause() { void popOrClause() {
_orMatchers.pop_front(); _orMatchers.pop_front();
} }
bool sameCriteriaCount( const Matcher &other ) const; /**
* @return true if this key matcher will return the same true/false
* value as the provided doc matcher.
*/
bool keyMatch( const Matcher &docMatcher ) const;
private: private:
// Only specify constrainIndexKey if matches() will be called with /**
// index keys having empty string field names. * Generate a matcher for the provided index key format using the
Matcher( const Matcher &other, const BSONObj &constrainIndexKey ); * provided full doc matcher.
*/
Matcher( const Matcher &docMatcher, const BSONObj &constrainIndexKe y );
void addBasic(const BSONElement &e, int c, bool isNot) { void addBasic(const BSONElement &e, int c, bool isNot) {
// 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( ElementMatcher( e , c, isNot ) ); _basics.push_back( ElementMatcher( e , c, isNot ) );
} }
void addRegex(const char *fieldName, const char *regex, const char *flags, bool isNot = false); void addRegex(const char *fieldName, const char *regex, const char *flags, bool isNot = false);
bool addOp( const BSONElement &e, const BSONElement &fe, bool isNot , const char *& regex, const char *&flags ); bool addOp( const BSONElement &e, const BSONElement &fe, bool isNot , const char *& regex, const char *&flags );
int valuesMatch(const BSONElement& l, const BSONElement& r, int op, const ElementMatcher& bm); int valuesMatch(const BSONElement& l, const BSONElement& r, int op, const ElementMatcher& bm);
bool parseOrNor( const BSONElement &e, bool subMatcher ); bool parseClause( const BSONElement &e );
void parseOr( const BSONElement &e, bool subMatcher, list< shared_p tr< Matcher > > &matchers ); void parseExtractedClause( const BSONElement &e, list< shared_ptr< Matcher > > &matchers );
Where *where; // set if query uses $where void parseMatchExpressionElement( const BSONElement &e, bool nested );
BSONObj jsobj; // the query pattern. e.g., { name : "joe" }
BSONObj constrainIndexKey_; Where *_where; // set if query uses $where
vector<ElementMatcher> basics; BSONObj _jsobj; // the query pattern. e.g., { nam e: "joe" }
bool haveSize; BSONObj _constrainIndexKey;
bool all; vector<ElementMatcher> _basics;
bool hasArray; bool _haveSize;
bool haveNeg; bool _all;
bool _hasArray;
bool _haveNeg;
/* $atomic - if true, a multi document operation (some removes, upd ates) /* $atomic - if true, a multi document operation (some removes, upd ates)
should be done atomically. in that case, we do not yi eld - should be done atomically. in that case, we do not yi eld -
i.e. we stay locked the whole time. i.e. we stay locked the whole time.
http://www.mongodb.org/display/DOCS/Removing[ http://www.mongodb.org/display/DOCS/Removing[
*/ */
bool _atomic; bool _atomic;
RegexMatcher regexs[4]; RegexMatcher _regexs[4];
int nRegex; int _nRegex;
// so we delete the mem when we're done: // so we delete the mem when we're done:
vector< shared_ptr< BSONObjBuilder > > _builders; vector< shared_ptr< BSONObjBuilder > > _builders;
list< shared_ptr< Matcher > > _andMatchers;
list< shared_ptr< Matcher > > _orMatchers; list< shared_ptr< Matcher > > _orMatchers;
list< shared_ptr< Matcher > > _norMatchers; list< shared_ptr< Matcher > > _norMatchers;
vector< shared_ptr< FieldRangeVector > > _orConstraints; vector< shared_ptr< FieldRangeVector > > _orDedupConstraints;
friend class CoveredIndexMatcher; friend class CoveredIndexMatcher;
}; };
// If match succeeds on index key, then attempt to match full document. // If match succeeds on index key, then attempt to match full document.
class CoveredIndexMatcher : boost::noncopyable { class CoveredIndexMatcher : boost::noncopyable {
public: public:
CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey Pattern , bool alwaysUseRecord=false ); CoveredIndexMatcher(const BSONObj &pattern, const BSONObj &indexKey Pattern , bool alwaysUseRecord=false );
bool matches(const BSONObj &o){ return _docMatcher->matches( o ); } bool matches(const BSONObj &o) { return _docMatcher->matches( o ); }
bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai ls * details = 0 ); bool matchesWithSingleKeyIndex(const BSONObj &key, const DiskLoc &r ecLoc , MatchDetails * details = 0 ) {
return matches( key, recLoc, details, true );
}
/**
* This is the preferred method for matching against a cursor, as i t
* can handle both multi and single key cursors.
*/
bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) ; bool matchesCurrent( Cursor * cursor , MatchDetails * details = 0 ) ;
bool needRecord(){ return _needRecord; } bool needRecord() { return _needRecord; }
Matcher& docMatcher() { return *_docMatcher; } Matcher& docMatcher() { return *_docMatcher; }
// once this is called, shouldn't use this matcher for matching any more // once this is called, shouldn't use this matcher for matching any more
void advanceOrClause( const shared_ptr< FieldRangeVector > &frv ) { void advanceOrClause( const shared_ptr< FieldRangeVector > &frv ) {
_docMatcher->addOrConstraint( frv ); _docMatcher->addOrDedupConstraint( frv );
// TODO this is not an optimal optimization, since we could ski p an entire // TODO this is not yet optimal. Since we could skip an entire
// or clause (if a match is impossible) between calls to advanc eOrClause() // or clause (if a match is impossible) between calls to advanc eOrClause()
// we may not pop all the clauses we can.
_docMatcher->popOrClause(); _docMatcher->popOrClause();
} }
CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt ern, bool alwaysUseRecord=false ) { CoveredIndexMatcher *nextClauseMatcher( const BSONObj &indexKeyPatt ern, bool alwaysUseRecord=false ) {
return new CoveredIndexMatcher( _docMatcher, indexKeyPattern, a lwaysUseRecord ); return new CoveredIndexMatcher( _docMatcher, indexKeyPattern, a lwaysUseRecord );
} }
string toString() const;
private: private:
bool matches(const BSONObj &key, const DiskLoc &recLoc , MatchDetai ls * details = 0 , bool keyUsable = true );
CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const BSONObj &indexKeyPattern , bool alwaysUseRecord=false ); CoveredIndexMatcher(const shared_ptr< Matcher > &docMatcher, const BSONObj &indexKeyPattern , bool alwaysUseRecord=false );
void init( bool alwaysUseRecord ); void init( bool alwaysUseRecord );
shared_ptr< Matcher > _docMatcher; shared_ptr< Matcher > _docMatcher;
Matcher _keyMatcher; Matcher _keyMatcher;
bool _needRecord;
bool _useRecordOnly; bool _needRecord; // if the key itself isn't good enough to determi ne a positive match
}; };
} // namespace mongo } // namespace mongo
 End of changes. 32 change blocks. 
67 lines changed or deleted 79 lines changed or added


 md5.h   md5.h 
skipping to change at line 30 skipping to change at line 30
L. Peter Deutsch L. Peter Deutsch
ghost@aladdin.com ghost@aladdin.com
*/ */
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
/* /*
Independent implementation of MD5 (RFC 1321). Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321, whose This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at text is available at
http://www.ietf.org/rfc/rfc1321.txt http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include (section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being any code or documentation that is identified in the RFC as being
copyrighted. copyrighted.
The original and principal author of md5.h is L. Peter Deutsch The original and principal author of md5.h is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history <ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order): that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed 2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321; references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically. now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>. Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version. 1999-05-03 lpd Original version.
*/ */
#ifndef md5_INCLUDED #ifndef md5_INCLUDED
# define md5_INCLUDED # define md5_INCLUDED
/* /*
* This package supports both compile-time and run-time determination of CP U * This package supports both compile-time and run-time determination of CP U
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
skipping to change at line 68 skipping to change at line 68
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
* run on either big- or little-endian CPUs, but will run slightly less * run on either big- or little-endian CPUs, but will run slightly less
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
*/ */
typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned char md5_byte_t; /* 8-bit byte */
typedef unsigned int md5_word_t; /* 32-bit word */ typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */ /* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s { typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */ md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */ md5_byte_t buf[64]; /* accumulate block */
} md5_state_t; } md5_state_t;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
/* Initialize the algorithm. */ /* Initialize the algorithm. */
void md5_init(md5_state_t *pms); void md5_init(md5_state_t *pms);
 End of changes. 4 change blocks. 
8 lines changed or deleted 8 lines changed or added


 message.h   message.h 
// Message.h // message.h
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../util/sock.h" #include "sock.h"
#include "../bson/util/atomic_int.h" #include "../../bson/util/atomic_int.h"
#include "hostandport.h" #include "hostandport.h"
namespace mongo { namespace mongo {
extern bool noUnixSocket;
class Message; class Message;
class MessagingPort; class MessagingPort;
class PiggyBackData; class PiggyBackData;
typedef AtomicUInt MSGID;
class Listener : boost::noncopyable {
public:
Listener(const string &ip, int p, bool logConnect=true ) : _port(p) , _ip(ip), _logConnect(logConnect), _elapsedTime(0){ }
virtual ~Listener() {
if ( _timeTracker == this )
_timeTracker = 0;
}
void initAndListen(); // never returns unless error (start a thread )
/* spawn a thread, etc., then return */
virtual void accepted(int sock, const SockAddr& from);
virtual void accepted(MessagingPort *mp){
assert(!"You must overwrite one of the accepted methods");
}
const int _port;
/**
* @return a rough estimate of elepased time since the server start ed
*/
long long getMyElapsedTimeMillis() const { return _elapsedTime; }
void setAsTimeTracker(){
_timeTracker = this;
}
static const Listener* getTimeTracker(){
return _timeTracker;
}
static long long getElapsedTimeMillis() {
if ( _timeTracker )
return _timeTracker->getMyElapsedTimeMillis();
return 0;
}
private:
string _ip;
bool _logConnect;
long long _elapsedTime;
static const Listener* _timeTracker;
};
class AbstractMessagingPort : boost::noncopyable {
public:
virtual ~AbstractMessagingPort() { }
virtual void reply(Message& received, Message& response, MSGID resp onseTo) = 0; // like the reply below, but doesn't rely on received.data sti ll being available
virtual void reply(Message& received, Message& response) = 0;
virtual HostAndPort remote() const = 0;
virtual unsigned remotePort() const = 0;
virtual int getClientId(){
int x = remotePort();
x = x << 16;
return x;
}
};
class MessagingPort : public AbstractMessagingPort {
public:
MessagingPort(int sock, const SockAddr& farEnd);
// in some cases the timeout will actually be 2x this value - eg we do a partial send,
// then the timeout fires, then we try to send again, then the time out fires again with
// no data sent, then we detect that the other side is down
MessagingPort(int timeout = 0, int logLevel = 0 );
virtual ~MessagingPort();
void shutdown();
bool connect(SockAddr& farEnd);
/* it's assumed if you reuse a message object, that it doesn't cros s MessagingPort's.
also, the Message data will go out of scope on the subsequent re cv call.
*/
bool recv(Message& m);
void reply(Message& received, Message& response, MSGID responseTo);
void reply(Message& received, Message& response);
bool call(Message& toSend, Message& response);
void say(Message& toSend, int responseTo = -1);
void piggyBack( Message& toSend , int responseTo = -1 );
virtual unsigned remotePort() const; typedef AtomicUInt MSGID;
virtual HostAndPort remote() const;
// send len or throw SocketException
void send( const char * data , int len, const char *context );
void send( const vector< pair< char *, int > > &data, const char *c ontext );
// recv len or throw SocketException
void recv( char * data , int len );
int unsafe_recv( char *buf, int max );
private:
int sock;
PiggyBackData * piggyBackData;
public:
SockAddr farEnd;
int _timeout;
int _logLevel; // passed to log() when logging errors
static void closeAllSockets(unsigned tagMask = 0xffffffff);
/* ports can be tagged with various classes. see closeAllSockets(t ag). defaults to 0. */
unsigned tag;
friend class PiggyBackData;
};
enum Operations { enum Operations {
opReply = 1, /* reply. responseTo is set. */ opReply = 1, /* reply. responseTo is set. */
dbMsg = 1000, /* generic msg command followed by a string */ dbMsg = 1000, /* generic msg command followed by a string */
dbUpdate = 2001, /* update object */ dbUpdate = 2001, /* update object */
dbInsert = 2002, dbInsert = 2002,
//dbGetByOID = 2003, //dbGetByOID = 2003,
dbQuery = 2004, dbQuery = 2004,
dbGetMore = 2005, dbGetMore = 2005,
dbDelete = 2006, dbDelete = 2006,
dbKillCursors = 2007 dbKillCursors = 2007
}; };
bool doesOpGetAResponse( int op ); bool doesOpGetAResponse( int op );
inline const char * opToString( int op ){ inline const char * opToString( int op ) {
switch ( op ){ switch ( op ) {
case 0: return "none"; case 0: return "none";
case opReply: return "reply"; case opReply: return "reply";
case dbMsg: return "msg"; case dbMsg: return "msg";
case dbUpdate: return "update"; case dbUpdate: return "update";
case dbInsert: return "insert"; case dbInsert: return "insert";
case dbQuery: return "query"; case dbQuery: return "query";
case dbGetMore: return "getmore"; case dbGetMore: return "getmore";
case dbDelete: return "remove"; case dbDelete: return "remove";
case dbKillCursors: return "killcursors"; case dbKillCursors: return "killcursors";
default: default:
PRINT(op); PRINT(op);
assert(0); assert(0);
return ""; return "";
} }
} }
inline bool opIsWrite( int op ){ inline bool opIsWrite( int op ) {
switch ( op ){ switch ( op ) {
case 0: case 0:
case opReply: case opReply:
case dbMsg: case dbMsg:
case dbQuery: case dbQuery:
case dbGetMore: case dbGetMore:
case dbKillCursors: case dbKillCursors:
return false; return false;
case dbUpdate: case dbUpdate:
skipping to change at line 203 skipping to change at line 89
default: default:
PRINT(op); PRINT(op);
assert(0); assert(0);
return ""; return "";
} }
} }
#pragma pack(1) #pragma pack(1)
/* see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol /* see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
*/ */
struct MSGHEADER { struct MSGHEADER {
int messageLength; // total message size, including this int messageLength; // total message size, including this
int requestID; // identifier for this message int requestID; // identifier for this message
int responseTo; // requestID from the original request int responseTo; // requestID from the original request
// (used in reponses from db) // (used in reponses from db)
int opCode; int opCode;
}; };
struct OP_GETMORE : public MSGHEADER { struct OP_GETMORE : public MSGHEADER {
MSGHEADER header; // standard message header MSGHEADER header; // standard message header
int ZERO_or_flags; // 0 - reserved for future use int ZERO_or_flags; // 0 - reserved for future use
//cstring fullCollectionName; // "dbname.collectionname" //cstring fullCollectionName; // "dbname.collectionname"
//int32 numberToReturn; // number of documents to return //int32 numberToReturn; // number of documents to return
//int64 cursorID; // cursorID from the OP_REPLY //int64 cursorID; // cursorID from the OP_REPLY
}; };
#pragma pack() #pragma pack()
#pragma pack(1) #pragma pack(1)
/* todo merge this with MSGHEADER (or inherit from it). */ /* todo merge this with MSGHEADER (or inherit from it). */
struct MsgData { struct MsgData {
int len; /* len of the msg, including this field */ int len; /* len of the msg, including this field */
MSGID id; /* request/reply id's match... */ MSGID id; /* request/reply id's match... */
MSGID responseTo; /* id of the message we are responding to */ MSGID responseTo; /* id of the message we are responding to */
int _operation; short _operation;
char _flags;
char _version;
int operation() const { int operation() const {
return _operation; return _operation;
} }
void setOperation(int o) { void setOperation(int o) {
_flags = 0;
_version = 0;
_operation = o; _operation = o;
} }
char _data[4]; char _data[4];
int& dataAsInt() { int& dataAsInt() {
return *((int *) _data); return *((int *) _data);
} }
bool valid(){ bool valid() {
if ( len <= 0 || len > ( 1024 * 1024 * 10 ) ) if ( len <= 0 || len > ( 4 * BSONObjMaxInternalSize ) )
return false; return false;
if ( _operation < 0 || _operation > 100000 ) if ( _operation < 0 || _operation > 30000 )
return false; return false;
return true; return true;
} }
long long getCursor(){ long long getCursor() {
assert( responseTo > 0 ); assert( responseTo > 0 );
assert( _operation == opReply ); assert( _operation == opReply );
long long * l = (long long *)(_data + 4); long long * l = (long long *)(_data + 4);
return l[0]; return l[0];
} }
int dataLen(); // len without header int dataLen(); // len without header
}; };
const int MsgDataHeaderSize = sizeof(MsgData) - 4; const int MsgDataHeaderSize = sizeof(MsgData) - 4;
inline int MsgData::dataLen() { inline int MsgData::dataLen() {
skipping to change at line 293 skipping to change at line 183
} }
int operation() const { return header()->operation(); } int operation() const { return header()->operation(); }
MsgData *singleData() const { MsgData *singleData() const {
massert( 13273, "single data buffer expected", _buf ); massert( 13273, "single data buffer expected", _buf );
return header(); return header();
} }
bool empty() const { return !_buf && _data.empty(); } bool empty() const { return !_buf && _data.empty(); }
int size() const{ int size() const {
int res = 0; int res = 0;
if ( _buf ){ if ( _buf ) {
res = _buf->len; res = _buf->len;
} else { }
for (MsgVec::const_iterator it = _data.begin(); it != _data .end(); ++it){ else {
for (MsgVec::const_iterator it = _data.begin(); it != _data .end(); ++it) {
res += it->second; res += it->second;
} }
} }
return res; return res;
} }
int dataSize() const { return size() - sizeof(MSGHEADER); }
// concat multiple buffers - noop if <2 buffers already, otherwise can be expensive copy // concat multiple buffers - noop if <2 buffers already, otherwise can be expensive copy
// can get rid of this if we make response handling smarter // can get rid of this if we make response handling smarter
void concat() { void concat() {
if ( _buf || empty() ) { if ( _buf || empty() ) {
return; return;
} }
assert( _freeIt ); assert( _freeIt );
int totalSize = 0; int totalSize = 0;
for( vector< pair< char *, int > >::const_iterator i = _data.be gin(); i != _data.end(); ++i ) { for( vector< pair< char *, int > >::const_iterator i = _data.be gin(); i != _data.end(); ++i ) {
skipping to change at line 398 skipping to change at line 291
memcpy(d->_data, msgdata, len); memcpy(d->_data, msgdata, len);
d->len = fixEndian(dataLen); d->len = fixEndian(dataLen);
d->setOperation(operation); d->setOperation(operation);
_setData( d, true ); _setData( d, true );
} }
bool doIFreeIt() { bool doIFreeIt() {
return _freeIt; return _freeIt;
} }
void send( MessagingPort &p, const char *context ) { void send( MessagingPort &p, const char *context );
if ( empty() ) {
return; string toString() const;
}
if ( _buf != 0 ) {
p.send( (char*)_buf, _buf->len, context );
} else {
p.send( _data, context );
}
}
private: private:
void _setData( MsgData *d, bool freeIt ) { void _setData( MsgData *d, bool freeIt ) {
_freeIt = freeIt; _freeIt = freeIt;
_buf = d; _buf = d;
} }
// if just one buffer, keep it in _buf, otherwise keep a sequence o f buffers in _data // if just one buffer, keep it in _buf, otherwise keep a sequence o f buffers in _data
MsgData * _buf; MsgData * _buf;
// byte buffer(s) - the first must contain at least a full MsgData unless using _buf for storage instead // byte buffer(s) - the first must contain at least a full MsgData unless using _buf for storage instead
typedef vector< pair< char*, int > > MsgVec; typedef vector< pair< char*, int > > MsgVec;
MsgVec _data; MsgVec _data;
bool _freeIt; bool _freeIt;
}; };
class SocketException : public DBException {
public:
enum Type { CLOSED , RECV_ERROR , SEND_ERROR } type;
SocketException( Type t ) : DBException( "socket exception" , 9001 ) , type(t){}
bool shouldPrint() const {
return type != CLOSED;
}
};
MSGID nextMessageId(); MSGID nextMessageId();
void setClientId( int id );
int getClientId();
extern TicketHolder connTicketHolder;
class ElapsedTracker {
public:
ElapsedTracker( int hitsBetweenMarks , int msBetweenMarks )
: _h( hitsBetweenMarks ) , _ms( msBetweenMarks ) , _pings(0){
_last = Listener::getElapsedTimeMillis();
}
/**
* call this for every iteration
* returns true if one of the triggers has gone off
*/
bool ping(){
if ( ( ++_pings % _h ) == 0 ){
_last = Listener::getElapsedTimeMillis();
return true;
}
long long now = Listener::getElapsedTimeMillis();
if ( now - _last > _ms ){
_last = now;
return true;
}
return false;
}
private:
int _h;
int _ms;
unsigned long long _pings;
long long _last;
};
} // namespace mongo } // namespace mongo
 End of changes. 20 change blocks. 
209 lines changed or deleted 43 lines changed or added


 message_server.h   message_server.h 
skipping to change at line 25 skipping to change at line 25
* limitations under the License. * limitations under the License.
*/ */
/* /*
abstract database server abstract database server
async io core, worker thread system async io core, worker thread system
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../../pch.h"
namespace mongo { namespace mongo {
class MessageHandler { class MessageHandler {
public: public:
virtual ~MessageHandler(){} virtual ~MessageHandler() {}
virtual void process( Message& m , AbstractMessagingPort* p ) = 0;
/**
* called once when a socket is connected
*/
virtual void connected( AbstractMessagingPort* p ) = 0;
/**
* called every time a message comes in
* handler is responsible for responding to client
*/
virtual void process( Message& m , AbstractMessagingPort* p , LastE rror * err ) = 0;
/**
* called once when a socket is disconnected
*/
virtual void disconnected( AbstractMessagingPort* p ) = 0; virtual void disconnected( AbstractMessagingPort* p ) = 0;
}; };
class MessageServer { class MessageServer {
public: public:
struct Options { struct Options {
int port; // port to bind to int port; // port to bind to
string ipList; // addresses to bind to string ipList; // addresses to bind to
Options() : port(0), ipList(""){} Options() : port(0), ipList("") {}
}; };
virtual ~MessageServer(){} virtual ~MessageServer() {}
virtual void run() = 0; virtual void run() = 0;
virtual void setAsTimeTracker() = 0; virtual void setAsTimeTracker() = 0;
}; };
// TODO use a factory here to decide between port and asio variations // TODO use a factory here to decide between port and asio variations
MessageServer * createServer( const MessageServer::Options& opts , Mess ageHandler * handler ); MessageServer * createServer( const MessageServer::Options& opts , Mess ageHandler * handler );
} }
 End of changes. 4 change blocks. 
5 lines changed or deleted 19 lines changed or added


 minilex.h   minilex.h 
skipping to change at line 78 skipping to change at line 78
/* /*
struct MiniLexNotUsed { struct MiniLexNotUsed {
strhashmap reserved; strhashmap reserved;
bool ic[256]; // ic=Identifier Character bool ic[256]; // ic=Identifier Character
bool starter[256]; bool starter[256];
// dm: very dumb about comments and escaped quotes -- but we are fa ster then at least, // dm: very dumb about comments and escaped quotes -- but we are fa ster then at least,
// albeit returning too much (which is ok for jsbobj current usage) . // albeit returning too much (which is ok for jsbobj current usage) .
void grabVariables(char *code , strhashmap& vars) { // 'code' modif ied and must stay in scope*/ void grabVariables(char *code , strhashmap& vars) { // 'code' modif ied and must stay in scope*/
char *p = code; char *p = code;
char last = 0; char last = 0;
while ( *p ) { while ( *p ) {
if ( starter[*p] ) { if ( starter[*p] ) {
char *q = p+1; char *q = p+1;
while ( *q && ic[*q] ) q++; while ( *q && ic[*q] ) q++;
const char *identifier = p; const char *identifier = p;
bool done = *q == 0; bool done = *q == 0;
*q = 0; *q = 0;
if ( !reserved.count(identifier) ) { if ( !reserved.count(identifier) ) {
// we try to be smart about 'obj' but have to be ca reful as obj.obj // we try to be smart about 'obj' but have to be careful as obj.obj
// can happen; this is so that nFields is right for simplistic where cases // can happen; this is so that nFields is right for simplis tic where cases
// so we can stop scanning in jsobj when we find th e field of interest. // so we can stop scanning in jsobj when we find the field of interest.
if ( strcmp(identifier,"obj")==0 && p>code && p[-1] != '.' ) if ( strcmp(identifier,"obj")==0 && p>code && p[-1] != '.' )
; ;
else else
vars[identifier] = 1; vars[identifier] = 1;
}
if ( done )
break;
p = q + 1;
continue;
}
if ( *p == '\'' ) {
p++;
while ( *p && *p != '\'' ) p++;
}
else if ( *p == '"' ) {
p++;
while ( *p && *p != '"' ) p++;
}
p++;
} }
if ( done )
break;
p = q + 1;
continue;
} }
MiniLex() { if ( *p == '\'' ) {
strhashmap atest; p++;
atest["foo"] = 3; while ( *p && *p != '\'' ) p++;
assert( atest.count("bar") == 0 ); }
assert( atest.count("foo") == 1 ); else if ( *p == '"' ) {
assert( atest["foo"] == 3 ); p++;
while ( *p && *p != '"' ) p++;
}
p++;
}
}
for ( int i = 0; i < 256; i++ ) { MiniLex() {
ic[i] = starter[i] = false; strhashmap atest;
} atest["foo"] = 3;
for ( int i = 'a'; i <= 'z'; i++ ) assert( atest.count("bar") == 0 );
ic[i] = starter[i] = true; assert( atest.count("foo") == 1 );
for ( int i = 'A'; i <= 'Z'; i++ ) assert( atest["foo"] == 3 );
ic[i] = starter[i] = true;
for ( int i = '0'; i <= '9'; i++ )
ic[i] = true;
for ( int i = 128; i < 256; i++ )
ic[i] = starter[i] = true;
ic['$'] = starter['$'] = true;
ic['_'] = starter['_'] = true;
reserved["break"] = true; for ( int i = 0; i < 256; i++ ) {
reserved["case"] = true; ic[i] = starter[i] = false;
reserved["catch"] = true; }
reserved["continue"] = true; for ( int i = 'a'; i <= 'z'; i++ )
reserved["default"] = true; ic[i] = starter[i] = true;
reserved["delete"] = true; for ( int i = 'A'; i <= 'Z'; i++ )
reserved["do"] = true; ic[i] = starter[i] = true;
reserved["else"] = true; for ( int i = '0'; i <= '9'; i++ )
reserved["finally"] = true; ic[i] = true;
reserved["for"] = true; for ( int i = 128; i < 256; i++ )
reserved["function"] = true; ic[i] = starter[i] = true;
reserved["if"] = true; ic['$'] = starter['$'] = true;
reserved["in"] = true; ic['_'] = starter['_'] = true;
reserved["instanceof"] = true;
reserved["new"] = true; reserved["break"] = true;
reserved["return"] = true; reserved["case"] = true;
reserved["switch"] = true; reserved["catch"] = true;
reserved["this"] = true; reserved["continue"] = true;
reserved["throw"] = true; reserved["default"] = true;
reserved["try"] = true; reserved["delete"] = true;
reserved["typeof"] = true; reserved["do"] = true;
reserved["var"] = true; reserved["else"] = true;
reserved["void"] = true; reserved["finally"] = true;
reserved["while"] = true; reserved["for"] = true;
reserved["with "] = true; reserved["function"] = true;
} reserved["if"] = true;
}; reserved["in"] = true;
*/ reserved["instanceof"] = true;
reserved["new"] = true;
reserved["return"] = true;
reserved["switch"] = true;
reserved["this"] = true;
reserved["throw"] = true;
reserved["try"] = true;
reserved["typeof"] = true;
reserved["var"] = true;
reserved["void"] = true;
reserved["while"] = true;
reserved["with "] = true;
}
};
*/
} // namespace mongo } // namespace mongo
 End of changes. 5 change blocks. 
80 lines changed or deleted 80 lines changed or added


 miniwebserver.h   miniwebserver.h 
skipping to change at line 20 skipping to change at line 20
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../../pch.h"
#include "message.h" #include "message.h"
#include "../db/jsobj.h" #include "message_port.h"
#include "listen.h"
#include "../../db/jsobj.h"
namespace mongo { namespace mongo {
class MiniWebServer : public Listener { class MiniWebServer : public Listener {
public: public:
MiniWebServer(const string &ip, int _port); MiniWebServer(const string& name, const string &ip, int _port);
virtual ~MiniWebServer() {} virtual ~MiniWebServer() {}
virtual void doRequest( virtual void doRequest(
const char *rq, // the full request const char *rq, // the full request
string url, string url,
// set these and return them: // set these and return them:
string& responseMsg, string& responseMsg,
int& responseCode, int& responseCode,
vector<string>& headers, // if completely empty, content-type: text/html will be added vector<string>& headers, // if completely empty, content-type: text/html will be added
const SockAddr &from const SockAddr &from
skipping to change at line 54 skipping to change at line 56
static string parseURL( const char * buf ); static string parseURL( const char * buf );
static string parseMethod( const char * headers ); static string parseMethod( const char * headers );
static string getHeader( const char * headers , string name ); static string getHeader( const char * headers , string name );
static const char *body( const char *buf ); static const char *body( const char *buf );
static string urlDecode(const char* s); static string urlDecode(const char* s);
static string urlDecode(string s) {return urlDecode(s.c_str());} static string urlDecode(string s) {return urlDecode(s.c_str());}
private: private:
void accepted(int s, const SockAddr &from); void accepted(Socket socket);
static bool fullReceive( const char *buf ); static bool fullReceive( const char *buf );
}; };
} // namespace mongo } // namespace mongo
 End of changes. 4 change blocks. 
4 lines changed or deleted 6 lines changed or added


 misc.h   misc.h 
/* @file util.h /* @file misc.h
*/ */
/* /*
* Copyright 2009 10gen Inc. * Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
skipping to change at line 37 skipping to change at line 37
inline void time_t_to_String(time_t t, char *buf) { inline void time_t_to_String(time_t t, char *buf) {
#if defined(_WIN32) #if defined(_WIN32)
ctime_s(buf, 32, &t); ctime_s(buf, 32, &t);
#else #else
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
} }
inline string time_t_to_String(time_t t = time(0) ){ inline string time_t_to_String(time_t t = time(0) ) {
char buf[64]; char buf[64];
#if defined(_WIN32) #if defined(_WIN32)
ctime_s(buf, sizeof(buf), &t); ctime_s(buf, sizeof(buf), &t);
#else #else
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
return buf; return buf;
} }
skipping to change at line 94 skipping to change at line 94
}; };
// Like strlen, but only scans up to n bytes. // Like strlen, but only scans up to n bytes.
// Returns -1 if no '0' found. // Returns -1 if no '0' found.
inline int strnlen( const char *s, int n ) { inline int strnlen( const char *s, int n ) {
for( int i = 0; i < n; ++i ) for( int i = 0; i < n; ++i )
if ( !s[ i ] ) if ( !s[ i ] )
return i; return i;
return -1; return -1;
} }
inline bool isNumber( char c ) {
return c >= '0' && c <= '9';
}
inline unsigned stringToNum(const char *str) {
unsigned x = 0;
const char *p = str;
while( 1 ) {
if( !isNumber(*p) ) {
if( *p == 0 && p != str )
break;
throw 0;
}
x = x * 10 + *p++ - '0';
}
return x;
}
} }
 End of changes. 3 change blocks. 
2 lines changed or deleted 21 lines changed or added


 mmap.h   mmap.h 
skipping to change at line 19 skipping to change at line 19
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <boost/thread/xtime.hpp>
#include "concurrency/rwlock.h"
namespace mongo { namespace mongo {
class MAdvise {
void *_p;
unsigned _len;
public:
enum Advice { Sequential=1 };
MAdvise(void *p, unsigned len, Advice a);
~MAdvise(); // destructor resets the range to MADV_NORMAL
};
/* the administrative-ish stuff here */ /* the administrative-ish stuff here */
class MongoFile : boost::noncopyable { class MongoFile : boost::noncopyable {
public: public:
/** Flushable has to fail nicely if the underlying object gets kill ed */ /** Flushable has to fail nicely if the underlying object gets kill ed */
class Flushable { class Flushable {
public: public:
virtual ~Flushable(){} virtual ~Flushable() {}
virtual void flush() = 0; virtual void flush() = 0;
}; };
virtual ~MongoFile() {}
enum Options {
SEQUENTIAL = 1, // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on win dows
READONLY = 2 // not contractually guaranteed, but if specifi ed the impl has option to fault writes
};
/** @param fun is called for each MongoFile.
calledl from within a mutex that MongoFile uses. so be careful not to deadlock.
*/
template < class F >
static void forEach( F fun );
/** note: you need to be in mmmutex when using this. forEach (above ) handles that for you automatically.
*/
static set<MongoFile*>& getAllFiles() { return mmfiles; }
// callbacks if you need them
static void (*notifyPreFlush)();
static void (*notifyPostFlush)();
static int flushAll( bool sync ); // returns n flushed
static long long totalMappedLength();
static void closeAllFiles( stringstream &message );
#if defined(_DEBUG)
static void markAllWritable();
static void unmarkAllWritable();
#else
static void markAllWritable() { }
static void unmarkAllWritable() { }
#endif
static bool exists(boost::filesystem::path p) { return boost::files ystem::exists(p); }
virtual bool isMongoMMF() { return false; }
string filename() const { return _filename; }
void setFilename(string fn);
private:
string _filename;
static int _flushAll( bool sync ); // returns n flushed
protected: protected:
virtual void close() = 0; virtual void close() = 0;
virtual void flush(bool sync) = 0; virtual void flush(bool sync) = 0;
/** /**
* returns a thread safe object that you can call flush on * returns a thread safe object that you can call flush on
* Flushable has to fail nicely if the underlying object gets kille d * Flushable has to fail nicely if the underlying object gets kille d
*/ */
virtual Flushable * prepareFlush() = 0; virtual Flushable * prepareFlush() = 0;
void created(); /* subclass must call after create */ void created(); /* subclass must call after create */
void destroyed(); /* subclass must call in destructor */
/* subclass must call in destructor (or at close).
removes this from pathToFile and other maps
safe to call more than once, albeit might be wasted work
ideal to call close to the close, if the close is well before ob ject destruction
*/
void destroyed();
virtual unsigned long long length() const = 0;
// only supporting on posix mmap // only supporting on posix mmap
virtual void _lock() {} virtual void _lock() {}
virtual void _unlock() {} virtual void _unlock() {}
static set<MongoFile*> mmfiles;
public: public:
virtual ~MongoFile() {} static map<string,MongoFile*> pathToFile;
virtual long length() = 0;
enum Options {
SEQUENTIAL = 1 // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on wind ows
};
static int flushAll( bool sync ); // returns n flushed // lock order: lock dbMutex before this if you lock both
static long long totalMappedLength(); static RWLockRecursive mmmutex;
static void closeAllFiles( stringstream &message ); };
// Locking allows writes. Reads are always allowed /** look up a MMF by filename. scoped mutex locking convention.
static void lockAll(); example:
static void unlockAll(); MMFFinderByName finder;
MongoMMF *a = finder.find("file_name_a");
MongoMMF *b = finder.find("file_name_b");
*/
class MongoFileFinder : boost::noncopyable {
public:
MongoFileFinder() : _lk(MongoFile::mmmutex) { }
/* can be "overriden" if necessary */ /** @return The MongoFile object associated with the specified file name. If no file is open
static bool exists(boost::filesystem::path p) { with the specified name, returns null.
return boost::filesystem::exists(p); */
MongoFile* findByPath(string path) {
map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find (path);
return i == MongoFile::pathToFile.end() ? NULL : i->second;
} }
};
#ifndef _DEBUG private:
// no-ops in production RWLockRecursive::Shared _lk;
inline void MongoFile::lockAll() {} };
inline void MongoFile::unlockAll() {}
#endif
struct MongoFileAllowWrites { struct MongoFileAllowWrites {
MongoFileAllowWrites(){ MongoFileAllowWrites() {
MongoFile::lockAll(); MongoFile::markAllWritable();
} }
~MongoFileAllowWrites(){ ~MongoFileAllowWrites() {
MongoFile::unlockAll(); MongoFile::unmarkAllWritable();
} }
}; };
/** template for what a new storage engine's class definition must impl ement
PRELIMINARY - subject to change.
*/
class StorageContainerTemplate : public MongoFile {
protected:
virtual void close();
virtual void flush(bool sync);
public:
virtual long length();
/** pointer to a range of space in this storage unit */
class Pointer {
public:
/** retried address of buffer at offset 'offset' withing the st orage unit. returned range is a contiguous
buffer reflecting what is in storage. caller will not read or write past 'len'.
note calls may be received that are at different points in a range and different lengths. however
for now assume that on writes, if a call is made, previousl y returned addresses are no longer valid. i.e.
p = at(10000, 500);
q = at(10000, 600);
after the second call it is ok if p is invalid.
*/
void* at(int offset, int len);
/** indicate that we wrote to the range (from a previous at() c all) and that it needs
flushing to disk.
*/
void written(int offset, int len);
bool isNull() const;
};
/** commit written() calls from above. */
void commit();
Pointer open(const char *filename);
Pointer open(const char *_filename, long &length, int options=0);
};
class MemoryMappedFile : public MongoFile { class MemoryMappedFile : public MongoFile {
protected:
virtual void* viewForFlushing() {
if( views.size() == 0 )
return 0;
assert( views.size() == 1 );
return views[0];
}
public: public:
class Pointer {
char *_base;
public:
Pointer() : _base(0) { }
Pointer(void *p) : _base((char*) p) { }
void* at(int offset, int maxLen) { return _base + offset; }
void grow(int offset, int len) { /* no action requir ed with mem mapped file */ }
bool isNull() const { return _base == 0; }
};
MemoryMappedFile(); MemoryMappedFile();
~MemoryMappedFile() {
destroyed(); virtual ~MemoryMappedFile() {
RWLockRecursive::Exclusive lk(mmmutex);
close(); close();
} }
void close();
// Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?) virtual void close();
void* map( const char *filename );
/*To replace map(): // Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?)
void* map(const char *filename);
Pointer open( const char *filename ) { /** @param options see MongoFile::Options */
void *p = map(filename); void* mapWithOptions(const char *filename, int options);
uassert(13077, "couldn't open/map file", p);
return Pointer(p);
}*/
/* Creates with length if DNE, otherwise uses existing file length, /* Creates with length if DNE, otherwise uses existing file length,
passed length. passed length.
@param options MongoFile::Options bits
*/ */
void* map(const char *filename, long &length, int options = 0 ); void* map(const char *filename, unsigned long long &length, int opt ions = 0 );
/* Create. Must not exist.
@param zero fill file with zeros when true
*/
void* create(string filename, unsigned long long len, bool zero);
void flush(bool sync); void flush(bool sync);
virtual Flushable * prepareFlush(); virtual Flushable * prepareFlush();
/*void* viewOfs() { long shortLength() const { return (long) len; }
return view; unsigned long long length() const { return len; }
}*/
long length() { /** create a new view with the specified properties.
return len; automatically cleaned up upon close/destruction of the MemoryMa ppedFile object.
} */
void* createReadOnlyMap();
void* createPrivateMap();
string filename() const { return _filename; } /** make the private map range writable (necessary for our windows implementation) */
static void makeWritable(void *, unsigned len)
#if defined(_WIN32)
;
#else
{ }
#endif
private: private:
static void updateLength( const char *filename, long &length ); static void updateLength( const char *filename, unsigned long long &length );
HANDLE fd; HANDLE fd;
HANDLE maphandle; HANDLE maphandle;
void *view; vector<void *> views;
long len; unsigned long long len;
string _filename;
#ifdef _WIN32 #ifdef _WIN32
boost::shared_ptr<mutex> _flushMutex; boost::shared_ptr<mutex> _flushMutex;
void clearWritableBits(void *privateView);
public:
static const unsigned ChunkSize = 64 * 1024 * 1024;
static const unsigned NChunks = 1024 * 1024;
#else
void clearWritableBits(void *privateView) { }
#endif #endif
protected: protected:
// only posix mmap implementations will support this // only posix mmap implementations will support this
virtual void _lock(); virtual void _lock();
virtual void _unlock(); virtual void _unlock();
/** close the current private view and open a new replacement */
void* remapPrivateView(void *oldPrivateAddr);
}; };
void printMemInfo( const char * where ); typedef MemoryMappedFile MMF;
#include "ramstore.h" /** p is called from within a mutex that MongoFile uses. so be careful not to deadlock. */
template < class F >
inline void MongoFile::forEach( F p ) {
RWLockRecursive::Shared lklk(mmmutex);
for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.e nd(); i++ )
p(*i);
}
#if defined(_WIN32)
class ourbitset {
volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as w e are doing double check locking
public:
ourbitset() {
memset((void*) bits, 0, sizeof(bits));
}
bool get(unsigned i) const {
unsigned x = i / 32;
assert( x < MemoryMappedFile::NChunks );
return (bits[x] & (1 << (i%32))) != 0;
}
void set(unsigned i) {
unsigned x = i / 32;
wassert( x < (MemoryMappedFile::NChunks*2/3) ); // warn if gett ing close to limit
assert( x < MemoryMappedFile::NChunks );
bits[x] |= (1 << (i%32));
}
void clear(unsigned i) {
unsigned x = i / 32;
assert( x < MemoryMappedFile::NChunks );
bits[x] &= ~(1 << (i%32));
}
};
extern ourbitset writable;
void makeChunkWritable(size_t chunkno);
inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) {
size_t p = (size_t) _p;
unsigned a = p/ChunkSize;
unsigned b = (p+len)/ChunkSize;
for( unsigned i = a; i <= b; i++ ) {
if( !writable.get(i) ) {
makeChunkWritable(i);
}
}
}
//#define _RAMSTORE
#if defined(_RAMSTORE)
typedef RamStoreFile MMF;
#else
typedef MemoryMappedFile MMF;
#endif #endif
} // namespace mongo } // namespace mongo
 End of changes. 35 change blocks. 
108 lines changed or deleted 183 lines changed or added


 module.h   module.h 
skipping to change at line 38 skipping to change at line 38
* Module is the base class for adding modules to MongoDB * Module is the base class for adding modules to MongoDB
* modules allow adding hooks and features to mongo * modules allow adding hooks and features to mongo
* the idea is to add hooks into the main code for module support where needed * the idea is to add hooks into the main code for module support where needed
* some ideas are: monitoring, indexes, full text search * some ideas are: monitoring, indexes, full text search
*/ */
class Module { class Module {
public: public:
Module( const string& name ); Module( const string& name );
virtual ~Module(); virtual ~Module();
boost::program_options::options_description_easy_init add_options() { boost::program_options::options_description_easy_init add_options() {
return _options.add_options(); return _options.add_options();
} }
/** /**
* read config from command line * read config from command line
*/ */
virtual void config( program_options::variables_map& params ) = 0; virtual void config( program_options::variables_map& params ) = 0;
/** /**
* called after configuration when the server is ready start * called after configuration when the server is ready start
*/ */
virtual void init() = 0; virtual void init() = 0;
/** /**
* called when the database is about to shutdown * called when the database is about to shutdown
*/ */
virtual void shutdown() = 0; virtual void shutdown() = 0;
const string& getName(){ return _name; } const string& getName() { return _name; }
// --- static things // --- static things
static void addOptions( program_options::options_description& optio ns ); static void addOptions( program_options::options_description& optio ns );
static void configAll( program_options::variables_map& params ); static void configAll( program_options::variables_map& params );
static void initAll(); static void initAll();
private: private:
static std::list<Module*> * _all; static std::list<Module*> * _all;
string _name; string _name;
 End of changes. 2 change blocks. 
2 lines changed or deleted 2 lines changed or added


 msg.h   msg.h 
skipping to change at line 50 skipping to change at line 50
void call(const lam&); void call(const lam&);
void requeue() { rq = true; } void requeue() { rq = true; }
protected: protected:
/* REMINDER : for use in mongod, you will want to have this cal l Client::initThread(). */ /* REMINDER : for use in mongod, you will want to have this cal l Client::initThread(). */
virtual void starting() { } virtual void starting() { }
private: private:
virtual bool initClient() { return true; } virtual bool initClient() { return true; }
virtual string name() { return _name; } virtual string name() const { return _name; }
void doWork(); void doWork();
deque<lam> d; deque<lam> d;
boost::mutex m; boost::mutex m;
boost::condition c; boost::condition c;
string _name; string _name;
bool rq; bool rq;
}; };
} }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 multicmd.h   multicmd.h 
skipping to change at line 41 skipping to change at line 41
BSONObj result; BSONObj result;
}; };
/* -- implementation ------------- */ /* -- implementation ------------- */
class _MultiCommandJob : public BackgroundJob { class _MultiCommandJob : public BackgroundJob {
public: public:
BSONObj& cmd; BSONObj& cmd;
Target& d; Target& d;
_MultiCommandJob(BSONObj& _cmd, Target& _d) : cmd(_cmd), d(_d) { } _MultiCommandJob(BSONObj& _cmd, Target& _d) : cmd(_cmd), d(_d) { }
private: private:
string name() { return "MultiCommandJob"; } string name() const { return "MultiCommandJob"; }
void run() { void run() {
try { try {
ScopedConn c(d.toHost); ScopedConn c(d.toHost);
d.ok = c->runCommand("admin", cmd, d.result); d.ok = c.runCommand("admin", cmd, d.result);
} }
catch(DBException&) { catch(DBException&) {
DEV log() << "dev caught dbexception on multiCommand " << d .toHost << rsLog; DEV log() << "dev caught dbexception on multiCommand " << d .toHost << rsLog;
} }
} }
}; };
inline void multiCommand(BSONObj cmd, list<Target>& L) { inline void multiCommand(BSONObj cmd, list<Target>& L) {
typedef shared_ptr<_MultiCommandJob> P; list< shared_ptr<BackgroundJob> > jobs;
list<P> jobs;
list<BackgroundJob *> _jobs;
for( list<Target>::iterator i = L.begin(); i != L.end(); i++ ) { for( list<Target>::iterator i = L.begin(); i != L.end(); i++ ) {
Target& d = *i; Target& d = *i;
_MultiCommandJob *j = new _MultiCommandJob(cmd, d); _MultiCommandJob *j = new _MultiCommandJob(cmd, d);
jobs.push_back(P(j)); jobs.push_back( shared_ptr<BackgroundJob>(j) );
_jobs.push_back(j); j->go();
} }
BackgroundJob::go(_jobs); for( list< shared_ptr<BackgroundJob> >::iterator i = jobs.begin(); i != jobs.end(); i++ ) {
BackgroundJob::wait(_jobs,5); (*i)->wait();
}
} }
} }
 End of changes. 7 change blocks. 
10 lines changed or deleted 9 lines changed or added


 mutex.h   mutex.h 
skipping to change at line 22 skipping to change at line 22
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <map> #include <map>
#include <set> #include <set>
#include "../heapcheck.h" #include "../heapcheck.h"
namespace mongo { namespace mongo {
extern bool __destroyingStatics; void printStackTrace( ostream &o );
class mutex; class mutex;
// only used on _DEBUG builds: inline boost::xtime incxtimemillis( long long s ) {
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += (int)( s / 1000 );
xt.nsec += (int)(( s % 1000 ) * 1000000);
if ( xt.nsec >= 1000000000 ) {
xt.nsec -= 1000000000;
xt.sec++;
}
return xt;
}
/** only used on _DEBUG builds.
MutexDebugger checks that we always acquire locks for multiple mute xes in a consistant (acyclic) order.
If we were inconsistent we could deadlock.
*/
class MutexDebugger { class MutexDebugger {
typedef const char * mid; // mid = mutex ID typedef const char * mid; // mid = mutex ID
typedef map<mid,int> Preceeding; typedef map<mid,int> Preceeding;
map< mid, int > maxNest; map< mid, int > maxNest;
boost::thread_specific_ptr< Preceeding > us; boost::thread_specific_ptr< Preceeding > us;
map< mid, set<mid> > followers; map< mid, set<mid> > followers;
boost::mutex &x; boost::mutex &x;
unsigned magic; unsigned magic;
void aBreakPoint() { } // for debugging
public: public:
// set these to create an assert that // set these to create an assert that
// b must never be locked before a // b must never be locked before a
// so // so
// a.lock(); b.lock(); is fine // a.lock(); b.lock(); is fine
// b.lock(); alone is fine too // b.lock(); alone is fine too
// only checked on _DEBUG builds. // only checked on _DEBUG builds.
string a,b; string a,b;
void aBreakPoint(){} /** outputs some diagnostic info on mutexes (on _DEBUG builds) */
void programEnding(); void programEnding();
MutexDebugger(); MutexDebugger();
void entering(mid m) { void entering(mid m) {
if( this == 0 ) return; if( this == 0 ) return;
assert( magic == 0x12345678 ); assert( magic == 0x12345678 );
Preceeding *_preceeding = us.get(); Preceeding *_preceeding = us.get();
if( _preceeding == 0 ) if( _preceeding == 0 )
us.reset( _preceeding = new Preceeding() ); us.reset( _preceeding = new Preceeding() );
Preceeding &preceeding = *_preceeding; Preceeding &preceeding = *_preceeding;
if( a == m ) { if( a == m ) {
skipping to change at line 63 skipping to change at line 81
assert( magic == 0x12345678 ); assert( magic == 0x12345678 );
Preceeding *_preceeding = us.get(); Preceeding *_preceeding = us.get();
if( _preceeding == 0 ) if( _preceeding == 0 )
us.reset( _preceeding = new Preceeding() ); us.reset( _preceeding = new Preceeding() );
Preceeding &preceeding = *_preceeding; Preceeding &preceeding = *_preceeding;
if( a == m ) { if( a == m ) {
aBreakPoint(); aBreakPoint();
if( preceeding[b.c_str()] ) { if( preceeding[b.c_str()] ) {
cout << "mutex problem " << b << " was locked before " << a << endl; cout << "****** MutexDebugger error! warning " << b << " was locked before " << a << endl;
assert(false); assert(false);
} }
} }
preceeding[m]++; preceeding[m]++;
if( preceeding[m] > 1 ) { if( preceeding[m] > 1 ) {
// recursive re-locking. // recursive re-locking.
if( preceeding[m] > maxNest[m] ) if( preceeding[m] > maxNest[m] )
maxNest[m] = preceeding[m]; maxNest[m] = preceeding[m];
return; return;
} }
bool failed = false; bool failed = false;
string err; string err;
{ {
boost::mutex::scoped_lock lk(x); boost::mutex::scoped_lock lk(x);
followers[m]; followers[m];
for( Preceeding::iterator i = preceeding.begin(); i != prec eeding.end(); i++ ) { for( Preceeding::iterator i = preceeding.begin(); i != prec eeding.end(); i++ ) {
if( m != i->first && i->second > 0 ) { if( m != i->first && i->second > 0 ) {
followers[i->first].insert(m); followers[i->first].insert(m);
if( followers[m].count(i->first) != 0 ){ if( followers[m].count(i->first) != 0 ) {
failed = true; failed = true;
stringstream ss; stringstream ss;
mid bad = i->first; mid bad = i->first;
ss << "mutex problem" << ss << "mutex problem" <<
"\n when locking " << m << "\n when locking " << m <<
"\n " << bad << " was already locked and s hould not be." "\n " << bad << " was already locked and sh ould not be."
"\n set a and b above to debug.\n"; "\n set a and b above to debug.\n";
stringstream q; stringstream q;
for( Preceeding::iterator i = preceeding.begin( ); i != preceeding.end(); i++ ) { for( Preceeding::iterator i = preceeding.begin( ); i != preceeding.end(); i++ ) {
if( i->first != m && i->first != bad && i-> second > 0 ) if( i->first != m && i->first != bad && i-> second > 0 )
q << " " << i->first << '\n'; q << " " << i->first << '\n';
} }
string also = q.str(); string also = q.str();
if( !also.empty() ) if( !also.empty() )
ss << "also locked before " << m << " in th is thread (no particular order):\n" << also; ss << "also locked before " << m << " in th is thread (no particular order):\n" << also;
err = ss.str(); err = ss.str();
break; break;
skipping to change at line 124 skipping to change at line 142
preceeding[m]--; preceeding[m]--;
if( preceeding[m] < 0 ) { if( preceeding[m] < 0 ) {
cout << "ERROR: lock count for " << m << " is " << preceedi ng[m] << endl; cout << "ERROR: lock count for " << m << " is " << preceedi ng[m] << endl;
assert( preceeding[m] >= 0 ); assert( preceeding[m] >= 0 );
} }
} }
}; };
extern MutexDebugger &mutexDebugger; extern MutexDebugger &mutexDebugger;
// If you create a local static instance of this class, that instance w ill be destroyed // If you create a local static instance of this class, that instance w ill be destroyed
// before all global static objects are destroyed, so __destroyingStati cs will be set // before all global static objects are destroyed, so _destroyingStatic s will be set
// to true before the global static variables are destroyed. // to true before the global static variables are destroyed.
class StaticObserver : boost::noncopyable { class StaticObserver : boost::noncopyable {
public: public:
~StaticObserver() { __destroyingStatics = true; } static bool _destroyingStatics;
~StaticObserver() { _destroyingStatics = true; }
}; };
// On pthread systems, it is an error to destroy a mutex while held. S tatic global /** On pthread systems, it is an error to destroy a mutex while held (b oost mutex
// mutexes may be held upon shutdown in our implementation, and this wa y we avoid * may use pthread). Static global mutexes may be held upon shutdow n in our
// destroying them. * implementation, and this way we avoid destroying them.
* NOT recursive.
*/
class mutex : boost::noncopyable { class mutex : boost::noncopyable {
public: public:
#if defined(_DEBUG) #if defined(_DEBUG)
const char *_name; const char * const _name;
#endif mutex(const char *name) : _name(name)
#if defined(_DEBUG)
mutex(const char *name)
: _name(name)
#else #else
mutex(const char *) mutex(const char *)
#endif #endif
{ {
_m = new boost::mutex(); _m = new boost::timed_mutex();
IGNORE_OBJECT( _m ); // Turn-off heap checking on _m IGNORE_OBJECT( _m ); // Turn-off heap checking on _m
} }
~mutex() { ~mutex() {
if( !__destroyingStatics ) { if( !StaticObserver::_destroyingStatics ) {
UNIGNORE_OBJECT( _m ); UNIGNORE_OBJECT( _m );
delete _m; delete _m;
} }
} }
class try_lock : boost::noncopyable {
public:
try_lock( mongo::mutex &m , int millis = 0 )
: _l( m.boost() , incxtimemillis( millis ) ) ,
#if BOOST_VERSION >= 103500
ok( _l.owns_lock() )
#else
ok( _l.locked() )
#endif
{ }
private:
boost::timed_mutex::scoped_timed_lock _l;
public:
const bool ok;
};
class scoped_lock : boost::noncopyable { class scoped_lock : boost::noncopyable {
public:
#if defined(_DEBUG) #if defined(_DEBUG)
mongo::mutex *mut; struct PostStaticCheck {
PostStaticCheck() {
if ( StaticObserver::_destroyingStatics ) {
cout << "trying to lock a mongo::mutex during stati c shutdown" << endl;
printStackTrace( cout );
}
}
};
PostStaticCheck _check;
mongo::mutex * const _mut;
#endif #endif
public: scoped_lock( mongo::mutex &m ) :
scoped_lock( mongo::mutex &m ) : _l( m.boost() ) {
#if defined(_DEBUG) #if defined(_DEBUG)
mut = &m; _mut(&m),
mutexDebugger.entering(mut->_name); #endif
_l( m.boost() ) {
#if defined(_DEBUG)
mutexDebugger.entering(_mut->_name);
#endif #endif
} }
~scoped_lock() { ~scoped_lock() {
#if defined(_DEBUG) #if defined(_DEBUG)
mutexDebugger.leaving(mut->_name); mutexDebugger.leaving(_mut->_name);
#endif #endif
} }
boost::mutex::scoped_lock &boost() { return _l; } boost::timed_mutex::scoped_lock &boost() { return _l; }
private: private:
boost::mutex::scoped_lock _l; boost::timed_mutex::scoped_lock _l;
}; };
private: private:
boost::mutex &boost() { return *_m; } boost::timed_mutex &boost() { return *_m; }
boost::mutex *_m; boost::timed_mutex *_m;
}; };
typedef mutex::scoped_lock scoped_lock; typedef mutex::scoped_lock scoped_lock;
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock;
/** The concept with SimpleMutex is that it is a basic lock/unlock with no
special functionality (such as try and try timeout). Thus it can be
implemented using OS-specific facilities in all environments (if desired).
On Windows, the implementation below is faster than boost mutex.
*/
#if defined(_WIN32)
class SimpleMutex : boost::noncopyable {
CRITICAL_SECTION _cs;
public:
SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); }
~SimpleMutex() { DeleteCriticalSection(&_cs); }
void lock() { EnterCriticalSection(&_cs); }
void unlock() { LeaveCriticalSection(&_cs); }
class scoped_lock : boost::noncopyable {
SimpleMutex& _m;
public:
scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); }
~scoped_lock() { _m.unlock(); }
};
};
#else
class SimpleMutex : boost::noncopyable {
public:
SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0 ) == 0 ); }
~SimpleMutex(){
if ( ! StaticObserver::_destroyingStatics ) {
assert( pthread_mutex_destroy(&_lock) == 0 );
}
}
void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); }
void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); }
class scoped_lock : boost::noncopyable {
SimpleMutex& _m;
public:
scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); }
~scoped_lock() { _m.unlock(); }
};
private:
pthread_mutex_t _lock;
};
#endif
} }
 End of changes. 26 change blocks. 
32 lines changed or deleted 127 lines changed or added


 mvar.h   mvar.h 
skipping to change at line 34 skipping to change at line 34
* You can also think of it as a box that can be either full or empty. * You can also think of it as a box that can be either full or empty.
*/ */
template <typename T> template <typename T>
class MVar { class MVar {
public: public:
enum State {EMPTY=0, FULL}; enum State {EMPTY=0, FULL};
// create an empty MVar // create an empty MVar
MVar() MVar()
: _state(EMPTY) : _state(EMPTY)
{} {}
// creates a full MVar // creates a full MVar
MVar(const T& val) MVar(const T& val)
: _state(FULL) : _state(FULL)
, _value(val) , _value(val)
{} {}
// puts val into the MVar and returns true or returns false if full // puts val into the MVar and returns true or returns false if full
// never blocks // never blocks
bool tryPut(const T& val){ bool tryPut(const T& val) {
// intentionally repeat test before and after lock // intentionally repeat test before and after lock
if (_state == FULL) return false; if (_state == FULL) return false;
Mutex::scoped_lock lock(_mutex); Mutex::scoped_lock lock(_mutex);
if (_state == FULL) return false; if (_state == FULL) return false;
_state = FULL; _state = FULL;
_value = val; _value = val;
// unblock threads waiting to 'take' // unblock threads waiting to 'take'
_condition.notify_all(); _condition.notify_all();
return true; return true;
} }
// puts val into the MVar // puts val into the MVar
// will block if the MVar is already full // will block if the MVar is already full
void put(const T& val){ void put(const T& val) {
Mutex::scoped_lock lock(_mutex); Mutex::scoped_lock lock(_mutex);
while (!tryPut(val)){ while (!tryPut(val)) {
// unlocks lock while waiting and relocks before returning // unlocks lock while waiting and relocks before returning
_condition.wait(lock); _condition.wait(lock);
} }
} }
// takes val out of the MVar and returns true or returns false if e mpty // takes val out of the MVar and returns true or returns false if e mpty
// never blocks // never blocks
bool tryTake(T& out){ bool tryTake(T& out) {
// intentionally repeat test before and after lock // intentionally repeat test before and after lock
if (_state == EMPTY) return false; if (_state == EMPTY) return false;
Mutex::scoped_lock lock(_mutex); Mutex::scoped_lock lock(_mutex);
if (_state == EMPTY) return false; if (_state == EMPTY) return false;
_state = EMPTY; _state = EMPTY;
out = _value; out = _value;
// unblock threads waiting to 'put' // unblock threads waiting to 'put'
_condition.notify_all(); _condition.notify_all();
return true; return true;
} }
// takes val out of the MVar // takes val out of the MVar
// will block if the MVar is empty // will block if the MVar is empty
T take(){ T take() {
T ret = T(); T ret = T();
Mutex::scoped_lock lock(_mutex); Mutex::scoped_lock lock(_mutex);
while (!tryTake(ret)){ while (!tryTake(ret)) {
// unlocks lock while waiting and relocks before returning // unlocks lock while waiting and relocks before returning
_condition.wait(lock); _condition.wait(lock);
} }
return ret; return ret;
} }
// Note: this is fast because there is no locking, but state could // Note: this is fast because there is no locking, but state could
// change before you get a chance to act on it. // change before you get a chance to act on it.
// Mainly useful for sanity checks / asserts. // Mainly useful for sanity checks / asserts.
State getState(){ return _state; } State getState() { return _state; }
private: private:
State _state; State _state;
T _value; T _value;
typedef boost::recursive_mutex Mutex; typedef boost::recursive_mutex Mutex;
Mutex _mutex; Mutex _mutex;
boost::condition _condition; boost::condition _condition;
}; };
} }
 End of changes. 9 change blocks. 
12 lines changed or deleted 12 lines changed or added


 namespace.h   namespace.h 
skipping to change at line 23 skipping to change at line 23
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "jsobj.h" #include "jsobj.h"
#include "queryutil.h" #include "querypattern.h"
#include "diskloc.h" #include "diskloc.h"
#include "../util/hashtab.h" #include "../util/hashtab.h"
#include "../util/mmap.h" #include "mongommf.h"
namespace mongo { namespace mongo {
/* in the mongo source code, "client" means "database". */ /* in the mongo source code, "client" means "database". */
const int MaxDatabaseLen = 256; // max str len for the db name, includi ng null char
// "database.a.b.c" -> "database" const int MaxDatabaseNameLen = 256; // max str len for the db name, inc luding null char
inline void nsToDatabase(const char *ns, char *database) {
const char *p = ns;
char *q = database;
while ( *p != '.' ) {
if ( *p == 0 )
break;
*q++ = *p++;
}
*q = 0;
if (q-database>=MaxDatabaseLen) {
log() << "nsToDatabase: ns too long. terminating, buf overrun c ondition" << endl;
dbexit( EXIT_POSSIBLE_CORRUPTION );
}
}
inline string nsToDatabase(const char *ns) {
char buf[MaxDatabaseLen];
nsToDatabase(ns, buf);
return buf;
}
inline string nsToDatabase(const string& ns) {
size_t i = ns.find( '.' );
if ( i == string::npos )
return ns;
return ns.substr( 0 , i );
}
/* e.g. /* e.g.
NamespaceString ns("acme.orders"); NamespaceString ns("acme.orders");
cout << ns.coll; // "orders" cout << ns.coll; // "orders"
*/ */
class NamespaceString { class NamespaceString {
public: public:
string db; string db;
string coll; // note collection names can have periods in them for organizing purposes (e.g. "system.indexes") string coll; // note collection names can have periods in them for organizing purposes (e.g. "system.indexes")
NamespaceString( const char * ns ) { init(ns); }
NamespaceString( const string& ns ) { init(ns.c_str()); }
string ns() const { return db + '.' + coll; }
bool isSystem() const { return strncmp(coll.c_str(), "system.", 7) == 0; }
/**
* @return true if ns is 'normal'. $ used for collections holding index data, which do not contain BSON objects in their records.
* special case for the local.oplog.$main ns -- naming it as such w as a mistake.
*/
static bool normal(const char* ns) {
const char *p = strchr(ns, '$');
if( p == 0 )
return true;
return strcmp( ns, "local.oplog.$main" ) == 0;
}
static bool special(const char *ns) {
return !normal(ns) || strstr(ns, ".system.");
}
private: private:
void init(const char *ns) { void init(const char *ns) {
const char *p = strchr(ns, '.'); const char *p = strchr(ns, '.');
if( p == 0 ) return; if( p == 0 ) return;
db = string(ns, p - ns); db = string(ns, p - ns);
coll = p + 1; coll = p + 1;
} }
public:
NamespaceString( const char * ns ) { init(ns); }
NamespaceString( const string& ns ) { init(ns.c_str()); }
string ns() const {
return db + '.' + coll;
}
bool isSystem() {
return strncmp(coll.c_str(), "system.", 7) == 0;
}
}; };
#pragma pack(1) #pragma pack(1)
/* This helper class is used to make the HashMap below in NamespaceD etails */ /* This helper class is used to make the HashMap below in NamespaceDeta ils e.g. see line:
HashTable<Namespace,NamespaceDetails> *ht;
*/
class Namespace { class Namespace {
public: public:
enum MaxNsLenValue { MaxNsLen = 128 }; explicit Namespace(const char *ns) { *this = ns; }
Namespace(const char *ns) { Namespace& operator=(const char *ns);
*this = ns;
}
Namespace& operator=(const char *ns) {
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 */
strcpy_s(buf, MaxNsLen, ns);
return *this;
}
/* for more than 10 indexes -- see NamespaceDetails::Extra */
string extraName(int i) {
char ex[] = "$extra";
ex[5] += i;
string s = string(buf) + ex;
massert( 10348 , "$extra: ns name too long", s.size() < MaxNsLe n);
return s;
}
bool isExtra() const {
const char *p = strstr(buf, "$extr");
return p && p[5] && p[6] == 0; //==0 important in case an index uses name "$extra_1" for example
}
bool hasDollarSign() const { return strchr( buf , '$' ) > 0; } bool hasDollarSign() const { return strchr( buf , '$' ) > 0; }
void kill() { buf[0] = 0x7f; } void kill() { buf[0] = 0x7f; }
bool operator==(const char *r) const { return strcmp(buf, r) == 0; } bool operator==(const char *r) const { return strcmp(buf, r) == 0; }
bool operator==(const Namespace& r) const { return strcmp(buf, r.bu f) == 0; } bool operator==(const Namespace& r) const { return strcmp(buf, r.bu f) == 0; }
int hash() const { int hash() const; // value returned is always > 0
unsigned x = 0;
const char *p = buf;
while ( *p ) {
x = x * 131 + *p;
p++;
}
return (x & 0x7fffffff) | 0x8000000; // must be > 0
}
/** size_t size() const { return strlen( buf ); }
( foo.bar ).getSisterNS( "blah" ) == foo.blah
perhaps this should move to the NamespaceString helper?
*/
string getSisterNS( const char * local ) {
assert( local && local[0] != '.' );
string old(buf);
if ( old.find( "." ) != string::npos )
old = old.substr( 0 , old.find( "." ) );
return old + "." + local;
}
string toString() const { string toString() const { return (string) buf; }
return (string)buf; operator string() const { return (string) buf; }
}
operator string() const { /* NamespaceDetails::Extra was added after fact to allow chaining o f data blocks to support more than 10 indexes
return (string)buf; (more than 10 IndexDetails). It's a bit hacky because of this l ate addition with backward
} file support. */
string extraName(int i) const;
bool isExtra() const; /* ends with $extr... -- when true an extra b lock not a normal NamespaceDetails block */
/** ( foo.bar ).getSisterNS( "blah" ) == foo.blah
perhaps this should move to the NamespaceString helper?
*/
string getSisterNS( const char * local ) const;
enum MaxNsLenValue { MaxNsLen = 128 };
private:
char buf[MaxNsLen]; char buf[MaxNsLen];
}; };
#pragma pack() #pragma pack()
} // namespace mongo } // namespace mongo
#include "index.h" #include "index.h"
namespace mongo { namespace mongo {
/** @return true if a client can modify this namespace /** @return true if a client can modify this namespace even though it i s under ".system."
things like *.system.users */ For example <dbname>.system.users is ok for regular clients to upda te.
@param write used when .system.js
*/
bool legalClientSystemNS( const string& ns , bool write ); bool legalClientSystemNS( const string& ns , bool write );
/* deleted lists -- linked lists of deleted records -- are placed in 'b uckets' of various sizes /* deleted lists -- linked lists of deleted records -- are placed in 'b uckets' of various sizes
so you can look for a deleterecord about the right size. so you can look for a deleterecord about the right size.
*/ */
const int Buckets = 19; const int Buckets = 19;
const int MaxBucket = 18; const int MaxBucket = 18;
extern int bucketSizes[]; extern int bucketSizes[];
#pragma pack(1) #pragma pack(1)
/* this is the "header" for a collection that has all its details. in the .ns file. /* NamespaceDetails : this is the "header" for a collection that has al l its details.
It's in the .ns file and this is a memory mapped region (thus the pa ck pragma above).
*/ */
class NamespaceDetails { class NamespaceDetails {
friend class NamespaceIndex;
enum { NIndexesExtra = 30,
NIndexesBase = 10
};
public: public:
struct ExtraOld { enum { NIndexesMax = 64, NIndexesExtra = 30, NIndexesBase = 10 };
// note we could use this field for more chaining later, so don 't waste it:
unsigned long long reserved1; /*-------- data fields, as present on disk : */
IndexDetails details[NIndexesExtra]; DiskLoc firstExtent;
unsigned reserved2; DiskLoc lastExtent;
unsigned reserved3; /* NOTE: capped collections v1 override the meaning of deletedList.
}; deletedList[0] points to a list of free records (DeletedRe cord's) for all extents in
the capped 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];
// ofs 168 (8 byte aligned)
struct Stats {
// datasize and nrecords MUST Be adjacent code assumes!
long long datasize; // this includes padding, but not record he aders
long long nrecords;
} stats;
int lastExtentSize;
int nIndexes;
private:
// ofs 192
IndexDetails _indexes[NIndexesBase];
public:
// ofs 352 (16 byte aligned)
int capped;
int max; // max # of objects for a cap ped table. TODO: should this be 64 bit?
double paddingFactor; // 1.0 = no padding.
// ofs 386 (16)
int flags;
DiskLoc capExtent;
DiskLoc capFirstNewRecord;
unsigned short dataFileVersion; // NamespaceDetails version. So we can do backward compatibility in the future. See filever.h
unsigned short indexFileVersion;
unsigned long long multiKeyIndexBits;
private:
// ofs 400 (16)
unsigned long long reservedA;
long long extraOffset; // where the $extra info is l ocated (bytes relative to this)
public:
int indexBuildInProgress; // 1 if in prog
unsigned reservedB;
// ofs 424 (8)
struct Capped2 {
unsigned long long cc2_ptr; // see capped.cpp
unsigned fileNumber;
} capped2;
char reserved[60];
/*-------- end data 496 bytes */
explicit NamespaceDetails( const DiskLoc &loc, bool _capped );
class Extra { class Extra {
long long _next; long long _next;
public: public:
IndexDetails details[NIndexesExtra]; IndexDetails details[NIndexesExtra];
private: private:
unsigned reserved2; unsigned reserved2;
unsigned reserved3; unsigned reserved3;
Extra(const Extra&) { assert(false); } Extra(const Extra&) { assert(false); }
Extra& operator=(const Extra& r) { assert(false); re turn *this; } Extra& operator=(const Extra& r) { assert(false); return *this; }
public: public:
Extra() { } Extra() { }
long ofsFrom(NamespaceDetails *d) { long ofsFrom(NamespaceDetails *d) {
return ((char *) this) - ((char *) d); return ((char *) this) - ((char *) d);
} }
void init() { memset(this, 0, sizeof(Extra)); } void init() { memset(this, 0, sizeof(Extra)); }
Extra* next(NamespaceDetails *d) { Extra* next(NamespaceDetails *d) {
if( _next == 0 ) return 0; if( _next == 0 ) return 0;
return (Extra*) (((char *) d) + _next); return (Extra*) (((char *) d) + _next);
} }
void setNext(long ofs) { _next = ofs; } void setNext(long ofs) { *getDur().writing(&_next) = ofs; }
void copy(NamespaceDetails *d, const Extra& e) { void copy(NamespaceDetails *d, const Extra& e) {
memcpy(this, &e, sizeof(Extra)); memcpy(this, &e, sizeof(Extra));
_next = 0; _next = 0;
} }
}; // Extra };
Extra* extra() { Extra* extra() {
if( extraOffset == 0 ) return 0; if( extraOffset == 0 ) return 0;
return (Extra *) (((char *) this) + extraOffset); return (Extra *) (((char *) this) + extraOffset);
} }
public:
/* add extra space for indexes when more than 10 */ /* add extra space for indexes when more than 10 */
Extra* allocExtra(const char *ns, int nindexessofar); Extra* allocExtra(const char *ns, int nindexessofar);
void copyingFrom(const char *thisns, NamespaceDetails *src); // mus t be called when renaming a NS to fix up extra void copyingFrom(const char *thisns, NamespaceDetails *src); // mus t be called when renaming a NS to fix up extra
enum { NIndexesMax = 64 };
BOOST_STATIC_ASSERT( NIndexesMax <= NIndexesBase + NIndexesExtra*2 );
BOOST_STATIC_ASSERT( NIndexesMax <= 64 ); // multiKey bits
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::ExtraOld) == 4 96 );
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) == 496 );
/* called when loaded from disk */ /* called when loaded from disk */
void onLoad(const Namespace& k); void onLoad(const Namespace& k);
NamespaceDetails( const DiskLoc &loc, bool _capped ); /* dump info on this namespace. for debugging. */
void dump(const Namespace& k);
DiskLoc firstExtent;
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 capped 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];
/* dump info on all extents for this namespace. for debugging. */
void dumpExtents(); void dumpExtents();
long long datasize;
long long nrecords;
int lastExtentSize;
int nIndexes;
private:
IndexDetails _indexes[NIndexesBase];
private: private:
Extent *theCapExtent() const { return capExtent.ext(); } Extent *theCapExtent() const { return capExtent.ext(); }
void advanceCapExtent( const char *ns ); void advanceCapExtent( const char *ns );
DiskLoc __capAlloc(int len); DiskLoc __capAlloc(int len);
DiskLoc cappedAlloc(const char *ns, int len); DiskLoc cappedAlloc(const char *ns, int len);
DiskLoc &cappedFirstDeletedInCurExtent(); DiskLoc &cappedFirstDeletedInCurExtent();
bool nextIsInCapExtent( const DiskLoc &dl ) const; bool nextIsInCapExtent( const DiskLoc &dl ) const;
public: public:
DiskLoc& cappedListOfAllDeletedRecords() { return deletedList[0]; } DiskLoc& cappedListOfAllDeletedRecords() { return deletedList[0]; }
DiskLoc& cappedLastDelRecLastExtent() { return deletedList[1]; } DiskLoc& cappedLastDelRecLastExtent() { return deletedList[1]; }
void cappedDumpDelInfo(); void cappedDumpDelInfo();
bool capLooped() const { return capped && capFirstNewRecord.isValid (); } bool capLooped() const { return capped && capFirstNewRecord.isValid (); }
bool inCapExtent( const DiskLoc &dl ) const; bool inCapExtent( const DiskLoc &dl ) const;
void cappedCheckMigrate(); void cappedCheckMigrate();
void cappedTruncateAfter(const char *ns, DiskLoc after, bool inclus ive); /** remove rest of the capped collection from this point onward */ /**
* Truncate documents newer than the document at 'end' from the cap ped
* collection. The collection cannot be completely emptied using t his
* function. An assertion will be thrown if that is attempted.
* @param inclusive - Truncate 'end' as well iff true
*/
void cappedTruncateAfter(const char *ns, DiskLoc end, bool inclusiv e);
/** Remove all documents from the capped collection */
void emptyCappedCollection(const char *ns); void emptyCappedCollection(const char *ns);
int capped;
int max; // max # of objects for a capped table. TODO: should this be 64 bit?
double paddingFactor; // 1.0 = no padding.
int flags;
DiskLoc capExtent;
DiskLoc capFirstNewRecord;
/* NamespaceDetails version. So we can do backward compatibility i n the future.
See filever.h
*/
unsigned short dataFileVersion;
unsigned short indexFileVersion;
unsigned long long multiKeyIndexBits;
private:
unsigned long long reservedA;
long long extraOffset; // where the $extra info is located (bytes r elative to this)
public:
int backgroundIndexBuildInProgress; // 1 if in prog
char reserved[76];
/* when a background index build is in progress, we don't count the index in nIndexes until /* when a background index build is in progress, we don't count the index in nIndexes until
complete, yet need to still use it in _indexRecord() - thus we u se this function for that. complete, yet need to still use it in _indexRecord() - thus we u se this function for that.
*/ */
int nIndexesBeingBuilt() const { return nIndexes + backgroundIndexB uildInProgress; } int nIndexesBeingBuilt() const { return nIndexes + indexBuildInProg ress; }
/* NOTE: be careful with flags. are we manipulating them in read l ocks? if so, /* NOTE: be careful with flags. are we manipulating them in read l ocks? if so,
this isn't thread safe. TODO this isn't thread safe. TODO
*/ */
enum NamespaceFlags { enum NamespaceFlags {
Flag_HaveIdIndex = 1 << 0 // set when we have _id index (ONLY i f ensureIdIndex was called -- 0 if that has never been called) Flag_HaveIdIndex = 1 << 0 // set when we have _id index (ONLY i f ensureIdIndex was called -- 0 if that has never been called)
}; };
IndexDetails& idx(int idxNo, bool missingExpected = false ) { IndexDetails& idx(int idxNo, bool missingExpected = false );
if( idxNo < NIndexesBase )
return _indexes[idxNo]; /** get the IndexDetails for the index currently being built in the background. (there is at most one) */
Extra *e = extra(); IndexDetails& inProgIdx() {
if ( ! e ){ DEV assert(indexBuildInProgress);
if ( missingExpected )
throw MsgAssertionException( 13283 , "Missing Extra" );
massert(13282, "missing Extra", e);
}
int i = idxNo - NIndexesBase;
if( i >= NIndexesExtra ) {
e = e->next(this);
if ( ! e ){
if ( missingExpected )
throw MsgAssertionException( 13283 , "missing extra " );
massert(13283, "missing Extra", e);
}
i -= NIndexesExtra;
}
return e->details[i];
}
IndexDetails& backgroundIdx() {
DEV assert(backgroundIndexBuildInProgress);
return idx(nIndexes); return idx(nIndexes);
} }
class IndexIterator { class IndexIterator {
friend class NamespaceDetails;
int i;
int n;
NamespaceDetails *d;
IndexIterator(NamespaceDetails *_d) {
d = _d;
i = 0;
n = d->nIndexes;
}
public: public:
int pos() { return i; } // note this is the next one to come int pos() { return i; } // note this is the next one to come
bool more() { return i < n; } bool more() { return i < n; }
IndexDetails& next() { return d->idx(i++); } IndexDetails& next() { return d->idx(i++); }
}; // IndexIterator private:
friend class NamespaceDetails;
int i, n;
NamespaceDetails *d;
IndexIterator(NamespaceDetails *_d);
};
IndexIterator ii() { return IndexIterator(this); } IndexIterator ii() { return IndexIterator(this); }
/* 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();
while( i.more() ) {
if( &i.next() == &idx )
return i.pos()-1;
}
massert( 10349 , "E12000 idxNo fails", false);
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 work on queries. for these, we have to do some dedup work on queries.
*/ */
bool isMultikey(int i) { bool isMultikey(int i) const { return (multiKeyIndexBits & (((unsig ned long long) 1) << i)) != 0; }
return (multiKeyIndexBits & (((unsigned long long) 1) << i)) != 0;
}
void setIndexIsMultikey(int i) { void setIndexIsMultikey(int i) {
dassert( i < NIndexesMax ); dassert( i < NIndexesMax );
multiKeyIndexBits |= (((unsigned long long) 1) << i); unsigned long long x = ((unsigned long long) 1) << i;
if( multiKeyIndexBits & x ) return;
*getDur().writing(&multiKeyIndexBits) |= x;
} }
void clearIndexIsMultikey(int i) { void clearIndexIsMultikey(int i) {
dassert( i < NIndexesMax ); dassert( i < NIndexesMax );
multiKeyIndexBits &= ~(((unsigned long long) 1) << i); unsigned long long x = ((unsigned long long) 1) << i;
if( (multiKeyIndexBits & x) == 0 ) return;
*getDur().writing(&multiKeyIndexBits) &= ~x;
} }
/* add a new index. does not add to system.indexes etc. - just to NamespaceDetails. /* add a new index. does not add to system.indexes etc. - just to NamespaceDetails.
caller must populate returned object. caller must populate returned object.
*/ */
IndexDetails& addIndex(const char *thisns, bool resetTransient=true ); IndexDetails& addIndex(const char *thisns, bool resetTransient=true );
void aboutToDeleteAnIndex() { flags &= ~Flag_HaveIdIndex; } void aboutToDeleteAnIndex() {
*getDur().writing(&flags) = flags & ~Flag_HaveIdIndex;
}
/* returns index of the first index in which the field is present. -1 if not present. */ /* returns index of the first index in which the field is present. -1 if not present. */
int fieldIsIndexed(const char *fieldName); int fieldIsIndexed(const char *fieldName);
void paddingFits() { void paddingFits() {
double x = paddingFactor - 0.01; double x = paddingFactor - 0.01;
if ( x >= 1.0 ) if ( x >= 1.0 ) {
paddingFactor = x; *getDur().writing(&paddingFactor) = x;
//getDur().setNoJournal(&paddingFactor, &x, sizeof(x));
}
} }
void paddingTooSmall() { void paddingTooSmall() {
double x = paddingFactor + 0.6; double x = paddingFactor + 0.6;
if ( x <= 2.0 ) if ( x <= 2.0 ) {
paddingFactor = x; *getDur().writing(&paddingFactor) = x;
} //getDur().setNoJournal(&paddingFactor, &x, sizeof(x));
//returns offset in indexes[]
int findIndexByName(const char *name) {
IndexIterator i = ii();
while( i.more() ) {
if ( strcmp(i.next().info.obj().getStringField("name"),name ) == 0 )
return i.pos()-1;
} }
return -1;
} }
//returns offset in indexes[] // @return offset in indexes[]
int findIndexByKeyPattern(const BSONObj& keyPattern) { int findIndexByName(const char *name);
IndexIterator i = ii();
while( i.more() ) { // @return offset in indexes[]
if( i.next().keyPattern() == keyPattern ) int findIndexByKeyPattern(const BSONObj& keyPattern);
return i.pos()-1;
}
return -1;
}
void findIndexByType( const string& name , vector<int>& matches ) { void findIndexByType( const string& name , vector<int>& matches ) {
IndexIterator i = ii(); IndexIterator i = ii();
while ( i.more() ){ while ( i.more() ) {
if ( i.next().getSpec().getTypeName() == name ) if ( i.next().getSpec().getTypeName() == name )
matches.push_back( i.pos() - 1 ); matches.push_back( i.pos() - 1 );
} }
} }
/* @return -1 = not found /* @return -1 = not found
generally id is first index, so not that expensive an operation (assuming present). generally id is first index, so not that expensive an operation (assuming present).
*/ */
int findIdIndex() { int findIdIndex() {
IndexIterator i = ii(); IndexIterator i = ii();
while( i.more() ) { while( i.more() ) {
if( i.next().isIdIndex() ) if( i.next().isIdIndex() )
return i.pos()-1; return i.pos()-1;
} }
return -1; return -1;
} }
bool haveIdIndex() {
return (flags & NamespaceDetails::Flag_HaveIdIndex) || findIdIn dex() >= 0;
}
/* return which "deleted bucket" for this size object */ /* return which "deleted bucket" for this size object */
static int bucket(int n) { static int bucket(int n) {
for ( int i = 0; i < Buckets; i++ ) for ( int i = 0; i < Buckets; i++ )
if ( bucketSizes[i] > n ) if ( bucketSizes[i] > n )
return i; return i;
return Buckets-1; return Buckets-1;
} }
/* allocate a new record. lenToAlloc includes headers. */ /* allocate a new record. lenToAlloc includes headers. */
DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc); DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc);
skipping to change at line 454 skipping to change at line 376
/* return which "deleted bucket" for this size object */ /* return which "deleted bucket" for this size object */
static int bucket(int n) { static int bucket(int n) {
for ( int i = 0; i < Buckets; i++ ) for ( int i = 0; i < Buckets; i++ )
if ( bucketSizes[i] > n ) if ( bucketSizes[i] > n )
return i; return i;
return Buckets-1; return Buckets-1;
} }
/* allocate a new record. lenToAlloc includes headers. */ /* allocate a new record. lenToAlloc includes headers. */
DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc); DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc);
/* add a given record to the deleted chains for this NS */ /* add a given record to the deleted chains for this NS */
void addDeletedRec(DeletedRecord *d, DiskLoc dloc); void addDeletedRec(DeletedRecord *d, DiskLoc dloc);
void dumpDeleted(set<DiskLoc> *extents = 0); void dumpDeleted(set<DiskLoc> *extents = 0);
// Start from firstExtent by default. // Start from firstExtent by default.
DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const ; DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const ;
// Start from lastExtent by default. // Start from lastExtent by default.
DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const;
long long storageSize( int * numExtents = 0 , BSONArrayBuilder * ex tentInfo = 0 ) const;
long long storageSize( int * numExtents = 0 ); int averageObjectSize() {
if ( stats.nrecords == 0 )
return 5;
return (int) (stats.datasize / stats.nrecords);
}
NamespaceDetails *writingWithoutExtra() {
return ( NamespaceDetails* ) getDur().writingPtr( this, sizeof( NamespaceDetails ) );
}
/** Make all linked Extra objects writeable as well */
NamespaceDetails *writingWithExtra();
private: private:
DiskLoc _alloc(const char *ns, int len); DiskLoc _alloc(const char *ns, int len);
void maybeComplain( const char *ns, int len ) const; void maybeComplain( const char *ns, int len ) const;
DiskLoc __stdAlloc(int len); DiskLoc __stdAlloc(int len);
void compact(); // combine adjacent deleted records void compact(); // combine adjacent deleted records
friend class NamespaceIndex;
struct ExtraOld {
// note we could use this field for more chaining later, so don 't waste it:
unsigned long long reserved1;
IndexDetails details[NIndexesExtra];
unsigned reserved2;
unsigned reserved3;
};
/** Update cappedLastDelRecLastExtent() after capExtent changed in cappedTruncateAfter() */
void cappedTruncateLastDelUpdate();
BOOST_STATIC_ASSERT( NIndexesMax <= NIndexesBase + NIndexesExtra*2 );
BOOST_STATIC_ASSERT( NIndexesMax <= 64 ); // multiKey bits
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::ExtraOld) == 496 );
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails::Extra) == 496 );
}; // NamespaceDetails }; // NamespaceDetails
#pragma pack() #pragma pack()
/* NamespaceDetailsTransient /* NamespaceDetailsTransient
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 todo: cleanup code, need abstractions and separation
*/ */
class NamespaceDetailsTransient : boost::noncopyable { class NamespaceDetailsTransient : boost::noncopyable {
BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 ); BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 );
/* general -------------------------------------------------------- ----- */ /* general -------------------------------------------------------- ----- */
private: private:
string _ns; string _ns;
void reset(); void reset();
static std::map< string, shared_ptr< NamespaceDetailsTransient > > _map; static std::map< string, shared_ptr< NamespaceDetailsTransient > > _map;
public: public:
NamespaceDetailsTransient(const char *ns) : _ns(ns), _keysComputed( false), _qcWriteCount(){ } NamespaceDetailsTransient(const char *ns) : _ns(ns), _keysComputed( false), _qcWriteCount() { }
private:
/* _get() is not threadsafe -- see get_inlock() comments */ /* _get() is not threadsafe -- see get_inlock() comments */
static NamespaceDetailsTransient& _get(const char *ns); static NamespaceDetailsTransient& _get(const char *ns);
/* use get_w() when doing write operations */ public:
/* use get_w() when doing write operations. this is safe as there i s only 1 write op and it's exclusive to everything else.
for reads you must lock and then use get_inlock() instead. */
static NamespaceDetailsTransient& get_w(const char *ns) { static NamespaceDetailsTransient& get_w(const char *ns) {
DEV assertInWriteLock(); DEV assertInWriteLock();
return _get(ns); return _get(ns);
} }
void addedIndex() { reset(); } void addedIndex() { reset(); }
void deletedIndex() { reset(); } void deletedIndex() { reset(); }
/* Drop cached information on all namespaces beginning with the spe cified prefix. /* 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. Can be useful as index namespaces share the same start as the re gular collection.
SLOW - sequential scan of all NamespaceDetailsTransient objects */ SLOW - sequential scan of all NamespaceDetailsTransient objects */
static void clearForPrefix(const char *prefix); static void clearForPrefix(const char *prefix);
static void eraseForPrefix(const char *prefix);
/**
* @return a cursor interface to the query optimizer. The implemen tation may
* utilize a single query plan or interleave results from multiple query
* plans before settling on a single query plan. Note that the sch ema of
* currKey() documents, the matcher(), and the isMultiKey() nature of the
* cursor may change over the course of iteration.
*
* @param order - If no index exists that satisfies this sort order , an
* empty shared_ptr will be returned.
*
* The returned cursor may @throw inside of advance() or recoverFro mYield() in
* certain error cases, for example if a capped overrun occurred du ring a yield.
* This indicates that the cursor was unable to perform a complete scan.
*
* This is a work in progress. Partial list of features not yet im plemented:
* - modification of scanned documents
* - covered indexes
*/
static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj &query, const BSONObj &order = BSONObj() );
/* indexKeys() cache ---------------------------------------------- ------ */ /* indexKeys() cache ---------------------------------------------- ------ */
/* assumed to be in write lock for this */ /* assumed to be in write lock for this */
private: private:
bool _keysComputed; bool _keysComputed;
set<string> _indexKeys; set<string> _indexKeys;
void computeIndexKeys(); void computeIndexKeys();
public: 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 secondary component of a co mpound index.) field is indexed (Note it might be a secondary component of a co mpound index.)
skipping to change at line 532 skipping to change at line 499
set<string>& indexKeys() { set<string>& indexKeys() {
DEV assertInWriteLock(); DEV assertInWriteLock();
if ( !_keysComputed ) if ( !_keysComputed )
computeIndexKeys(); computeIndexKeys();
return _indexKeys; return _indexKeys;
} }
/* IndexSpec caching */ /* IndexSpec caching */
private: private:
map<const IndexDetails*,IndexSpec> _indexSpecs; map<const IndexDetails*,IndexSpec> _indexSpecs;
static mongo::mutex _isMutex; static SimpleMutex _isMutex;
public: public:
const IndexSpec& getIndexSpec( const IndexDetails * details ){ const IndexSpec& getIndexSpec( const IndexDetails * details ) {
IndexSpec& spec = _indexSpecs[details]; IndexSpec& spec = _indexSpecs[details];
if ( ! spec._finishedInit ){ if ( ! spec._finishedInit ) {
scoped_lock lk(_isMutex); SimpleMutex::scoped_lock lk(_isMutex);
if ( ! spec._finishedInit ){ if ( ! spec._finishedInit ) {
spec.reset( details ); spec.reset( details );
assert( spec._finishedInit ); assert( spec._finishedInit );
} }
} }
return spec; return spec;
} }
/* query cache (for query optimizer) ------------------------------ ------- */ /* query cache (for query optimizer) ------------------------------ ------- */
private: private:
int _qcWriteCount; int _qcWriteCount;
map< QueryPattern, pair< BSONObj, long long > > _qcCache; map< QueryPattern, pair< BSONObj, long long > > _qcCache;
public: public:
static mongo::mutex _qcMutex; static SimpleMutex _qcMutex;
/* you must be in the qcMutex when calling this (and using the retu rned val): */ /* you must be in the qcMutex when calling this (and using the retu rned val): */
static NamespaceDetailsTransient& get_inlock(const char *ns) { static NamespaceDetailsTransient& get_inlock(const char *ns) {
return _get(ns); return _get(ns);
} }
void clearQueryCache() { // public for unit tests void clearQueryCache() { // public for unit tests
_qcCache.clear(); _qcCache.clear();
_qcWriteCount = 0; _qcWriteCount = 0;
} }
/* you must notify the cache if you are doing writes, as query plan optimality will change */ /* you must notify the cache if you are doing writes, as query plan optimality will change */
void notifyOfWriteOp() { void notifyOfWriteOp() {
if ( _qcCache.empty() ) if ( _qcCache.empty() )
return; return;
if ( ++_qcWriteCount >= 100 ) if ( ++_qcWriteCount >= 1000 )
clearQueryCache(); clearQueryCache();
} }
BSONObj indexForPattern( const QueryPattern &pattern ) { BSONObj indexForPattern( const QueryPattern &pattern ) {
return _qcCache[ pattern ].first; return _qcCache[ pattern ].first;
} }
long long nScannedForPattern( const QueryPattern &pattern ) { long long nScannedForPattern( const QueryPattern &pattern ) {
return _qcCache[ 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 ) {
_qcCache[ pattern ] = make_pair( indexKey, nScanned ); _qcCache[ pattern ] = make_pair( indexKey, nScanned );
skipping to change at line 594 skipping to change at line 561
} }
/* 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;
public: public:
NamespaceIndex(const string &dir, const string &database) : NamespaceIndex(const string &dir, const string &database) :
ht( 0 ), dir_( dir ), database_( database ) {} ht( 0 ), dir_( dir ), database_( database ) {}
/* returns true if new db will be created if we init lazily */ /* returns true if new db will be created if we init lazily */
bool exists() const; bool exists() const;
void init(); void init();
void add_ns(const char *ns, DiskLoc& loc, bool capped) { void add_ns(const char *ns, DiskLoc& loc, bool capped) {
NamespaceDetails details( loc, capped ); NamespaceDetails details( loc, capped );
add_ns( ns, details ); add_ns( ns, details );
} }
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( 10081 , "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;
}*/ }*/
NamespaceDetails* details(const char *ns) { NamespaceDetails* details(const char *ns) {
if ( !ht ) if ( !ht )
return 0; return 0;
Namespace n(ns); Namespace n(ns);
NamespaceDetails *d = ht->get(n); NamespaceDetails *d = ht->get(n);
if ( d && d->capped ) if ( d && d->capped )
d->cappedCheckMigrate(); d->cappedCheckMigrate();
return d; return d;
} }
void kill_ns(const char *ns) { void kill_ns(const char *ns);
if ( !ht )
return;
Namespace n(ns);
ht->kill(n);
for( int i = 0; i<=1; i++ ) {
try {
Namespace extra(n.extraName(i).c_str());
ht->kill(extra);
}
catch(DBException&) { }
}
}
bool find(const char *ns, DiskLoc& loc) { bool find(const char *ns, DiskLoc& loc) {
NamespaceDetails *l = details(ns); NamespaceDetails *l = details(ns);
if ( l ) { if ( l ) {
loc = l->firstExtent; loc = l->firstExtent;
return true; return true;
} }
return false; return false;
} }
bool allocated() const { bool allocated() const {
return ht != 0; return ht != 0;
} }
void getNamespaces( list<string>& tofill , bool onlyCollections = t rue ) const; void getNamespaces( list<string>& tofill , bool onlyCollections = t rue ) const;
NamespaceDetails::Extra* newExtra(const char *ns, int n, NamespaceD etails *d); NamespaceDetails::Extra* newExtra(const char *ns, int n, NamespaceD etails *d);
boost::filesystem::path path() const; boost::filesystem::path path() const;
private:
unsigned long long fileLength() const { return f.length(); }
private:
void maybeMkdir() const; void maybeMkdir() const;
MMF f; MongoMMF f;
HashTable<Namespace,NamespaceDetails,MMF::Pointer> *ht; HashTable<Namespace,NamespaceDetails> *ht;
string dir_; string dir_;
string database_; string database_;
}; };
extern string dbpath; // --dbpath parm extern string dbpath; // --dbpath parm
extern bool directoryperdb; extern bool directoryperdb;
// Rename a namespace within current 'client' db. // Rename a namespace within current 'client' db.
// (Arguments should include db name) // (Arguments should include db name)
void renameNamespace( const char *from, const char *to ); void renameNamespace( const char *from, const char *to );
// "database.a.b.c" -> "database"
inline void nsToDatabase(const char *ns, char *database) {
const char *p = ns;
char *q = database;
while ( *p != '.' ) {
if ( *p == 0 )
break;
*q++ = *p++;
}
*q = 0;
if (q-database>=MaxDatabaseNameLen) {
log() << "nsToDatabase: ns too long. terminating, buf overrun c ondition" << endl;
dbexit( EXIT_POSSIBLE_CORRUPTION );
}
}
inline string nsToDatabase(const char *ns) {
char buf[MaxDatabaseNameLen];
nsToDatabase(ns, buf);
return buf;
}
inline string nsToDatabase(const string& ns) {
size_t i = ns.find( '.' );
if ( i == string::npos )
return ns;
return ns.substr( 0 , i );
}
} // namespace mongo } // namespace mongo
 End of changes. 73 change blocks. 
281 lines changed or deleted 264 lines changed or added


 nonce.h   nonce.h 
// nonce.h // @file nonce.h
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
namespace mongo { namespace mongo {
typedef unsigned long long nonce; typedef unsigned long long nonce64;
struct Security { struct Security {
Security(); Security();
static nonce64 getNonce();
nonce getNonce(); static nonce64 getNonceDuringInit(); // use this version during glo bal var constructors
private:
/** safe during global var initialization */ nonce64 _getNonce();
nonce getNonceInitSafe() { nonce64 __getNonce();
init();
return getNonce();
}
private:
ifstream *_devrandom; ifstream *_devrandom;
static bool _initialized; bool _initialized;
void init(); // can call more than once void init(); // can call more than once
}; };
extern Security security;
} // namespace mongo } // namespace mongo
 End of changes. 5 change blocks. 
15 lines changed or deleted 9 lines changed or added


 ntservice.h   ntservice.h 
skipping to change at line 25 skipping to change at line 25
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#if defined(_WIN32) #if defined(_WIN32)
#include <windows.h> #include <windows.h>
namespace mongo { namespace mongo {
typedef bool ( *ServiceCallback )( void ); typedef bool ( *ServiceCallback )( void );
void serviceParamsCheck( program_options::variables_map& params, const std::string dbpath, int argc, char* argv[] ); bool serviceParamsCheck( program_options::variables_map& params, const std::string dbpath, int argc, char* argv[] );
class ServiceController { class ServiceController {
public: public:
ServiceController(); ServiceController();
virtual ~ServiceController() {} virtual ~ServiceController() {}
static bool installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, const std::wstr ing& serviceUser, const std::wstring& servicePassword, const std::string db path, int argc, char* argv[] ); static bool installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, const std::wstr ing& serviceUser, const std::wstring& servicePassword, const std::string db path, int argc, char* argv[] );
static bool removeService( const std::wstring& serviceName ); static bool removeService( const std::wstring& serviceName );
static bool startService( const std::wstring& serviceName, ServiceC allback startService ); static bool startService( const std::wstring& serviceName, ServiceC allback startService );
static bool reportStatus( DWORD reportState, DWORD waitHint = 0 ); static bool reportStatus( DWORD reportState, DWORD waitHint = 0 );
static void WINAPI initService( DWORD argc, LPTSTR *argv ); static void WINAPI initService( DWORD argc, LPTSTR *argv );
static void WINAPI serviceCtrl( DWORD ctrlCode ); static void WINAPI serviceCtrl( DWORD ctrlCode );
protected: protected:
static std::wstring _serviceName; static std::wstring _serviceName;
static SERVICE_STATUS_HANDLE _statusHandle; static SERVICE_STATUS_HANDLE _statusHandle;
static ServiceCallback _serviceCallback; static ServiceCallback _serviceCallback;
}; };
} // namespace mongo } // namespace mongo
#endif #endif
 End of changes. 3 change blocks. 
6 lines changed or deleted 6 lines changed or added


 oid.h   oid.h 
skipping to change at line 25 skipping to change at line 25
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../util/hex.h" #include "../util/hex.h"
namespace mongo { namespace mongo {
#pragma pack(1) #pragma pack(1)
/** Object ID type. /** Object ID type.
BSON objects typically have an _id field for the object id. This f ield should be the first BSON objects typically have an _id field for the object id. This f ield should be the first
member of the object when present. class OID is a special type tha t is a 12 byte id which member of the object when present. class OID is a special type tha t is a 12 byte id which
is likely to be unique to the system. You may also use other types for _id's. is likely to be unique to the system. You may also use other types for _id's.
When _id field is missing from a BSON object, on an insert the data base may insert one When _id field is missing from a BSON object, on an insert the data base may insert one
automatically in certain circumstances. automatically in certain circumstances.
Warning: You must call OID::newState() after a fork(). Warning: You must call OID::newState() after a fork().
Typical contents of the BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch),
a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Not e that the timestamp and counter fields must
be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure
a mostly increasing order.
*/ */
class OID { class OID {
union {
struct{
long long a;
unsigned b;
};
unsigned char data[12];
};
static unsigned _machine;
public: public:
/** call this after a fork */ OID() : a(0), b(0) { }
static void newState();
/** initialize to 'null' */ /** init from a 24 char hex string */
void clear() { a = 0; b = 0; } explicit OID(const string &s) { init(s); }
const unsigned char *getData() const { return data; } /** initialize to 'null' */
void clear() { a = 0; b = 0; }
bool operator==(const OID& r) { const unsigned char *getData() const { return data; }
return a==r.a&&b==r.b;
}
bool operator!=(const OID& r) {
return a!=r.a||b!=r.b;
}
/** The object ID output as 24 hex digits. */ bool operator==(const OID& r) const { return a==r.a && b==r.b; }
string str() const { bool operator!=(const OID& r) const { return a!=r.a || b!=r.b; }
return toHexLower(data, 12); int compare( const OID& other ) const { return memcmp( data , other .data , 12 ); }
} bool operator<( const OID& other ) const { return compare( other ) < 0; }
bool operator<=( const OID& other ) const { return compare( other ) <= 0; }
/** @return the object ID output as 24 hex digits */
string str() const { return toHexLower(data, 12); }
string toString() const { return str(); } string toString() const { return str(); }
static OID gen() { OID o; o.init(); return o; } static OID gen() { OID o; o.init(); return o; }
static unsigned staticMachine(){ return _machine; } /** sets the contents to a new oid / randomized value */
/**
sets the contents to a new oid / randomized value
*/
void init(); void init();
/** Set to the hex string value specified. */ /** init from a 24 char hex string */
void init( string s ); void init( string s );
/** Set to the min/max OID that could be generated at given timesta mp. */ /** Set to the min/max OID that could be generated at given timesta mp. */
void init( Date_t date, bool max=false ); void init( Date_t date, bool max=false );
time_t asTimeT(); time_t asTimeT();
Date_t asDateT() { return asTimeT() * (long long)1000; } Date_t asDateT() { return asTimeT() * (long long)1000; }
bool isSet() const { return a || b; } bool isSet() const { return a || b; }
int compare( const OID& other ) const { return memcmp( data , other .data , 12 ); } /** call this after a fork to update the process id */
static void justForked();
bool operator<( const OID& other ) const { return compare( other ) < 0; } static unsigned getMachineId(); // features command uses
static void regenMachineId(); // used by unit tests
private:
struct MachineAndPid {
unsigned char _machineNumber[3];
unsigned short _pid;
bool operator!=(const OID::MachineAndPid& rhs) const;
};
static MachineAndPid ourMachine, ourMachineAndPid;
union {
struct {
// 12 bytes total
unsigned char _time[4];
MachineAndPid _machineAndPid;
unsigned char _inc[3];
};
struct {
long long a;
unsigned b;
};
unsigned char data[12];
};
static unsigned ourPid();
static void foldInPid(MachineAndPid& x);
static MachineAndPid genMachineAndPid();
}; };
#pragma pack() #pragma pack()
ostream& operator<<( ostream &s, const OID &o ); ostream& operator<<( ostream &s, const OID &o );
inline StringBuilder& operator<< (StringBuilder& s, const OID& o) { ret urn (s << o.str()); } inline StringBuilder& operator<< (StringBuilder& s, const OID& o) { ret urn (s << o.str()); }
/** Formatting mode for generating JSON from BSON. /** Formatting mode for generating JSON from BSON.
See <http://mongodb.onconfluence.com/display/DOCS/Mongo+Extended+JS ON> See <http://mongodb.onconfluence.com/display/DOCS/Mongo+Extended+JS ON>
for details. for details.
*/ */
 End of changes. 13 change blocks. 
31 lines changed or deleted 50 lines changed or added


 oplog.h   oplog.h 
skipping to change at line 29 skipping to change at line 29
/* /*
local.oplog.$main is the default local.oplog.$main is the default
*/ */
#pragma once #pragma once
#include "pdfile.h" #include "pdfile.h"
#include "db.h" #include "db.h"
#include "dbhelpers.h" #include "dbhelpers.h"
#include "query.h" #include "clientcursor.h"
#include "queryoptimizer.h"
#include "../client/dbclient.h" #include "../client/dbclient.h"
#include "../util/optime.h" #include "../util/optime.h"
#include "../util/timer.h"
namespace mongo { namespace mongo {
void createOplog(); void createOplog();
void _logOpObjRS(const BSONObj& op); void _logOpObjRS(const BSONObj& op);
/** Write operation to the log (local.oplog.$main) /** Write operation to the log (local.oplog.$main)
@param opstr @param opstr
skipping to change at line 66 skipping to change at line 66
/** puts obj in the oplog as a comment (a no-op). Just for diags. /** puts obj in the oplog as a comment (a no-op). Just for diags.
convention is convention is
{ msg : "text", ... } { msg : "text", ... }
*/ */
void logOpComment(const BSONObj& obj); void logOpComment(const BSONObj& obj);
void oplogCheckCloseDatabase( Database * db ); void oplogCheckCloseDatabase( Database * db );
extern int __findingStartInitialTimeout; // configurable for testing extern int __findingStartInitialTimeout; // configurable for testing
class QueryPlan;
/** Implements an optimized procedure for finding the first op in the o plog. */
class FindingStartCursor { class FindingStartCursor {
public: public:
FindingStartCursor( const QueryPlan & qp ) :
_qp( qp ), /**
_findingStart( true ), * The cursor will attempt to find the first op in the oplog matchi ng the
_findingStartMode(), * 'ts' field of the qp's query.
_findingStartTimer( 0 ), */
_findingStartCursor( 0 ) FindingStartCursor( const QueryPlan & qp );
{ init(); }
/** @return true if the first matching op in the oplog has been fou nd. */
bool done() const { return !_findingStart; } bool done() const { return !_findingStart; }
shared_ptr<Cursor> cRelease() { return _c; }
void next() { /** @return cursor pointing to the first matching op, if done(). */
if ( !_findingStartCursor || !_findingStartCursor->c->ok() ) { shared_ptr<Cursor> cursor() { verify( 14835, done() ); return _c; }
_findingStart = false;
_c = _qp.newCursor(); // on error, start from beginning /** Iterate the cursor, to continue trying to find matching op. */
destroyClientCursor(); void next();
return;
} /** Yield cursor, if not done(). */
switch( _findingStartMode ) {
case Initial: {
if ( !_matcher->matches( _findingStartCursor->c->currKe y(), _findingStartCursor->c->currLoc() ) ) {
_findingStart = false; // found first record out of query range, so scan normally
_c = _qp.newCursor( _findingStartCursor->c->currLoc () );
destroyClientCursor();
return;
}
_findingStartCursor->c->advance();
RARELY {
if ( _findingStartTimer.seconds() >= __findingStart InitialTimeout ) {
createClientCursor( startLoc( _findingStartCurs or->c->currLoc() ) );
_findingStartMode = FindExtent;
return;
}
}
return;
}
case FindExtent: {
if ( !_matcher->matches( _findingStartCursor->c->currKe y(), _findingStartCursor->c->currLoc() ) ) {
_findingStartMode = InExtent;
return;
}
DiskLoc prev = prevLoc( _findingStartCursor->c->currLoc () );
if ( prev.isNull() ) { // hit beginning, so start scann ing from here
createClientCursor();
_findingStartMode = InExtent;
return;
}
// There might be a more efficient implementation than creating new cursor & client cursor each time,
// not worrying about that for now
createClientCursor( prev );
return;
}
case InExtent: {
if ( _matcher->matches( _findingStartCursor->c->currKey (), _findingStartCursor->c->currLoc() ) ) {
_findingStart = false; // found first record in que ry range, so scan normally
_c = _qp.newCursor( _findingStartCursor->c->currLoc () );
destroyClientCursor();
return;
}
_findingStartCursor->c->advance();
return;
}
default: {
massert( 12600, "invalid _findingStartMode", false );
}
}
}
bool prepareToYield() { bool prepareToYield() {
if ( _findingStartCursor ) { if ( _findingStartCursor ) {
return _findingStartCursor->prepareToYield( _yieldData ); return _findingStartCursor->prepareToYield( _yieldData );
} }
return true; return false;
} }
/** Recover from cursor yield. */
void recoverFromYield() { void recoverFromYield() {
if ( _findingStartCursor ) { if ( _findingStartCursor ) {
if ( !ClientCursor::recoverFromYield( _yieldData ) ) { if ( !ClientCursor::recoverFromYield( _yieldData ) ) {
_findingStartCursor = 0; _findingStartCursor.reset( 0 );
} }
} }
} }
private: private:
enum FindingStartMode { Initial, FindExtent, InExtent }; enum FindingStartMode { Initial, FindExtent, InExtent };
const QueryPlan &_qp; const QueryPlan &_qp;
bool _findingStart; bool _findingStart;
FindingStartMode _findingStartMode; FindingStartMode _findingStartMode;
auto_ptr< CoveredIndexMatcher > _matcher; auto_ptr< CoveredIndexMatcher > _matcher;
Timer _findingStartTimer; Timer _findingStartTimer;
ClientCursor * _findingStartCursor; ClientCursor::CleanupPointer _findingStartCursor;
shared_ptr<Cursor> _c; shared_ptr<Cursor> _c;
ClientCursor::YieldData _yieldData; ClientCursor::YieldData _yieldData;
DiskLoc startLoc( const DiskLoc &rec ) { DiskLoc extentFirstLoc( const DiskLoc &rec );
Extent *e = rec.rec()->myExtent( rec );
if ( !_qp.nsd()->capLooped() || ( e->myLoc != _qp.nsd()->capExt ent ) )
return e->firstRecord;
// Likely we are on the fresh side of capExtent, so return firs t fresh record.
// If we are on the stale side of capExtent, then the collectio n is small and it
// doesn't matter if we start the extent scan with capFirstNewR ecord.
return _qp.nsd()->capFirstNewRecord;
}
// should never have an empty extent in the oplog, so don't worry a bout that case DiskLoc prevExtentFirstLoc( const DiskLoc &rec );
DiskLoc prevLoc( const DiskLoc &rec ) { void createClientCursor( const DiskLoc &startLoc = DiskLoc() );
Extent *e = rec.rec()->myExtent( rec );
if ( _qp.nsd()->capLooped() ) {
if ( e->xprev.isNull() )
e = _qp.nsd()->lastExtent.ext();
else
e = e->xprev.ext();
if ( e->myLoc != _qp.nsd()->capExtent )
return e->firstRecord;
} else {
if ( !e->xprev.isNull() ) {
e = e->xprev.ext();
return e->firstRecord;
}
}
return DiskLoc(); // reached beginning of collection
}
void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) {
shared_ptr<Cursor> c = _qp.newCursor( startLoc );
_findingStartCursor = new ClientCursor(QueryOption_NoCursorTime out, c, _qp.ns());
}
void destroyClientCursor() { void destroyClientCursor() {
if ( _findingStartCursor ) { _findingStartCursor.reset( 0 );
ClientCursor::erase( _findingStartCursor->cursorid );
_findingStartCursor = 0;
}
}
void init() {
// Use a ClientCursor here so we can release db mutex while sca nning
// oplog (can take quite a while with large oplogs).
shared_ptr<Cursor> c = _qp.newReverseCursor();
_findingStartCursor = new ClientCursor(QueryOption_NoCursorTime out, c, _qp.ns(), BSONObj());
_findingStartTimer.reset();
_findingStartMode = Initial;
BSONElement tsElt = _qp.originalQuery()[ "ts" ];
massert( 13044, "no ts field in query", !tsElt.eoo() );
BSONObjBuilder b;
b.append( tsElt );
BSONObj tsQuery = b.obj();
_matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey()) );
} }
void init();
bool firstDocMatchesOrEmpty() const;
}; };
void pretouchOperation(const BSONObj& op); void pretouchOperation(const BSONObj& op);
void pretouchN(vector<BSONObj>&, unsigned a, unsigned b); void pretouchN(vector<BSONObj>&, unsigned a, unsigned b);
void applyOperation_inlock(const BSONObj& op); /**
* take an op and apply locally
* used for applying from an oplog
* @param fromRepl really from replication or for testing/internal/comm and/etc...
*/
void applyOperation_inlock(const BSONObj& op , bool fromRepl = true );
} }
 End of changes. 14 change blocks. 
119 lines changed or deleted 38 lines changed or added


 oplogreader.h   oplogreader.h 
skipping to change at line 15 skipping to change at line 15
#include "../client/dbclient.h" #include "../client/dbclient.h"
#include "../client/constants.h" #include "../client/constants.h"
#include "dbhelpers.h" #include "dbhelpers.h"
namespace mongo { namespace mongo {
/* started abstracting out the querying of the primary/master's oplog /* started abstracting out the querying of the primary/master's oplog
still fairly awkward but a start. still fairly awkward but a start.
*/ */
class OplogReader { class OplogReader {
auto_ptr<DBClientConnection> _conn; shared_ptr<DBClientConnection> _conn;
auto_ptr<DBClientCursor> cursor; shared_ptr<DBClientCursor> cursor;
public: public:
OplogReader() { OplogReader() {
} }
~OplogReader() { ~OplogReader() {
} }
void resetCursor() { void resetCursor() {
cursor.reset(); cursor.reset();
} }
void resetConnection() { void resetConnection() {
cursor.reset(); cursor.reset();
_conn.reset(); _conn.reset();
} }
DBClientConnection* conn() { return _conn.get(); } DBClientConnection* conn() { return _conn.get(); }
BSONObj findOne(const char *ns, const Query& q) { BSONObj findOne(const char *ns, const Query& q) {
return conn()->findOne(ns, q); return conn()->findOne(ns, q, 0, QueryOption_SlaveOk);
} }
BSONObj getLastOp(const char *ns) { BSONObj getLastOp(const char *ns) {
return findOne(ns, Query().sort(reverseNaturalObj)); return findOne(ns, Query().sort(reverseNaturalObj));
} }
/* ok to call if already connected */ /* ok to call if already connected */
bool connect(string hostname); bool connect(string hostname);
bool connect(const BSONObj& rid, const int from, const string& to);
void tailCheck() { void tailCheck() {
if( cursor.get() && cursor->isDead() ) { if( cursor.get() && cursor->isDead() ) {
log() << "repl: old cursor isDead, will initiate a new one" << endl; log() << "repl: old cursor isDead, will initiate a new one" << endl;
resetCursor(); resetCursor();
} }
} }
bool haveCursor() { return cursor.get() != 0; } bool haveCursor() { return cursor.get() != 0; }
void query(const char *ns, const BSONObj& query) { void query(const char *ns, const BSONObj& query) {
assert( !haveCursor() ); assert( !haveCursor() );
cursor = _conn->query(ns, query, 0, 0, 0, QueryOption_SlaveOk); cursor.reset( _conn->query(ns, query, 0, 0, 0, QueryOption_Slav eOk).release() );
} }
void tailingQuery(const char *ns, const BSONObj& query) { void queryGTE(const char *ns, OpTime t) {
BSONObjBuilder q;
q.appendDate("$gte", t.asDate());
BSONObjBuilder q2;
q2.append("ts", q.done());
query(ns, q2.done());
}
void tailingQuery(const char *ns, const BSONObj& query, const BSONO bj* fields=0) {
assert( !haveCursor() ); assert( !haveCursor() );
log(2) << "repl: " << ns << ".find(" << query.toString() << ')' << endl; log(2) << "repl: " << ns << ".find(" << query.toString() << ')' << endl;
cursor = _conn->query( ns, query, 0, 0, 0, cursor.reset( _conn->query( ns, query, 0, 0, fields,
QueryOption_CursorTailable | QueryOption_ SlaveOk | QueryOption_OplogReplay | QueryOption_CursorTailable | QueryO ption_SlaveOk | QueryOption_OplogReplay |
/* TODO: slaveok maybe shouldn't use? */ /* TODO: slaveok maybe shouldn't us e? */
QueryOption_AwaitData QueryOption_AwaitData
); ).release() );
} }
void tailingQueryGTE(const char *ns, OpTime t) { void tailingQueryGTE(const char *ns, OpTime t, const BSONObj* field s=0) {
BSONObjBuilder q; BSONObjBuilder q;
q.appendDate("$gte", t.asDate()); q.appendDate("$gte", t.asDate());
BSONObjBuilder query; BSONObjBuilder query;
query.append("ts", q.done()); query.append("ts", q.done());
tailingQuery(ns, query.done()); tailingQuery(ns, query.done(), fields);
}
/* Do a tailing query, but only send the ts field back. */
void ghostQueryGTE(const char *ns, OpTime t) {
const BSONObj fields = BSON("ts" << 1 << "_id" << 0);
return tailingQueryGTE(ns, t, &fields);
} }
bool more() { bool more() {
assert( cursor.get() ); assert( cursor.get() );
return cursor->more(); return cursor->more();
} }
bool moreInCurrentBatch() { bool moreInCurrentBatch() {
assert( cursor.get() ); assert( cursor.get() );
return cursor->moreInCurrentBatch(); return cursor->moreInCurrentBatch();
} }
skipping to change at line 96 skipping to change at line 112
return cursor->hasResultFlag(ResultFlag_AwaitCapable); return cursor->hasResultFlag(ResultFlag_AwaitCapable);
} }
void peek(vector<BSONObj>& v, int n) { void peek(vector<BSONObj>& v, int n) {
if( cursor.get() ) if( cursor.get() )
cursor->peek(v,n); cursor->peek(v,n);
} }
BSONObj nextSafe() { return cursor->nextSafe(); } BSONObj nextSafe() { return cursor->nextSafe(); }
BSONObj next() { BSONObj next() { return cursor->next(); }
return cursor->next();
}
void putBack(BSONObj op) { void putBack(BSONObj op) { cursor->putBack(op); }
cursor->putBack(op);
} private:
bool commonConnect(const string& hostName);
bool passthroughHandshake(const BSONObj& rid, const int f);
}; };
} }
 End of changes. 10 change blocks. 
18 lines changed or deleted 34 lines changed or added


 optime.h   optime.h 
skipping to change at line 26 skipping to change at line 26
*/ */
#pragma once #pragma once
#include "../db/concurrency.h" #include "../db/concurrency.h"
namespace mongo { namespace mongo {
void exitCleanly( ExitCode code ); void exitCleanly( ExitCode code );
struct ClockSkewException : public DBException { struct ClockSkewException : public DBException {
ClockSkewException() : DBException( "clock skew exception" , 20001 ){} ClockSkewException() : DBException( "clock skew exception" , 20001 ) {}
}; };
/* replsets use RSOpTime. /* replsets used to use RSOpTime.
M/S uses OpTime. M/S uses OpTime.
But this is useable from both. But this is useable from both.
*/ */
typedef unsigned long long ReplTime; typedef unsigned long long ReplTime;
/* Operation sequence #. A combination of current second plus an ordin al value. /* Operation sequence #. A combination of current second plus an ordin al value.
*/ */
#pragma pack(4) #pragma pack(4)
class OpTime { class OpTime {
unsigned i; unsigned i; // ordinal comes first so we can do a single 64 bit com pare on little endian
unsigned secs; unsigned secs;
static OpTime last; static OpTime last;
static OpTime skewed();
public: public:
static void setLast(const Date_t &date) { static void setLast(const Date_t &date) {
last = OpTime(date); last = OpTime(date);
} }
unsigned getSecs() const { unsigned getSecs() const {
return secs; return secs;
} }
unsigned getInc() const {
return i;
}
OpTime(Date_t date) { OpTime(Date_t date) {
reinterpret_cast<unsigned long long&>(*this) = date.millis; reinterpret_cast<unsigned long long&>(*this) = date.millis;
dassert( (int)secs >= 0 );
} }
OpTime(ReplTime x) { OpTime(ReplTime x) {
reinterpret_cast<unsigned long long&>(*this) = x; reinterpret_cast<unsigned long long&>(*this) = x;
dassert( (int)secs >= 0 );
} }
OpTime(unsigned a, unsigned b) { OpTime(unsigned a, unsigned b) {
secs = a; secs = a;
i = b; i = b;
dassert( (int)secs >= 0 );
}
OpTime( const OpTime& other ) {
secs = other.secs;
i = other.i;
dassert( (int)secs >= 0 );
} }
OpTime() { OpTime() {
secs = 0; secs = 0;
i = 0; i = 0;
} }
static OpTime now() { // it isn't generally safe to not be locked for this. so use now(). some tests use this.
static OpTime now_inlock() {
unsigned t = (unsigned) time(0); unsigned t = (unsigned) time(0);
// DEV assertInWriteLock();
if ( t < last.secs ){
bool toLog = false;
ONCE toLog = true;
RARELY toLog = true;
if ( last.i & 0x80000000 )
toLog = true;
if ( toLog )
log() << "clock skew detected prev: " << last.secs << " now: " << t << " trying to handle..." << endl;
if ( last.i & 0x80000000 ) {
log() << "ERROR Large clock skew detected, shutting dow n" << endl;
throw ClockSkewException();
}
t = last.secs;
}
if ( last.secs == t ) { if ( last.secs == t ) {
last.i++; last.i++;
return last; return last;
} }
if ( t < last.secs ) {
return skewed(); // separate function to keep out of the ho t code path
}
last = OpTime(t, 1); last = OpTime(t, 1);
return last; return last;
} }
static OpTime now() {
DEV dbMutex.assertWriteLocked();
return now_inlock();
}
/* We store OpTime's in the database as BSON Date datatype -- we ne eded some sort of /* We store OpTime's in the database as BSON Date datatype -- we ne eded some sort of
64 bit "container" for these values. While these are not really " Dates", that seems a 64 bit "container" for these values. While these are not really " Dates", that seems a
better choice for now than say, Number, which is floating point. Note the BinData type better choice for now than say, Number, which is floating point. Note the BinData type
is perhaps the cleanest choice, lacking a true unsigned64 datatype , but BinData has 5 is perhaps the cleanest choice, lacking a true unsigned64 datatype , but BinData has 5
bytes of overhead. bytes of overhead.
*/ */
unsigned long long asDate() const { unsigned long long asDate() const {
return reinterpret_cast<const unsigned long long*>(&i)[0]; return reinterpret_cast<const unsigned long long*>(&i)[0];
} }
 End of changes. 12 change blocks. 
19 lines changed or deleted 24 lines changed or added


 ordering.h   ordering.h 
skipping to change at line 22 skipping to change at line 22
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
namespace mongo { namespace mongo {
/** A precomputation of a BSON key pattern. // todo: ideally move to db/ instead of bson/, but elim any dependencie s first
/** A precomputation of a BSON index or sort key pattern. That is some thing like:
{ a : 1, b : -1 }
The constructor is private to make conversion more explicit so we n otice where we call make(). The constructor is private to make conversion more explicit so we n otice where we call make().
Over time we should push this up higher and higher. Over time we should push this up higher and higher.
*/ */
class Ordering { class Ordering {
const unsigned bits; unsigned bits;
const unsigned nkeys; Ordering(unsigned b) : bits(b) { }
Ordering(unsigned b,unsigned n) : bits(b),nkeys(n) { }
public: public:
Ordering(const Ordering& r) : bits(r.bits) { }
void operator=(const Ordering& r) {
bits = r.bits;
}
/** so, for key pattern { a : 1, b : -1 } /** so, for key pattern { a : 1, b : -1 }
get(0) == 1 get(0) == 1
get(1) == -1 get(1) == -1
*/ */
int get(int i) const { int get(int i) const {
return ((1 << i) & bits) ? -1 : 1; return ((1 << i) & bits) ? -1 : 1;
} }
// for woCompare... // for woCompare...
unsigned descending(unsigned mask) const { return bits & mask; } unsigned descending(unsigned mask) const { return bits & mask; }
operator string() const { /*operator string() const {
StringBuilder buf(32); StringBuilder buf(32);
for ( unsigned i=0; i<nkeys; i++) for ( unsigned i=0; i<nkeys; i++)
buf.append( get(i) > 0 ? "+" : "-" ); buf.append( get(i) > 0 ? "+" : "-" );
return buf.str(); return buf.str();
} }*/
static Ordering make(const BSONObj& obj) { static Ordering make(const BSONObj& obj) {
unsigned b = 0; unsigned b = 0;
BSONObjIterator k(obj); BSONObjIterator k(obj);
unsigned n = 0; unsigned n = 0;
while( 1 ) { while( 1 ) {
BSONElement e = k.next(); BSONElement e = k.next();
if( e.eoo() ) if( e.eoo() )
break; break;
uassert( 13103, "too many compound keys", n <= 31 ); uassert( 13103, "too many compound keys", n <= 31 );
if( e.number() < 0 ) if( e.number() < 0 )
b |= (1 << n); b |= (1 << n);
n++; n++;
} }
return Ordering(b,n); return Ordering(b);
} }
}; };
} }
 End of changes. 7 change blocks. 
8 lines changed or deleted 15 lines changed or added


 parallel.h   parallel.h 
skipping to change at line 27 skipping to change at line 27
/** /**
tools for working in parallel/sharded/clustered environment tools for working in parallel/sharded/clustered environment
*/ */
#include "../pch.h" #include "../pch.h"
#include "dbclient.h" #include "dbclient.h"
#include "redef_macros.h" #include "redef_macros.h"
#include "../db/dbmessage.h" #include "../db/dbmessage.h"
#include "../db/matcher.h" #include "../db/matcher.h"
#include "../util/concurrency/mvar.h"
namespace mongo { namespace mongo {
/** /**
* holder for a server address and a query to run * holder for a server address and a query to run
*/ */
class ServerAndQuery { class ServerAndQuery {
public: public:
ServerAndQuery( const string& server , BSONObj extra = BSONObj() , BSONObj orderObject = BSONObj() ) : ServerAndQuery( const string& server , BSONObj extra = BSONObj() , BSONObj orderObject = BSONObj() ) :
_server( server ) , _extra( extra.getOwned() ) , _orderObject( orderObject.getOwned() ){ _server( server ) , _extra( extra.getOwned() ) , _orderObject( orderObject.getOwned() ) {
} }
bool operator<( const ServerAndQuery& other ) const{ bool operator<( const ServerAndQuery& other ) const {
if ( ! _orderObject.isEmpty() ) if ( ! _orderObject.isEmpty() )
return _orderObject.woCompare( other._orderObject ) < 0; return _orderObject.woCompare( other._orderObject ) < 0;
if ( _server < other._server ) if ( _server < other._server )
return true; return true;
if ( other._server > _server ) if ( other._server > _server )
return false; return false;
return _extra.woCompare( other._extra ) < 0; return _extra.woCompare( other._extra ) < 0;
} }
skipping to change at line 91 skipping to change at line 92
static BSONObj concatQuery( const BSONObj& query , const BSONObj& e xtraFilter ); static BSONObj concatQuery( const BSONObj& query , const BSONObj& e xtraFilter );
virtual string type() const = 0; virtual string type() const = 0;
virtual BSONObj explain(); virtual BSONObj explain();
protected: protected:
virtual void _init() = 0; virtual void _init() = 0;
auto_ptr<DBClientCursor> query( const string& server , int num = 0 , BSONObj extraFilter = BSONObj() , int skipLeft = 0 ); auto_ptr<DBClientCursor> query( const string& server , int num = 0 , BSONObj extraFilter = BSONObj() , int skipLeft = 0 , bool lazy=false );
BSONObj explain( const string& server , BSONObj extraFilter = BSONO bj() ); BSONObj explain( const string& server , BSONObj extraFilter = BSONO bj() );
/**
* checks the cursor for any errors
* will throw an exceptionif an error is encountered
*/
void _checkCursor( DBClientCursor * cursor );
static BSONObj _concatFilter( const BSONObj& filter , const BSONObj & extraFilter ); static BSONObj _concatFilter( const BSONObj& filter , const BSONObj & extraFilter );
virtual void _explain( map< string,list<BSONObj> >& out ) = 0; virtual void _explain( map< string,list<BSONObj> >& out ) = 0;
string _ns; string _ns;
BSONObj _query; BSONObj _query;
int _options; int _options;
BSONObj _fields; BSONObj _fields;
int _batchSize; int _batchSize;
bool _didInit; bool _didInit;
bool _done; bool _done;
}; };
class FilteringClientCursor { class FilteringClientCursor {
public: public:
FilteringClientCursor( const BSONObj filter = BSONObj() ); FilteringClientCursor( const BSONObj filter = BSONObj() );
FilteringClientCursor( DBClientCursor* cursor , const BSONObj filte r = BSONObj() );
FilteringClientCursor( auto_ptr<DBClientCursor> cursor , const BSON Obj filter = BSONObj() ); FilteringClientCursor( auto_ptr<DBClientCursor> cursor , const BSON Obj filter = BSONObj() );
~FilteringClientCursor(); ~FilteringClientCursor();
void reset( auto_ptr<DBClientCursor> cursor ); void reset( auto_ptr<DBClientCursor> cursor );
void reset( DBClientCursor* cursor );
bool more(); bool more();
BSONObj next(); BSONObj next();
BSONObj peek(); BSONObj peek();
DBClientCursor* raw() { return _cursor.get(); }
private: private:
void _advance(); void _advance();
Matcher _matcher; Matcher _matcher;
auto_ptr<DBClientCursor> _cursor; auto_ptr<DBClientCursor> _cursor;
BSONObj _next; BSONObj _next;
bool _done; bool _done;
}; };
class Servers { class Servers {
public: public:
Servers(){ Servers() {
} }
void add( const ServerAndQuery& s ){ void add( const ServerAndQuery& s ) {
add( s._server , s._extra ); add( s._server , s._extra );
} }
void add( const string& server , const BSONObj& filter ){ void add( const string& server , const BSONObj& filter ) {
vector<BSONObj>& mine = _filters[server]; vector<BSONObj>& mine = _filters[server];
mine.push_back( filter.getOwned() ); mine.push_back( filter.getOwned() );
} }
// TOOO: pick a less horrible name // TOOO: pick a less horrible name
class View { class View {
View( const Servers* s ){ View( const Servers* s ) {
for ( map<string, vector<BSONObj> >::const_iterator i=s->_f ilters.begin(); i!=s->_filters.end(); ++i ){ for ( map<string, vector<BSONObj> >::const_iterator i=s->_f ilters.begin(); i!=s->_filters.end(); ++i ) {
_servers.push_back( i->first ); _servers.push_back( i->first );
_filters.push_back( i->second ); _filters.push_back( i->second );
} }
} }
public: public:
int size() const { int size() const {
return _servers.size(); return _servers.size();
} }
string getServer( int n ) const { string getServer( int n ) const {
skipping to change at line 197 skipping to change at line 209
class SerialServerClusteredCursor : public ClusteredCursor { class SerialServerClusteredCursor : public ClusteredCursor {
public: public:
SerialServerClusteredCursor( const set<ServerAndQuery>& servers , Q ueryMessage& q , int sortOrder=0); SerialServerClusteredCursor( const set<ServerAndQuery>& servers , Q ueryMessage& q , int sortOrder=0);
virtual bool more(); virtual bool more();
virtual BSONObj next(); virtual BSONObj next();
virtual string type() const { return "SerialServer"; } virtual string type() const { return "SerialServer"; }
protected: protected:
virtual void _explain( map< string,list<BSONObj> >& out ); virtual void _explain( map< string,list<BSONObj> >& out );
void _init(){} void _init() {}
vector<ServerAndQuery> _servers; vector<ServerAndQuery> _servers;
unsigned _serverIndex; unsigned _serverIndex;
FilteringClientCursor _current; FilteringClientCursor _current;
int _needToSkip; int _needToSkip;
}; };
/** /**
skipping to change at line 266 skipping to change at line 278
} }
/** /**
blocks until command is done blocks until command is done
returns ok() returns ok()
*/ */
bool join(); bool join();
private: private:
CommandResult( const string& server , const string& db , const BSONObj& cmd ); CommandResult( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * conn );
string _server; string _server;
string _db; string _db;
int _options;
BSONObj _cmd; BSONObj _cmd;
DBClientBase * _conn;
scoped_ptr<ScopedDbConnection> _connHolder; // used if not prov ided a connection
boost::thread _thr; scoped_ptr<DBClientCursor> _cursor;
BSONObj _res; BSONObj _res;
bool _done;
bool _ok; bool _ok;
bool _done;
friend class Future; friend class Future;
}; };
static void commandThread(); /**
* @param server server name
static shared_ptr<CommandResult> spawnCommand( const string& server , const string& db , const BSONObj& cmd ); * @param db db name
* @param cmd cmd to exec
private: * @param conn optional connection to use. will use standard poole d if non-specified
static shared_ptr<CommandResult> * _grab; */
static shared_ptr<CommandResult> spawnCommand( const string& server , const string& db , const BSONObj& cmd , int options , DBClientBase * con n = 0 );
}; };
} }
#include "undef_macros.h" #include "undef_macros.h"
 End of changes. 20 change blocks. 
18 lines changed or deleted 34 lines changed or added


 password.h   password.h 
skipping to change at line 51 skipping to change at line 51
void xparse( boost::any& value_store, void xparse( boost::any& value_store,
const std::vector<std::string>& new_tokens ) const { const std::vector<std::string>& new_tokens ) const {
if ( !value_store.empty() ) if ( !value_store.empty() )
#if BOOST_VERSION >= 104200 #if BOOST_VERSION >= 104200
boost::throw_exception( boost::program_options::validation_ error( boost::program_options::validation_error::multiple_values_not_allowe d ) ); boost::throw_exception( boost::program_options::validation_ error( boost::program_options::validation_error::multiple_values_not_allowe d ) );
#else #else
boost::throw_exception( boost::program_options::validation_ error( "multiple values not allowed" ) ); boost::throw_exception( boost::program_options::validation_ error( "multiple values not allowed" ) );
#endif #endif
else if ( !new_tokens.empty() ) else if ( !new_tokens.empty() )
boost::program_options::typed_value<std::string>::xparse boost::program_options::typed_value<std::string>::xparse
(value_store, new_tokens); (value_store, new_tokens);
else else
value_store = std::string(); value_store = std::string();
} }
}; };
std::string askPassword(); std::string askPassword();
} }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 pch.h   pch.h 
// pch.h : include file for standard system include files, /** @file pch.h : include file for standard system include files,
// or project specific include files that are used frequently, but * or project specific include files that are used frequently, but
// are changed infrequently * are changed infrequently
// */
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
skipping to change at line 34 skipping to change at line 34
#if defined(MONGO_EXPOSE_MACROS) #if defined(MONGO_EXPOSE_MACROS)
# define JS_C_STRINGS_ARE_UTF8 # define JS_C_STRINGS_ARE_UTF8
# undef SUPPORT_UCP # undef SUPPORT_UCP
# define SUPPORT_UCP # define SUPPORT_UCP
# undef SUPPORT_UTF8 # undef SUPPORT_UTF8
# define SUPPORT_UTF8 # define SUPPORT_UTF8
# undef _CRT_SECURE_NO_WARNINGS # undef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#if defined(WIN32)
#ifndef _WIN32
#define _WIN32
#endif
#endif
#if defined(_WIN32) #if defined(_WIN32)
// for rand_s() usage:
# define _CRT_RAND_S
# ifndef NOMINMAX # ifndef NOMINMAX
# define NOMINMAX # define NOMINMAX
# endif # endif
#define WIN32_LEAN_AND_MEAN
# include <winsock2.h> //this must be included before the first windows.h i nclude # include <winsock2.h> //this must be included before the first windows.h i nclude
# include <ws2tcpip.h> # include <ws2tcpip.h>
# include <wspiapi.h> # include <wspiapi.h>
# include <windows.h> # include <windows.h>
#endif #endif
#if defined(__linux__) && defined(MONGO_EXPOSE_MACROS)
// glibc's optimized versions are better than g++ builtins
# define __builtin_strcmp strcmp
# define __builtin_strlen strlen
# define __builtin_memchr memchr
# define __builtin_memcmp memcmp
# define __builtin_memcpy memcpy
# define __builtin_memset memset
# define __builtin_memmove memmove
#endif
#include <ctime> #include <ctime>
#include <cstring>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <memory> #include <memory>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <map> #include <map>
#include <vector> #include <vector>
#include <set> #include <set>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sstream> #include <sstream>
#include <signal.h> #include <signal.h>
#include "targetver.h" #include "targetver.h"
#include "time.h" #include "time.h"
#include "string.h" #include "string.h"
#include "limits.h" #include "limits.h"
#include <boost/any.hpp> //#include <boost/any.hpp>
#include <boost/archive/iterators/transform_width.hpp> #include "boost/thread/once.hpp"
//#include <boost/archive/iterators/transform_width.hpp>
#define BOOST_FILESYSTEM_VERSION 2
#include <boost/filesystem/convenience.hpp> #include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/exception.hpp> #include <boost/filesystem/exception.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/smart_ptr.hpp> #include <boost/smart_ptr.hpp>
#include <boost/function.hpp> #include <boost/function.hpp>
#include "boost/bind.hpp" #include "boost/bind.hpp"
#include "boost/function.hpp" #include "boost/function.hpp"
#include <boost/thread/tss.hpp> #include <boost/thread/tss.hpp>
skipping to change at line 114 skipping to change at line 123
// pdfile versions // pdfile versions
const int VERSION = 4; const int VERSION = 4;
const int VERSION_MINOR = 5; const int VERSION_MINOR = 5;
enum ExitCode { enum ExitCode {
EXIT_CLEAN = 0 , EXIT_CLEAN = 0 ,
EXIT_BADOPTIONS = 2 , EXIT_BADOPTIONS = 2 ,
EXIT_REPLICATION_ERROR = 3 , EXIT_REPLICATION_ERROR = 3 ,
EXIT_NEED_UPGRADE = 4 , EXIT_NEED_UPGRADE = 4 ,
EXIT_SHARDING_ERROR = 5 ,
EXIT_KILL = 12 , EXIT_KILL = 12 ,
EXIT_ABRUPT = 14 , EXIT_ABRUPT = 14 ,
EXIT_NTSERVICE_ERROR = 20 , EXIT_NTSERVICE_ERROR = 20 ,
EXIT_JAVA = 21 , EXIT_JAVA = 21 ,
EXIT_OOM_MALLOC = 42 , EXIT_OOM_MALLOC = 42 ,
EXIT_OOM_REALLOC = 43 , EXIT_OOM_REALLOC = 43 ,
EXIT_FS = 45 , EXIT_FS = 45 ,
EXIT_CLOCK_SKEW = 47 , EXIT_CLOCK_SKEW = 47 ,
EXIT_NET_ERROR = 48 , EXIT_NET_ERROR = 48 ,
EXIT_POSSIBLE_CORRUPTION = 60 , // this means we detected a possibl e corruption situation, like a buf overflow EXIT_POSSIBLE_CORRUPTION = 60 , // this means we detected a possibl e corruption situation, like a buf overflow
skipping to change at line 139 skipping to change at line 149
void dbexit( ExitCode returnCode, const char *whyMsg = "", bool tryToGe tLock = false); void dbexit( ExitCode returnCode, const char *whyMsg = "", bool tryToGe tLock = false);
/** /**
this is here so you can't just type exit() to quit the program this is here so you can't just type exit() to quit the program
you should either use dbexit to shutdown cleanly, or ::exit to tell the system to quit you should either use dbexit to shutdown cleanly, or ::exit to tell the system to quit
if you use this, you'll get a link error since mongo::exit isn't def ined if you use this, you'll get a link error since mongo::exit isn't def ined
*/ */
void exit( ExitCode returnCode ); void exit( ExitCode returnCode );
bool inShutdown(); bool inShutdown();
} // namespace mongo
namespace mongo {
using namespace boost::filesystem; using namespace boost::filesystem;
void asserted(const char *msg, const char *file, unsigned line); void asserted(const char *msg, const char *file, unsigned line);
} }
#define MONGO_assert(_Expression) (void)( (!!(_Expression)) || (mongo::asse rted(#_Expression, __FILE__, __LINE__), 0) ) // TODO: Rework the headers so we don't need this craziness
#include "bson/inline_decls.h"
#define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::asserted(#_Expression, __FILE__, __LINE__), 0) )
#include "util/debug_util.h" #include "util/debug_util.h"
#include "util/goodies.h" #include "util/goodies.h"
#include "util/log.h" #include "util/log.h"
#include "util/allocator.h" #include "util/allocator.h"
#include "util/assert_util.h" #include "util/assert_util.h"
namespace mongo { namespace mongo {
void sayDbContext(const char *msg = 0); void sayDbContext(const char *msg = 0);
void rawOut( const string &s ); void rawOut( const string &s );
} // namespace mongo
namespace mongo {
typedef char _TCHAR; typedef char _TCHAR;
using boost::uint32_t; using boost::uint32_t;
using boost::uint64_t; using boost::uint64_t;
/** called by mongos, mongod, test. do not call from clients and such.
invoked before about everything except global var construction.
*/
void doPreServerStatupInits();
} // namespace mongo } // namespace mongo
#endif // MONGO_PCH_H #endif // MONGO_PCH_H
 End of changes. 12 change blocks. 
22 lines changed or deleted 32 lines changed or added


 pdfile.h   pdfile.h 
skipping to change at line 32 skipping to change at line 32
database.2 database.2
... ...
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/mmap.h" #include "../util/mmap.h"
#include "diskloc.h" #include "diskloc.h"
#include "jsobjmanipulator.h" #include "jsobjmanipulator.h"
#include "namespace.h" #include "namespace-inl.h"
#include "client.h" #include "client.h"
#include "mongommf.h"
namespace mongo { namespace mongo {
class DataFileHeader; class DataFileHeader;
class Extent; class Extent;
class Record; class Record;
class Cursor; class Cursor;
class OpDebug; class OpDebug;
void dropDatabase(string db); void dropDatabase(string db);
bool repairDatabase(string db, string &errmsg, bool preserveClonedFiles OnFailure = false, bool backupOriginalFiles = false); bool repairDatabase(string db, string &errmsg, bool preserveClonedFiles OnFailure = 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 *deferIdIndex = 0); bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe plication, bool *deferIdIndex = 0);
shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, const DiskLoc &startLoc=DiskLoc()); shared_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, const DiskLoc &startLoc=DiskLoc());
// -1 if library unavailable. bool isValidNS( const StringData& ns );
boost::intmax_t freeSpace( const string &path = dbpath );
/*--------------------------------------------------------------------- */ /*--------------------------------------------------------------------- */
class MongoDataFile { class MongoDataFile {
friend class DataFileMgr; friend class DataFileMgr;
friend class BasicCursor; friend class BasicCursor;
public: public:
MongoDataFile(int fn) : fileNo(fn) { } MongoDataFile(int fn) : _mb(0), 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);
DataFileHeader *getHeader() { DataFileHeader *getHeader() { return header(); }
return header;
} unsigned long long length() const { return mmf.length(); }
/* return max size an extent may be */ /* return max size an extent may be */
static int maxSize(); static int maxSize();
/** fsync */
void flush( bool sync ); void flush( bool sync );
/** only use fore debugging */
Extent* debug_getExtent(DiskLoc loc) { return _getExtent( loc ); }
private: private:
void badOfs(int) const; void badOfs(int) const;
void badOfs2(int) const; void badOfs2(int) const;
int defaultSize( const char *filename ) const; int defaultSize( const char *filename ) const;
Extent* getExtent(DiskLoc loc); Extent* getExtent(DiskLoc loc) const;
Extent* _getExtent(DiskLoc loc); Extent* _getExtent(DiskLoc loc) const;
Record* recordAt(DiskLoc dl); Record* recordAt(DiskLoc dl);
Record* makeRecord(DiskLoc dl, int size); Record* makeRecord(DiskLoc dl, int size);
void grow(DiskLoc dl, int size); void grow(DiskLoc dl, int size);
MMF mmf; char* p() const { return (char *) _mb; }
MMF::Pointer _p; DataFileHeader* header() { return (DataFileHeader*) _mb; }
DataFileHeader *header;
MongoMMF mmf;
void *_mb; // the memory mapped view
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. */ /* see if we can find an extent of the right size in the freelist. */
static Extent* allocFromFreeList(const char *ns, int approxSize, bo ol capped = false); static Extent* allocFromFreeList(const char *ns, int approxSize, bo ol capped = false);
/** @return DiskLoc where item ends up */ /** @return DiskLoc where item ends up */
// changedId should be initialized to false // changedId should be initialized to false
const DiskLoc updateRecord( const DiskLoc updateRecord(
const char *ns, const char *ns,
NamespaceDetails *d, NamespaceDetails *d,
NamespaceDetailsTransient *nsdt, NamespaceDetailsTransient *nsdt,
Record *toupdate, const DiskLoc& dl, Record *toupdate, const DiskLoc& dl,
const char *buf, int len, OpDebug& debug, bool &changedId, bool god=false); const char *buf, int len, OpDebug& debug, bool god=false);
// 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 );
/** @param obj both and in and out param -- insert can sometimes mo dify an object (such as add _id). */ /** insert will add an _id to the object if not present. if you wo uld like to see the final object
DiskLoc insertWithObjMod(const char *ns, BSONObj &o, bool god = fal se); after such an addition, use this method.
@param o both and in and out param
*/
DiskLoc insertWithObjMod(const char *ns, BSONObj & /*out*/o, bool g od = false);
/** @param obj in value only for this version. */ /** @param obj in value only for this version. */
void insertNoReturnVal(const char *ns, BSONObj o, bool god = false) ; void insertNoReturnVal(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, bool mayAddIndex = true, bool *addedID = 0);
void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false, bool noWarn = false);
static shared_ptr<Cursor> findAll(const char *ns, const DiskLoc &st artLoc = DiskLoc()); static shared_ptr<Cursor> findAll(const char *ns, const DiskLoc &st artLoc = 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
*/ */
Record* fast_oplog_insert(NamespaceDetails *d, const char *ns, int len); Record* fast_oplog_insert(NamespaceDetails *d, const char *ns, int len);
static Extent* getExtent(const DiskLoc& dl); static Extent* getExtent(const DiskLoc& dl);
static Record* getRecord(const DiskLoc& dl); static Record* getRecord(const DiskLoc& dl);
static DeletedRecord* makeDeletedRecord(const DiskLoc& dl, int len) ; static DeletedRecord* makeDeletedRecord(const DiskLoc& dl, int len) ;
static void grow(const DiskLoc& dl, int len);
/* does not clean up indexes, etc. : just deletes the record in the pdfile. */ void deleteRecord(const char *ns, Record *todelete, const DiskLoc& dl, bool cappedOK = false, bool noWarn = false, bool logOp=false);
/* does not clean up indexes, etc. : just deletes the record in the pdfile. use deleteRecord() to unindex */
void _deleteRecord(NamespaceDetails *d, const char *ns, Record *tod elete, const DiskLoc& dl); void _deleteRecord(NamespaceDetails *d, const char *ns, Record *tod elete, const DiskLoc& dl);
private: private:
vector<MongoDataFile *> files; vector<MongoDataFile *> files;
}; };
extern DataFileMgr theDataFileMgr; extern DataFileMgr theDataFileMgr;
#pragma pack(1) #pragma pack(1)
class DeletedRecord { class DeletedRecord {
public: public:
int lengthWithHeaders; int lengthWithHeaders;
int extentOfs; int extentOfs;
DiskLoc nextDeleted; DiskLoc nextDeleted;
DiskLoc myExtentLoc(const DiskLoc& myLoc) const {
return DiskLoc(myLoc.a(), extentOfs);
}
Extent* myExtent(const DiskLoc& myLoc) { Extent* myExtent(const DiskLoc& myLoc) {
return DataFileMgr::getExtent(DiskLoc(myLoc.a(), extentOfs)); return DataFileMgr::getExtent(DiskLoc(myLoc.a(), extentOfs));
} }
}; };
/* Record is a record in a datafile. DeletedRecord is similar but for deleted space. /* Record is a record in a datafile. DeletedRecord is similar but for deleted space.
*11:03:20 AM) dm10gen: regarding extentOfs... *11:03:20 AM) dm10gen: regarding extentOfs...
(11:03:42 AM) dm10gen: an extent is a continugous disk area, which cont ains many Records and DeleteRecords (11:03:42 AM) dm10gen: an extent is a continugous disk area, which cont ains many Records and DeleteRecords
(11:03:56 AM) dm10gen: a DiskLoc has two pieces, the fileno and ofs. ( 64 bit total) (11:03:56 AM) dm10gen: a DiskLoc has two pieces, the fileno and ofs. ( 64 bit total)
(11:04:16 AM) dm10gen: to keep the headesr small, instead of storing a 64 bit ptr to the full extent address, we keep just the offset (11:04:16 AM) dm10gen: to keep the headesr small, instead of storing a 64 bit ptr to the full extent address, we keep just the offset
(11:04:29 AM) dm10gen: we can do this as we know the record's address, and it has the same fileNo (11:04:29 AM) dm10gen: we can do this as we know the record's address, and it has the same fileNo
(11:04:33 AM) dm10gen: see class DiskLoc for more info (11:04:33 AM) dm10gen: see class DiskLoc for more info
(11:04:43 AM) dm10gen: so that is how Record::myExtent() works (11:04:43 AM) dm10gen: so that is how Record::myExtent() works
(11:04:53 AM) dm10gen: on an alloc(), when we build a new Record, we mu st popular its extentOfs then (11:04:53 AM) dm10gen: on an alloc(), when we build a new Record, we mu st populate its extentOfs then
*/ */
class Record { class Record {
public: public:
enum HeaderSizeValue { HeaderSize = 16 }; enum HeaderSizeValue { HeaderSize = 16 };
int lengthWithHeaders; int lengthWithHeaders;
int extentOfs; int extentOfs;
int nextOfs; int nextOfs;
int prevOfs; int prevOfs;
/** be careful when referencing this that your write intent was cor rect */
char data[4]; char data[4];
int netLength() { int netLength() {
return lengthWithHeaders - HeaderSize; return lengthWithHeaders - HeaderSize;
} }
//void setNewLength(int netlen) { lengthWithHeaders = netlen + Head erSize; } //void setNewLength(int netlen) { lengthWithHeaders = netlen + Head erSize; }
/* use this when a record is deleted. basically a union with next/p rev fields */ /* use this when a record is deleted. basically a union with next/p rev fields */
DeletedRecord& asDeleted() { DeletedRecord& asDeleted() {
return *((DeletedRecord*) this); return *((DeletedRecord*) this);
} }
skipping to change at line 196 skipping to change at line 209
DeletedRecord& asDeleted() { DeletedRecord& asDeleted() {
return *((DeletedRecord*) this); return *((DeletedRecord*) this);
} }
Extent* myExtent(const DiskLoc& myLoc) { Extent* myExtent(const DiskLoc& myLoc) {
return DataFileMgr::getExtent(DiskLoc(myLoc.a(), extentOfs)); return DataFileMgr::getExtent(DiskLoc(myLoc.a(), extentOfs));
} }
/* get the next record in the namespace, traversing extents as nece ssary */ /* get the next record in the namespace, traversing extents as nece ssary */
DiskLoc getNext(const DiskLoc& myLoc); DiskLoc getNext(const DiskLoc& myLoc);
DiskLoc getPrev(const DiskLoc& myLoc); DiskLoc getPrev(const DiskLoc& myLoc);
DiskLoc nextInExtent(const DiskLoc& myLoc) {
if ( nextOfs == DiskLoc::NullOfs )
return DiskLoc();
assert( nextOfs );
return DiskLoc(myLoc.a(), nextOfs);
}
struct NP {
int nextOfs;
int prevOfs;
};
NP* np() { return (NP*) &nextOfs; }
// ---------------------
// memory cache
// ---------------------
/**
* touches the data so that is in physical memory
* @param entireRecrd if false, only the header and first byte is t ouched
* if true, the entire record is touched
* */
void touch( bool entireRecrd = false );
/**
* @return if this record is likely in physical memory
* its not guaranteed because its possible it gets swapped out in a very unlucky windows
*/
bool likelyInPhysicalMemory();
/**
* tell the cache this Record was accessed
* @return this, for simple chaining
*/
Record* accessed();
static bool MemoryTrackingEnabled;
}; };
/* extents are datafile regions where all the records within the region /* extents are datafile regions where all the records within the region
belong to the same namespace. belong to the same namespace.
(11:12:35 AM) dm10gen: when the extent is allocated, all its empty spac e is stuck into one big DeletedRecord (11:12:35 AM) dm10gen: when the extent is allocated, all its empty spac e is stuck into one big DeletedRecord
(11:12:55 AM) dm10gen: and that is placed on the free list (11:12:55 AM) dm10gen: and that is placed on the free list
*/ */
class Extent { class Extent {
public: public:
unsigned magic; unsigned magic;
DiskLoc myLoc; DiskLoc myLoc;
DiskLoc xnext, xprev; /* next/prev extent for this namespace */ DiskLoc xnext, xprev; /* next/prev extent for this namespace */
/* which namespace this extent is for. this is just for troublesho oting really /* which namespace this extent is for. this is just for troublesho oting really
and won't even be correct if the collection were renamed! and won't even be correct if the collection were renamed!
*/ */
Namespace nsDiagnostic; Namespace nsDiagnostic;
int length; /* size of the extent, including these fields */ int length; /* size of the extent, including these fields */
DiskLoc firstRecord, lastRecord; DiskLoc firstRecord;
DiskLoc lastRecord;
char _extentData[4]; char _extentData[4];
static int HeaderSize() { return sizeof(Extent)-4; } static int HeaderSize() { return sizeof(Extent)-4; }
bool validates() { bool validates() {
return !(firstRecord.isNull() ^ lastRecord.isNull()) && return !(firstRecord.isNull() ^ lastRecord.isNull()) &&
length >= 0 && !myLoc.isNull(); length >= 0 && !myLoc.isNull();
} }
BSONObj dump() {
return BSON( "loc" << myLoc.toString() << "xnext" << xnext.toSt ring() << "xprev" << xprev.toString()
<< "nsdiag" << nsDiagnostic.toString()
<< "size" << length << "firstRecord" << firstRecord.t oString() << "lastRecord" << lastRecord.toString());
}
void dump(iostream& s) { void dump(iostream& s) {
s << " loc:" << myLoc.toString() << " xnext:" << xnext.toStr ing() << " xprev:" << xprev.toString() << '\n'; s << " loc:" << myLoc.toString() << " xnext:" << xnext.toStr ing() << " xprev:" << xprev.toString() << '\n';
s << " nsdiag:" << nsDiagnostic.buf << '\n'; s << " nsdiag:" << nsDiagnostic.toString() << '\n';
s << " size:" << length << " firstRecord:" << firstRecord.to String() << " lastRecord:" << lastRecord.toString() << '\n'; s << " size:" << length << " firstRecord:" << firstRecord.to String() << " lastRecord:" << lastRecord.toString() << '\n';
} }
/* assumes already zeroed -- insufficient for block 'reuse' perhaps /* assumes already zeroed -- insufficient for block 'reuse' perhaps
Returns a DeletedRecord location which is the data in the extent re ady for us. Returns a DeletedRecord location which is the data in the extent re ady for us.
Caller will need to add that to the freelist structure in namespace detail. Caller will need to add that to the freelist structure in namespace detail.
*/ */
DiskLoc init(const char *nsname, int _length, int _fileNo, int _off set); DiskLoc init(const char *nsname, int _length, int _fileNo, int _off set, bool capped);
/* like init(), but for a reuse case */ /* like init(), but for a reuse case */
DiskLoc reuse(const char *nsname); DiskLoc reuse(const char *nsname, bool newUseIsAsCapped);
void assertOk() { bool isOk() const { return magic == 0x41424344; }
assert(magic == 0x41424344); void assertOk() const { assert(isOk()); }
}
Record* newRecord(int len); Record* newRecord(int len);
Record* getRecord(DiskLoc dl) { Record* getRecord(DiskLoc dl) {
assert( !dl.isNull() ); assert( !dl.isNull() );
assert( dl.sameFile(myLoc) ); assert( dl.sameFile(myLoc) );
int x = dl.getOfs() - myLoc.getOfs(); int x = dl.getOfs() - myLoc.getOfs();
assert( x > 0 ); assert( x > 0 );
return (Record *) (((char *) this) + x); return (Record *) (((char *) this) + x);
} }
Extent* getNextExtent() { Extent* getNextExtent() { return xnext.isNull() ? 0 : DataFileMgr:: getExtent(xnext); }
return xnext.isNull() ? 0 : DataFileMgr::getExtent(xnext); Extent* getPrevExtent() { return xprev.isNull() ? 0 : DataFileMgr:: getExtent(xprev); }
}
Extent* getPrevExtent() {
return xprev.isNull() ? 0 : DataFileMgr::getExtent(xprev);
}
static int maxSize(); static int maxSize();
static int minSize() { return 0x100; }
/**
* @param len lengt of record we need
* @param lastRecord size of last extent which is a factor in next extent size
*/
static int followupSize(int len, int lastExtentLen);
/** get a suggested size for the first extent in a namespace
* @param len length of record we need to insert
*/
static int initialSize(int len);
struct FL {
DiskLoc firstRecord;
DiskLoc lastRecord;
};
/** often we want to update just the firstRecord and lastRecord fie lds.
this helper is for that -- for use with getDur().writing() meth od
*/
FL* fl() { return (FL*) &firstRecord; }
/** caller must declare write intent first */
void markEmpty();
private:
DiskLoc _reuse(const char *nsname, bool newUseIsAsCapped); // recyc le an extent and reuse it for a different ns
}; };
/* /* a datafile - i.e. the "dbname.<#>" files :
---------------------- ----------------------
Header DataFileHeader
---------------------- ----------------------
Extent (for a particular namespace) Extent (for a particular namespace)
Record Record
... ...
Record (some chained for unused space) Record (some chained for unused space)
---------------------- ----------------------
more Extents... more Extents...
---------------------- ----------------------
*/ */
class DataFileHeader { class DataFileHeader {
public: public:
int version; int version;
int versionMinor; int versionMinor;
int fileLength; int fileLength;
DiskLoc unused; /* unused is the portion of the file that doesn't b elong to any allocated extents. -1 = no more */ DiskLoc unused; /* unused is the portion of the file that doesn't b elong to any allocated extents. -1 = no more */
int unusedLength; int unusedLength;
char reserved[8192 - 4*4 - 8]; char reserved[8192 - 4*4 - 8];
char data[4]; char data[4]; // first extent starts here
enum { HeaderSize = 8192 }; enum { HeaderSize = 8192 };
bool currentVersion() const { bool isCurrentVersion() const { return ( version == VERSION ) && ( versionMinor == VERSION_MINOR ); }
return ( version == VERSION ) && ( versionMinor == VERSION_MINO R );
}
bool uninitialized() const {
if ( version == 0 ) return true;
return false;
}
/*Record* __getRecord(DiskLoc dl) { bool uninitialized() const { return version == 0; }
int ofs = dl.getOfs();
assert( ofs >= HeaderSize );
return (Record*) (((char *) this) + ofs);
}*/
void init(int fileno, int filelength) { void init(int fileno, int filelength, const char* filename) {
if ( uninitialized() ) { if ( uninitialized() ) {
assert(filelength > 32768 ); if( !(filelength > 32768 ) ) {
massert(13640, str::stream() << "DataFileHeader looks c orrupt at file open filelength:" << filelength << " fileno:" << fileno, fal se);
}
getDur().createdFile(filename, filelength);
assert( HeaderSize == 8192 ); assert( HeaderSize == 8192 );
fileLength = filelength; DataFileHeader *h = getDur().writing(this);
version = VERSION; h->fileLength = filelength;
versionMinor = VERSION_MINOR; h->version = VERSION;
unused.setOfs( fileno, HeaderSize ); h->versionMinor = VERSION_MINOR;
h->unused.set( fileno, HeaderSize );
assert( (data-(char*)this) == HeaderSize ); assert( (data-(char*)this) == HeaderSize );
unusedLength = fileLength - HeaderSize - 16; h->unusedLength = fileLength - HeaderSize - 16;
//memcpy(data+unusedLength, " \nthe end\n", 16);
} }
} }
bool isEmpty() const { bool isEmpty() const {
return uninitialized() || ( unusedLength == fileLength - Header Size - 16 ); return uninitialized() || ( unusedLength == fileLength - Header Size - 16 );
} }
}; };
#pragma pack() #pragma pack()
inline Extent* MongoDataFile::_getExtent(DiskLoc loc) { inline Extent* MongoDataFile::_getExtent(DiskLoc loc) const {
loc.assertOk(); loc.assertOk();
Extent *e = (Extent *) _p.at(loc.getOfs(), Extent::HeaderSize()); Extent *e = (Extent *) (p()+loc.getOfs());
return e; return e;
} }
inline Extent* MongoDataFile::getExtent(DiskLoc loc) { inline Extent* MongoDataFile::getExtent(DiskLoc loc) const {
Extent *e = _getExtent(loc); Extent *e = _getExtent(loc);
e->assertOk(); e->assertOk();
return e; return e;
} }
} // namespace mongo } // namespace mongo
#include "cursor.h" #include "cursor.h"
namespace mongo { namespace mongo {
inline Record* MongoDataFile::recordAt(DiskLoc dl) { inline Record* MongoDataFile::recordAt(DiskLoc dl) {
int ofs = dl.getOfs(); int ofs = dl.getOfs();
if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert - external call to keep out of the normal code path if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert - external call to keep out of the normal code path
return (Record*) _p.at(ofs, -1); return (Record*) (p()+ofs);
} }
inline void MongoDataFile::grow(DiskLoc dl, int size) {
int ofs = dl.getOfs();
_p.grow(ofs, size);
}
inline Record* MongoDataFile::makeRecord(DiskLoc dl, int size) { inline Record* MongoDataFile::makeRecord(DiskLoc dl, int size) {
int ofs = dl.getOfs(); int ofs = dl.getOfs();
if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert - external call to keep out of the normal code path if( ofs < DataFileHeader::HeaderSize ) badOfs(ofs); // will uassert - external call to keep out of the normal code path
return (Record*) _p.at(ofs, size); return (Record*) (p()+ofs);
} }
inline DiskLoc Record::getNext(const DiskLoc& myLoc) { inline DiskLoc Record::getNext(const DiskLoc& myLoc) {
if ( nextOfs != DiskLoc::NullOfs ) { if ( nextOfs != DiskLoc::NullOfs ) {
/* defensive */ /* defensive */
if ( nextOfs >= 0 && nextOfs < 10 ) { if ( nextOfs >= 0 && nextOfs < 10 ) {
sayDbContext("Assertion failure - Record::getNext() referen cing a deleted record?"); sayDbContext("Assertion failure - Record::getNext() referen cing a deleted record?");
return DiskLoc(); return DiskLoc();
} }
skipping to change at line 396 skipping to change at line 461
Extent *e = myExtent(myLoc); Extent *e = myExtent(myLoc);
if ( e->xprev.isNull() ) if ( e->xprev.isNull() )
return DiskLoc(); return DiskLoc();
return e->xprev.ext()->lastRecord; return e->xprev.ext()->lastRecord;
} }
inline Record* DiskLoc::rec() const { inline Record* DiskLoc::rec() const {
return DataFileMgr::getRecord(*this); return DataFileMgr::getRecord(*this);
} }
inline BSONObj DiskLoc::obj() const { inline BSONObj DiskLoc::obj() const {
return BSONObj(rec()); return BSONObj(rec()->accessed());
} }
inline DeletedRecord* DiskLoc::drec() const { inline DeletedRecord* DiskLoc::drec() const {
assert( fileNo != -1 ); assert( _a != -1 );
return (DeletedRecord*) rec(); return (DeletedRecord*) rec();
} }
inline Extent* DiskLoc::ext() const { inline Extent* DiskLoc::ext() const {
return DataFileMgr::getExtent(*this); return DataFileMgr::getExtent(*this);
} }
/*--------------------------------------------------------------------- */ template< class V >
inline
const BtreeBucket<V> * DiskLoc::btree() const {
assert( _a != -1 );
return (const BtreeBucket<V> *) rec()->data;
}
} // namespace mongo } // namespace mongo
#include "rec.h"
#include "database.h" #include "database.h"
namespace mongo { namespace mongo {
// Heritable class to implement an operation that may be applied to all
// files in a database using _applyOpToDataFiles()
class FileOp {
public:
virtual ~FileOp() {}
// Return true if file exists and operation successful
virtual bool apply( const boost::filesystem::path &p ) = 0;
virtual const char * op() const = 0;
};
void _applyOpToDataFiles( const char *database, FileOp &fo, bool afterA llocator = false, const string& path = dbpath );
inline void _deleteDataFiles(const char *database) {
if ( directoryperdb ) {
BOOST_CHECK_EXCEPTION( boost::filesystem::remove_all( boost::fi lesystem::path( dbpath ) / database ) );
return;
}
class : public FileOp {
virtual bool apply( const boost::filesystem::path &p ) {
return boost::filesystem::remove( p );
}
virtual const char * op() const {
return "remove";
}
} deleter;
_applyOpToDataFiles( database, deleter, true );
}
boost::intmax_t dbSize( const char *database ); boost::intmax_t dbSize( const char *database );
inline NamespaceIndex* nsindex(const char *ns) { inline NamespaceIndex* nsindex(const char *ns) {
Database *database = cc().database(); Database *database = cc().database();
assert( database ); assert( database );
DEV { DEV {
char buf[256]; char buf[256];
nsToDatabase(ns, buf); nsToDatabase(ns, buf);
if ( database->name != buf ) { if ( database->name != buf ) {
out() << "ERROR: attempt to write to wrong database databas e\n"; out() << "ERROR: attempt to write to wrong database databas e\n";
skipping to change at line 466 skipping to change at line 507
} }
} }
return &database->namespaceIndex; return &database->namespaceIndex;
} }
inline NamespaceDetails* nsdetails(const char *ns) { inline NamespaceDetails* nsdetails(const char *ns) {
// if this faults, did you set the current db first? (Client::Cont ext + dblock) // if this faults, did you set the current db first? (Client::Cont ext + dblock)
return nsindex(ns)->details(ns); return nsindex(ns)->details(ns);
} }
inline MongoDataFile& DiskLoc::pdf() const {
assert( fileNo != -1 );
return *cc().database()->getFile(fileNo);
}
inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) {
assert( dl.a() != -1 ); assert( dl.a() != -1 );
return cc().database()->getFile(dl.a())->getExtent(dl); return cc().database()->getFile(dl.a())->getExtent(dl);
} }
inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { inline Record* DataFileMgr::getRecord(const DiskLoc& dl) {
assert( dl.a() != -1 ); assert( dl.a() != -1 );
return cc().database()->getFile(dl.a())->recordAt(dl); return cc().database()->getFile(dl.a())->recordAt(dl);
} }
BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) );
inline void DataFileMgr::grow(const DiskLoc& dl, int len) {
assert( dl.a() != -1 );
cc().database()->getFile(dl.a())->grow(dl, len);
}
inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl, int len) { inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl, int len) {
assert( dl.a() != -1 ); assert( dl.a() != -1 );
return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor d(dl, sizeof(DeletedRecord)); return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor d(dl, sizeof(DeletedRecord));
} }
void ensureHaveIdIndex(const char *ns); void ensureHaveIdIndex(const char *ns);
bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name , string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name , string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex );
/** inline BSONObj::BSONObj(const Record *r) {
* @return true if ns is ok init(r->data);
*/
inline bool nsDollarCheck( const char* ns ){
if ( strchr( ns , '$' ) == 0 )
return true;
return strcmp( ns, "local.oplog.$main" ) == 0;
} }
} // namespace mongo } // namespace mongo
 End of changes. 54 change blocks. 
121 lines changed or deleted 147 lines changed or added


 processinfo.h   processinfo.h 
skipping to change at line 56 skipping to change at line 56
*/ */
int getResidentSize(); int getResidentSize();
/** /**
* Append platform-specific data to obj * Append platform-specific data to obj
*/ */
void getExtraInfo(BSONObjBuilder& info); void getExtraInfo(BSONObjBuilder& info);
bool supported(); bool supported();
bool blockCheckSupported(); static bool blockCheckSupported();
bool blockInMemory( char * start ); static bool blockInMemory( char * start );
private: private:
pid_t _pid; pid_t _pid;
}; };
void writePidFile( const std::string& path ); void writePidFile( const std::string& path );
void printMemInfo( const char * whereContextStr = 0 );
} }
 End of changes. 2 change blocks. 
2 lines changed or deleted 4 lines changed or added


 query.h   query.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 "../pch.h" #include "../../pch.h"
#include "../util/message.h" #include "../../util/net/message.h"
#include "dbmessage.h" #include "../dbmessage.h"
#include "jsobj.h" #include "../jsobj.h"
#include "diskloc.h" #include "../diskloc.h"
#include "../projection.h"
/* db request message format
unsigned opid; // arbitary; will be echoed back
byte operation;
int options;
then for:
dbInsert:
string collection;
a series of JSObjects
dbDelete:
string collection;
int flags=0; // 1=DeleteSingle
JSObject query;
dbUpdate:
string collection;
int flags; // 1=upsert
JSObject query;
JSObject objectToUpdate;
objectToUpdate may include { $inc: <field> } or { $set: ... }, see struct Mod.
dbQuery:
string collection;
int nToSkip;
int nToReturn; // how many you want back as the beginning of the c ursor data (0=no limit)
// greater than zero is simply a hint on how many obje cts to send back per "cursor batch".
// a negative number indicates a hard limit.
JSObject query;
[JSObject fieldsToReturn]
dbGetMore:
string collection; // redundant, might use for security.
int nToReturn;
int64 cursorID;
dbKillCursors=2007:
int n;
int64 cursorIDs[n];
Note that on Update, there is only one object, which is different
from insert where you can pass a list of objects to insert in the db.
Note that the update field layout is very similar layout to Query.
*/
// struct QueryOptions, QueryResult, QueryResultFlags in: // struct QueryOptions, QueryResult, QueryResultFlags in:
#include "../client/dbclient.h" #include "../../client/dbclient.h"
namespace mongo { namespace mongo {
extern const int MaxBytesToReturnToClientAtOnce; extern const int MaxBytesToReturnToClientAtOnce;
// for an existing query (ie a ClientCursor), send back additional info rmation.
struct GetMoreWaitException { };
QueryResult* processGetMore(const char *ns, int ntoreturn, long long cu rsorid , CurOp& op, int pass, bool& exhaust); QueryResult* processGetMore(const char *ns, int ntoreturn, long long cu rsorid , CurOp& op, int pass, bool& exhaust);
struct UpdateResult {
bool existing; // if existing objects were modified
bool mod; // was this a $ mod
long long num; // how many objects touched
OID upserted; // if something was upserted, the new _id of the obj ect
UpdateResult( bool e, bool m, unsigned long long n , const BSONObj& upsertedObject = BSONObj() )
: existing(e) , mod(m), num(n){
upserted.clear();
BSONElement id = upsertedObject["_id"];
if ( ! e && n == 1 && id.type() == jstOID ){
upserted = id.OID();
}
}
};
class RemoveSaver;
/* returns true if an existing object was updated, false if no existing object was found.
multi - update multiple objects - mostly useful with things like $se t
god - allow access to system namespaces
*/
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BS ONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug );
UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& up dateobj, BSONObj pattern,
bool upsert, bool multi , bool logop , OpDe bug& debug , RemoveSaver * rs = 0 );
// If justOne is true, deletedId is set to the id of the deleted object .
long long deleteObjects(const char *ns, BSONObj pattern, bool justOne, bool logop = false, bool god=false, RemoveSaver * rs=0);
long long runCount(const char *ns, const BSONObj& cmd, string& err); long long runCount(const char *ns, const BSONObj& cmd, string& err);
const char * runQuery(Message& m, QueryMessage& q, CurOp& curop, Messag e &result); const char * runQuery(Message& m, QueryMessage& q, CurOp& curop, Messag e &result);
/* This is for languages whose "objects" are not well ordered (JSON is well ordered). /* This is for languages whose "objects" are not well ordered (JSON is well ordered).
[ { a : ... } , { b : ... } ] -> { a : ..., b : ... } [ { a : ... } , { b : ... } ] -> { a : ..., b : ... }
*/ */
inline BSONObj transformOrderFromArrayFormat(BSONObj order) { inline BSONObj transformOrderFromArrayFormat(BSONObj order) {
/* note: this is slow, but that is ok as order will have very few p ieces */ /* note: this is slow, but that is ok as order will have very few p ieces */
BSONObjBuilder b; BSONObjBuilder b;
skipping to change at line 144 skipping to change at line 69
} }
return b.obj(); return b.obj();
} }
/** /**
* this represents a total user query * this represents a total user query
* includes fields from the query message, both possible query levels * includes fields from the query message, both possible query levels
* parses everything up front * parses everything up front
*/ */
class ParsedQuery { class ParsedQuery : boost::noncopyable {
public: public:
ParsedQuery( QueryMessage& qm ) ParsedQuery( QueryMessage& qm )
: _ns( qm.ns ) , _ntoskip( qm.ntoskip ) , _ntoreturn( qm.ntoret urn ) , _options( qm.queryOptions ){ : _ns( qm.ns ) , _ntoskip( qm.ntoskip ) , _ntoreturn( qm.ntoret urn ) , _options( qm.queryOptions ) {
init( qm.query ); init( qm.query );
initFields( qm.fields ); initFields( qm.fields );
} }
ParsedQuery( const char* ns , int ntoskip , int ntoreturn , int que ryoptions , const BSONObj& query , const BSONObj& fields ) ParsedQuery( const char* ns , int ntoskip , int ntoreturn , int que ryoptions , const BSONObj& query , const BSONObj& fields )
: _ns( ns ) , _ntoskip( ntoskip ) , _ntoreturn( ntoreturn ) , _ options( queryoptions ){ : _ns( ns ) , _ntoskip( ntoskip ) , _ntoreturn( ntoreturn ) , _ options( queryoptions ) {
init( query ); init( query );
initFields( fields ); initFields( fields );
} }
~ParsedQuery(){}
const char * ns() const { return _ns; } const char * ns() const { return _ns; }
bool isLocalDB() const { return strncmp(_ns, "local.", 6) == 0; } bool isLocalDB() const { return strncmp(_ns, "local.", 6) == 0; }
const BSONObj& getFilter() const { return _filter; } const BSONObj& getFilter() const { return _filter; }
FieldMatcher* getFields() const { return _fields.get(); } Projection* getFields() const { return _fields.get(); }
shared_ptr<FieldMatcher> getFieldPtr() const { return _fields; } shared_ptr<Projection> getFieldPtr() const { return _fields; }
int getSkip() const { return _ntoskip; } int getSkip() const { return _ntoskip; }
int getNumToReturn() const { return _ntoreturn; } int getNumToReturn() const { return _ntoreturn; }
bool wantMore() const { return _wantMore; } bool wantMore() const { return _wantMore; }
int getOptions() const { return _options; } int getOptions() const { return _options; }
bool hasOption( int x ) const { return x & _options; } bool hasOption( int x ) const { return x & _options; }
bool isExplain() const { return _explain; } bool isExplain() const { return _explain; }
bool isSnapshot() const { return _snapshot; } bool isSnapshot() const { return _snapshot; }
bool returnKey() const { return _returnKey; } bool returnKey() const { return _returnKey; }
skipping to change at line 212 skipping to change at line 135
return n >= _ntoreturn || len > MaxBytesToReturnToClientAtOnce; return n >= _ntoreturn || len > MaxBytesToReturnToClientAtOnce;
} }
bool enough( int n ) const { bool enough( int n ) const {
if ( _ntoreturn == 0 ) if ( _ntoreturn == 0 )
return false; return false;
return n >= _ntoreturn; return n >= _ntoreturn;
} }
private: private:
void init( const BSONObj& q ){ void init( const BSONObj& q ) {
_reset(); _reset();
uassert( 10105 , "bad skip value in query", _ntoskip >= 0); uassert( 10105 , "bad skip value in query", _ntoskip >= 0);
if ( _ntoreturn < 0 ){ if ( _ntoreturn < 0 ) {
/* _ntoreturn greater than zero is simply a hint on how man y objects to send back per /* _ntoreturn greater than zero is simply a hint on how man y objects to send back per
"cursor batch". "cursor batch".
A negative number indicates a hard limit. A negative number indicates a hard limit.
*/ */
_wantMore = false; _wantMore = false;
_ntoreturn = -_ntoreturn; _ntoreturn = -_ntoreturn;
} }
BSONElement e = q["query"]; BSONElement e = q["query"];
if ( ! e.isABSONObj() ) if ( ! e.isABSONObj() )
e = q["$query"]; e = q["$query"];
if ( e.isABSONObj() ){ if ( e.isABSONObj() ) {
_filter = e.embeddedObject(); _filter = e.embeddedObject();
_initTop( q ); _initTop( q );
} }
else { else {
_filter = q; _filter = q;
} }
} }
void _reset(){ void _reset() {
_wantMore = true; _wantMore = true;
_explain = false; _explain = false;
_snapshot = false; _snapshot = false;
_returnKey = false; _returnKey = false;
_showDiskLoc = false; _showDiskLoc = false;
_maxScan = 0; _maxScan = 0;
} }
void _initTop( const BSONObj& top ){ void _initTop( const BSONObj& top ) {
BSONObjIterator i( top ); BSONObjIterator i( top );
while ( i.more() ){ while ( i.more() ) {
BSONElement e = i.next(); BSONElement e = i.next();
const char * name = e.fieldName(); const char * name = e.fieldName();
if ( strcmp( "$orderby" , name ) == 0 || if ( strcmp( "$orderby" , name ) == 0 ||
strcmp( "orderby" , name ) == 0 ){ strcmp( "orderby" , name ) == 0 ) {
if ( e.type() == Object ) if ( e.type() == Object ) {
_order = e.embeddedObject(); _order = e.embeddedObject();
else if ( e.type() == Array ) }
else if ( e.type() == Array ) {
_order = transformOrderFromArrayFormat( _order ); _order = transformOrderFromArrayFormat( _order );
else }
assert( 0 ); else {
uasserted(13513, "sort must be an object or array") ;
}
continue;
} }
else if ( strcmp( "$explain" , name ) == 0 )
_explain = e.trueValue();
else if ( strcmp( "$snapshot" , name ) == 0 )
_snapshot = e.trueValue();
else if ( strcmp( "$min" , name ) == 0 )
_min = e.embeddedObject();
else if ( strcmp( "$max" , name ) == 0 )
_max = e.embeddedObject();
else if ( strcmp( "$hint" , name ) == 0 )
_hint = e;
else if ( strcmp( "$returnKey" , name ) == 0 )
_returnKey = e.trueValue();
else if ( strcmp( "$maxScan" , name ) == 0 )
_maxScan = e.numberInt();
else if ( strcmp( "$showDiskLoc" , name ) == 0 )
_showDiskLoc = e.trueValue();
if( *name == '$' ) {
name++;
if ( strcmp( "explain" , name ) == 0 )
_explain = e.trueValue();
else if ( strcmp( "snapshot" , name ) == 0 )
_snapshot = e.trueValue();
else if ( strcmp( "min" , name ) == 0 )
_min = e.embeddedObject();
else if ( strcmp( "max" , name ) == 0 )
_max = e.embeddedObject();
else if ( strcmp( "hint" , name ) == 0 )
_hint = e;
else if ( strcmp( "returnKey" , name ) == 0 )
_returnKey = e.trueValue();
else if ( strcmp( "maxScan" , name ) == 0 )
_maxScan = e.numberInt();
else if ( strcmp( "showDiskLoc" , name ) == 0 )
_showDiskLoc = e.trueValue();
else if ( strcmp( "comment" , name ) == 0 ) {
; // no-op
}
}
} }
if ( _snapshot ){ if ( _snapshot ) {
uassert( 12001 , "E12001 can't sort with $snapshot", _order .isEmpty() ); uassert( 12001 , "E12001 can't sort with $snapshot", _order .isEmpty() );
uassert( 12002 , "E12002 can't use hint with $snapshot", _h int.eoo() ); uassert( 12002 , "E12002 can't use hint with $snapshot", _h int.eoo() );
} }
} }
void initFields( const BSONObj& fields ){ void initFields( const BSONObj& fields ) {
if ( fields.isEmpty() ) if ( fields.isEmpty() )
return; return;
_fields.reset( new FieldMatcher() ); _fields.reset( new Projection() );
_fields->add( fields ); _fields->init( fields );
}
ParsedQuery( const ParsedQuery& other ){
assert(0);
} }
const char* _ns; const char * const _ns;
int _ntoskip; const int _ntoskip;
int _ntoreturn; int _ntoreturn;
int _options;
BSONObj _filter; BSONObj _filter;
shared_ptr< FieldMatcher > _fields; BSONObj _order;
const int _options;
shared_ptr< Projection > _fields;
bool _wantMore; bool _wantMore;
bool _explain; bool _explain;
bool _snapshot; bool _snapshot;
bool _returnKey; bool _returnKey;
bool _showDiskLoc; bool _showDiskLoc;
BSONObj _min; BSONObj _min;
BSONObj _max; BSONObj _max;
BSONElement _hint; BSONElement _hint;
BSONObj _order;
int _maxScan; int _maxScan;
}; };
} // namespace mongo } // namespace mongo
#include "clientcursor.h"
 End of changes. 29 change blocks. 
132 lines changed or deleted 58 lines changed or added


 queryoptimizer.h   queryoptimizer.h 
/* queryoptimizer.h */ // @file queryoptimizer.h
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
skipping to change at line 25 skipping to change at line 25
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "cursor.h" #include "cursor.h"
#include "jsobj.h" #include "jsobj.h"
#include "queryutil.h" #include "queryutil.h"
#include "matcher.h" #include "matcher.h"
#include "../util/message.h" #include "../util/net/listen.h"
#include <queue>
namespace mongo { namespace mongo {
class IndexDetails; class IndexDetails;
class IndexType; class IndexType;
class ElapsedTracker;
/** A plan for executing a query using the given index spec and FieldRa ngeSet. */
class QueryPlan : boost::noncopyable { class QueryPlan : boost::noncopyable {
public: public:
QueryPlan(NamespaceDetails *_d,
int _idxNo, // -1 = no index /**
const FieldRangeSet &fbs, * @param originalFrsp - original constraints for this query clause . If null, frsp will be used instead.
*/
QueryPlan(NamespaceDetails *d,
int idxNo, // -1 = no index
const FieldRangeSetPair &frsp,
const FieldRangeSetPair *originalFrsp,
const BSONObj &originalQuery, const BSONObj &originalQuery,
const BSONObj &order, const BSONObj &order,
const BSONObj &startKey = BSONObj(), const BSONObj &startKey = BSONObj(),
const BSONObj &endKey = BSONObj() , const BSONObj &endKey = BSONObj(),
string special="" ); string special="" );
/* If true, no other index can do better. */ /** @return true iff no other plans should be considered. */
bool optimal() const { return optimal_; } bool optimal() const { return _optimal; }
/* ScanAndOrder processing will be required if true */ /* @return true iff this plan should not be considered at all. */
bool scanAndOrderRequired() const { return scanAndOrderRequired_; } bool unhelpful() const { return _unhelpful; }
/* When true, the index we are using has keys such that it can comp letely resolve the /** @return true iff ScanAndOrder processing will be required for r esult set. */
query expression to match by itself without ever checking the main object. bool scanAndOrderRequired() const { return _scanAndOrderRequired; }
/**
* @return true iff the index we are using has keys such that it ca n completely resolve the
* query expression to match by itself without ever checking the ma in object.
*/ */
bool exactKeyMatch() const { return exactKeyMatch_; } bool exactKeyMatch() const { return _exactKeyMatch; }
/* If true, the startKey and endKey are unhelpful and the index ord er doesn't match the /** @return true iff this QueryPlan would perform an unindexed scan . */
requested sort order */ bool willScanTable() const { return _idxNo < 0 && !_impossible; }
bool unhelpful() const { return unhelpful_; }
int direction() const { return direction_; } /** @return a new cursor based on this QueryPlan's index and FieldR angeSet. */
shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() , int numWanted=0 ) const; shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() , int numWanted=0 ) const;
/** @return a new reverse cursor if this is an unindexed plan. */
shared_ptr<Cursor> newReverseCursor() const; shared_ptr<Cursor> newReverseCursor() const;
/** Register this plan as a winner for its QueryPattern, with speci fied 'nscanned'. */
void registerSelf( long long nScanned ) const;
int direction() const { return _direction; }
BSONObj indexKey() const; BSONObj indexKey() const;
bool willScanTable() const { return !index_ && fbs_.matchPossible() ; } bool indexed() const { return _index; }
const char *ns() const { return fbs_.ns(); } int idxNo() const { return _idxNo; }
NamespaceDetails *nsd() const { return d; } const char *ns() const { return _frs.ns(); }
NamespaceDetails *nsd() const { return _d; }
BSONObj originalQuery() const { return _originalQuery; } BSONObj originalQuery() const { return _originalQuery; }
BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return fbs_.simplifiedQuery( fields ); } BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const { return _frs.simplifiedQuery( fields ); }
const FieldRange &range( const char *fieldName ) const { return fbs _.range( fieldName ); } const FieldRange &range( const char *fieldName ) const { return _fr s.range( fieldName ); }
void registerSelf( long long nScanned ) const; shared_ptr<FieldRangeVector> originalFrv() const { return _original Frv; }
shared_ptr< FieldRangeVector > frv() const { return _frv; }
const FieldRangeSet &multikeyFrs() const { return _frsMulti; }
/** just for testing */
shared_ptr<FieldRangeVector> frv() const { return _frv; }
bool isMultiKey() const;
private: private:
NamespaceDetails *d; NamespaceDetails * _d;
int idxNo; int _idxNo;
const FieldRangeSet &fbs_; const FieldRangeSet &_frs;
const FieldRangeSet &_frsMulti;
const BSONObj &_originalQuery; const BSONObj &_originalQuery;
const BSONObj &order_; const BSONObj &_order;
const IndexDetails *index_; const IndexDetails * _index;
bool optimal_; bool _optimal;
bool scanAndOrderRequired_; bool _scanAndOrderRequired;
bool exactKeyMatch_; bool _exactKeyMatch;
int direction_; int _direction;
shared_ptr< FieldRangeVector > _frv; shared_ptr<FieldRangeVector> _frv;
shared_ptr<FieldRangeVector> _originalFrv;
BSONObj _startKey; BSONObj _startKey;
BSONObj _endKey; BSONObj _endKey;
bool endKeyInclusive_; bool _endKeyInclusive;
bool unhelpful_; bool _unhelpful;
bool _impossible;
string _special; string _special;
IndexType * _type; IndexType * _type;
bool _startOrEndSpec; bool _startOrEndSpec;
}; };
// Inherit from this interface to implement a new query operation. /**
// The query optimizer will clone the QueryOp that is provided, giving * Inherit from this interface to implement a new query operation.
// each clone its own query plan. * The query optimizer will clone the QueryOp that is provided, giving
* each clone its own query plan.
*
* Normal sequence of events:
* 1) A new QueryOp is generated using createChild().
* 2) A QueryPlan is assigned to this QueryOp with setQueryPlan().
* 3) _init() is called on the QueryPlan.
* 4) next() is called repeatedly, with nscanned() checked after each c all.
* 5) In one of these calls to next(), setComplete() is called.
* 6) The QueryPattern for the QueryPlan may be recorded as a winner.
*/
class QueryOp { class QueryOp {
public: public:
QueryOp() : _complete(), _stopRequested(), _qp(), _error() {} QueryOp() : _complete(), _stopRequested(), _qp(), _error() {}
// Used when handing off from one QueryOp type to another /** Used when handing off from one QueryOp to another. */
QueryOp( const QueryOp &other ) : QueryOp( const QueryOp &other ) :
_complete(), _stopRequested(), _qp(), _error(), _matcher( other._ma tcher ), _complete(), _stopRequested(), _qp(), _error(), _matcher( other ._matcher ),
_orConstraint( other._orConstraint ) {} _orConstraint( other._orConstraint ) {}
virtual ~QueryOp() {} virtual ~QueryOp() {}
/** these gets called after a query plan is set */ /** @return QueryPlan assigned to this QueryOp by the query optimiz er. */
void init() { const QueryPlan &qp() const { return *_qp; }
if ( _oldMatcher.get() ) {
_matcher.reset( _oldMatcher->nextClauseMatcher( qp().indexK ey() ) );
} else {
_matcher.reset( new CoveredIndexMatcher( qp().originalQuery (), qp().indexKey(), alwaysUseRecord() ) );
}
_init();
}
virtual void next() = 0;
virtual bool mayRecordPlan() const = 0;
/** Advance to next potential matching document (eg using a cursor) . */
virtual void next() = 0;
/**
* @return current 'nscanned' metric for this QueryOp. Used to com pare
* cost to other QueryOps.
*/
virtual long long nscanned() = 0;
/** Take any steps necessary before the db mutex is yielded. */
virtual bool prepareToYield() { massert( 13335, "yield not supporte d", false ); return false; } virtual bool prepareToYield() { massert( 13335, "yield not supporte d", false ); return false; }
/** Recover once the db mutex is regained. */
virtual void recoverFromYield() { massert( 13336, "yield not suppor ted", false ); } virtual void recoverFromYield() { massert( 13336, "yield not suppor ted", false ); }
virtual long long nscanned() = 0; /**
* @return true iff the QueryPlan for this QueryOp may be registere d
* as a winning plan.
*/
virtual bool mayRecordPlan() const = 0;
/** @return a copy of the inheriting class, which will be run with its own /** @return true iff the implementation called setComplete() or set Stop(). */
query plan. If multiple plan sets are required for an $or query,
the QueryOp of the winning plan from a given set will b e cloned
to generate QueryOps for the subsequent plan set. This function
should only be called after the query op has completed executing.
*/
QueryOp *createChild() {
if( _orConstraint.get() ) {
_matcher->advanceOrClause( _orConstraint );
_orConstraint.reset();
}
QueryOp *ret = _createChild();
ret->_oldMatcher = _matcher;
return ret;
}
bool complete() const { return _complete; } bool complete() const { return _complete; }
bool error() const { return _error; } /** @return true iff the implementation called steStop(). */
bool stopRequested() const { return _stopRequested; } bool stopRequested() const { return _stopRequested; }
/** @return true iff the implementation threw an exception. */
bool error() const { return _error; }
/** @return the exception thrown by implementation if one was throw n. */
ExceptionInfo exception() const { return _exception; } ExceptionInfo exception() const { return _exception; }
const QueryPlan &qp() const { return *_qp; }
// To be called by QueryPlanSet::Runner only. /** To be called by QueryPlanSet::Runner only. */
void setQueryPlan( const QueryPlan *qp ) { _qp = qp; }
QueryOp *createChild();
void setQueryPlan( const QueryPlan *qp ) { _qp = qp; assert( _qp != NULL ); }
void init();
void setException( const DBException &e ) { void setException( const DBException &e ) {
_error = true; _error = true;
_exception = e.getInfo(); _exception = e.getInfo();
} }
shared_ptr< CoveredIndexMatcher > matcher() const { return _matcher ; }
shared_ptr<CoveredIndexMatcher> matcher( const shared_ptr<Cursor>& c ) const {
return matcher( c.get() );
}
shared_ptr<CoveredIndexMatcher> matcher( Cursor* c ) const {
if( ! c ) return _matcher;
return c->matcher() ? c->matcherPtr() : _matcher;
}
protected: protected:
/** Call if all results have been found. */
void setComplete() { void setComplete() {
_orConstraint = qp().frv(); _orConstraint = qp().originalFrv();
_complete = true; _complete = true;
} }
/** Call if the scan is complete even if not all results have been found. */
void setStop() { setComplete(); _stopRequested = true; } void setStop() { setComplete(); _stopRequested = true; }
/** Handle initialization after a QueryPlan has been set. */
virtual void _init() = 0; virtual void _init() = 0;
/** @return a copy of the inheriting class, which will be run with its own query plan. */
virtual QueryOp *_createChild() const = 0; virtual QueryOp *_createChild() const = 0;
virtual bool alwaysUseRecord() const { return false; } virtual bool alwaysUseRecord() const { return false; }
private: private:
bool _complete; bool _complete;
bool _stopRequested; bool _stopRequested;
ExceptionInfo _exception; ExceptionInfo _exception;
const QueryPlan *_qp; const QueryPlan *_qp;
bool _error; bool _error;
shared_ptr< CoveredIndexMatcher > _matcher; shared_ptr<CoveredIndexMatcher> _matcher;
shared_ptr< CoveredIndexMatcher > _oldMatcher; shared_ptr<CoveredIndexMatcher> _oldMatcher;
shared_ptr< FieldRangeVector > _orConstraint; shared_ptr<FieldRangeVector> _orConstraint;
}; };
// Set of candidate query plans for a particular query. Used for runni ng // temp. this class works if T::operator< is variant unlike a regular stl priority queue.
// a QueryOp on these plans. // but it's very slow. however if v.size() is always very small, it wo uld be fine,
// maybe even faster than a smart impl that does more memory allocation s.
template<class T>
class our_priority_queue : boost::noncopyable {
vector<T> v;
public:
our_priority_queue() {
v.reserve(4);
}
int size() const { return v.size(); }
bool empty() const { return v.empty(); }
void push(const T & x) {
v.push_back(x);
}
T pop() {
size_t t = 0;
for( size_t i = 1; i < v.size(); i++ ) {
if( v[t] < v[i] )
t = i;
}
T ret = v[t];
v.erase(v.begin()+t);
return ret;
}
};
/**
* A set of candidate query plans for a query. This class can return a best buess plan or run a
* QueryOp on all the plans.
*/
class QueryPlanSet { class QueryPlanSet {
public: public:
typedef boost::shared_ptr< QueryPlan > PlanPtr; typedef boost::shared_ptr<QueryPlan> QueryPlanPtr;
typedef vector< PlanPtr > PlanSet; typedef vector<QueryPlanPtr> PlanSet;
/**
* @param originalFrsp - original constraints for this query clause ; if null, frsp will be used.
*/
QueryPlanSet( const char *ns, QueryPlanSet( const char *ns,
auto_ptr< FieldRangeSet > frs, auto_ptr<FieldRangeSetPair> frsp,
const BSONObj &originalQuery, auto_ptr<FieldRangeSetPair> originalFrsp,
const BSONObj &order, const BSONObj &originalQuery,
const BSONElement *hint = 0, const BSONObj &order,
bool honorRecordedPlan = true, const BSONElement *hint = 0,
const BSONObj &min = BSONObj(), bool honorRecordedPlan = true,
const BSONObj &max = BSONObj(), const BSONObj &min = BSONObj(),
bool bestGuessOnly = false, const BSONObj &max = BSONObj(),
bool mayYield = false); bool bestGuessOnly = false,
int nPlans() const { return plans_.size(); } bool mayYield = false);
shared_ptr< QueryOp > runOp( QueryOp &op );
template< class T > /** @return number of candidate plans. */
shared_ptr< T > runOp( T &op ) { int nPlans() const { return _plans.size(); }
return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& >( op ) ) );
/**
* Clone op for each query plan, and @return the first cloned op to call
* setComplete() or setStop().
*/
shared_ptr<QueryOp> runOp( QueryOp &op );
template<class T>
shared_ptr<T> runOp( T &op ) {
return dynamic_pointer_cast<T>( runOp( static_cast<QueryOp&>( o p ) ) );
} }
/** Initialize or iterate a runner generated from @param originalOp . */
shared_ptr<QueryOp> nextOp( QueryOp &originalOp, bool retried = fal se );
/** Yield the runner member. */
bool prepareToYield();
void recoverFromYield();
QueryPlanPtr firstPlan() const { return _plans[ 0 ]; }
/** @return metadata about cursors and index bounds for all plans, suitable for explain output. */
BSONObj explain() const; BSONObj explain() const;
bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } /** @return true iff a plan is selected based on previous success o f this plan. */
PlanPtr getBestGuess() const; bool usingPrerecordedPlan() const { return _usingPrerecordedPlan; }
/** @return a single plan that may work well for the specified quer y. */
QueryPlanPtr getBestGuess() const;
//for testing //for testing
const FieldRangeSet &fbs() const { return *fbs_; } const FieldRangeSetPair &frsp() const { return *_frsp; }
const FieldRangeSetPair *originalFrsp() const { return _originalFrs p.get(); }
bool modifiedKeys() const;
bool hasMultiKey() const;
private: private:
void addOtherPlans( bool checkFirst ); void addOtherPlans( bool checkFirst );
void addPlan( PlanPtr plan, bool checkFirst ) { void addPlan( QueryPlanPtr plan, bool checkFirst ) {
if ( checkFirst && plan->indexKey().woCompare( plans_[ 0 ]->ind exKey() ) == 0 ) if ( checkFirst && plan->indexKey().woCompare( _plans[ 0 ]->ind exKey() ) == 0 )
return; return;
plans_.push_back( plan ); _plans.push_back( plan );
} }
void init(); void init();
void addHint( IndexDetails &id ); void addHint( IndexDetails &id );
struct Runner { class Runner {
public:
Runner( QueryPlanSet &plans, QueryOp &op ); Runner( QueryPlanSet &plans, QueryOp &op );
shared_ptr< QueryOp > run();
void mayYield( const vector< shared_ptr< QueryOp > > &ops ); /**
QueryOp &op_; * Iterate interactively through candidate documents on all pla ns.
QueryPlanSet &plans_; * QueryOp objects are returned at each interleaved step.
*/
/** @return a plan that has completed, otherwise an arbitrary p lan. */
shared_ptr<QueryOp> init();
/**
* Move the Runner forward one iteration, and @return the plan for
* this iteration.
*/
shared_ptr<QueryOp> next();
/** @return next non error op if there is one, otherwise an err or op. */
shared_ptr<QueryOp> nextNonError();
bool prepareToYield();
void recoverFromYield();
/** Run until first op completes. */
shared_ptr<QueryOp> runUntilFirstCompletes();
void mayYield();
QueryOp &_op;
QueryPlanSet &_plans;
static void initOp( QueryOp &op ); static void initOp( QueryOp &op );
static void nextOp( QueryOp &op ); static void nextOp( QueryOp &op );
static bool prepareToYield( QueryOp &op ); static bool prepareToYieldOp( QueryOp &op );
static void recoverFromYield( QueryOp &op ); static void recoverFromYieldOp( QueryOp &op );
private:
vector<shared_ptr<QueryOp> > _ops;
struct OpHolder {
OpHolder( const shared_ptr<QueryOp> &op ) : _op( op ), _off set() {}
shared_ptr<QueryOp> _op;
long long _offset;
bool operator<( const OpHolder &other ) const {
return _op->nscanned() + _offset > other._op->nscanned( ) + other._offset;
}
};
our_priority_queue<OpHolder> _queue;
}; };
const char *ns;
const char *_ns;
BSONObj _originalQuery; BSONObj _originalQuery;
auto_ptr< FieldRangeSet > fbs_; auto_ptr<FieldRangeSetPair> _frsp;
PlanSet plans_; auto_ptr<FieldRangeSetPair> _originalFrsp;
bool mayRecordPlan_; PlanSet _plans;
bool usingPrerecordedPlan_; bool _mayRecordPlan;
BSONObj hint_; bool _usingPrerecordedPlan;
BSONObj order_; BSONObj _hint;
long long oldNScanned_; BSONObj _order;
bool honorRecordedPlan_; long long _oldNScanned;
BSONObj min_; bool _honorRecordedPlan;
BSONObj max_; BSONObj _min;
BSONObj _max;
string _special; string _special;
bool _bestGuessOnly; bool _bestGuessOnly;
bool _mayYield; bool _mayYield;
ElapsedTracker _yieldSometimesTracker; ElapsedTracker _yieldSometimesTracker;
shared_ptr<Runner> _runner;
}; };
// Handles $or type queries by generating a QueryPlanSet for each $or c lause /** Handles $or type queries by generating a QueryPlanSet for each $or clause. */
// NOTE on our $or implementation: In our current qo implementation we don't
// keep statistics on our data, but we can conceptualize the problem of
// selecting an index when statistics exist for all index ranges. The
// d-hitting set problem on k sets and n elements can be reduced to the
// problem of index selection on k $or clauses and n index ranges (wher e
// d is the max number of indexes, and the number of ranges n is unboun ded).
// In light of the fact that d-hitting set is np complete, and we don't even
// track statistics (so cost calculations are expensive) our first
// implementation uses the following greedy approach: We take one $or c lause
// at a time and treat each as a separate query for index selection pur poses.
// But if an index range is scanned for a particular $or clause, we eli minate
// that range from all subsequent clauses. One could imagine an opposi te
// implementation where we select indexes based on the union of index r anges
// for all $or clauses, but this can have much poorer worst case behavi or.
// (An index range that suits one $or clause may not suit another, and this
// is worse than the typical case of index range choice staleness becau se
// with $or the clauses may likely be logically distinct.) The greedy
// implementation won't do any worse than all the $or clauses individua lly,
// and it can often do better. In the first cut we are intentionally u sing
// QueryPattern tracking to record successful plans on $or clauses for use by
// subsequent $or clauses, even though there may be a significant aggre gate
// $nor component that would not be represented in QueryPattern.
class MultiPlanScanner { class MultiPlanScanner {
public: public:
MultiPlanScanner( const char *ns, MultiPlanScanner( const char *ns,
const BSONObj &query, const BSONObj &query,
const BSONObj &order, const BSONObj &order,
const BSONElement *hint = 0, const BSONElement *hint = 0,
bool honorRecordedPlan = true, bool honorRecordedPlan = true,
const BSONObj &min = BSONObj(), const BSONObj &min = BSONObj(),
const BSONObj &max = BSONObj(), const BSONObj &max = BSONObj(),
bool bestGuessOnly = false, bool bestGuessOnly = false,
bool mayYield = false); bool mayYield = false);
shared_ptr< QueryOp > runOp( QueryOp &op );
template< class T > /**
shared_ptr< T > runOp( T &op ) { * Clone op for each query plan of a single $or clause, and @return the first cloned op
return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp& >( op ) ) ); * to call setComplete() or setStop().
*/
shared_ptr<QueryOp> runOpOnce( QueryOp &op );
template<class T>
shared_ptr<T> runOpOnce( T &op ) {
return dynamic_pointer_cast<T>( runOpOnce( static_cast<QueryOp& >( op ) ) );
} }
shared_ptr< QueryOp > runOpOnce( QueryOp &op );
template< class T > /**
shared_ptr< T > runOpOnce( T &op ) { * For each $or clause, calls runOpOnce on the child QueryOp cloned from the winning QueryOp
return dynamic_pointer_cast< T >( runOpOnce( static_cast< Query Op& >( op ) ) ); * of the previous $or clause (or from the supplied 'op' for the fi rst $or clause).
*/
shared_ptr<QueryOp> runOp( QueryOp &op );
template<class T>
shared_ptr<T> runOp( T &op ) {
return dynamic_pointer_cast<T>( runOp( static_cast<QueryOp&>( o p ) ) );
} }
bool mayRunMore() const { return _or ? ( !_tableScanned && !_fros.o rFinished() ) : _i == 0; }
/** Initialize or iterate a runner generated from @param originalOp . */
void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp = originalOp; }
shared_ptr<QueryOp> nextOp();
/** Yield the runner member. */
bool prepareToYield();
void recoverFromYield();
/**
* @return a single simple cursor if the scanner would run a single cursor
* for this query, otherwise return an empty shared_ptr.
*/
shared_ptr<Cursor> singleCursor() const;
/** @return true iff more $or clauses need to be scanned. */
bool mayRunMore() const { return _or ? ( !_tableScanned && !_org->o rFinished() ) : _i == 0; }
/** @return non-$or version of explain output. */
BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp lain(); } BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp lain(); }
// just report this when only one query op /** @return true iff this is not a $or query and a plan is selected based on previous success of this plan. */
bool usingPrerecordedPlan() const { bool usingPrerecordedPlan() const { return !_or && _currentQps->usi ngPrerecordedPlan(); }
return !_or && _currentQps->usingPrerecordedPlan(); /** Don't attempt to scan multiple plans, just use the best guess. */
}
void setBestGuessOnly() { _bestGuessOnly = true; } void setBestGuessOnly() { _bestGuessOnly = true; }
/** Yielding is allowed while running each QueryPlan. */
void mayYield( bool val ) { _mayYield = val; } void mayYield( bool val ) { _mayYield = val; }
bool modifiedKeys() const { return _currentQps->modifiedKeys(); }
bool hasMultiKey() const { return _currentQps->hasMultiKey(); }
private: private:
void assertNotOr() const { void assertNotOr() const {
massert( 13266, "not implemented for $or query", !_or ); massert( 13266, "not implemented for $or query", !_or );
} }
void assertMayRunMore() const {
massert( 13271, "can't run more ops", mayRunMore() );
}
shared_ptr<QueryOp> nextOpBeginningClause();
shared_ptr<QueryOp> nextOpHandleEndOfClause();
bool uselessOr( const BSONElement &hint ) const; bool uselessOr( const BSONElement &hint ) const;
const char * _ns; const char * _ns;
bool _or; bool _or;
BSONObj _query; BSONObj _query;
FieldRangeOrSet _fros; shared_ptr<OrRangeGenerator> _org; // May be null in certain non $o r query cases.
auto_ptr< QueryPlanSet > _currentQps; auto_ptr<QueryPlanSet> _currentQps;
int _i; int _i;
bool _honorRecordedPlan; bool _honorRecordedPlan;
bool _bestGuessOnly; bool _bestGuessOnly;
BSONObj _hint; BSONObj _hint;
bool _mayYield; bool _mayYield;
bool _tableScanned; bool _tableScanned;
shared_ptr<QueryOp> _baseOp;
}; };
/** Provides a cursor interface for certain limited uses of a MultiPlan Scanner. */
class MultiCursor : public Cursor { class MultiCursor : public Cursor {
public: public:
class CursorOp : public QueryOp { class CursorOp : public QueryOp {
public: public:
CursorOp() {} CursorOp() {}
CursorOp( const QueryOp &other ) : QueryOp( other ) {} CursorOp( const QueryOp &other ) : QueryOp( other ) {}
virtual shared_ptr< Cursor > newCursor() const = 0; virtual shared_ptr<Cursor> newCursor() const = 0;
}; };
// takes ownership of 'op' /** takes ownership of 'op' */
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr< CursorOp > op = shared_ptr< CursorOp >(), bool mayYield = false ) MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr<CursorOp> op = shared_ptr<CursorOp>(), bool mayYield = f alse );
: _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj( ), BSONObj(), !op.get(), mayYield ) ), _nscanned() { /**
if ( op.get() ) { * Used
_op = op; * 1. To handoff a query to a getMore()
} else { * 2. To handoff a QueryOptimizerCursor
_op.reset( new NoOp() ); * @param nscanned is an optional initial value, if not supplied ns canned()
} * will always return -1
if ( _mps->mayRunMore() ) { */
nextClause(); MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso r> &c, const shared_ptr<CoveredIndexMatcher> &matcher, const QueryOp &op, l ong long nscanned = -1 );
if ( !ok() ) {
advance();
}
} else {
_c.reset( new BasicCursor( DiskLoc() ) );
}
}
// used to handoff a query to a getMore()
MultiCursor( auto_ptr< MultiPlanScanner > mps, const shared_ptr< Cu rsor > &c, const shared_ptr< CoveredIndexMatcher > &matcher, const QueryOp &op )
: _op( new NoOp( op ) ), _c( c ), _mps( mps ), _matcher( matcher ), _nscanned( -1 ) {
_mps->setBestGuessOnly();
_mps->mayYield( false ); // with a NoOp, there's no need to yie ld in QueryPlanSet
if ( !ok() ) {
// would have been advanced by UserQueryOp if possible
advance();
}
}
virtual bool ok() { return _c->ok(); } virtual bool ok() { return _c->ok(); }
virtual Record* _current() { return _c->_current(); } virtual Record* _current() { return _c->_current(); }
virtual BSONObj current() { return _c->current(); } virtual BSONObj current() { return _c->current(); }
virtual DiskLoc currLoc() { return _c->currLoc(); } virtual DiskLoc currLoc() { return _c->currLoc(); }
virtual bool advance() { virtual bool advance() {
_c->advance(); _c->advance();
while( !ok() && _mps->mayRunMore() ) { while( !ok() && _mps->mayRunMore() ) {
nextClause(); nextClause();
} }
return ok(); return ok();
} }
virtual BSONObj currKey() const { return _c->currKey(); } virtual BSONObj currKey() const { return _c->currKey(); }
virtual DiskLoc refLoc() { return _c->refLoc(); } virtual DiskLoc refLoc() { return _c->refLoc(); }
virtual void noteLocation() { virtual void noteLocation() { _c->noteLocation(); }
_c->noteLocation(); virtual void checkLocation() { _c->checkLocation(); }
}
virtual void checkLocation() {
_c->checkLocation();
}
virtual bool supportGetMore() { return true; } virtual bool supportGetMore() { return true; }
virtual bool supportYields() { return _c->supportYields(); } virtual bool supportYields() { return _c->supportYields(); }
// with update we could potentially get the same document on multip le virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); }
// indexes, but update appears to already handle this with seenObje cts
// so we don't have to do anything special here. /**
virtual bool getsetdup(DiskLoc loc) { * with update we could potentially get the same document on multip le
return _c->getsetdup( loc ); * indexes, but update appears to already handle this with seenObje cts
} * so we don't have to do anything special here.
virtual CoveredIndexMatcher *matcher() const { return _matcher.get( ); } */
// return -1 if we're a getmore handoff virtual bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc ); }
virtual bool modifiedKeys() const { return _mps->modifiedKeys(); }
virtual bool isMultiKey() const { return _mps->hasMultiKey(); }
virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu rn _matcher; }
virtual CoveredIndexMatcher* matcher() const { return _matcher.get( ); }
/** return -1 if we're a getmore handoff */
virtual long long nscanned() { return _nscanned >= 0 ? _nscanned + _c->nscanned() : _nscanned; } virtual long long nscanned() { return _nscanned >= 0 ? _nscanned + _c->nscanned() : _nscanned; }
// just for testing /** just for testing */
shared_ptr< Cursor > sub_c() const { return _c; } shared_ptr<Cursor> sub_c() const { return _c; }
private: private:
class NoOp : public CursorOp { class NoOp : public CursorOp {
public: public:
NoOp() {} NoOp() {}
NoOp( const QueryOp &other ) : CursorOp( other ) {} NoOp( const QueryOp &other ) : CursorOp( other ) {}
virtual void _init() { setComplete(); } virtual void _init() { setComplete(); }
virtual void next() {} virtual void next() {}
virtual bool mayRecordPlan() const { return false; } virtual bool mayRecordPlan() const { return false; }
virtual QueryOp *_createChild() const { return new NoOp(); } virtual QueryOp *_createChild() const { return new NoOp(); }
virtual shared_ptr< Cursor > newCursor() const { return qp().ne wCursor(); } virtual shared_ptr<Cursor> newCursor() const { return qp().newC ursor(); }
virtual long long nscanned() { assert( false ); return 0; } virtual long long nscanned() { assert( false ); return 0; }
}; };
void nextClause() { void nextClause();
if ( _nscanned >= 0 && _c.get() ) { shared_ptr<CursorOp> _op;
_nscanned += _c->nscanned(); shared_ptr<Cursor> _c;
} auto_ptr<MultiPlanScanner> _mps;
shared_ptr< CursorOp > best = _mps->runOpOnce( *_op ); shared_ptr<CoveredIndexMatcher> _matcher;
if ( ! best->complete() )
throw MsgAssertionException( best->exception() );
_c = best->newCursor();
_matcher = best->matcher();
_op = best;
}
shared_ptr< CursorOp > _op;
shared_ptr< Cursor > _c;
auto_ptr< MultiPlanScanner > _mps;
shared_ptr< CoveredIndexMatcher > _matcher;
long long _nscanned; long long _nscanned;
}; };
// NOTE min, max, and keyPattern will be updated to be consistent with the selected index. /** NOTE min, max, and keyPattern will be updated to be consistent with the selected index. */
IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO NObj &min, BSONObj &max, BSONObj &keyPattern ); IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO NObj &min, BSONObj &max, BSONObj &keyPattern );
inline bool isSimpleIdQuery( const BSONObj& query ){ bool isSimpleIdQuery( const BSONObj& query );
BSONObjIterator i(query);
if( !i.more() ) return false;
BSONElement e = i.next();
if( i.more() ) return false;
if( strcmp("_id", e.fieldName()) != 0 ) return false;
return e.isSimpleType(); // e.g. not something like { _id : { $gt : ...
}
// matcher() will always work on the returned cursor /**
inline shared_ptr< Cursor > bestGuessCursor( const char *ns, const BSON Obj &query, const BSONObj &sort ) { * @return a single cursor that may work well for the given query.
if( !query.getField( "$or" ).eoo() ) { * It is possible no cursor is returned if the sort is not supported by an index. Clients are responsible
return shared_ptr< Cursor >( new MultiCursor( ns, query, sort ) ); * for checking this if they are not sure an index for a sort exists, a nd defaulting to a non-sort if
} else { * no suitable indices exist.
auto_ptr< FieldRangeSet > frs( new FieldRangeSet( ns, query ) ) ; */
shared_ptr< Cursor > ret = QueryPlanSet( ns, frs, query, sort ) .getBestGuess()->newCursor(); shared_ptr<Cursor> bestGuessCursor( const char *ns, const BSONObj &quer y, const BSONObj &sort );
if ( !query.isEmpty() ) {
shared_ptr< CoveredIndexMatcher > matcher( new CoveredIndex Matcher( query, ret->indexKeyPattern() ) ); /**
ret->setMatcher( matcher ); * Add-on functionality for queryutil classes requiring access to index ing
} * functionality not currently linked to mongos.
return ret; * TODO Clean this up a bit, possibly with separate sharded and non sha rded
} * implementations for the appropriate queryutil classes or by pulling index
} * related functionality into separate wrapper classes.
*/
struct QueryUtilIndexed {
/** @return true if the index may be useful according to its KeySpe c. */
static bool indexUseful( const FieldRangeSetPair &frsp, NamespaceDe tails *d, int idxNo, const BSONObj &order );
/** Clear any indexes recorded as the best for either the single or multi key pattern. */
static void clearIndexesForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order );
/** Return a recorded best index for the single or multi key patter n. */
static pair< BSONObj, long long > bestIndexForPatterns( const Field RangeSetPair &frsp, const BSONObj &order );
static bool uselessOr( const OrRangeGenerator& org, NamespaceDetail s *d, int hintIdx );
};
} // namespace mongo } // namespace mongo
 End of changes. 69 change blocks. 
250 lines changed or deleted 391 lines changed or added


 queryutil.h   queryutil.h 
// queryutil.h // @file queryutil.h - Utility classes representing ranges of valid BSONEle ment values for a query.
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "jsobj.h" #include "jsobj.h"
#include "indexkey.h"
namespace mongo { namespace mongo {
/**
* One side of an interval of valid BSONElements, specified by a value and a
* boolean indicating whether the interval includes the value.
*/
struct FieldBound { struct FieldBound {
BSONElement _bound; BSONElement _bound;
bool _inclusive; bool _inclusive;
bool operator==( const FieldBound &other ) const { bool operator==( const FieldBound &other ) const {
return _bound.woCompare( other._bound ) == 0 && return _bound.woCompare( other._bound ) == 0 &&
_inclusive == other._inclusive; _inclusive == other._inclusive;
} }
void flipInclusive() { _inclusive = !_inclusive; } void flipInclusive() { _inclusive = !_inclusive; }
}; };
/** A closed interval composed of a lower and an upper FieldBound. */
struct FieldInterval { struct FieldInterval {
FieldInterval() : _cachedEquality( -1 ) {} FieldInterval() : _cachedEquality( -1 ) {}
FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) { FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) {
_lower._bound = _upper._bound = e; _lower._bound = _upper._bound = e;
_lower._inclusive = _upper._inclusive = true; _lower._inclusive = _upper._inclusive = true;
} }
FieldBound _lower; FieldBound _lower;
FieldBound _upper; FieldBound _upper;
/** @return true iff no single element can be contained in the inte rval. */
bool strictValid() const { bool strictValid() const {
int cmp = _lower._bound.woCompare( _upper._bound, false ); int cmp = _lower._bound.woCompare( _upper._bound, false );
return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._ inclusive ) ); return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._ inclusive ) );
} }
bool equality() const { /** @return true iff the interval is an equality constraint. */
if ( _cachedEquality == -1 ) { bool equality() const;
_cachedEquality = ( _lower._inclusive && _upper._inclusive && _lower._bound.woCompare( _upper._bound, false ) == 0 );
}
return _cachedEquality;
}
mutable int _cachedEquality; mutable int _cachedEquality;
string toString() const;
}; };
// range of a field's value that may be determined from query -- used t o /**
// determine index limits * An ordered list of FieldIntervals expressing constraints on valid
* BSONElement values for a field.
*/
class FieldRange { class FieldRange {
public: public:
FieldRange( const BSONElement &e = BSONObj().firstElement() , bool isNot=false , bool optimize=true ); FieldRange( const BSONElement &e , bool singleKey , bool isNot=fals e , bool optimize=true );
/** @return Range intersection with 'other'. */
const FieldRange &operator&=( const FieldRange &other ); const FieldRange &operator&=( const FieldRange &other );
/** @return Range union with 'other'. */
const FieldRange &operator|=( const FieldRange &other ); const FieldRange &operator|=( const FieldRange &other );
// does not remove fully contained ranges (eg [1,3] - [2,2] doesn't remove anything) /** @return Range of elements elements included in 'this' but not ' other'. */
// in future we can change so that an or on $in:[3] combined with $ gt:2 doesn't scan 3 a second time
const FieldRange &operator-=( const FieldRange &other ); const FieldRange &operator-=( const FieldRange &other );
// true iff other includes this /** @return true iff this range is a subset of 'other'. */
bool operator<=( const FieldRange &other ); bool operator<=( const FieldRange &other ) const;
/**
* If there are any valid values for this range, the extreme values can
* be extracted.
*/
BSONElement min() const { assert( !empty() ); return _intervals[ 0 ]._lower._bound; } BSONElement min() const { assert( !empty() ); return _intervals[ 0 ]._lower._bound; }
BSONElement max() const { assert( !empty() ); return _intervals[ _i ntervals.size() - 1 ]._upper._bound; } BSONElement max() const { assert( !empty() ); return _intervals[ _i ntervals.size() - 1 ]._upper._bound; }
bool minInclusive() const { assert( !empty() ); return _intervals[ 0 ]._lower._inclusive; } bool minInclusive() const { assert( !empty() ); return _intervals[ 0 ]._lower._inclusive; }
bool maxInclusive() const { assert( !empty() ); return _intervals[ _intervals.size() - 1 ]._upper._inclusive; } bool maxInclusive() const { assert( !empty() ); return _intervals[ _intervals.size() - 1 ]._upper._inclusive; }
bool equality() const {
return /** @return true iff this range expresses a single equality interva l. */
!empty() &&; bool equality() const;
min().woCompare( max(), false ) == 0 && /** @return true if all the intervals for this range are equalities */
maxInclusive() &&; bool inQuery() const;
minInclusive(); /** @return true iff this range does not include every BSONElement */
} bool nontrivial() const;
bool inQuery() const { /** @return true iff this range matches no BSONElements. */
if ( equality() ) {
return true;
}
for( vector< FieldInterval >::const_iterator i = _intervals.beg in(); i != _intervals.end(); ++i ) {
if ( !i->equality() ) {
return false;
}
}
return true;
}
bool nontrivial() const {
return
! empty() &&
( _intervals.size() != 1 ||
minKey.firstElement().woCompare( min(), false ) != 0 ||
maxKey.firstElement().woCompare( max(), false ) != 0 );
}
bool empty() const { return _intervals.empty(); } bool empty() const { return _intervals.empty(); }
/** Empty the range so it matches no BSONElements. */
void makeEmpty() { _intervals.clear(); } void makeEmpty() { _intervals.clear(); }
const vector< FieldInterval > &intervals() const { return _i ntervals; } const vector<FieldInterval> &intervals() const { return _intervals; }
string getSpecial() const { return _special; } string getSpecial() const { return _special; }
void setExclusiveBounds() { /** Make component intervals noninclusive. */
for( vector< FieldInterval >::iterator i = _intervals.begin(); i != _intervals.end(); ++i ) { void setExclusiveBounds();
i->_lower._inclusive = false; /**
i->_upper._inclusive = false; * Constructs a range where all FieldIntervals and FieldBounds are in
} * the opposite order of the current range.
} * NOTE the resulting intervals might not be strictValid().
// constructs a range which is the reverse of the current one */
// note - the resulting intervals may not be strictValid() void reverse( FieldRange &ret ) const;
void reverse( FieldRange &ret ) const {
assert( _special.empty() ); string toString() const;
ret._intervals.clear();
ret._objData = _objData;
for( vector< FieldInterval >::const_reverse_iterator i = _inter vals.rbegin(); i != _intervals.rend(); ++i ) {
FieldInterval fi;
fi._lower = i->_upper;
fi._upper = i->_lower;
ret._intervals.push_back( fi );
}
}
private: private:
BSONObj addObj( const BSONObj &o ); BSONObj addObj( const BSONObj &o );
void finishOperation( const vector< FieldInterval > &newIntervals, const FieldRange &other ); void finishOperation( const vector<FieldInterval> &newIntervals, co nst FieldRange &other );
vector< FieldInterval > _intervals; vector<FieldInterval> _intervals;
vector< BSONObj > _objData; // Owns memory for our BSONElements.
vector<BSONObj> _objData;
string _special; string _special;
bool _singleKey;
}; };
// implements query pattern matching, used to determine if a query is /**
// similar to an earlier query and should use the same plan * A BoundList contains intervals specified by inclusive start
class QueryPattern { * and end bounds. The intervals should be nonoverlapping and occur in
public: * the specified direction of traversal. For example, given a simple i ndex {i:1}
friend class FieldRangeSet; * and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList
enum Type { * would be valid for index {i:-1} with direction -1.
Equality, */
LowerBound, typedef vector<pair<BSONObj,BSONObj> > BoundList;
UpperBound,
UpperAndLowerBound
};
// for testing only, speed unimportant
bool operator==( const QueryPattern &other ) const {
bool less = operator<( other );
bool more = other.operator<( *this );
assert( !( less && more ) );
return !( less || more );
}
bool operator!=( const QueryPattern &other ) const {
return !operator==( other );
}
bool operator<( const QueryPattern &other ) const {
map< string, Type >::const_iterator i = _fieldTypes.begin();
map< string, Type >::const_iterator j = other._fieldTypes.begin ();
while( i != _fieldTypes.end() ) {
if ( j == other._fieldTypes.end() )
return false;
if ( i->first < j->first )
return true;
else if ( i->first > j->first )
return false;
if ( i->second < j->second )
return true;
else if ( i->second > j->second )
return false;
++i;
++j;
}
if ( j != other._fieldTypes.end() )
return true;
return _sort.woCompare( other._sort ) < 0;
}
private:
QueryPattern() {}
void setSort( const BSONObj sort ) {
_sort = normalizeSort( sort );
}
BSONObj static normalizeSort( const BSONObj &spec ) {
if ( spec.isEmpty() )
return spec;
int direction = ( spec.firstElement().number() >= 0 ) ? 1 : -1;
BSONObjIterator i( spec );
BSONObjBuilder b;
while( i.moreWithEOO() ) {
BSONElement e = i.next();
if ( e.eoo() )
break;
b.append( e.fieldName(), direction * ( ( e.number() >= 0 ) ? -1 : 1 ) );
}
return b.obj();
}
map< string, Type > _fieldTypes;
BSONObj _sort;
};
// a BoundList contains intervals specified by inclusive start class QueryPattern;
// and end bounds. The intervals should be nonoverlapping and occur in
// the specified direction of traversal. For example, given a simple i ndex {i:1}
// and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList
// would be valid for index {i:-1} with direction -1.
typedef vector< pair< BSONObj, BSONObj > > BoundList;
// ranges of fields' value that may be determined from query -- used to /**
// determine index limits * A set of FieldRanges determined from constraints on the fields of a query,
* that may be used to determine index bounds.
*/
class FieldRangeSet { class FieldRangeSet {
public: public:
friend class FieldRangeOrSet; friend class OrRangeGenerator;
friend class FieldRangeVector; friend class FieldRangeVector;
FieldRangeSet( const char *ns, const BSONObj &query , bool optimize =true ); FieldRangeSet( const char *ns, const BSONObj &query , bool singleKe y , bool optimize=true );
/** @return true if there is a nontrivial range for the given field . */
bool hasRange( const char *fieldName ) const { bool hasRange( const char *fieldName ) const {
map< string, FieldRange >::const_iterator f = _ranges.find( fie ldName ); map<string, FieldRange>::const_iterator f = _ranges.find( field Name );
return f != _ranges.end(); return f != _ranges.end();
} }
const FieldRange &range( const char *fieldName ) const { /** @return range for the given field. */
map< string, FieldRange >::const_iterator f = _ranges.find( fie ldName ); const FieldRange &range( const char *fieldName ) const;
if ( f == _ranges.end() ) /** @return range for the given field. */
return trivialRange(); FieldRange &range( const char *fieldName );
return f->second; /** @return the number of nontrivial ranges. */
} int nNontrivialRanges() const;
FieldRange &range( const char *fieldName ) { /**
map< string, FieldRange >::iterator f = _ranges.find( fieldName ); * @return true if a match could be possible on every field. Genera lly this
if ( f == _ranges.end() ) * is not useful information for a single key FieldRangeSet and
return trivialRange(); * matchPossibleForIndex() should be used instead.
return f->second; */
} bool matchPossible() const;
int nNontrivialRanges() const { /**
int count = 0; * @return true if a match could be possible given the value of _si ngleKey
for( map< string, FieldRange >::const_iterator i = _ranges.begi n(); i != _ranges.end(); ++i ) { * and index key 'keyPattern'.
if ( i->second.nontrivial() ) * @param keyPattern May be {} or {$natural:1} for a non index scan .
++count; */
} bool matchPossibleForIndex( const BSONObj &keyPattern ) const;
return count;
}
const char *ns() const { return _ns; } const char *ns() const { return _ns; }
// if fields is specified, order fields of returned object to match those of 'fields'
/**
* @return a simplified query from the extreme values of the nontri vial
* fields.
* @param fields If specified, the fields of the returned object ar e
* ordered to match those of 'fields'.
*/
BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const;
bool matchPossible() const {
for( map< string, FieldRange >::const_iterator i = _ranges.begi n(); i != _ranges.end(); ++i )
if ( i->second.empty() )
return false;
return true;
}
QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; QueryPattern pattern( const BSONObj &sort = BSONObj() ) const;
string getSpecial() const; string getSpecial() const;
const FieldRangeSet &operator-=( const FieldRangeSet &other ) {
int nUnincluded = 0; /**
string unincludedKey; * @return a FieldRangeSet approximation of the documents in 'this' but
map< string, FieldRange >::iterator i = _ranges.begin(); * not in 'other'. The approximation will be a superset of the doc uments
map< string, FieldRange >::const_iterator j = other._ranges.beg in(); * in 'this' but not 'other'.
while( nUnincluded < 2 && i != _ranges.end() && j != other._ran ges.end() ) { */
int cmp = i->first.compare( j->first ); const FieldRangeSet &operator-=( const FieldRangeSet &other );
if ( cmp == 0 ) { /** @return intersection of 'this' with 'other'. */
if ( i->second <= j->second ) { const FieldRangeSet &operator&=( const FieldRangeSet &other );
// nothing
} else { /**
++nUnincluded; * @return an ordered list of bounds generated using an index key p attern
unincludedKey = i->first; * and traversal direction.
} *
++i; * NOTE This function is deprecated in the query optimizer and only
++j; * currently used by the sharding code.
} else if ( cmp < 0 ) { */
++i;
} else {
// other has a bound we don't, nothing can be done
return *this;
}
}
if ( j != other._ranges.end() ) {
// other has a bound we don't, nothing can be done
return *this;
}
if ( nUnincluded > 1 ) {
return *this;
}
if ( nUnincluded == 0 ) {
makeEmpty();
return *this;
}
// nUnincluded == 1
_ranges[ unincludedKey ] -= other._ranges[ unincludedKey ];
appendQueries( other );
return *this;
}
const FieldRangeSet &operator&=( const FieldRangeSet &other ) {
map< string, FieldRange >::iterator i = _ranges.begin();
map< string, FieldRange >::const_iterator j = other._ranges.beg in();
while( i != _ranges.end() && j != other._ranges.end() ) {
int cmp = i->first.compare( j->first );
if ( cmp == 0 ) {
i->second &= j->second;
++i;
++j;
} else if ( cmp < 0 ) {
++i;
} else {
_ranges[ j->first ] = j->second;
++j;
}
}
while( j != other._ranges.end() ) {
_ranges[ j->first ] = j->second;
++j;
}
appendQueries( other );
return *this;
}
// TODO get rid of this
BoundList indexBounds( const BSONObj &keyPattern, int direction ) c onst; BoundList indexBounds( const BSONObj &keyPattern, int direction ) c onst;
/**
* @return - A new FieldRangeSet based on this FieldRangeSet, but w ith only
* a subset of the fields.
* @param fields - Only fields which are represented as field names in this object
* will be included in the returned FieldRangeSet.
*/
FieldRangeSet *subset( const BSONObj &fields ) const;
bool singleKey() const { return _singleKey; }
BSONObj originalQuery() const { return _queries[ 0 ]; }
private: private:
void appendQueries( const FieldRangeSet &other ) { void appendQueries( const FieldRangeSet &other );
for( vector< BSONObj >::const_iterator i = other._queries.begin (); i != other._queries.end(); ++i ) { void makeEmpty();
_queries.push_back( *i );
}
}
void makeEmpty() {
for( map< string, FieldRange >::iterator i = _ranges.begin(); i != _ranges.end(); ++i ) {
i->second.makeEmpty();
}
}
void processQueryField( const BSONElement &e, bool optimize ); void processQueryField( const BSONElement &e, bool optimize );
void processOpElement( const char *fieldName, const BSONElement &f, bool isNot, bool optimize ); void processOpElement( const char *fieldName, const BSONElement &f, bool isNot, bool optimize );
static FieldRange *trivialRange_; static FieldRange *__singleKeyTrivialRange;
static FieldRange &trivialRange(); static FieldRange *__multiKeyTrivialRange;
mutable map< string, FieldRange > _ranges; const FieldRange &trivialRange() const;
map<string,FieldRange> _ranges;
const char *_ns; const char *_ns;
// make sure memory for FieldRange BSONElements is owned // Owns memory for FieldRange BSONElements.
vector< BSONObj > _queries; vector<BSONObj> _queries;
bool _singleKey;
}; };
class FieldRangeVector { class NamespaceDetails;
/**
* A pair of FieldRangeSets, one representing constraints for single ke y
* indexes and the other representing constraints for multi key indexes and
* unindexed scans. In several member functions the caller is asked to
* supply an index so that the implementation may utilize the proper
* FieldRangeSet and return results that are appropriate with respect t o that
* supplied index.
*/
class FieldRangeSetPair {
public: public:
FieldRangeVector( const FieldRangeSet &frs, const BSONObj &keyPatte rn, int direction ) FieldRangeSetPair( const char *ns, const BSONObj &query, bool optim ize=true )
:_keyPattern( keyPattern ), _direction( direction >= 0 ? 1 : -1 ) :_singleKey( ns, query, true, optimize ), _multiKey( ns, query, fal se, optimize ) {}
{
_queries = frs._queries; /**
BSONObjIterator i( _keyPattern ); * @return the appropriate single or multi key FieldRangeSet for th e specified index.
while( i.more() ) { * @param idxNo -1 for non index scan.
BSONElement e = i.next(); */
int number = (int) e.number(); // returns 0.0 if not numeri c const FieldRangeSet &frsForIndex( const NamespaceDetails* nsd, int idxNo ) const;
bool forward = ( ( number >= 0 ? 1 : -1 ) * ( direction >= 0 ? 1 : -1 ) > 0 );
if ( forward ) { /** @return a field range in the single key FieldRangeSet. */
_ranges.push_back( frs.range( e.fieldName() ) ); const FieldRange &singleKeyRange( const char *fieldName ) const {
} else { return _singleKey.range( fieldName );
_ranges.push_back( FieldRange() );
frs.range( e.fieldName() ).reverse( _ranges.back() );
}
assert( !_ranges.back().empty() );
}
uassert( 13385, "combinatorial limit of $in partitioning of res ult set exceeded", size() < 1000000 );
}
long long size() {
long long ret = 1;
for( vector< FieldRange >::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) {
ret *= i->intervals().size();
}
return ret;
}
BSONObj startKey() const {
BSONObjBuilder b;
for( vector< FieldRange >::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) {
const FieldInterval &fi = i->intervals().front();
b.appendAs( fi._lower._bound, "" );
}
return b.obj();
}
BSONObj endKey() const {
BSONObjBuilder b;
for( vector< FieldRange >::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) {
const FieldInterval &fi = i->intervals().back();
b.appendAs( fi._upper._bound, "" );
}
return b.obj();
} }
BSONObj obj() const { /** @return true if the range limits are equivalent to an empty que ry. */
BSONObjBuilder b; bool noNontrivialRanges() const;
BSONObjIterator k( _keyPattern ); /** @return false if a match is impossible regardless of index. */
for( int i = 0; i < (int)_ranges.size(); ++i ) { bool matchPossible() const { return _multiKey.matchPossible(); }
BSONArrayBuilder a( b.subarrayStart( k.next().fieldName() ) ); /**
for( vector< FieldInterval >::const_iterator j = _ranges[ i ].intervals().begin(); * @return false if a match is impossible on the specified index.
j != _ranges[ i ].intervals().end(); ++j ) { * @param idxNo -1 for non index scan.
a << BSONArray( BSON_ARRAY( j->_lower._bound << j->_upp er._bound ).clientReadable() ); */
} bool matchPossibleForIndex( NamespaceDetails *d, int idxNo, const B SONObj &keyPattern ) const;
a.done();
} const char *ns() const { return _singleKey.ns(); }
return b.obj();
string getSpecial() const { return _singleKey.getSpecial(); }
/** Intersect with another FieldRangeSetPair. */
FieldRangeSetPair &operator&=( const FieldRangeSetPair &other );
/**
* Subtract a FieldRangeSet, generally one expressing a range that has
* already been scanned.
*/
FieldRangeSetPair &operator-=( const FieldRangeSet &scanned );
BoundList singleKeyIndexBounds( const BSONObj &keyPattern, int dire ction ) const {
return _singleKey.indexBounds( keyPattern, direction );
} }
BSONObj originalQuery() const { return _singleKey.originalQuery(); }
private:
FieldRangeSetPair( const FieldRangeSet &singleKey, const FieldRange Set &multiKey )
:_singleKey( singleKey ), _multiKey( multiKey ) {}
void assertValidIndex( const NamespaceDetails *d, int idxNo ) const ;
void assertValidIndexOrNoIndex( const NamespaceDetails *d, int idxN o ) const;
/** matchPossibleForIndex() must be true. */
BSONObj simplifiedQueryForIndex( NamespaceDetails *d, int idxNo, co nst BSONObj &keyPattern ) const;
FieldRangeSet _singleKey;
FieldRangeSet _multiKey;
friend class OrRangeGenerator;
friend struct QueryUtilIndexed;
};
class IndexSpec;
/**
* An ordered list of fields and their FieldRanges, correspoinding to v alid
* index keys for a given index spec.
*/
class FieldRangeVector {
public:
/**
* @param frs The valid ranges for all fields, as defined by the qu ery spec
* @param indexSpec The index spec (key pattern and info)
* @param direction The direction of index traversal
*/
FieldRangeVector( const FieldRangeSet &frs, const IndexSpec &indexS pec, int direction );
/** @return the number of index ranges represented by 'this' */
long long size();
/** @return starting point for an index traversal. */
BSONObj startKey() const;
/** @return end point for an index traversal. */
BSONObj endKey() const;
/** @return a client readable representation of 'this' */
BSONObj obj() const;
/**
* @return true iff the provided document matches valid ranges on a ll
* of this FieldRangeVector's fields, which is the case iff this do cument
* would be returned while scanning the index corresponding to this
* FieldRangeVector. This function is used for $or clause deduping .
*/
bool matches( const BSONObj &obj ) const; bool matches( const BSONObj &obj ) const;
class Iterator {
public: /**
Iterator( const FieldRangeVector &v ) : _v( v ), _i( _v._ranges .size(), -1 ), _cmp( _v._ranges.size(), 0 ), _inc( _v._ranges.size(), false ), _after() { * @return first key of 'obj' that would be encountered by a forwar d
} * index scan using this FieldRangeVector, BSONObj() if no such key .
static BSONObj minObject() { */
BSONObjBuilder b; BSONObj firstMatch( const BSONObj &obj ) const;
b.appendMinKey( "" );
return b.obj();
}
static BSONObj maxObject() {
BSONObjBuilder b;
b.appendMaxKey( "" );
return b.obj();
}
bool advance() {
int i = _i.size() - 1;
while( i >= 0 && _i[ i ] >= ( (int)_v._ranges[ i ].interval s().size() - 1 ) ) {
--i;
}
if( i >= 0 ) {
_i[ i ]++;
for( unsigned j = i + 1; j < _i.size(); ++j ) {
_i[ j ] = 0;
}
} else {
_i[ 0 ] = _v._ranges[ 0 ].intervals().size();
}
return ok();
}
// return value
// -2 end of iteration
// -1 no skipping
// >= 0 skip parameter
int advance( const BSONObj &curr );
const vector< const BSONElement * > &cmp() const { return _cmp; }
const vector< bool > &inc() const { return _inc; }
bool after() const { return _after; }
void prepDive();
void setZero( int i ) {
for( int j = i; j < (int)_i.size(); ++j ) {
_i[ j ] = 0;
}
}
void setMinus( int i ) {
for( int j = i; j < (int)_i.size(); ++j ) {
_i[ j ] = -1;
}
}
bool ok() {
return _i[ 0 ] < (int)_v._ranges[ 0 ].intervals().size();
}
BSONObj startKey() {
BSONObjBuilder b;
for( int unsigned i = 0; i < _i.size(); ++i ) {
const FieldInterval &fi = _v._ranges[ i ].intervals()[ _i[ i ] ];
b.appendAs( fi._lower._bound, "" );
}
return b.obj();
}
// temp
BSONObj endKey() {
BSONObjBuilder b;
for( int unsigned i = 0; i < _i.size(); ++i ) {
const FieldInterval &fi = _v._ranges[ i ].intervals()[ _i[ i ] ];
b.appendAs( fi._upper._bound, "" );
}
return b.obj();
}
// check
private:
const FieldRangeVector &_v;
vector< int > _i;
vector< const BSONElement* > _cmp;
vector< bool > _inc;
bool _after;
};
private: private:
int matchingLowElement( const BSONElement &e, int i, bool direction , bool &lowEquality ) const; int matchingLowElement( const BSONElement &e, int i, bool direction , bool &lowEquality ) const;
bool matchesElement( const BSONElement &e, int i, bool direction ) const; bool matchesElement( const BSONElement &e, int i, bool direction ) const;
vector< FieldRange > _ranges; bool matchesKey( const BSONObj &key ) const;
BSONObj _keyPattern; vector<FieldRange> _ranges;
const IndexSpec &_indexSpec;
int _direction; int _direction;
vector< BSONObj > _queries; // make sure mem owned vector<BSONObj> _queries; // make sure mem owned
friend class FieldRangeVectorIterator;
}; };
// generages FieldRangeSet objects, accounting for or clauses /**
class FieldRangeOrSet { * Helper class for iterating through an ordered representation of keys
* to find those keys that match a specified FieldRangeVector.
*/
class FieldRangeVectorIterator {
public: public:
FieldRangeOrSet( const char *ns, const BSONObj &query , bool optimi ze=true ); FieldRangeVectorIterator( const FieldRangeVector &v ) : _v( v ), _i ( _v._ranges.size(), -1 ), _cmp( _v._ranges.size(), 0 ), _inc( _v._ranges.s ize(), false ), _after() {
// if there's a useless or clause, we won't use or ranges to help w ith scanning
bool orFinished() const { return _orFound && _orSets.empty(); }
// removes first or clause, and removes the field ranges it covers from all subsequent or clauses
// this could invalidate the result of the last topFrs()
void popOrClause() {
massert( 13274, "no or clause to pop", !orFinished() );
const FieldRangeSet &toPop = _orSets.front();
list< FieldRangeSet >::iterator i = _orSets.begin();
++i;
while( i != _orSets.end() ) {
*i -= toPop;
if( !i->matchPossible() ) {
i = _orSets.erase( i );
} else {
++i;
}
}
_oldOrSets.push_front( toPop );
_orSets.pop_front();
} }
FieldRangeSet *topFrs() const { static BSONObj minObject() {
FieldRangeSet *ret = new FieldRangeSet( _baseSet ); BSONObjBuilder b; b.appendMinKey( "" );
if (_orSets.size()){ return b.obj();
*ret &= _orSets.front();
}
return ret;
} }
void allClausesSimplified( vector< BSONObj > &ret ) const { static BSONObj maxObject() {
for( list< FieldRangeSet >::const_iterator i = _orSets.begin(); i != _orSets.end(); ++i ) { BSONObjBuilder b; b.appendMaxKey( "" );
if ( i->matchPossible() ) { return b.obj();
ret.push_back( i->simplifiedQuery() );
}
}
} }
string getSpecial() const { return _baseSet.getSpecial(); } /**
* @return Suggested advance method, based on current key.
bool moreOrClauses() const { return !_orSets.empty(); } * -2 Iteration is complete, no need to advance.
* -1 Advance to the next key, without skipping.
* >=0 Skip parameter. If @return is r, skip to the key comprised
* of the first r elements of curr followed by the (r+1)th and
* remaining elements of cmp() (with inclusivity specified by
* the (r+1)th and remaining elements of inc()). If after() i s
* true, skip past this key not to it.
*/
int advance( const BSONObj &curr );
const vector<const BSONElement *> &cmp() const { return _cmp; }
const vector<bool> &inc() const { return _inc; }
bool after() const { return _after; }
void prepDive();
void setZero( int i ) { for( int j = i; j < (int)_i.size(); ++j ) _ i[ j ] = 0; }
void setMinus( int i ) { for( int j = i; j < (int)_i.size(); ++j ) _i[ j ] = -1; }
bool ok() { return _i[ 0 ] < (int)_v._ranges[ 0 ].intervals().size( ); }
BSONObj startKey();
// temp
BSONObj endKey();
private: private:
FieldRangeSet _baseSet; const FieldRangeVector &_v;
list< FieldRangeSet > _orSets; vector<int> _i;
list< FieldRangeSet > _oldOrSets; // make sure memory is owned vector<const BSONElement*> _cmp;
bool _orFound; vector<bool> _inc;
bool _after;
}; };
/** /**
used for doing field limiting * As we iterate through $or clauses this class generates a FieldRangeS etPair
* for the current $or clause, in some cases by excluding ranges that w ere
* included in a previous clause.
*/ */
class FieldMatcher { class OrRangeGenerator {
public: public:
FieldMatcher() OrRangeGenerator( const char *ns, const BSONObj &query , bool optim ize=true );
: _include(true)
, _special(false)
, _includeID(true)
, _skip(0)
, _limit(-1)
{}
void add( const BSONObj& o ); /**
* @return true iff we are done scanning $or clauses. if there's a
* useless or clause, we won't use or index ranges to help with sca nning.
*/
bool orFinished() const { return _orFound && _orSets.empty(); }
/** Iterates to the next $or clause by removing the current $or cla use. */
void popOrClause( NamespaceDetails *nsd, int idxNo, const BSONObj & keyPattern );
void popOrClauseSingleKey();
/** @return FieldRangeSetPair for the current $or clause. */
FieldRangeSetPair *topFrsp() const;
/**
* @return original FieldRangeSetPair for the current $or clause. W hile the
* original bounds are looser, they are composed of fewer ranges an d it
* is faster to do operations with them; when they can be used inst ead of
* more precise bounds, they should.
*/
FieldRangeSetPair *topFrspOriginal() const;
void append( BSONObjBuilder& b , const BSONElement& e ) const; string getSpecial() const { return _baseSet.getSpecial(); }
BSONObj getSpec() const; bool moreOrClauses() const { return !_orSets.empty(); }
bool includeID() { return _includeID; }
private: private:
void assertMayPopOrClause();
void add( const string& field, bool include ); void popOrClause( const FieldRangeSet *toDiff, NamespaceDetails *d = 0, int idxNo = -1, const BSONObj &keyPattern = BSONObj() );
void add( const string& field, int skip, int limit ); FieldRangeSetPair _baseSet;
void appendArray( BSONObjBuilder& b , const BSONObj& a , bool neste d=false) const; list<FieldRangeSetPair> _orSets;
list<FieldRangeSetPair> _originalOrSets;
bool _include; // true if default at this level is to include // ensure memory is owned
bool _special; // true if this level can't be skipped or included w ithout recursing list<FieldRangeSetPair> _oldOrSets;
//TODO: benchmark vector<pair> vs map bool _orFound;
typedef map<string, boost::shared_ptr<FieldMatcher> > FieldMap; friend struct QueryUtilIndexed;
FieldMap _fields;
BSONObj _source;
bool _includeID;
// used for $slice operator
int _skip;
int _limit;
}; };
/** returns a string that when used as a matcher, would match a super s et of regex() /** returns a string that when used as a matcher, would match a super s et of regex()
returns "" for complex regular expressions returns "" for complex regular expressions
used to optimize queries in some simple regex cases that start with '^' used to optimize queries in some simple regex cases that start with '^'
if purePrefix != NULL, sets it to whether the regex can be converte d to a range query if purePrefix != NULL, sets it to whether the regex can be converte d to a range query
*/ */
string simpleRegex(const char* regex, const char* flags, bool* purePref ix=NULL); string simpleRegex(const char* regex, const char* flags, bool* purePref ix=NULL);
/** returns the upper bound of a query that matches prefix */ /** returns the upper bound of a query that matches prefix */
string simpleRegexEnd( string prefix ); string simpleRegexEnd( string prefix );
long long applySkipLimit( long long num , const BSONObj& cmd ); long long applySkipLimit( long long num , const BSONObj& cmd );
} // namespace mongo } // namespace mongo
#include "queryutil-inl.h"
 End of changes. 54 change blocks. 
446 lines changed or deleted 315 lines changed or added


 queue.h   queue.h 
skipping to change at line 21 skipping to change at line 21
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/goodies.h"
#include <queue> #include <queue>
#include "../util/timer.h"
namespace mongo { namespace mongo {
/** /**
* simple blocking queue * simple blocking queue
*/ */
template<typename T> class BlockingQueue : boost::noncopyable { template<typename T> class BlockingQueue : boost::noncopyable {
public: public:
BlockingQueue() : _lock("BlockingQueue") { } BlockingQueue() : _lock("BlockingQueue") { }
void push(T const& t){ void push(T const& t) {
scoped_lock l( _lock ); scoped_lock l( _lock );
_queue.push( t ); _queue.push( t );
_condition.notify_one(); _condition.notify_one();
} }
bool empty() const { bool empty() const {
scoped_lock l( _lock ); scoped_lock l( _lock );
return _queue.empty(); return _queue.empty();
} }
bool tryPop( T & t ){ size_t size() const {
scoped_lock l( _lock );
return _queue.size();
}
bool tryPop( T & t ) {
scoped_lock l( _lock ); scoped_lock l( _lock );
if ( _queue.empty() ) if ( _queue.empty() )
return false; return false;
t = _queue.front(); t = _queue.front();
_queue.pop(); _queue.pop();
return true; return true;
} }
T blockingPop(){ T blockingPop() {
scoped_lock l( _lock ); scoped_lock l( _lock );
while( _queue.empty() ) while( _queue.empty() )
_condition.wait( l.boost() ); _condition.wait( l.boost() );
T t = _queue.front(); T t = _queue.front();
_queue.pop(); _queue.pop();
return t; return t;
} }
/**
* blocks waiting for an object until maxSecondsToWait passes
* if got one, return true and set in t
* otherwise return false and t won't be changed
*/
bool blockingPop( T& t , int maxSecondsToWait ) {
Timer timer;
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.sec += maxSecondsToWait;
scoped_lock l( _lock );
while( _queue.empty() ) {
if ( ! _condition.timed_wait( l.boost() , xt ) )
return false;
}
t = _queue.front();
_queue.pop();
return true;
}
private: private:
std::queue<T> _queue; std::queue<T> _queue;
mutable mongo::mutex _lock; mutable mongo::mutex _lock;
boost::condition _condition; boost::condition _condition;
}; };
} }
 End of changes. 6 change blocks. 
4 lines changed or deleted 34 lines changed or added


 ramlog.h   ramlog.h 
// log.h // ramlog.h
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "log.h" #include "log.h"
#include "mongoutils/html.h"
namespace mongo { namespace mongo {
class RamLog : public Tee { class RamLog : public Tee {
enum {
N = 128,
C = 256
};
char lines[N][C];
unsigned h, n;
public: public:
RamLog() { RamLog( string name );
h = 0; n = 0;
for( int i = 0; i < N; i++ )
lines[i][C-1] = 0;
}
virtual void write(LogLevel ll, const string& str) { virtual void write(LogLevel ll, const string& str);
char *p = lines[(h+n)%N];
if( str.size() < C )
strcpy(p, str.c_str());
else
memcpy(p, str.c_str(), C-1);
if( n < N ) n++;
else h = (h+1) % N;
}
void get( vector<const char*>& v) const { void get( vector<const char*>& v) const;
for( unsigned x=0, i=h; x++ < n; i=(i+1)%N )
v.push_back(lines[i]);
}
static int repeats(const vector<const char *>& v, int i) { void toHTML(stringstream& s);
for( int j = i-1; j >= 0 && j+8 > i; j-- ) {
if( strcmp(v[i]+20,v[j]+20) == 0 ) {
for( int x = 1; ; x++ ) {
if( j+x == i ) return j;
if( i+x>=(int) v.size() ) return -1;
if( strcmp(v[i+x]+20,v[j+x]+20) ) return -1;
}
return -1;
}
}
return -1;
}
static string clean(const vector<const char *>& v, int i, string li ne="") { static RamLog* get( string name );
if( line.empty() ) line = v[i]; static void getNames( vector<string>& names );
if( i > 0 && strncmp(v[i], v[i-1], 11) == 0 )
return string(" ") + line.substr(11);
return v[i];
}
static string color(string line) { time_t lastWrite() { return _lastWrite; } // 0 if no writes
string s = str::after(line, "replSet ");
if( str::startsWith(s, "warning") || startsWith(s, "error") )
return html::red(line);
if( str::startsWith(s, "info") ) {
if( str::endsWith(s, " up\n") )
return html::green(line);
else if( str::contains(s, " down ") || str::endsWith(s, " d own\n") )
return html::yellow(line);
return line; //html::blue(line);
}
return line; protected:
} static int repeats(const vector<const char *>& v, int i);
static string clean(const vector<const char *>& v, int i, string li ne="");
static string color(string line);
/* turn http:... into an anchor */ /* turn http:... into an anchor */
string linkify(const char *s) { static string linkify(const char *s);
const char *p = s;
const char *h = strstr(p, "http://");
if( h == 0 ) return s;
const char *sp = h + 7;
while( *sp && *sp != ' ' ) sp++;
string url(h, sp-h);
stringstream ss;
ss << string(s, h-s) << "<a href=\"" << url << "\">" << url << "</a>" << sp;
return ss.str();
}
void toHTML(stringstream& s) { private:
vector<const char*> v; ~RamLog(); // want this private as we want to leak so we can use th em till the very end
get( v );
bool first = true; enum {
s << "<pre>\n"; N = 128, // number of links
for( int i = 0; i < (int)v.size(); i++ ) { C = 256 // max size of line
assert( strlen(v[i]) > 20 ); };
int r = repeats(v, i); char lines[N][C];
if( r < 0 ) { unsigned h; // current position
s << color( linkify( clean(v,i).c_str() ) ); unsigned n; // numer of lines stores 0 o N
} string _name;
else {
stringstream x;
x << string(v[i], 0, 20);
int nr = (i-r);
int last = i+nr-1;
for( ; r < i ; r++ ) x << '.';
if( 1 ) {
stringstream r;
if( nr == 1 ) r << "repeat last line";
else r << "repeats last " << nr << " lines; ends " << string(v[last]+4,0,15);
first = false; s << html::a("", r.str(), clean(v,i, x.str()));
}
else s << x.str();
s << '\n';
i = last;
}
}
s << "</pre>\n";
}
typedef map<string,RamLog*> RM;
static mongo::mutex* _namedLock;
static RM* _named;
time_t _lastWrite;
}; };
} }
 End of changes. 14 change blocks. 
102 lines changed or deleted 27 lines changed or added


 redef_macros.h   redef_macros.h 
/** @file redef_macros.h - redefine macros from undef_macros.h */ /** @file redef_macros.h macros the implementation uses.
@see undef_macros.h undefines these after use to minimize name pollutio n.
*/
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
skipping to change at line 53 skipping to change at line 56
#define ctime MONGO_ctime #define ctime MONGO_ctime
// util/debug_util.h // util/debug_util.h
#define DEV MONGO_DEV #define DEV MONGO_DEV
#define DEBUGGING MONGO_DEBUGGING #define DEBUGGING MONGO_DEBUGGING
#define SOMETIMES MONGO_SOMETIMES #define SOMETIMES MONGO_SOMETIMES
#define OCCASIONALLY MONGO_OCCASIONALLY #define OCCASIONALLY MONGO_OCCASIONALLY
#define RARELY MONGO_RARELY #define RARELY MONGO_RARELY
#define ONCE MONGO_ONCE #define ONCE MONGO_ONCE
// util/log.h
#define LOG MONGO_LOG
#undef MONGO_MACROS_CLEANED #undef MONGO_MACROS_CLEANED
#endif #endif
 End of changes. 2 change blocks. 
1 lines changed or deleted 7 lines changed or added


 repl.h   repl.h 
skipping to change at line 33 skipping to change at line 33
at the master: at the master:
local.oplog.$<source> local.oplog.$<source>
*/ */
#pragma once #pragma once
#include "pdfile.h" #include "pdfile.h"
#include "db.h" #include "db.h"
#include "dbhelpers.h" #include "dbhelpers.h"
#include "query.h"
#include "queryoptimizer.h"
#include "../client/dbclient.h" #include "../client/dbclient.h"
#include "../util/optime.h" #include "../util/optime.h"
#include "oplog.h" #include "oplog.h"
#include "../util/concurrency/thread_pool.h" #include "../util/concurrency/thread_pool.h"
#include "oplogreader.h" #include "oplogreader.h"
#include "cloner.h"
namespace mongo { namespace mongo {
/* replication slave? (possibly with slave or repl pair nonmaster) /* replication slave? (possibly with slave)
--slave cmd line setting -> SimpleSlave --slave cmd line setting -> SimpleSlave
*/ */
typedef enum { NotSlave=0, SimpleSlave, ReplPairSlave } SlaveTypes; typedef enum { NotSlave=0, SimpleSlave } SlaveTypes;
class ReplSettings { class ReplSettings {
public: public:
SlaveTypes slave; SlaveTypes slave;
/* true means we are master and doing replication. if we are not w riting to oplog (no --master or repl pairing), /** true means we are master and doing replication. if we are not writing to oplog, this won't be true. */
this won't be true.
*/
bool master; bool master;
int opIdMem;
bool fastsync; bool fastsync;
bool autoresync; bool autoresync;
int slavedelay; int slavedelay;
set<string> discoveredSeeds;
BSONObj reconfig;
ReplSettings() ReplSettings()
: slave(NotSlave) , master(false) , opIdMem(100000000) , fastsy nc() , autoresync(false), slavedelay() { : slave(NotSlave) , master(false) , fastsync() , autoresync(fal se), slavedelay(), discoveredSeeds() {
} }
}; };
extern ReplSettings replSettings; extern ReplSettings replSettings;
bool cloneFrom(const char *masterHost, string& errmsg, const string& fr omdb, bool logForReplication,
bool slaveOk, bool useReplAuth, bool snap shot);
/* A replication exception */ /* A replication exception */
class SyncException : public DBException { class SyncException : public DBException {
public: public:
SyncException() : DBException( "sync exception" , 10001 ){} SyncException() : DBException( "sync exception" , 10001 ) {}
}; };
/* A Source is a source from which we can pull (replicate) data. /* A Source is a source from which we can pull (replicate) data.
stored in collection local.sources. stored in collection local.sources.
Can be a group of things to replicate for several databases. Can be a group of things to replicate for several databases.
{ host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: . .., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } { host: ..., source: ..., only: ..., syncedTo: ..., dbsNextPass: { ... }, incompleteCloneDbs: { ... } }
'source' defaults to 'main'; support for multiple source names is 'source' defaults to 'main'; support for multiple source names is
not done (always use main for now). not done (always use main for now).
*/ */
class ReplSource { class ReplSource {
auto_ptr<ThreadPool> tp; shared_ptr<ThreadPool> tp;
bool resync(string db); void resync(string db);
/* pull some operations from the master's oplog, and apply them. */ /** @param alreadyLocked caller already put us in write lock if tru e */
int sync_pullOpLog(int& nApplied); void sync_pullOpLog_applyOperation(BSONObj& op, bool alreadyLocked) ;
void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai l); /* pull some operations from the master's oplog, and apply them.
calls sync_pullOpLog_applyOperation
*/
int sync_pullOpLog(int& nApplied);
/* we only clone one database per pass, even if a lot need done. T his helps us /* we only clone one database per pass, even if a lot need done. T his helps us
avoid overflowing the master's transaction log by doing too much work before going avoid overflowing the master's transaction log by doing too much work before going
back to read more transactions. (Imagine a scenario of slave sta rtup where we try to back to read more transactions. (Imagine a scenario of slave sta rtup where we try to
clone 100 databases in one pass.) clone 100 databases in one pass.)
*/ */
set<string> addDbNextPass; set<string> addDbNextPass;
set<string> incompleteCloneDbs; set<string> incompleteCloneDbs;
ReplSource(); ReplSource();
// returns the dummy ns used to do the drop // returns the dummy ns used to do the drop
string resyncDrop( const char *db, const char *requester ); string resyncDrop( const char *db, const char *requester );
// returns possibly unowned id spec for the operation.
static BSONObj idForOp( const BSONObj &op, bool &mod );
static void updateSetsWithOp( const BSONObj &op, bool mayUpdateStor age );
// call without the db mutex // call without the db mutex
void syncToTailOfRemoteLog(); void syncToTailOfRemoteLog();
// call with the db mutex
OpTime nextLastSavedLocalTs() const;
void setLastSavedLocalTs( const OpTime &nextLocalTs );
// call without the db mutex
void resetSlave();
// call with the db mutex
// returns false if the slave has been reset
bool updateSetsWithLocalOps( OpTime &localLogTail, bool mayUnlock ) ;
string ns() const { return string( "local.oplog.$" ) + sourceName() ; } string ns() const { return string( "local.oplog.$" ) + sourceName() ; }
unsigned _sleepAdviceTime; unsigned _sleepAdviceTime;
/**
* If 'db' is a new database and its name would conflict with that of
* an existing database, synchronize these database names with the
* master.
* @return true iff an op with the specified ns may be applied.
*/
bool handleDuplicateDbName( const BSONObj &op, const char *ns, cons t char *db );
public: public:
OplogReader oplogReader; OplogReader oplogReader;
static void applyOperation(const BSONObj& op); static void applyOperation(const BSONObj& op);
bool replacing; // in "replace mode" -- see CmdReplacePeer
bool paired; // --pair in use
string hostName; // ip addr or hostname plus optionally, ":<port >" string hostName; // ip addr or hostname plus optionally, ":<port >"
string _sourceName; // a logical source name. string _sourceName; // a logical source name.
string sourceName() const { string sourceName() const { return _sourceName.empty() ? "main" : _ sourceName; }
return _sourceName.empty() ? "main" : _sourceName;
}
string only; // only a certain db. note that in the sources collect ion, this may not be changed once you start replicating. string only; // only a certain db. note that in the sources collect ion, this may not be changed once you start replicating.
/* the last time point we have already synced up to (in the remote/ master's oplog). */ /* the last time point we have already synced up to (in the remote/ master's oplog). */
OpTime syncedTo; OpTime syncedTo;
/* This is for repl pairs.
_lastSavedLocalTs is the most recent point in the local log that we know is consistent
with the remote log ( ie say the local op log has entries ABCDE and the remote op log
has ABCXY, then _lastSavedLocalTs won't be greater than C until we have reconciled
the DE-XY difference.)
*/
OpTime _lastSavedLocalTs;
int nClonedThisPass; int nClonedThisPass;
typedef vector< shared_ptr< ReplSource > > SourceVector; typedef vector< shared_ptr< ReplSource > > SourceVector;
static void loadAll(SourceVector&); static void loadAll(SourceVector&);
explicit ReplSource(BSONObj); explicit ReplSource(BSONObj);
/* -1 = error */ /* -1 = error */
int sync(int& nApplied); int sync(int& nApplied);
void save(); // write ourself to local.sources void save(); // write ourself to local.sources
skipping to change at line 188 skipping to change at line 171
return 0; return 0;
int wait = _sleepAdviceTime - unsigned( time( 0 ) ); int wait = _sleepAdviceTime - unsigned( time( 0 ) );
return wait > 0 ? wait : 0; return wait > 0 ? wait : 0;
} }
static bool throttledForceResyncDead( const char *requester ); static bool throttledForceResyncDead( const char *requester );
static void forceResyncDead( const char *requester ); static void forceResyncDead( const char *requester );
void forceResync( const char *requester ); void forceResync( const char *requester );
}; };
// class for managing a set of ids in memory bool anyReplEnabled();
class MemIds { void appendReplicationInfo( BSONObjBuilder& result , bool authed , int level = 0 );
public:
MemIds() : size_() {}
friend class IdTracker;
void reset() {
imp_.clear();
size_ = 0;
}
bool get( const char *ns, const BSONObj &id ) { return imp_[ ns ].c ount( id ); }
void set( const char *ns, const BSONObj &id, bool val ) {
if ( val ) {
if ( imp_[ ns ].insert( id.getOwned() ).second ) {
size_ += id.objsize() + sizeof( BSONObj );
}
} else {
if ( imp_[ ns ].erase( id ) == 1 ) {
size_ -= id.objsize() + sizeof( BSONObj );
}
}
}
long long roughSize() const {
return size_;
}
private:
typedef map< string, BSONObjSetDefaultOrder > IdSets;
IdSets imp_;
long long size_;
};
// class for managing a set of ids in a db collection
// All functions must be called with db mutex held
class DbIds {
public:
DbIds( const string & name ) : impl_( name, BSON( "ns" << 1 << "id" << 1 ) ) {}
void reset() {
impl_.reset();
}
bool get( const char *ns, const BSONObj &id ) {
return impl_.get( key( ns, id ) );
}
void set( const char *ns, const BSONObj &id, bool val ) {
impl_.set( key( ns, id ), val );
}
private:
static BSONObj key( const char *ns, const BSONObj &id ) {
BSONObjBuilder b;
b << "ns" << ns;
// rename _id to id since there may be duplicates
b.appendAs( id.firstElement(), "id" );
return b.obj();
}
DbSet impl_;
};
// class for tracking ids and mod ids, in memory or on disk /**
// All functions must be called with db mutex held * Helper class used to set and query an ignore state for a named datab ase.
// Kind of sloppy class structure, for now just want to keep the in mem * The ignore state will expire after a specified OpTime.
// version speedy. */
// see http://www.mongodb.org/display/DOCS/Pairing+Internals class DatabaseIgnorer {
class IdTracker {
public: public:
IdTracker() : /** Indicate that operations for 'db' should be ignored until after 'futureOplogTime' */
dbIds_( "local.temp.replIds" ), void doIgnoreUntilAfter( const string &db, const OpTime &futureOplo gTime );
dbModIds_( "local.temp.replModIds" ), /**
inMem_( true ), * Query ignore state of 'db'; if 'currentOplogTime' is after the i gnore
maxMem_( replSettings.opIdMem ) { * limit, the ignore state will be cleared.
} */
void reset( int maxMem = replSettings.opIdMem ) { bool ignoreAt( const string &db, const OpTime &currentOplogTime );
memIds_.reset();
memModIds_.reset();
dbIds_.reset();
dbModIds_.reset();
maxMem_ = maxMem;
inMem_ = true;
}
bool haveId( const char *ns, const BSONObj &id ) {
if ( inMem_ )
return get( memIds_, ns, id );
else
return get( dbIds_, ns, id );
}
bool haveModId( const char *ns, const BSONObj &id ) {
if ( inMem_ )
return get( memModIds_, ns, id );
else
return get( dbModIds_, ns, id );
}
void haveId( const char *ns, const BSONObj &id, bool val ) {
if ( inMem_ )
set( memIds_, ns, id, val );
else
set( dbIds_, ns, id, val );
}
void haveModId( const char *ns, const BSONObj &id, bool val ) {
if ( inMem_ )
set( memModIds_, ns, id, val );
else
set( dbModIds_, ns, id, val );
}
// will release the db mutex
void mayUpgradeStorage() {
if ( !inMem_ || memIds_.roughSize() + memModIds_.roughSize() <= maxMem_ )
return;
log() << "saving master modified id information to collection" << endl;
upgrade( memIds_, dbIds_ );
upgrade( memModIds_, dbModIds_ );
memIds_.reset();
memModIds_.reset();
inMem_ = false;
}
bool inMem() const { return inMem_; }
private: private:
template< class T >; map< string, OpTime > _ignores;
bool get( T &ids, const char *ns, const BSONObj &id ) {
return ids.get( ns, id );
}
template< class T >
void set( T &ids, const char *ns, const BSONObj &id, bool val ) {
ids.set( ns, id, val );
}
void upgrade( MemIds &a, DbIds &b ) {
for( MemIds::IdSets::const_iterator i = a.imp_.begin(); i != a. imp_.end(); ++i ) {
for( BSONObjSetDefaultOrder::const_iterator j = i->second.b egin(); j != i->second.end(); ++j ) {
set( b, i->first.c_str(), *j, true );
RARELY {
dbtemprelease t;
}
}
}
}
MemIds memIds_;
MemIds memModIds_;
DbIds dbIds_;
DbIds dbModIds_;
bool inMem_;
int maxMem_;
}; };
bool anyReplEnabled();
void appendReplicationInfo( BSONObjBuilder& result , bool authed , int level = 0 );
} // namespace mongo } // namespace mongo
 End of changes. 26 change blocks. 
182 lines changed or deleted 43 lines changed or added


 repl_block.h   repl_block.h 
skipping to change at line 31 skipping to change at line 31
#include "../pch.h" #include "../pch.h"
#include "client.h" #include "client.h"
#include "curop.h" #include "curop.h"
/** /**
local.slaves - current location for all slaves local.slaves - current location for all slaves
*/ */
namespace mongo { namespace mongo {
void updateSlaveLocation( CurOp& curop, const char * ns , OpTime lastOp ); void updateSlaveLocation( CurOp& curop, const char * oplog_ns , OpTime lastOp );
/** @return true if op has made it to w servers */
bool opReplicatedEnough( OpTime op , int w ); bool opReplicatedEnough( OpTime op , int w );
bool opReplicatedEnough( OpTime op , BSONElement w );
void resetSlaveCache(); void resetSlaveCache();
unsigned getSlaveCount();
} }
 End of changes. 3 change blocks. 
1 lines changed or deleted 6 lines changed or added


 request.h   request.h 
skipping to change at line 21 skipping to change at line 21
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../util/message.h" #include "../util/net/message.h"
#include "../db/dbmessage.h" #include "../db/dbmessage.h"
#include "config.h" #include "config.h"
#include "util.h" #include "util.h"
namespace mongo { namespace mongo {
class OpCounters; class OpCounters;
class ClientInfo; class ClientInfo;
class Request : boost::noncopyable { class Request : boost::noncopyable {
skipping to change at line 66 skipping to change at line 66
bool isShardingEnabled() const { bool isShardingEnabled() const {
assert( _didInit ); assert( _didInit );
return _config->isShardingEnabled(); return _config->isShardingEnabled();
} }
ChunkManagerPtr getChunkManager() const { ChunkManagerPtr getChunkManager() const {
assert( _didInit ); assert( _didInit );
return _chunkManager; return _chunkManager;
} }
int getClientId() const {
return _clientId;
}
ClientInfo * getClientInfo() const { ClientInfo * getClientInfo() const {
return _clientInfo; return _clientInfo;
} }
void checkAuth() const;
// ---- remote location info ----- // ---- remote location info -----
Shard primaryShard() const ; Shard primaryShard() const ;
// ---- low level access ---- // ---- low level access ----
void reply( Message & response , const string& fromServer ); void reply( Message & response , const string& fromServer );
Message& m() { return _m; } Message& m() { return _m; }
DbMessage& d() { return _d; } DbMessage& d() { return _d; }
skipping to change at line 102 skipping to change at line 101
private: private:
Message& _m; Message& _m;
DbMessage _d; DbMessage _d;
AbstractMessagingPort* _p; AbstractMessagingPort* _p;
MSGID _id; MSGID _id;
DBConfigPtr _config; DBConfigPtr _config;
ChunkManagerPtr _chunkManager; ChunkManagerPtr _chunkManager;
int _clientId;
ClientInfo * _clientInfo; ClientInfo * _clientInfo;
OpCounters* _counter; OpCounters* _counter;
bool _didInit; bool _didInit;
}; };
typedef map<int,ClientInfo*> ClientCache;
class ClientInfo {
public:
ClientInfo( int clientId );
~ClientInfo();
string getRemote() const { return _remote; }
void addShard( const string& shard );
set<string> * getPrev() const { return _prev; };
void newRequest( AbstractMessagingPort* p = 0 );
void disconnect();
static ClientInfo * get( int clientId = 0 , bool create = true );
static void disconnect( int clientId );
const set<string>& sinceLastGetError() const { return _sinceLastGet Error; }
void clearSinceLastGetError(){
_sinceLastGetError.clear();
}
private:
int _id;
string _remote;
set<string> _a;
set<string> _b;
set<string> * _cur;
set<string> * _prev;
int _lastAccess;
set<string> _sinceLastGetError;
static mongo::mutex _clientsLock;
static ClientCache& _clients;
static boost::thread_specific_ptr<ClientInfo> _tlInfo;
};
} }
#include "strategy.h" #include "strategy.h"
 End of changes. 5 change blocks. 
44 lines changed or deleted 3 lines changed or added


 rs.h   rs.h 
skipping to change at line 24 skipping to change at line 24
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../../util/concurrency/list.h" #include "../../util/concurrency/list.h"
#include "../../util/concurrency/value.h" #include "../../util/concurrency/value.h"
#include "../../util/concurrency/msg.h" #include "../../util/concurrency/msg.h"
#include "../../util/hostandport.h" #include "../../util/net/hostandport.h"
#include "../commands.h" #include "../commands.h"
#include "../oplogreader.h"
#include "rs_exception.h" #include "rs_exception.h"
#include "rs_optime.h" #include "rs_optime.h"
#include "rs_member.h" #include "rs_member.h"
#include "rs_config.h" #include "rs_config.h"
/**
* Order of Events
*
* On startup, if the --replSet option is present, startReplSets is called.
* startReplSets forks off a new thread for replica set activities. It cre ates
* the global theReplSet variable and calls go() on it.
*
* theReplSet's constructor changes the replica set's state to RS_STARTUP,
* starts the replica set manager, and loads the config (if the replica set
* has been initialized).
*/
namespace mongo { namespace mongo {
struct HowToFixUp; struct HowToFixUp;
struct Target; struct Target;
class DBClientConnection; class DBClientConnection;
class ReplSetImpl; class ReplSetImpl;
class OplogReader; class OplogReader;
extern bool replSet; // true if using repl sets extern bool replSet; // true if using repl sets
extern class ReplSet *theReplSet; // null until initialized extern class ReplSet *theReplSet; // null until initialized
extern Tee *rsLog; extern Tee *rsLog;
/* member of a replica set */ /* member of a replica set */
class Member : public List1<Member>::Base { class Member : public List1<Member>::Base {
private:
~Member(); // intentionally unimplemented as should never be called -- see List1<>::Base.
Member(const Member&);
public: public:
Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg *c, bool self); Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg *c, bool self);
string fullName() const { return h().toString(); } string fullName() const { return h().toString(); }
const ReplSetConfig::MemberCfg& config() const { return _config; } const ReplSetConfig::MemberCfg& config() const { return _config; }
const HeartbeatInfo& hbinfo() const { return _hbinfo; } const HeartbeatInfo& hbinfo() const { return _hbinfo; }
HeartbeatInfo& get_hbinfo() { return _hbinfo; } HeartbeatInfo& get_hbinfo() { return _hbinfo; }
string lhb() const { return _hbinfo.lastHeartbeatMsg; } string lhb() const { return _hbinfo.lastHeartbeatMsg; }
MemberState state() const { return _hbinfo.hbstate; } MemberState state() const { return _hbinfo.hbstate; }
const HostAndPort& h() const { return _h; } const HostAndPort& h() const { return _h; }
unsigned id() const { return _hbinfo.id(); } unsigned id() const { return _hbinfo.id(); }
bool potentiallyHot() const { return _config.potentiallyHot(); } // not arbiter, not priority 0 bool potentiallyHot() const { return _config.potentiallyHot(); } // not arbiter, not priority 0
void summarizeMember(stringstream& s) const; void summarizeMember(stringstream& s) const;
friend class ReplSetImpl;
private: private:
friend class ReplSetImpl;
const ReplSetConfig::MemberCfg _config; const ReplSetConfig::MemberCfg _config;
const HostAndPort _h; const HostAndPort _h;
HeartbeatInfo _hbinfo; HeartbeatInfo _hbinfo;
}; };
class Manager : public task::Server { class Manager : public task::Server {
ReplSetImpl *rs; ReplSetImpl *rs;
bool busyWithElectSelf; bool busyWithElectSelf;
int _primary; int _primary;
/** @param two - if true two primaries were seen. this can happen transiently, in addition to our /** @param two - if true two primaries were seen. this can happen transiently, in addition to our
polling being only occasional. in this case null is returned, but the caller should polling being only occasional. in this case null is returned, but the caller should
not assume primary itself in that situation. not assume primary itself in that situation.
*/ */
const Member* findOtherPrimary(bool& two); const Member* findOtherPrimary(bool& two);
void noteARemoteIsPrimary(const Member *); void noteARemoteIsPrimary(const Member *);
void checkElectableSet();
virtual void starting(); virtual void starting();
public: public:
Manager(ReplSetImpl *rs); Manager(ReplSetImpl *rs);
~Manager(); virtual ~Manager();
void msgReceivedNewConfig(BSONObj); void msgReceivedNewConfig(BSONObj);
void msgCheckNewState(); void msgCheckNewState();
}; };
class GhostSync : public task::Server {
struct GhostSlave {
GhostSlave() : last(0), slave(0), init(false) {}
OplogReader reader;
OpTime last;
Member* slave;
bool init;
};
/**
* This is a cache of ghost slaves
*/
typedef map<mongo::OID,GhostSlave> MAP;
MAP _ghostCache;
RWLock _lock; // protects _ghostCache
ReplSetImpl *rs;
virtual void starting();
public:
GhostSync(ReplSetImpl *_rs) : task::Server("rsGhostSync"), _lock("G hostSync"), rs(_rs) {}
~GhostSync() {
log() << "~GhostSync() called" << rsLog;
}
/**
* Replica sets can sync in a hierarchical fashion, which throws of f w
* calculation on the master. percolate() faux-syncs from an upstr eam
* node so that the primary will know what the slaves are up to.
*
* We can't just directly sync to the primary because it could be
* unreachable, e.g., S1--->S2--->S3--->P. S2 should ghost sync fr om S3
* and S3 can ghost sync from the primary.
*
* Say we have an S1--->S2--->P situation and this node is S2. rid
* would refer to S1. S2 would create a ghost slave of S1 and conn ect
* it to P (_currentSyncTarget). Then it would use this connection to
* pretend to be S1, replicating off of P.
*/
void percolate(const BSONObj& rid, const OpTime& last);
void associateSlave(const BSONObj& rid, const int memberId);
void updateSlave(const mongo::OID& id, const OpTime& last);
};
struct Target; struct Target;
class Consensus { class Consensus {
ReplSetImpl &rs; ReplSetImpl &rs;
struct LastYea { struct LastYea {
LastYea() : when(0), who(0xffffffff) { } LastYea() : when(0), who(0xffffffff) { }
time_t when; time_t when;
unsigned who; unsigned who;
}; };
Atomic<LastYea> ly; static mutex lyMutex;
Guarded<LastYea,lyMutex> ly;
unsigned yea(unsigned memberId); // throws VoteException unsigned yea(unsigned memberId); // throws VoteException
void electionFailed(unsigned meid); void electionFailed(unsigned meid);
void _electSelf(); void _electSelf();
bool weAreFreshest(bool& allUp, int& nTies); bool weAreFreshest(bool& allUp, int& nTies);
bool sleptLast; // slept last elect() pass bool sleptLast; // slept last elect() pass
public: public:
Consensus(ReplSetImpl *t) : rs(*t) { Consensus(ReplSetImpl *t) : rs(*t) {
sleptLast = false; sleptLast = false;
steppedDown = 0; steppedDown = 0;
} }
skipping to change at line 117 skipping to change at line 179
time_t steppedDown; time_t steppedDown;
int totalVotes() const; int totalVotes() const;
bool aMajoritySeemsToBeUp() const; bool aMajoritySeemsToBeUp() const;
bool shouldRelinquish() const; bool shouldRelinquish() const;
void electSelf(); void electSelf();
void electCmdReceived(BSONObj, BSONObjBuilder*); void electCmdReceived(BSONObj, BSONObjBuilder*);
void multiCommand(BSONObj cmd, list<Target>& L); void multiCommand(BSONObj cmd, list<Target>& L);
}; };
/** most operations on a ReplSet object should be done while locked. th at logic implemented here. */ /**
* most operations on a ReplSet object should be done while locked. tha t
* logic implemented here.
*
* Order of locking: lock the replica set, then take a rwlock.
*/
class RSBase : boost::noncopyable { class RSBase : boost::noncopyable {
public: public:
const unsigned magic; const unsigned magic;
void assertValid() { assert( magic == 0x12345677 ); } void assertValid() { assert( magic == 0x12345677 ); }
private: private:
mutex m; mongo::mutex m;
int _locked; int _locked;
ThreadLocalValue<bool> _lockedByMe; ThreadLocalValue<bool> _lockedByMe;
protected: protected:
RSBase() : magic(0x12345677), m("RSBase"), _locked(0) { } RSBase() : magic(0x12345677), m("RSBase"), _locked(0) { }
~RSBase() { ~RSBase() {
/* this can happen if we throw in the constructor; otherwise ne ver happens. thus we log it as it is quite unusual. */ /* this can happen if we throw in the constructor; otherwise ne ver happens. thus we log it as it is quite unusual. */
log() << "replSet ~RSBase called" << rsLog; log() << "replSet ~RSBase called" << rsLog;
} }
public:
class lock { class lock {
RSBase& rsbase; RSBase& rsbase;
auto_ptr<scoped_lock> sl; auto_ptr<scoped_lock> sl;
public: public:
lock(RSBase* b) : rsbase(*b) { lock(RSBase* b) : rsbase(*b) {
if( rsbase._lockedByMe.get() ) if( rsbase._lockedByMe.get() )
return; // recursive is ok... return; // recursive is ok...
sl.reset( new scoped_lock(rsbase.m) ); sl.reset( new scoped_lock(rsbase.m) );
DEV assert(rsbase._locked == 0); DEV assert(rsbase._locked == 0);
skipping to change at line 156 skipping to change at line 224
~lock() { ~lock() {
if( sl.get() ) { if( sl.get() ) {
assert( rsbase._lockedByMe.get() ); assert( rsbase._lockedByMe.get() );
DEV assert(rsbase._locked == 1); DEV assert(rsbase._locked == 1);
rsbase._lockedByMe.set(false); rsbase._lockedByMe.set(false);
rsbase._locked--; rsbase._locked--;
} }
} }
}; };
public:
/* for asserts */ /* for asserts */
bool locked() const { return _locked != 0; } bool locked() const { return _locked != 0; }
/* if true, is locked, and was locked by this thread. note if false , it could be in the lock or not for another /* if true, is locked, and was locked by this thread. note if false , it could be in the lock or not for another
just for asserts & such so we can make the contracts clear on wh o locks what when. just for asserts & such so we can make the contracts clear on wh o locks what when.
we don't use these locks that frequently, so the little bit of o verhead is fine. we don't use these locks that frequently, so the little bit of o verhead is fine.
*/ */
bool lockedByMe() { return _lockedByMe.get(); } bool lockedByMe() { return _lockedByMe.get(); }
}; };
skipping to change at line 178 skipping to change at line 245
/* safe container for our state that keeps member pointer and state var iables always aligned */ /* safe container for our state that keeps member pointer and state var iables always aligned */
class StateBox : boost::noncopyable { class StateBox : boost::noncopyable {
public: public:
struct SP { // SP is like pair<MemberState,const Member *> but nice r struct SP { // SP is like pair<MemberState,const Member *> but nice r
SP() : state(MemberState::RS_STARTUP), primary(0) { } SP() : state(MemberState::RS_STARTUP), primary(0) { }
MemberState state; MemberState state;
const Member *primary; const Member *primary;
}; };
const SP get() { const SP get() {
scoped_lock lk(m); rwlock lk(m, false);
return sp; return sp;
} }
MemberState getState() const { return sp.state; } MemberState getState() const {
const Member* getPrimary() const { return sp.primary; } rwlock lk(m, false);
return sp.state;
}
const Member* getPrimary() const {
rwlock lk(m, false);
return sp.primary;
}
void change(MemberState s, const Member *self) { void change(MemberState s, const Member *self) {
scoped_lock lk(m); rwlock lk(m, true);
if( sp.state != s ) { if( sp.state != s ) {
log() << "replSet " << s.toString() << rsLog; log() << "replSet " << s.toString() << rsLog;
} }
sp.state = s; sp.state = s;
if( s.primary() ) { if( s.primary() ) {
sp.primary = self; sp.primary = self;
} }
else { else {
if( self == sp.primary ) if( self == sp.primary )
sp.primary = 0; sp.primary = 0;
} }
} }
void set(MemberState s, const Member *p) { void set(MemberState s, const Member *p) {
scoped_lock lk(m); rwlock lk(m, true);
sp.state = s; sp.primary = p; sp.state = s;
sp.primary = p;
} }
void setSelfPrimary(const Member *self) { change(MemberState::RS_PR IMARY, self); } void setSelfPrimary(const Member *self) { change(MemberState::RS_PR IMARY, self); }
void setOtherPrimary(const Member *mem) { void setOtherPrimary(const Member *mem) {
scoped_lock lk(m); rwlock lk(m, true);
assert( !sp.state.primary() ); assert( !sp.state.primary() );
sp.primary = mem; sp.primary = mem;
} }
void noteRemoteIsPrimary(const Member *remote) { void noteRemoteIsPrimary(const Member *remote) {
scoped_lock lk(m); rwlock lk(m, true);
if( !sp.state.secondary() ) if( !sp.state.secondary() && !sp.state.fatal() )
sp.state = MemberState::RS_RECOVERING; sp.state = MemberState::RS_RECOVERING;
sp.primary = remote; sp.primary = remote;
} }
StateBox() : m("StateBox") { } StateBox() : m("StateBox") { }
private: private:
mutex m; RWLock m;
SP sp; SP sp;
}; };
void parseReplsetCmdLine(string cfgString, string& setname, vector<Host AndPort>& seeds, set<HostAndPort>& seedSet ); void parseReplsetCmdLine(string cfgString, string& setname, vector<Host AndPort>& seeds, set<HostAndPort>& seedSet );
/** Parameter given to the --replSet command line option (parsed). /** Parameter given to the --replSet command line option (parsed).
Syntax is "<setname>/<seedhost1>,<seedhost2>" Syntax is "<setname>/<seedhost1>,<seedhost2>"
where setname is a name and seedhost is "<host>[:<port>]" */ where setname is a name and seedhost is "<host>[:<port>]" */
class ReplSetCmdline { class ReplSetCmdline {
public: public:
skipping to change at line 244 skipping to change at line 318
singleton and long lived. singleton and long lived.
*/ */
class ReplSetImpl : protected RSBase { class ReplSetImpl : protected RSBase {
public: public:
/** info on our state if the replset isn't yet "up". for example, if we are pre-initiation. */ /** info on our state if the replset isn't yet "up". for example, if we are pre-initiation. */
enum StartupStatus { enum StartupStatus {
PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3, PRESTART=0, LOADINGCONFIG=1, BADCONFIG=2, EMPTYCONFIG=3,
EMPTYUNREACHABLE=4, STARTED=5, SOON=6 EMPTYUNREACHABLE=4, STARTED=5, SOON=6
}; };
static StartupStatus startupStatus; static StartupStatus startupStatus;
static string startupStatusMsg; static DiagStr startupStatusMsg;
static string stateAsHtml(MemberState state); static string stateAsHtml(MemberState state);
/* todo thread */ /* todo thread */
void msgUpdateHBInfo(HeartbeatInfo); void msgUpdateHBInfo(HeartbeatInfo);
StateBox box; StateBox box;
OpTime lastOpTimeWritten; OpTime lastOpTimeWritten;
long long lastH; // hash we use to make sure we are reading the rig ht flow of ops and aren't on an out-of-date "fork" long long lastH; // hash we use to make sure we are reading the rig ht flow of ops and aren't on an out-of-date "fork"
private: private:
set<ReplSetHealthPollTask*> healthTasks; set<ReplSetHealthPollTask*> healthTasks;
void endOldHealthTasks(); void endOldHealthTasks();
void startHealthTaskFor(Member *m); void startHealthTaskFor(Member *m);
private:
Consensus elect; Consensus elect;
bool ok() const { return !box.getState().fatal(); }
void relinquish(); void relinquish();
void forgetPrimary(); void forgetPrimary();
protected: protected:
bool _stepDown(); bool _stepDown(int secs);
bool _freeze(int secs);
private: private:
void assumePrimary(); void assumePrimary();
void loadLastOpTimeWritten(); void loadLastOpTimeWritten(bool quiet=false);
void changeState(MemberState s); void changeState(MemberState s);
/**
* Find the closest member (using ping time) with a higher latest o ptime.
*/
const Member* getMemberToSyncTo();
Member* _currentSyncTarget;
// set of electable members' _ids
set<unsigned> _electableSet;
protected: protected:
// "heartbeat message" // "heartbeat message"
// sent in requestHeartbeat respond in field "hbm" // sent in requestHeartbeat respond in field "hbm"
char _hbmsg[256]; // we change this unlocked, thus not an stl::stri ng char _hbmsg[256]; // we change this unlocked, thus not an stl::stri ng
time_t _hbmsgTime; // when it was logged time_t _hbmsgTime; // when it was logged
public: public:
void sethbmsg(string s, int logLevel = 0); void sethbmsg(string s, int logLevel = 0);
/**
* Election with Priorities
*
* Each node (n) keeps a set of nodes that could be elected primary .
* Each node in this set:
*
* 1. can connect to a majority of the set
* 2. has a priority greater than 0
* 3. has an optime within 10 seconds of the most up-to-date node
* that n can reach
*
* If a node fails to meet one or more of these criteria, it is rem oved
* from the list. This list is updated whenever the node receives a
* heartbeat.
*
* When a node sends an "am I freshest?" query, the node receiving the
* query checks their electable list to make sure that no one else is
* electable AND higher priority. If this check passes, the node w ill
* return an "ok" response, if not, it will veto.
*
* If a node is primary and there is another node with higher prior ity
* on the electable list (i.e., it must be synced to within 10 seco nds
* of the current primary), the node (or nodes) with connections to both
* the primary and the secondary with higher priority will issue
* replSetStepDown requests to the primary to allow the higher-prio rity
* node to take over.
*/
void addToElectable(const unsigned m) { lock lk(this); _electableSe t.insert(m); }
void rmFromElectable(const unsigned m) { lock lk(this); _electableS et.erase(m); }
bool iAmElectable() { lock lk(this); return _electableSet.find(_sel f->id()) != _electableSet.end(); }
bool isElectable(const unsigned id) { lock lk(this); return _electa bleSet.find(id) != _electableSet.end(); }
Member* getMostElectable();
protected: protected:
bool initFromConfig(ReplSetConfig& c, bool reconf=false); // true i f ok; throws if config really bad; false if config doesn't include self /**
* Load a new config as the replica set's main config.
*
* If there is a "simple" change (just adding a node), this shortcu ts
* the config. Returns true if the config was changed. Returns fal se
* if the config doesn't include a this node. Throws an exception if
* something goes very wrong.
*
* Behavior to note:
* - locks this
* - intentionally leaks the old _cfg and any old _members (if the
* change isn't strictly additive)
*/
bool initFromConfig(ReplSetConfig& c, bool reconf=false);
void _fillIsMaster(BSONObjBuilder&); void _fillIsMaster(BSONObjBuilder&);
void _fillIsMasterHost(const Member*, vector<string>&, vector<strin g>&, vector<string>&); void _fillIsMasterHost(const Member*, vector<string>&, vector<strin g>&, vector<string>&);
const ReplSetConfig& config() { return *_cfg; } const ReplSetConfig& config() { return *_cfg; }
string name() const { return _name; } /* @return replica set's logi cal name */ string name() const { return _name; } /* @return replica set's logi cal name */
MemberState state() const { return box.getState(); } MemberState state() const { return box.getState(); }
void _fatal(); void _fatal();
void _getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) con st; void _getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) con st;
void _summarizeAsHtml(stringstream&) const; void _summarizeAsHtml(stringstream&) const;
void _summarizeStatus(BSONObjBuilder&) const; // for replSetGetStat us command void _summarizeStatus(BSONObjBuilder&) const; // for replSetGetStat us command
skipping to change at line 303 skipping to change at line 428
ReplSetImpl(ReplSetCmdline&); ReplSetImpl(ReplSetCmdline&);
/* call afer constructing to start - returns fairly quickly after l aunching its threads */ /* call afer constructing to start - returns fairly quickly after l aunching its threads */
void _go(); void _go();
private: private:
string _name; string _name;
const vector<HostAndPort> *_seeds; const vector<HostAndPort> *_seeds;
ReplSetConfig *_cfg; ReplSetConfig *_cfg;
/** load our configuration from admin.replset. try seed machines t oo. /**
@return true if ok; throws if config really bad; false if confi g doesn't include self * Finds the configuration with the highest version number and atte mpts
*/ * load it.
*/
bool _loadConfigFinish(vector<ReplSetConfig>& v); bool _loadConfigFinish(vector<ReplSetConfig>& v);
/**
* Gather all possible configs (from command line seeds, our own co nfig
* doc, and any hosts listed therein) and try to initiate from the most
* recent config we find.
*/
void loadConfig(); void loadConfig();
list<HostAndPort> memberHostnames() const; list<HostAndPort> memberHostnames() const;
const ReplSetConfig::MemberCfg& myConfig() const { return _self->co nfig(); } const ReplSetConfig::MemberCfg& myConfig() const { return _config; }
bool iAmArbiterOnly() const { return myConfig().arbiterOnly; } bool iAmArbiterOnly() const { return myConfig().arbiterOnly; }
bool iAmPotentiallyHot() const { return myConfig().potentiallyHot() ; } bool iAmPotentiallyHot() const {
return myConfig().potentiallyHot() && // not an arbiter
elect.steppedDown <= time(0) && // not stepped down/frozen
state() == MemberState::RS_SECONDARY; // not stale
}
protected: protected:
Member *_self; Member *_self;
bool _buildIndexes; // = _self->config().buildIndexes
void setSelfTo(Member *); // use this as it sets buildIndexes var
private: private:
List1<Member> _members; /* all members of the set EXCEPT self. */ List1<Member> _members; // all members of the set EXCEPT _self.
ReplSetConfig::MemberCfg _config; // config of _self
unsigned _id; // _id of _self
int _maintenanceMode; // if we should stay in recovering state
public: public:
unsigned selfId() const { return _self->id(); } // this is called from within a writelock in logOpRS
unsigned selfId() const { return _id; }
Manager *mgr; Manager *mgr;
GhostSync *ghost;
/**
* This forces a secondary to go into recovering state and stay the re
* until this is called again, passing in "false". Multiple thread s can
* call this and it will leave maintenance mode once all of the cal lers
* have called it again, passing in false.
*/
void setMaintenanceMode(const bool inc);
private: private:
Member* head() const { return _members.head(); } Member* head() const { return _members.head(); }
public: public:
const Member* findById(unsigned id) const; const Member* findById(unsigned id) const;
private: private:
void _getTargets(list<Target>&, int &configVersion); void _getTargets(list<Target>&, int &configVersion);
void getTargets(list<Target>&, int &configVersion); void getTargets(list<Target>&, int &configVersion);
void startThreads(); void startThreads();
friend class FeedbackThread; friend class FeedbackThread;
friend class CmdReplSetElect; friend class CmdReplSetElect;
friend class Member; friend class Member;
friend class Manager; friend class Manager;
friend class GhostSync;
friend class Consensus; friend class Consensus;
private: private:
/* pulling data from primary related - see rs_sync.cpp */ /* pulling data from primary related - see rs_sync.cpp */
bool initialSyncOplogApplication(string hn, const Member *primary, OpTime applyGTE, OpTime minValid); bool initialSyncOplogApplication(const Member *primary, OpTime appl yGTE, OpTime minValid);
void _syncDoInitialSync(); void _syncDoInitialSync();
void syncDoInitialSync(); void syncDoInitialSync();
void _syncThread(); void _syncThread();
bool tryToGoLiveAsASecondary(OpTime&); // readlocks bool tryToGoLiveAsASecondary(OpTime&); // readlocks
void syncTail(); void syncTail();
void syncApply(const BSONObj &o); void syncApply(const BSONObj &o);
unsigned _syncRollback(OplogReader& r); unsigned _syncRollback(OplogReader& r);
void syncRollback(OplogReader& r); void syncRollback(OplogReader& r);
void syncFixUp(HowToFixUp& h, OplogReader& r); void syncFixUp(HowToFixUp& h, OplogReader& r);
bool _getOplogReader(OplogReader& r, string& hn);
bool _isStale(OplogReader& r, const string& hn);
public: public:
void syncThread(); void syncThread();
const OpTime lastOtherOpTime() const;
}; };
class ReplSet : public ReplSetImpl { class ReplSet : public ReplSetImpl {
public: public:
ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin e) { } ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin e) { }
bool stepDown() { return _stepDown(); } // for the replSetStepDown command
bool stepDown(int secs) { return _stepDown(secs); }
// for the replSetFreeze command
bool freeze(int secs) { return _freeze(secs); }
string selfFullName() { string selfFullName() {
lock lk(this); assert( _self );
return _self->fullName(); return _self->fullName();
} }
bool buildIndexes() const { return _buildIndexes; }
/* call after constructing to start - returns fairly quickly after la[unching its threads */ /* call after constructing to start - returns fairly quickly after la[unching its threads */
void go() { _go(); } void go() { _go(); }
void fatal() { _fatal(); } void fatal() { _fatal(); }
bool isPrimary(); bool isPrimary() { return box.getState().primary(); }
bool isSecondary(); bool isSecondary() { return box.getState().secondary(); }
MemberState state() const { return ReplSetImpl::state(); } MemberState state() const { return ReplSetImpl::state(); }
string name() const { return ReplSetImpl::name(); } string name() const { return ReplSetImpl::name(); }
const ReplSetConfig& config() { return ReplSetImpl::config(); } const ReplSetConfig& config() { return ReplSetImpl::config(); }
void getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) cons t { _getOplogDiagsAsHtml(server_id,ss); } void getOplogDiagsAsHtml(unsigned server_id, stringstream& ss) cons t { _getOplogDiagsAsHtml(server_id,ss); }
void summarizeAsHtml(stringstream& ss) const { _summarizeAsHtml(ss) ; } void summarizeAsHtml(stringstream& ss) const { _summarizeAsHtml(ss) ; }
void summarizeStatus(BSONObjBuilder& b) const { _summarizeStatus(b ); } void summarizeStatus(BSONObjBuilder& b) const { _summarizeStatus(b ); }
void fillIsMaster(BSONObjBuilder& b) { _fillIsMaster(b); } void fillIsMaster(BSONObjBuilder& b) { _fillIsMaster(b); }
/* we have a new config (reconfig) - apply it. /**
@param comment write a no-op comment to the oplog about it. onl y makes sense if one is primary and initiating the reconf. * We have a new config (reconfig) - apply it.
*/ * @param comment write a no-op comment to the oplog about it. onl y
* makes sense if one is primary and initiating the reconf.
*
* The slaves are updated when they get a heartbeat indicating the new
* config. The comment is a no-op.
*/
void haveNewConfig(ReplSetConfig& c, bool comment); void haveNewConfig(ReplSetConfig& c, bool comment);
/* if we delete old configs, this needs to assure locking. currentl y we don't so it is ok. */ /**
* Pointer assignment isn't necessarily atomic, so this needs to as sure
* locking, even though we don't delete old configs.
*/
const ReplSetConfig& getConfig() { return config(); } const ReplSetConfig& getConfig() { return config(); }
bool lockedByMe() { return RSBase::lockedByMe(); } bool lockedByMe() { return RSBase::lockedByMe(); }
// heartbeat msg to send to others; descriptive diagnostic info // heartbeat msg to send to others; descriptive diagnostic info
string hbmsg() const { string hbmsg() const {
if( time(0)-_hbmsgTime > 120 ) return ""; if( time(0)-_hbmsgTime > 120 ) return "";
return _hbmsg; return _hbmsg;
} }
}; };
/** base class for repl set commands. checks basic things such as in r s mode before the command /**
does its real work * Base class for repl set commands. Checks basic things such if we're in
*/ * rs mode before the command does its real work.
*/
class ReplSetCommand : public Command { class ReplSetCommand : public Command {
protected: protected:
ReplSetCommand(const char * s, bool show=false) : Command(s, show) { } ReplSetCommand(const char * s, bool show=false) : Command(s, show) { }
virtual bool slaveOk() const { return true; } virtual bool slaveOk() const { return true; }
virtual bool adminOnly() const { return true; } virtual bool adminOnly() const { return true; }
virtual bool logTheOp() { return false; } virtual bool logTheOp() { return false; }
virtual LockType locktype() const { return NONE; } virtual LockType locktype() const { return NONE; }
virtual void help( stringstream &help ) const { help << "internal"; } virtual void help( stringstream &help ) const { help << "internal"; }
/**
* Some replica set commands call this and then call check(). This is
* intentional, as they might do things before theReplSet is initia lized
* that still need to be checked for auth.
*/
bool checkAuth(string& errmsg, BSONObjBuilder& result) {
if( !noauth && adminOnly() ) {
AuthenticationInfo *ai = cc().getAuthenticationInfo();
if (!ai->isAuthorizedForLock("admin", locktype())) {
errmsg = "replSet command unauthorized";
return false;
}
}
return true;
}
bool check(string& errmsg, BSONObjBuilder& result) { bool check(string& errmsg, BSONObjBuilder& result) {
if( !replSet ) { if( !replSet ) {
errmsg = "not running with --replSet"; errmsg = "not running with --replSet";
return false; return false;
} }
if( theReplSet == 0 ) { if( theReplSet == 0 ) {
result.append("startupStatus", ReplSet::startupStatus); result.append("startupStatus", ReplSet::startupStatus);
errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno wn error 2" : ReplSet::startupStatusMsg; string s;
errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno wn error 2" : ReplSet::startupStatusMsg.get();
if( ReplSet::startupStatus == 3 ) if( ReplSet::startupStatus == 3 )
result.append("info", "run rs.initiate(...) if not yet done for the set"); result.append("info", "run rs.initiate(...) if not yet done for the set");
return false; return false;
} }
return true;
return checkAuth(errmsg, result);
} }
}; };
/**
* does local authentication
* directly authorizes against AuthenticationInfo
*/
void replLocalAuth();
/** inlines ----------------- */ /** inlines ----------------- */
inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig: :MemberCfg *c, bool self) : inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig: :MemberCfg *c, bool self) :
_config(*c), _h(h), _hbinfo(ord) _config(*c), _h(h), _hbinfo(ord) {
{ assert(c);
if( self ) if( self )
_hbinfo.health = 1.0; _hbinfo.health = 1.0;
} }
inline bool ReplSet::isPrimary() {
/* todo replset */
return box.getState().primary();
}
inline bool ReplSet::isSecondary() {
return box.getState().secondary();
}
} }
 End of changes. 60 change blocks. 
58 lines changed or deleted 243 lines changed or added


 rs_member.h   rs_member.h 
skipping to change at line 22 skipping to change at line 22
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** replica set member */ /** replica set member */
#pragma once #pragma once
#include "../../util/concurrency/value.h"
namespace mongo { namespace mongo {
/* /*
RS_STARTUP serving still starting up, or still trying to initiat e the set RS_STARTUP serving still starting up, or still trying to initiat e the set
RS_PRIMARY this server thinks it is primary RS_PRIMARY this server thinks it is primary
RS_SECONDARY this server thinks it is a secondary (slave mode) RS_SECONDARY this server thinks it is a secondary (slave mode)
RS_RECOVERING recovering/resyncing; after recovery usually auto-tra nsitions to secondary RS_RECOVERING recovering/resyncing; after recovery usually auto-tra nsitions to secondary
RS_FATAL something bad has occurred and server is not complete ly offline with regard to the replica set. fatal error. RS_FATAL something bad has occurred and server is not complete ly offline with regard to the replica set. fatal error.
RS_STARTUP2 loaded config, still determining who is primary RS_STARTUP2 loaded config, still determining who is primary
*/ */
struct MemberState { struct MemberState {
enum MS { enum MS {
RS_STARTUP, RS_STARTUP = 0,
RS_PRIMARY, RS_PRIMARY = 1,
RS_SECONDARY, RS_SECONDARY = 2,
RS_RECOVERING, RS_RECOVERING = 3,
RS_FATAL, RS_FATAL = 4,
RS_STARTUP2, RS_STARTUP2 = 5,
RS_UNKNOWN, /* remote node not yet reached */ RS_UNKNOWN = 6, /* remote node not yet reached */
RS_ARBITER, RS_ARBITER = 7,
RS_DOWN, /* node not reachable for a report */ RS_DOWN = 8, /* node not reachable for a report */
RS_ROLLBACK RS_ROLLBACK = 9
} s; } s;
MemberState(MS ms = RS_UNKNOWN) : s(ms) { } MemberState(MS ms = RS_UNKNOWN) : s(ms) { }
explicit MemberState(int ms) : s((MS) ms) { } explicit MemberState(int ms) : s((MS) ms) { }
bool startup() const { return s == RS_STARTUP; }
bool primary() const { return s == RS_PRIMARY; } bool primary() const { return s == RS_PRIMARY; }
bool secondary() const { return s == RS_SECONDARY; } bool secondary() const { return s == RS_SECONDARY; }
bool recovering() const { return s == RS_RECOVERING; } bool recovering() const { return s == RS_RECOVERING; }
bool startup2() const { return s == RS_STARTUP2; } bool startup2() const { return s == RS_STARTUP2; }
bool fatal() const { return s == RS_FATAL; } bool fatal() const { return s == RS_FATAL; }
bool rollback() const { return s == RS_ROLLBACK; } bool rollback() const { return s == RS_ROLLBACK; }
bool readable() const { return s == RS_PRIMARY || s == RS_SECONDARY ; }
string toString() const; string toString() const;
bool operator==(const MemberState& r) const { return s == r.s; } bool operator==(const MemberState& r) const { return s == r.s; }
bool operator!=(const MemberState& r) const { return s != r.s; } bool operator!=(const MemberState& r) const { return s != r.s; }
}; };
/* this is supposed to be just basic information on a member, /* this is supposed to be just basic information on a member,
and copy constructable. */ and copy constructable. */
class HeartbeatInfo { class HeartbeatInfo {
unsigned _id; unsigned _id;
public: public:
HeartbeatInfo() : _id(0xffffffff),hbstate(MemberState::RS_UNKNOWN), health(-1.0),downSince(0),skew(INT_MIN) { } HeartbeatInfo() : _id(0xffffffff),hbstate(MemberState::RS_UNKNOWN), health(-1.0),downSince(0),skew(INT_MIN) { }
HeartbeatInfo(unsigned id); HeartbeatInfo(unsigned id);
bool up() const { return health > 0; }
unsigned id() const { return _id; } unsigned id() const { return _id; }
MemberState hbstate; MemberState hbstate;
double health; double health;
time_t upSince; time_t upSince;
long long downSince; long long downSince;
time_t lastHeartbeat; time_t lastHeartbeat;
string lastHeartbeatMsg; DiagStr lastHeartbeatMsg;
OpTime opTime; OpTime opTime;
int skew; int skew;
unsigned int ping; // milliseconds
static unsigned int numPings;
bool up() const { return health > 0; }
/** health is set to -1 on startup. that means we haven't even che cked yet. 0 means we checked and it failed. */
bool maybeUp() const { return health != 0; }
long long timeDown() const; // ms long long timeDown() const; // ms
/* true if changed in a way of interest to the repl set manager. */ /* true if changed in a way of interest to the repl set manager. */
bool changed(const HeartbeatInfo& old) const; bool changed(const HeartbeatInfo& old) const;
}; };
inline HeartbeatInfo::HeartbeatInfo(unsigned id) : _id(id) { inline HeartbeatInfo::HeartbeatInfo(unsigned id) : _id(id) {
hbstate = MemberState::RS_UNKNOWN; hbstate = MemberState::RS_UNKNOWN;
health = -1.0; health = -1.0;
downSince = 0; downSince = 0;
lastHeartbeat = upSince = 0; lastHeartbeat = upSince = 0;
skew = INT_MIN; skew = INT_MIN;
} }
inline bool HeartbeatInfo::changed(const HeartbeatInfo& old) const { inline bool HeartbeatInfo::changed(const HeartbeatInfo& old) const {
return health != old.health || return health != old.health ||
hbstate != old.hbstate; hbstate != old.hbstate;
} }
inline string MemberState::toString() const {
switch ( s ) {
case RS_STARTUP: return "STARTUP";
case RS_PRIMARY: return "PRIMARY";
case RS_SECONDARY: return "SECONDARY";
case RS_RECOVERING: return "RECOVERING";
case RS_FATAL: return "FATAL";
case RS_STARTUP2: return "STARTUP2";
case RS_ARBITER: return "ARBITER";
case RS_DOWN: return "DOWN";
case RS_ROLLBACK: return "ROLLBACK";
case RS_UNKNOWN: return "UNKNOWN";
}
return "";
}
} }
 End of changes. 8 change blocks. 
12 lines changed or deleted 38 lines changed or added


 rwlock.h   rwlock.h 
// rwlock.h // @file rwlock.h generic reader-writer lock (cross platform support)
/* /*
* Copyright (C) 2010 10gen Inc. * Copyright (C) 2010 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3, * it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "mutex.h" #include "mutex.h"
#include "../time_support.h"
#if BOOST_VERSION >= 103500 // this requires newer windows versions
#define BOOST_RWLOCK // it works better than sharable_mutex under high contention
#else #if defined(_WIN64)
//#define MONGO_USE_SRW_ON_WINDOWS 1
#endif
#if defined(_WIN32) #if !defined(MONGO_USE_SRW_ON_WINDOWS)
#error need boost >= 1.35 for windows
#endif
#include <pthread.h> #if BOOST_VERSION >= 103500
# define BOOST_RWLOCK
#else
# if defined(_WIN32)
# error need boost >= 1.35 for windows
# endif
# include <pthread.h>
#endif
#if defined(_WIN32)
# include "shared_mutex_win.hpp"
namespace mongo {
typedef boost::modified_shared_mutex shared_mutex;
}
# undef assert
# define assert MONGO_assert
#elif defined(BOOST_RWLOCK)
# include <boost/thread/shared_mutex.hpp>
# undef assert
# define assert MONGO_assert
#endif #endif
#ifdef BOOST_RWLOCK
#include <boost/thread/shared_mutex.hpp>
#undef assert
#define assert MONGO_assert
#endif #endif
namespace mongo { namespace mongo {
#ifdef BOOST_RWLOCK #if defined(MONGO_USE_SRW_ON_WINDOWS) && defined(_WIN32)
class RWLock {
boost::shared_mutex _m; // Windows RWLock implementation (requires newer versions of windows th us the above macro)
class RWLock : boost::noncopyable {
public: public:
#if defined(_DEBUG) RWLock(const char *, int lowPriorityWaitMS=0 ) : _lowPriorityWaitMS (lowPriorityWaitMS)
const char *_name; { InitializeSRWLock(&_lock); }
RWLock(const char *name) : _name(name) { } ~RWLock() { }
#else const char * implType() const { return "WINSRW"; }
RWLock(const char *) { } int lowPriorityWaitMS() const { return _lowPriorityWaitMS; }
#endif void lock() { AcquireSRWLockExclusive(&_lock); }
void lock(){ void unlock() { ReleaseSRWLockExclusive(&_lock); }
_m.lock(); void lock_shared() { AcquireSRWLockShared(&_lock); }
#if defined(_DEBUG) void unlock_shared() { ReleaseSRWLockShared(&_lock); }
mutexDebugger.entering(_name); bool lock_shared_try( int millis ) {
#endif if( TryAcquireSRWLockShared(&_lock) )
return true;
if( millis == 0 )
return false;
unsigned long long end = curTimeMicros64() + millis*1000;
while( 1 ) {
Sleep(1);
if( TryAcquireSRWLockShared(&_lock) )
return true;
if( curTimeMicros64() >= end )
break;
}
return false;
} }
void unlock(){ bool lock_try( int millis = 0 ) {
#if defined(_DEBUG) if( TryAcquireSRWLockExclusive(&_lock) ) // quick check to opti mistically avoid calling curTimeMicros64
mutexDebugger.leaving(_name); return true;
#endif if( millis == 0 )
return false;
unsigned long long end = curTimeMicros64() + millis*1000;
do {
Sleep(1);
if( TryAcquireSRWLockExclusive(&_lock) )
return true;
} while( curTimeMicros64() < end );
return false;
}
private:
SRWLOCK _lock;
const int _lowPriorityWaitMS;
};
#elif defined(BOOST_RWLOCK)
// Boost based RWLock implementation
class RWLock : boost::noncopyable {
shared_mutex _m;
const int _lowPriorityWaitMS;
public:
const char * const _name;
RWLock(const char *name, int lowPriorityWait=0) : _lowPriorityWaitM S(lowPriorityWait) , _name(name) { }
const char * implType() const { return "boost"; }
int lowPriorityWaitMS() const { return _lowPriorityWaitMS; }
void lock() {
_m.lock();
DEV mutexDebugger.entering(_name);
}
/*void lock() {
// This sequence gives us the lock semantics we want: specifica lly that write lock acquisition is
// greedy EXCEPT when someone already is in upgradable state.
lockAsUpgradable();
upgrade();
DEV mutexDebugger.entering(_name);
}*/
void unlock() {
DEV mutexDebugger.leaving(_name);
_m.unlock(); _m.unlock();
} }
void lock_shared(){ void lockAsUpgradable() {
_m.lock_shared(); _m.lock_upgrade();
}
void unlockFromUpgradable() { // upgradable -> unlocked
_m.unlock_upgrade();
}
void upgrade() { // upgradable -> exclusive lock
_m.unlock_upgrade_and_lock();
} }
void unlock_shared(){ void lock_shared() {
_m.lock_shared();
}
void unlock_shared() {
_m.unlock_shared(); _m.unlock_shared();
} }
bool lock_shared_try( int millis ){ bool lock_shared_try( int millis ) {
boost::system_time until = get_system_time(); if( _m.timed_lock_shared( boost::posix_time::milliseconds(milli s) ) ) {
until += boost::posix_time::milliseconds(millis);
if( _m.timed_lock_shared( until ) ) {
return true; return true;
} }
return false; return false;
} }
bool lock_try( int millis = 0 ){ bool lock_try( int millis = 0 ) {
boost::system_time until = get_system_time(); if( _m.timed_lock( boost::posix_time::milliseconds(millis) ) ) {
until += boost::posix_time::milliseconds(millis); DEV mutexDebugger.entering(_name);
if( _m.timed_lock( until ) ) {
#if defined(_DEBUG)
mutexDebugger.entering(_name);
#endif
return true; return true;
} }
return false; return false;
} }
}; };
#else #else
class RWLock {
pthread_rwlock_t _lock;
inline void check( int x ){ // Posix RWLock implementation
if( x == 0 ) class RWLock : boost::noncopyable {
pthread_rwlock_t _lock;
const int _lowPriorityWaitMS;
static void check( int x ) {
if( MONGO_likely(x == 0) )
return; return;
log() << "pthread rwlock failed: " << x << endl; log() << "pthread rwlock failed: " << x << endl;
assert( x == 0 ); assert( x == 0 );
} }
public: public:
#if defined(_DEBUG)
const char *_name; const char *_name;
RWLock(const char *name) : _name(name) { RWLock(const char *name, int lowPriorityWaitMS=0) : _lowPriorityWai tMS(lowPriorityWaitMS), _name(name)
#else {
RWLock(const char *) {
#endif
check( pthread_rwlock_init( &_lock , 0 ) ); check( pthread_rwlock_init( &_lock , 0 ) );
} }
~RWLock(){ ~RWLock() {
if ( ! __destroyingStatics ){ if ( ! StaticObserver::_destroyingStatics ) {
check( pthread_rwlock_destroy( &_lock ) ); wassert( pthread_rwlock_destroy( &_lock ) == 0 ); // wasser t as don't want to throw from a destructor
} }
} }
void lock(){ const char * implType() const { return "posix"; }
int lowPriorityWaitMS() const { return _lowPriorityWaitMS; }
void lock() {
check( pthread_rwlock_wrlock( &_lock ) ); check( pthread_rwlock_wrlock( &_lock ) );
#if defined(_DEBUG) DEV mutexDebugger.entering(_name);
mutexDebugger.entering(_name);
#endif
} }
void unlock(){ void unlock() {
#if defined(_DEBUG)
mutexDebugger.leaving(_name); mutexDebugger.leaving(_name);
#endif
check( pthread_rwlock_unlock( &_lock ) ); check( pthread_rwlock_unlock( &_lock ) );
} }
void lock_shared(){ void lock_shared() {
check( pthread_rwlock_rdlock( &_lock ) ); check( pthread_rwlock_rdlock( &_lock ) );
} }
void unlock_shared(){ void unlock_shared() {
check( pthread_rwlock_unlock( &_lock ) ); check( pthread_rwlock_unlock( &_lock ) );
} }
bool lock_shared_try( int millis ){ bool lock_shared_try( int millis ) {
return _try( millis , false ); return _try( millis , false );
} }
bool lock_try( int millis = 0 ){ bool lock_try( int millis = 0 ) {
if( _try( millis , true ) ) { if( _try( millis , true ) ) {
#if defined(_DEBUG) DEV mutexDebugger.entering(_name);
mutexDebugger.entering(_name);
#endif
return true; return true;
} }
return false; return false;
} }
bool _try( int millis , bool write ){ bool _try( int millis , bool write ) {
while ( true ) { while ( true ) {
int x = write ? int x = write ?
pthread_rwlock_trywrlock( &_lock ) : pthread_rwlock_trywrlock( &_lock ) :
pthread_rwlock_tryrdlock( &_lock ); pthread_rwlock_tryrdlock( &_lock );
if ( x <= 0 ) { if ( x <= 0 ) {
return true; return true;
} }
if ( millis-- <= 0 ) if ( millis-- <= 0 )
return false; return false;
if ( x == EBUSY ){ if ( x == EBUSY ) {
sleepmillis(1); sleepmillis(1);
continue; continue;
} }
check(x); check(x);
} }
return false; return false;
} }
}; };
#endif #endif
class rwlock_try_write { /** throws on failure to acquire in the specified time period. */
RWLock& _l; class rwlock_try_write : boost::noncopyable {
public: public:
struct exception { }; struct exception { };
rwlock_try_write(RWLock& l, int millis = 0) : _l(l) { rwlock_try_write(RWLock& l, int millis = 0) : _l(l) {
if( !l.lock_try(millis) ) throw exception(); if( !l.lock_try(millis) )
throw exception();
} }
~rwlock_try_write() { _l.unlock(); } ~rwlock_try_write() { _l.unlock(); }
private:
RWLock& _l;
}; };
/* scoped lock */ class rwlock_shared : boost::noncopyable {
struct rwlock { public:
rwlock( const RWLock& lock , bool write , bool alreadyHaveLock = fa lse ) rwlock_shared(RWLock& rwlock) : _r(rwlock) {_r.lock_shared(); }
: _lock( (RWLock&)lock ) , _write( write ){ ~rwlock_shared() { _r.unlock_shared(); }
private:
RWLock& _r;
};
if ( ! alreadyHaveLock ){ /* scoped lock for RWLock */
if ( _write ) class rwlock : boost::noncopyable {
_lock.lock(); public:
else /**
* @param write acquire write lock if true sharable if false
* @param lowPriority if > 0, will try to get the lock non-greedily for that many ms
*/
rwlock( const RWLock& lock , bool write, /* bool alreadyHaveLock = false , */int lowPriorityWaitMS = 0 )
: _lock( (RWLock&)lock ) , _write( write ) {
{
if ( _write ) {
if ( ! lowPriorityWaitMS && lock.lowPriorityWaitMS() )
lowPriorityWaitMS = lock.lowPriorityWaitMS();
if ( lowPriorityWaitMS ) {
bool got = false;
for ( int i=0; i<lowPriorityWaitMS; i++ ) {
if ( _lock.lock_try(0) ) {
got = true;
break;
}
int sleep = 1;
if ( i > ( lowPriorityWaitMS / 20 ) )
sleep = 10;
sleepmillis(sleep);
i += ( sleep - 1 );
}
if ( ! got ) {
log() << "couldn't get lazy rwlock" << endl;
_lock.lock();
}
}
else {
_lock.lock();
}
}
else {
_lock.lock_shared(); _lock.lock_shared();
}
} }
} }
~rwlock() {
~rwlock(){
if ( _write ) if ( _write )
_lock.unlock(); _lock.unlock();
else else
_lock.unlock_shared(); _lock.unlock_shared();
} }
private:
RWLock& _lock; RWLock& _lock;
bool _write; const bool _write;
};
/** recursive on shared locks is ok for this implementation */
class RWLockRecursive : boost::noncopyable {
ThreadLocalValue<int> _state;
RWLock _lk;
friend class Exclusive;
public:
/** @param lpwaitms lazy wait */
RWLockRecursive(const char *name, int lpwaitms) : _lk(name, lpwaitm s) { }
void assertExclusivelyLocked() {
dassert( _state.get() < 0 );
}
// RWLockRecursive::Exclusive scoped lock
class Exclusive : boost::noncopyable {
RWLockRecursive& _r;
rwlock *_scopedLock;
public:
Exclusive(RWLockRecursive& r) : _r(r), _scopedLock(0) {
int s = _r._state.get();
dassert( s <= 0 );
if( s == 0 )
_scopedLock = new rwlock(_r._lk, true);
_r._state.set(s-1);
}
~Exclusive() {
int s = _r._state.get();
DEV wassert( s < 0 ); // wassert: don't throw from destruct ors
_r._state.set(s+1);
delete _scopedLock;
}
};
// RWLockRecursive::Shared scoped lock
class Shared : boost::noncopyable {
RWLockRecursive& _r;
bool _alreadyExclusive;
public:
Shared(RWLockRecursive& r) : _r(r) {
int s = _r._state.get();
_alreadyExclusive = s < 0;
if( !_alreadyExclusive ) {
dassert( s >= 0 ); // -1 would mean exclusive
if( s == 0 )
_r._lk.lock_shared();
_r._state.set(s+1);
}
}
~Shared() {
if( _alreadyExclusive ) {
DEV wassert( _r._state.get() < 0 );
}
else {
int s = _r._state.get() - 1;
if( s == 0 )
_r._lk.unlock_shared();
_r._state.set(s);
DEV wassert( s >= 0 );
}
}
};
}; };
} }
 End of changes. 42 change blocks. 
90 lines changed or deleted 268 lines changed or added


 scanandorder.h   scanandorder.h 
skipping to change at line 23 skipping to change at line 23
* 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 "indexkey.h"
#include "queryutil.h"
#include "projection.h"
namespace mongo { namespace mongo {
/* todo: /* todo:
_ handle compound keys with differing directions. we don't handle t his yet: neither here nor in indexes i think!!!
_ limit amount of data _ limit amount of data
*/ */
/* see also IndexDetails::getKeysFromObject, which needs some merging w ith this. */
class KeyType : boost::noncopyable { class KeyType : boost::noncopyable {
public: public:
BSONObj pattern; // e.g., { ts : -1 } IndexSpec _spec;
FieldRangeVector _keyCutter;
public: public:
KeyType(BSONObj _keyPattern) { KeyType(BSONObj pattern, const FieldRangeSet &frs):
pattern = _keyPattern; _spec((assert(!pattern.isEmpty()),pattern)),
assert( !pattern.isEmpty() ); _keyCutter(frs, _spec, 1) {
} }
// returns the key value for o /**
* @return first key of the object that would be encountered while
* scanning index with keySpec 'pattern' using constraints 'frs', o r
* BSONObj() if no such key.
*/
BSONObj getKeyFromObject(BSONObj o) { BSONObj getKeyFromObject(BSONObj o) {
return o.extractFields(pattern,true); return _keyCutter.firstMatch(o);
} }
}; };
/* todo: /* todo:
_ respect limit _ respect limit
_ check for excess mem usage _ check for excess mem usage
_ response size limit from runquery; push it up a bit. _ response size limit from runquery; push it up a bit.
*/ */
inline void fillQueryResultFromObj(BufBuilder& bb, FieldMatcher *filter , BSONObj& js, DiskLoc* loc=NULL) { inline void fillQueryResultFromObj(BufBuilder& bb, Projection *filter, const BSONObj& js, DiskLoc* loc=NULL) {
if ( filter ) { if ( filter ) {
BSONObjBuilder b( bb ); BSONObjBuilder b( bb );
BSONObjIterator i( js ); filter->transform( js , b );
while ( i.more() ){
BSONElement e = i.next();
const char * fname = e.fieldName();
if ( strcmp( fname , "_id" ) == 0 ){
if (filter->includeID())
b.append( e );
} else {
filter->append( b , e );
}
}
if (loc) if (loc)
b.append("$diskLoc", loc->toBSONObj()); b.append("$diskLoc", loc->toBSONObj());
b.done(); b.done();
} else if (loc) { }
else if (loc) {
BSONObjBuilder b( bb ); BSONObjBuilder b( bb );
b.appendElements(js); b.appendElements(js);
b.append("$diskLoc", loc->toBSONObj()); b.append("$diskLoc", loc->toBSONObj());
b.done(); b.done();
} else { }
else {
bb.appendBuf((void*) js.objdata(), js.objsize()); bb.appendBuf((void*) js.objdata(), js.objsize());
} }
} }
typedef multimap<BSONObj,BSONObj,BSONObjCmp> BestMap; typedef multimap<BSONObj,BSONObj,BSONObjCmp> BestMap;
class ScanAndOrder { class ScanAndOrder {
BestMap best; // key -> full object public:
int startFrom; static const unsigned MaxScanAndOrderBytes;
int limit; // max to send back.
KeyType order;
unsigned approxSize;
void _add(BSONObj& k, BSONObj o, DiskLoc* loc) {
if (!loc){
best.insert(make_pair(k.getOwned(),o.getOwned()));
} else {
BSONObjBuilder b;
b.appendElements(o);
b.append("$diskLoc", loc->toBSONObj());
best.insert(make_pair(k.getOwned(), b.obj().getOwned()));
}
}
void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i, DiskL oc* loc) { ScanAndOrder(int startFrom, int limit, BSONObj order, const FieldRa ngeSet &frs) :
/* todo : we don't correct approxSize here. */ _best( BSONObjCmp( order ) ),
const BSONObj& worstBestKey = i->first; _startFrom(startFrom), _order(order, frs) {
int c = worstBestKey.woCompare(k, order.pattern); _limit = limit > 0 ? limit + _startFrom : 0x7fffffff;
if ( c > 0 ) { _approxSize = 0;
// k is better, 'upgrade'
best.erase(i);
_add(k, o, loc);
}
} }
public: int size() const { return _best.size(); }
ScanAndOrder(int _startFrom, int _limit, BSONObj _order) :
best( BSONObjCmp( _order ) ),
startFrom(_startFrom), order(_order) {
limit = _limit > 0 ? _limit + startFrom : 0x7fffffff;
approxSize = 0;
}
int size() const { void add(BSONObj o, DiskLoc* loc);
return best.size();
}
void add(BSONObj o, DiskLoc* loc) { /* scanning complete. stick the query result in b for n objects. */
assert( o.isValid() ); void fill(BufBuilder& b, Projection *filter, int& nout ) const;
BSONObj k = order.getKeyFromObject(o);
if ( (int) best.size() < limit ) {
approxSize += k.objsize();
approxSize += o.objsize();
/* note : adjust when bson return limit adjusts. note this limit should be a bit higher. */ private:
uassert( 10128 , "too much data for sort() with no index. add an index or specify a smaller limit", approxSize < 32 * 1024 * 1024 );
_add(k, o, loc); void _add(BSONObj& k, BSONObj o, DiskLoc* loc);
return;
}
BestMap::iterator i;
assert( best.end() != best.begin() );
i = best.end();
i--;
_addIfBetter(k, o, i, loc);
}
void _fill(BufBuilder& b, FieldMatcher *filter, int& nout, BestMap: :iterator begin, BestMap::iterator end) { void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i, DiskL oc* loc);
int n = 0;
int nFilled = 0;
for ( BestMap::iterator i = begin; i != end; i++ ) {
n++;
if ( n <= startFrom )
continue;
BSONObj& o = i->second;
fillQueryResultFromObj(b, filter, o);
nFilled++;
if ( nFilled >= limit )
break;
uassert( 10129 , "too much data for sort() with no index", b.len() < 4000000 ); // appserver limit
}
nout = nFilled;
}
/* scanning complete. stick the query result in b for n objects. */ BestMap _best; // key -> full object
void fill(BufBuilder& b, FieldMatcher *filter, int& nout) { int _startFrom;
_fill(b, filter, nout, best.begin(), best.end()); int _limit; // max to send back.
} KeyType _order;
unsigned _approxSize;
}; };
} // namespace mongo } // namespace mongo
 End of changes. 20 change blocks. 
96 lines changed or deleted 40 lines changed or added


 security.h   security.h 
skipping to change at line 23 skipping to change at line 23
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "nonce.h" #include "nonce.h"
#include "concurrency.h" #include "concurrency.h"
#include "security_common.h"
#include "../util/concurrency/spin_lock.h"
namespace mongo { // this is used by both mongos and mongod
// --noauth cmd line option namespace mongo {
extern bool noauth;
/* for a particular db */ /*
* for a particular db
* levels
* 0 : none
* 1 : read
* 2 : write
*/
struct Auth { struct Auth {
Auth() { level = 0; } Auth() { level = 0; }
int level; int level;
string user;
}; };
class AuthenticationInfo : boost::noncopyable { class AuthenticationInfo : boost::noncopyable {
mongo::mutex _lock;
map<string, Auth> m; // dbname -> auth
static int warned;
public: public:
bool isLocalHost; bool isLocalHost;
AuthenticationInfo() : _lock("AuthenticationInfo") { isLocalHost = false; }
~AuthenticationInfo() { AuthenticationInfo(){ isLocalHost = false; }
} ~AuthenticationInfo() {}
// -- modifiers ----
void logout(const string& dbname ) { void logout(const string& dbname ) {
scoped_lock lk(_lock); scoped_spinlock lk(_lock);
m.erase(dbname); _dbs.erase(dbname);
}
void authorize(const string& dbname ) {
scoped_lock lk(_lock);
m[dbname].level = 2;
} }
void authorizeReadOnly(const string& dbname) { void authorize(const string& dbname , const string& user ) {
scoped_lock lk(_lock); scoped_spinlock lk(_lock);
m[dbname].level = 1; _dbs[dbname].level = 2;
_dbs[dbname].user = user;
}
void authorizeReadOnly(const string& dbname , const string& user ) {
scoped_spinlock lk(_lock);
_dbs[dbname].level = 1;
_dbs[dbname].user = user;
} }
bool isAuthorized(const string& dbname) { return _isAuthorized( dbn ame, 2 ); }
bool isAuthorizedReads(const string& dbname) { return _isAuthorized ( dbname, 1 ); }
bool isAuthorizedForLock(const string& dbname, int lockType ) { ret urn _isAuthorized( dbname , lockType > 0 ? 2 : 1 ); }
void print(); // -- accessors ---
protected: bool isAuthorized(const string& dbname) const {
bool _isAuthorized(const string& dbname, int level) { return _isAuthorized( dbname, 2 );
if( m[dbname].level >= level ) return true;
if( noauth ) return true;
if( m["admin"].level >= level ) return true;
if( m["local"].level >= level ) return true;
return _isAuthorizedSpecialChecks( dbname );
} }
bool _isAuthorizedSpecialChecks( const string& dbname ); bool isAuthorizedReads(const string& dbname) const {
return _isAuthorized( dbname, 1 );
}
bool isAuthorizedForLock(const string& dbname, int lockType ) const {
return _isAuthorized( dbname , lockType > 0 ? 2 : 1 );
}
string getUser( const string& dbname ) const;
void print() const;
protected:
/** takes a lock */
bool _isAuthorized(const string& dbname, int level) const;
bool _isAuthorizedSingle_inlock(const string& dbname, int level) co nst;
/** cannot call this locked */
bool _isAuthorizedSpecialChecks( const string& dbname ) const ;
private:
mutable SpinLock _lock;
typedef map<string,Auth> MA;
MA _dbs; // dbname -> auth
static bool _warned;
}; };
} // namespace mongo } // namespace mongo
 End of changes. 13 change blocks. 
32 lines changed or deleted 61 lines changed or added


 server.h   server.h 
skipping to change at line 20 skipping to change at line 20
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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/>.
*/ */
#include <string> #include <string>
#include "../util/message.h" #include "../util/net/message.h"
#include "../db/jsobj.h" #include "../db/jsobj.h"
namespace mongo { namespace mongo {
extern OID serverID; extern OID serverID;
// from request.cpp // from request.cpp
void processRequest(Message& m, MessagingPort& p); void processRequest(Message& m, MessagingPort& p);
} }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 shard.h   shard.h 
// shard.h // @file shard.h
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
skipping to change at line 29 skipping to change at line 29
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "../client/connpool.h" #include "../client/connpool.h"
namespace mongo { namespace mongo {
class ShardConnection; class ShardConnection;
class ShardStatus; class ShardStatus;
/*
* A "shard" is a database (replica pair typically) which represents
* one partition of the overall database.
*/
class Shard { class Shard {
public: public:
Shard() Shard()
: _name("") , _addr("") , _maxSize(0) , _isDraining( false ){ : _name("") , _addr("") , _maxSize(0) , _isDraining( false ) {
} }
Shard( const string& name , const string& addr, long long maxSize = 0 , bool isDraining = false ) Shard( const string& name , const string& addr, long long maxSize = 0 , bool isDraining = false )
: _name(name) , _addr( addr ) , _maxSize( maxSize ) , _isDraini ng( isDraining ){ : _name(name) , _addr( addr ) , _maxSize( maxSize ) , _isDraini ng( isDraining ) {
_setAddr( addr );
} }
Shard( const string& ident ){ Shard( const string& ident ) {
reset( ident ); reset( ident );
} }
Shard( const Shard& other ) Shard( const Shard& other )
: _name( other._name ) , _addr( other._addr ) , _maxSize( other ._maxSize ) , _isDraining( other._isDraining ){ : _name( other._name ) , _addr( other._addr ) , _cs( other._cs ) ,
_maxSize( other._maxSize ) , _isDraining( other._isDraining ) , _rs( other._rs ) {
} }
Shard( const Shard* other ) Shard( const Shard* other )
: _name( other->_name ) , _addr( other->_addr ), _maxSize( othe r->_maxSize ) , _isDraining( other->_isDraining ){ : _name( other->_name ) , _addr( other->_addr ), _cs( other->_c s ) ,
_maxSize( other->_maxSize ) , _isDraining( other->_isDraining ) , _rs( other->_rs ) {
} }
static Shard make( const string& ident ){ static Shard make( const string& ident ) {
Shard s; Shard s;
s.reset( ident ); s.reset( ident );
return s; return s;
} }
static bool isAShard( const string& ident );
/** /**
* @param ident either name or address * @param ident either name or address
*/ */
void reset( const string& ident ); void reset( const string& ident );
void setAddress( const string& addr , bool authoritative = false ); void setAddress( const ConnectionString& cs );
ConnectionString getAddress() const { return _cs; }
string getName() const { string getName() const {
assert( _name.size() ); assert( _name.size() );
return _name; return _name;
} }
string getConnString() const { string getConnString() const {
assert( _addr.size() ); assert( _addr.size() );
return _addr; return _addr;
} }
skipping to change at line 129 skipping to change at line 137
return _addr.size() > 0 && _addr.size() > 0; return _addr.size() > 0 && _addr.size() > 0;
} }
BSONObj runCommand( const string& db , const string& simple ) const { BSONObj runCommand( const string& db , const string& simple ) const {
return runCommand( db , BSON( simple << 1 ) ); return runCommand( db , BSON( simple << 1 ) );
} }
BSONObj runCommand( const string& db , const BSONObj& cmd ) const ; BSONObj runCommand( const string& db , const BSONObj& cmd ) const ;
ShardStatus getStatus() const ; ShardStatus getStatus() const ;
/**
* mostly for replica set
* retursn true if node is the shard
* of if the replica set contains node
*/
bool containsNode( const string& node ) const;
static void getAllShards( vector<Shard>& all ); static void getAllShards( vector<Shard>& all );
static void printShardInfo( ostream& out ); static void printShardInfo( ostream& out );
/** /**
* @parm current - shard where the chunk/database currently lives i n * @parm current - shard where the chunk/database currently lives i n
* @return the currently emptiest shard, if best then current, or E MPTY * @return the currently emptiest shard, if best then current, or E MPTY
*/ */
static Shard pick( const Shard& current = EMPTY ); static Shard pick( const Shard& current = EMPTY );
static void reloadShardInfo(); static void reloadShardInfo();
static void removeShard( const string& name ); static void removeShard( const string& name );
static bool isMember( const string& addr ); static bool isAShardNode( const string& ident );
static Shard EMPTY; static Shard EMPTY;
private: private:
void _rsInit();
void _setAddr( const string& addr );
string _name; string _name;
string _addr; string _addr;
ConnectionString _cs;
long long _maxSize; // in MBytes, 0 is unlimited long long _maxSize; // in MBytes, 0 is unlimited
bool _isDraining; // shard is currently being removed bool _isDraining; // shard is currently being removed
ReplicaSetMonitorPtr _rs;
}; };
class ShardStatus { class ShardStatus {
public: public:
ShardStatus( const Shard& shard , const BSONObj& obj ); ShardStatus( const Shard& shard , const BSONObj& obj );
friend ostream& operator << (ostream& out, const ShardStatus& s) { friend ostream& operator << (ostream& out, const ShardStatus& s) {
out << s.toString(); out << s.toString();
return out; return out;
} }
string toString() const { string toString() const {
stringstream ss; stringstream ss;
ss << "shard: " << _shard << " mapped: " << _mapped << " writeL ock: " << _writeLock; ss << "shard: " << _shard << " mapped: " << _mapped << " writeL ock: " << _writeLock;
return ss.str(); return ss.str();
} }
bool operator<( const ShardStatus& other ) const{ bool operator<( const ShardStatus& other ) const {
return _mapped < other._mapped; return _mapped < other._mapped;
} }
Shard shard() const { Shard shard() const {
return _shard; return _shard;
} }
long long mapped() const { long long mapped() const {
return _mapped; return _mapped;
} }
bool hasOpsQueued() const {
return _hasOpsQueued;
}
private: private:
Shard _shard; Shard _shard;
long long _mapped; long long _mapped;
bool _hasOpsQueued; // true if 'writebacks' are pending
double _writeLock; double _writeLock;
}; };
class ShardConnection : public AScopedConnection { class ShardConnection : public AScopedConnection {
public: public:
ShardConnection( const Shard * s , const string& ns ); ShardConnection( const Shard * s , const string& ns, bool ignoreDir ect = false );
ShardConnection( const Shard& s , const string& ns ); ShardConnection( const Shard& s , const string& ns, bool ignoreDire ct = false );
ShardConnection( const string& addr , const string& ns ); ShardConnection( const string& addr , const string& ns, bool ignore Direct = false );
~ShardConnection(); ~ShardConnection();
void done(); void done();
void kill(); void kill();
DBClientBase& conn(){ DBClientBase& conn() {
_finishInit(); _finishInit();
assert( _conn ); assert( _conn );
return *_conn; return *_conn;
} }
DBClientBase* operator->(){ DBClientBase* operator->() {
_finishInit(); _finishInit();
assert( _conn ); assert( _conn );
return _conn; return _conn;
} }
DBClientBase* get(){ DBClientBase* get() {
_finishInit(); _finishInit();
assert( _conn ); assert( _conn );
return _conn; return _conn;
} }
string getHost() const { string getHost() const {
return _addr; return _addr;
} }
bool setVersion() { bool setVersion() {
_finishInit(); _finishInit();
return _setVersion; return _setVersion;
} }
static void sync(); static void sync();
void donotCheckVersion(){ void donotCheckVersion() {
_setVersion = false; _setVersion = false;
_finishedInit = true; _finishedInit = true;
} }
bool ok() const { return _conn > 0; }
/** /**
this just passes through excpet it checks for stale configs this just passes through excpet it checks for stale configs
*/ */
bool runCommand( const string& db , const BSONObj& cmd , BSONObj& r es ); bool runCommand( const string& db , const BSONObj& cmd , BSONObj& r es );
/** checks all of my thread local connections for the version of th is ns */ /** checks all of my thread local connections for the version of th is ns */
static void checkMyConnectionVersions( const string & ns ); static void checkMyConnectionVersions( const string & ns );
private: private:
void _init(); void _init( bool ignoreDirect = false );
void _finishInit(); void _finishInit();
bool _finishedInit; bool _finishedInit;
string _addr; string _addr;
string _ns; string _ns;
DBClientBase* _conn; DBClientBase* _conn;
bool _setVersion; bool _setVersion;
}; };
extern DBConnectionPool shardConnectionPool;
class ShardingConnectionHook : public DBConnectionHook {
public:
ShardingConnectionHook( bool shardedConnections )
: _shardedConnections( shardedConnections ) {
}
virtual void onCreate( DBClientBase * conn );
virtual void onHandedOut( DBClientBase * conn );
virtual void onDestory( DBClientBase * conn );
bool _shardedConnections;
};
} }
 End of changes. 26 change blocks. 
20 lines changed or deleted 64 lines changed or added


 shardkey.h   shardkey.h 
skipping to change at line 36 skipping to change at line 36
/* A ShardKeyPattern is a pattern indicating what data to extract from the object to make the shard key from. /* A ShardKeyPattern is a pattern indicating what data to extract from the object to make the shard key from.
Analogous to an index key pattern. Analogous to an index key pattern.
*/ */
class ShardKeyPattern { class ShardKeyPattern {
public: public:
ShardKeyPattern( BSONObj p = BSONObj() ); ShardKeyPattern( BSONObj p = BSONObj() );
/** /**
global min is the lowest possible value for this key global min is the lowest possible value for this key
e.g. { num : MinKey } e.g. { num : MinKey }
*/ */
BSONObj globalMin() const { return gMin; } BSONObj globalMin() const { return gMin; }
/** /**
global max is the highest possible value for this key global max is the highest possible value for this key
*/ */
BSONObj globalMax() const { return gMax; } BSONObj globalMax() const { return gMax; }
bool isGlobalMin( const BSONObj& k ) const{ bool isGlobalMin( const BSONObj& k ) const {
return k.woCompare( globalMin() ) == 0; return k.woCompare( globalMin() ) == 0;
} }
bool isGlobalMax( const BSONObj& k ) const{ bool isGlobalMax( const BSONObj& k ) const {
return k.woCompare( globalMax() ) == 0; return k.woCompare( globalMax() ) == 0;
} }
bool isGlobal( const BSONObj& k ) const{ bool isGlobal( const BSONObj& k ) const {
return isGlobalMin( k ) || isGlobalMax( k ); return isGlobalMin( k ) || isGlobalMax( k );
} }
/** compare shard keys from the objects specified /** compare shard keys from the objects specified
l < r negative l < r negative
l == r 0 l == r 0
l > r positive l > r positive
*/ */
int compare( const BSONObj& l , const BSONObj& r ) const; int compare( const BSONObj& l , const BSONObj& r ) const;
/** /**
@return whether or not obj has all fields in this shard key patt ern @return whether or not obj has all fields in this shard key patt ern
e.g. e.g.
ShardKey({num:1}).hasShardKey({ name:"joe", num:3 }) is true ShardKey({num:1}).hasShardKey({ name:"joe", num:3 }) is true
*/ */
bool hasShardKey( const BSONObj& obj ) const; bool hasShardKey( const BSONObj& obj ) const;
BSONObj key() const { return pattern; } BSONObj key() const { return pattern; }
string toString() const; string toString() const;
BSONObj extractKey(const BSONObj& from) const; BSONObj extractKey(const BSONObj& from) const;
bool partOfShardKey(const char* key ) const {
return pattern.hasField(key);
}
bool partOfShardKey(const string& key ) const { bool partOfShardKey(const string& key ) const {
return patternfields.count( key ) > 0; return pattern.hasField(key.c_str());
} }
/** /**
* @return * @return
* true if 'this' is a prefix (not necessarily contained) of 'other Pattern'. * true if 'this' is a prefix (not necessarily contained) of 'other Pattern'.
*/ */
bool isPrefixOf( const BSONObj& otherPattern ) const; bool isPrefixOf( const BSONObj& otherPattern ) const;
/**
* @return BSONObj with _id and shardkey at front. May return origi nal object.
*/
BSONObj moveToFront(const BSONObj& obj) const;
private: private:
BSONObj pattern; BSONObj pattern;
BSONObj gMin; BSONObj gMin;
BSONObj gMax; BSONObj gMax;
/* question: better to have patternfields precomputed or not? depe nds on if we use copy constructor often. */ /* question: better to have patternfields precomputed or not? depe nds on if we use copy constructor often. */
set<string> patternfields; set<string> patternfields;
}; };
inline BSONObj ShardKeyPattern::extractKey(const BSONObj& from) const { inline BSONObj ShardKeyPattern::extractKey(const BSONObj& from) const {
BSONObj k = from.extractFields(pattern); BSONObj k = from;
bool needExtraction = false;
BSONObjIterator a(from);
BSONObjIterator b(pattern);
while (a.more() && b.more()){
if (strcmp(a.next().fieldName(), b.next().fieldName()) != 0){
needExtraction = true;
break;
}
}
if (needExtraction || a.more() != b.more())
k = from.extractFields(pattern);
uassert(13334, "Shard Key must be less than 512 bytes", k.objsize() < 512); uassert(13334, "Shard Key must be less than 512 bytes", k.objsize() < 512);
return k; return k;
} }
} }
 End of changes. 9 change blocks. 
8 lines changed or deleted 30 lines changed or added


 signal_handlers.h   signal_handlers.h 
skipping to change at line 25 skipping to change at line 25
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
namespace mongo { namespace mongo {
/* /**
* @parm signal that this hadler is called for
*
* Obtains the log file handler and writes the current thread's stack t race to * Obtains the log file handler and writes the current thread's stack t race to
* it. This call issues an exit(). The function can safely be called fr om within a * it. This call issues an exit(). The function can safely be called fr om within a
* signal handler. * signal handler.
*
* @param signal that this hadler is called for
*/ */
void printStackAndExit( int signalNum ); void printStackAndExit( int signalNum );
} // namespace mongo } // namespace mongo
 End of changes. 2 change blocks. 
3 lines changed or deleted 3 lines changed or added


 snapshots.h   snapshots.h 
skipping to change at line 105 skipping to change at line 105
private: private:
mongo::mutex _lock; mongo::mutex _lock;
int _n; int _n;
boost::scoped_array<SnapshotData> _snapshots; boost::scoped_array<SnapshotData> _snapshots;
int _loc; int _loc;
int _stored; int _stored;
}; };
class SnapshotThread : public BackgroundJob { class SnapshotThread : public BackgroundJob {
public: public:
string name() { return "snapshot"; } virtual string name() const { return "snapshot"; }
void run(); void run();
}; };
extern Snapshots statsSnapshots; extern Snapshots statsSnapshots;
extern SnapshotThread snapshotThread; extern SnapshotThread snapshotThread;
} }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 sock.h   sock.h 
// sock.h // @file sock.h
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include "../pch.h" #include "../../pch.h"
#include <stdio.h> #include <stdio.h>
#include <sstream> #include <sstream>
#include "goodies.h" #include "../goodies.h"
#include "../db/jsobj.h" #include "../../db/cmdline.h"
#include "../mongoutils/str.h"
namespace mongo {
const int SOCK_FAMILY_UNKNOWN_ERROR=13078;
#if defined(_WIN32)
typedef short sa_family_t;
typedef int socklen_t;
inline int getLastError() {
return WSAGetLastError();
}
inline const char* gai_strerror(int code) {
return ::gai_strerrorA(code);
}
inline void disableNagle(int sock) {
int x = 1;
if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &x, sizeof (x)) )
out() << "ERROR: disableNagle failed" << endl;
if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &x, sizeof (x)) )
out() << "ERROR: SO_KEEPALIVE failed" << endl;
}
inline void prebindOptions( int sock ) {
}
// This won't actually be used on windows
struct sockaddr_un {
short sun_family;
char sun_path[108]; // length from unix header
};
#else
} // namespace mongo #ifndef _WIN32
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h> #include <errno.h>
#include <netdb.h>
#ifdef __openbsd__ #ifdef __openbsd__
# include <sys/uio.h> # include <sys/uio.h>
#endif #endif
#ifndef AI_ADDRCONFIG #endif // _WIN32
# define AI_ADDRCONFIG 0
#ifdef MONGO_SSL
#include <openssl/ssl.h>
#endif #endif
namespace mongo { namespace mongo {
inline void closesocket(int s) { const int SOCK_FAMILY_UNKNOWN_ERROR=13078;
close(s);
}
const int INVALID_SOCKET = -1;
typedef int SOCKET;
inline void disableNagle(int sock) { void disableNagle(int sock);
int x = 1;
#ifdef SOL_TCP #if defined(_WIN32)
int level = SOL_TCP;
#else
int level = SOL_SOCKET;
#endif
if ( setsockopt(sock, level, TCP_NODELAY, (char *) &x, sizeof(x)) ) typedef short sa_family_t;
log() << "ERROR: disableNagle failed: " << errnoWithDescription () << endl; typedef int socklen_t;
#ifdef SO_KEEPALIVE // This won't actually be used on windows
if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &x, sizeof (x)) ) struct sockaddr_un {
log() << "ERROR: SO_KEEPALIVE failed: " << errnoWithDescription () << endl; short sun_family;
#endif char sun_path[108]; // length from unix header
};
} #else // _WIN32
inline void prebindOptions( int sock ) {
DEV log() << "doing prebind option" << endl;
int x = 1;
if ( setsockopt( sock , SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) < 0 )
out() << "Failed to set socket opt, SO_REUSEADDR" << endl;
}
#endif inline void closesocket(int s) { close(s); }
const int INVALID_SOCKET = -1;
typedef int SOCKET;
inline string makeUnixSockPath(int port){ #endif // _WIN32
return "/tmp/mongodb-" + BSONObjBuilder::numStr(port) + ".sock";
}
inline void setSockTimeouts(int sock, int secs) { inline string makeUnixSockPath(int port) {
struct timeval tv; return mongoutils::str::stream() << cmdLine.socket << "/mongodb-" < < port << ".sock";
tv.tv_sec = secs;
tv.tv_usec = 0;
bool report = logLevel > 3; // solaris doesn't provide these
DEV report = true;
bool ok = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, s izeof(tv) ) == 0;
if( report && !ok ) log() << "unabled to set SO_RCVTIMEO" << endl;
ok = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &tv, sizeof (tv) ) == 0;
DEV if( report && !ok ) log() << "unabled to set SO_RCVTIMEO" << en dl;
} }
// 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);
void enableIPv6(bool state=true); void enableIPv6(bool state=true);
bool IPv6Enabled(); bool IPv6Enabled();
void setSockTimeouts(int sock, double secs);
/**
* wrapped around os representation of network address
*/
struct SockAddr { struct SockAddr {
SockAddr() { SockAddr() {
addressSize = sizeof(sa); addressSize = sizeof(sa);
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));
sa.ss_family = AF_UNSPEC; sa.ss_family = AF_UNSPEC;
} }
SockAddr(int sourcePort); /* listener side */ SockAddr(int sourcePort); /* listener side */
SockAddr(const char *ip, int port); /* EndPoint (remote) side, or i f you want to specify which interface locally */ SockAddr(const char *ip, int port); /* EndPoint (remote) side, or i f you want to specify which interface locally */
template <typename T> template <typename T> T& as() { return *(T*)(&sa); }
T& as() { return *(T*)(&sa); } template <typename T> const T& as() const { return *(const T*)(&sa) ; }
template <typename T>
const T& as() const { return *(const T*)(&sa); }
string toString(bool includePort=true) const{
string out = getAddr();
if (includePort && getType() != AF_UNIX && getType() != AF_UNSP EC)
out += ':' + BSONObjBuilder::numStr(getPort());
return out;
}
// returns one of AF_INET, AF_INET6, or AF_UNIX string toString(bool includePort=true) const;
sa_family_t getType() const {
return sa.ss_family;
}
unsigned getPort() const { /**
switch (getType()){ * @return one of AF_INET, AF_INET6, or AF_UNIX
case AF_INET: return ntohs(as<sockaddr_in>().sin_port); */
case AF_INET6: return ntohs(as<sockaddr_in6>().sin6_port); sa_family_t getType() const;
case AF_UNIX: return 0;
case AF_UNSPEC: return 0;
default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad dress family", false); return 0;
}
}
string getAddr() const { unsigned getPort() const;
switch (getType()){
case AF_INET:
case AF_INET6: {
const int buflen=128;
char buffer[buflen];
int ret = getnameinfo(raw(), addressSize, buffer, bufle n, NULL, 0, NI_NUMERICHOST);
massert(13082, gai_strerror(ret), ret == 0);
return buffer;
}
case AF_UNIX: return (addressSize > 2 ? as<sockaddr_un>(). sun_path : "anonymous unix socket"); string getAddr() const;
case AF_UNSPEC: return "(NONE)";
default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad dress family", false); return "";
}
}
bool isLocalHost() const; bool isLocalHost() const;
bool operator==(const SockAddr& r) const { bool operator==(const SockAddr& r) const;
if (getType() != r.getType())
return false;
if (getPort() != r.getPort())
return false;
switch (getType()){
case AF_INET: return as<sockaddr_in>().sin_addr.s_addr == r.as<sockaddr_in>().sin_addr.s_addr;
case AF_INET6: return memcmp(as<sockaddr_in6>().sin6_addr.s 6_addr, r.as<sockaddr_in6>().sin6_addr.s6_addr, sizeof(in6_addr)) == 0;
case AF_UNIX: return strcmp(as<sockaddr_un>().sun_path, r. as<sockaddr_un>().sun_path) == 0;
case AF_UNSPEC: return true; // assume all unspecified addr esses are the same
default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad dress family", false);
}
}
bool operator!=(const SockAddr& r) const {
return !(*this == r);
}
bool operator<(const SockAddr& r) const {
if (getType() < r.getType())
return true;
else if (getType() > r.getType())
return false;
if (getPort() < r.getPort()) bool operator!=(const SockAddr& r) const;
return true;
else if (getPort() > r.getPort())
return false;
switch (getType()){ bool operator<(const SockAddr& r) const;
case AF_INET: return as<sockaddr_in>().sin_addr.s_addr < r .as<sockaddr_in>().sin_addr.s_addr;
case AF_INET6: return memcmp(as<sockaddr_in6>().sin6_addr.s 6_addr, r.as<sockaddr_in6>().sin6_addr.s6_addr, sizeof(in6_addr)) < 0;
case AF_UNIX: return strcmp(as<sockaddr_un>().sun_path, r. as<sockaddr_un>().sun_path) < 0;
case AF_UNSPEC: return false;
default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad dress family", false);
}
}
const sockaddr* raw() const {return (sockaddr*)&sa;} const sockaddr* raw() const {return (sockaddr*)&sa;}
sockaddr* raw() {return (sockaddr*)&sa;} sockaddr* raw() {return (sockaddr*)&sa;}
socklen_t addressSize; socklen_t addressSize;
private: private:
struct sockaddr_storage sa; struct sockaddr_storage sa;
}; };
extern SockAddr unknownAddress; // ( "0.0.0.0", 0 ) extern SockAddr unknownAddress; // ( "0.0.0.0", 0 )
const int MaxMTU = 16384; /** this is not cache and does a syscall */
string getHostName();
inline string getHostName() {
char buf[256];
int ec = gethostname(buf, 127);
if ( ec || *buf == 0 ) {
log() << "can't get this server's hostname " << errnoWithDescri ption() << endl;
return "";
}
return buf;
}
/** this is cached, so if changes during the process lifetime
* will be stale */
string getHostNameCached(); string getHostNameCached();
class ListeningSockets { /**
* thrown by Socket and SockAddr
*/
class SocketException : public DBException {
public: public:
ListeningSockets() : _mutex("ListeningSockets"), _sockets( new set< int>() ) { } const enum Type { CLOSED , RECV_ERROR , SEND_ERROR, RECV_TIMEOUT, S END_TIMEOUT, FAILED_STATE, CONNECT_ERROR } _type;
void add( int sock ){
scoped_lock lk( _mutex ); SocketException( Type t , string server , int code = 9001 , string extra="" )
_sockets->insert( sock ); : DBException( "socket exception" , code ) , _type(t) , _server (server), _extra(extra){ }
} virtual ~SocketException() throw() {}
void remove( int sock ){
scoped_lock lk( _mutex ); bool shouldPrint() const { return _type != CLOSED; }
_sockets->erase( sock ); virtual string toString() const;
}
void closeAll(){
set<int>* s;
{
scoped_lock lk( _mutex );
s = _sockets;
_sockets = new set<int>();
}
for ( set<int>::iterator i=s->begin(); i!=s->end(); i++ ) {
int sock = *i;
log() << "closing listening socket: " << sock << endl;
closesocket( sock );
}
}
static ListeningSockets* get();
private: private:
mongo::mutex _mutex; string _server;
set<int>* _sockets; string _extra;
static ListeningSockets* _instance; };
#ifdef MONGO_SSL
class SSLManager : boost::noncopyable {
public:
SSLManager( bool client );
void setupPEM( const string& keyFile , const string& password );
void setupPubPriv( const string& privateKeyFile , const string& pub licKeyFile );
/**
* creates an SSL context to be used for this file descriptor
* caller should delete
*/
SSL * secure( int fd );
static int password_cb( char *buf,int num, int rwflag,void *userdat a );
private:
bool _client;
SSL_CTX* _context;
string _password;
};
#endif
/**
* thin wrapped around file descriptor and system calls
* todo: ssl
*/
class Socket {
public:
Socket(int sock, const SockAddr& farEnd);
/** In some cases the timeout will actually be 2x this value - eg w e do a partial send,
then the timeout fires, then we try to send again, then the tim eout fires again with
no data sent, then we detect that the other side is down.
Generally you don't want a timeout, you should be very prepared for errors if you set one.
*/
Socket(double so_timeout = 0, int logLevel = 0 );
bool connect(SockAddr& farEnd);
void close();
void send( const char * data , int len, const char *context );
void send( const vector< pair< char *, int > > &data, const char *c ontext );
// recv len or throw SocketException
void recv( char * data , int len );
int unsafe_recv( char *buf, int max );
int getLogLevel() const { return _logLevel; }
void setLogLevel( int ll ) { _logLevel = ll; }
SockAddr remoteAddr() const { return _remote; }
string remoteString() const { return _remote.toString(); }
unsigned remotePort() const { return _remote.getPort(); }
void clearCounters() { _bytesIn = 0; _bytesOut = 0; }
long long getBytesIn() const { return _bytesIn; }
long long getBytesOut() const { return _bytesOut; }
void setTimeout( double secs );
#ifdef MONGO_SSL
/** secures inline */
void secure( SSLManager * ssl );
void secureAccepted( SSLManager * ssl );
#endif
/**
* call this after a fork for server sockets
*/
void postFork();
private:
void _init();
/** raw send, same semantics as ::send */
int _send( const char * data , int len );
/** sends dumbly, just each buffer at a time */
void _send( const vector< pair< char *, int > > &data, const char * context );
/** raw recv, same semantics as ::recv */
int _recv( char * buf , int max );
int _fd;
SockAddr _remote;
double _timeout;
long long _bytesIn;
long long _bytesOut;
#ifdef MONGO_SSL
shared_ptr<SSL> _ssl;
SSLManager * _sslAccepted;
#endif
protected:
int _logLevel; // passed to log() when logging errors
}; };
} // namespace mongo } // namespace mongo
 End of changes. 32 change blocks. 
195 lines changed or deleted 166 lines changed or added


 spin_lock.h   spin_lock.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 "pch.h" #include "mutex.h"
#include "rwlock.h"
namespace mongo { namespace mongo {
/** /**
* The spinlock currently requires late GCC support routines to be effi cient. * The spinlock currently requires late GCC support routines to be effi cient.
* Other platforms default to a mutex implemenation. * Other platforms default to a mutex implemenation.
*/ */
class SpinLock { class SpinLock : boost::noncopyable {
public: public:
SpinLock(); SpinLock();
~SpinLock(); ~SpinLock();
void lock(); void lock();
void unlock(); void unlock();
static bool isfast(); // true if a real spinlock on this platform
private: private:
#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) #if defined(_WIN32)
volatile bool _locked;
#elif defined(_WIN32)
CRITICAL_SECTION _cs; CRITICAL_SECTION _cs;
#elif defined(__USE_XOPEN2K)
pthread_spinlock_t _lock;
#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
volatile bool _locked;
#else #else
// default to a scoped mutex if not implemented // default to a mutex if not implemented
RWLock _mutex; SimpleMutex _mutex;
#endif #endif
};
// Non-copyable, non-assignable class scoped_spinlock : boost::noncopyable {
SpinLock(SpinLock&); public:
SpinLock& operator=(SpinLock&); scoped_spinlock( SpinLock& l ) : _l(l) {
_l.lock();
}
~scoped_spinlock() {
_l.unlock();}
private:
SpinLock& _l;
}; };
} // namespace mongo } // namespace mongo
 End of changes. 8 change blocks. 
11 lines changed or deleted 21 lines changed or added


 str.h   str.h 
skipping to change at line 33 skipping to change at line 33
(3) may require boost headers, but not libs (3) may require boost headers, but not libs
(4) are clean and easy to use in any c++ project without pulling in lots of other stuff (4) are clean and easy to use in any c++ project without pulling in lots of other stuff
Note: within this module, we use int for all offsets -- there are no uns igned offsets Note: within this module, we use int for all offsets -- there are no uns igned offsets
and no size_t's. If you need 3 gigabyte long strings, don't use this mo dule. and no size_t's. If you need 3 gigabyte long strings, don't use this mo dule.
*/ */
#include <string> #include <string>
#include <sstream> #include <sstream>
// this violates the README rules for mongoutils:
#include "../../bson/util/builder.h"
namespace mongoutils { namespace mongoutils {
namespace str { namespace str {
using namespace std; typedef std::string string;
/** the idea here is to make one liners easy. e.g.: /** the idea here is to make one liners easy. e.g.:
return str::stream() << 1 << ' ' << 2; return str::stream() << 1 << ' ' << 2;
since the following doesn't work: since the following doesn't work:
(stringstream() << 1).str(); (stringstream() << 1).str();
*/ */
class stream { class stream {
public: public:
stringstream ss; mongo::StringBuilder ss;
template<class T> template<class T>
stream& operator<<(const T& v) { stream& operator<<(const T& v) {
ss << v; ss << v;
return *this; return *this;
} }
operator std::string () const { return ss.str(); } operator std::string () const { return ss.str(); }
}; };
inline bool startsWith(const char *str, const char *prefix) { inline bool startsWith(const char *str, const char *prefix) {
const char *s = str; const char *s = str;
const char *p = prefix; const char *p = prefix;
while( *p ) { while( *p ) {
if( *p != *s ) return false; if( *p != *s ) return false;
p++; s++; p++; s++;
} }
skipping to change at line 78 skipping to change at line 79
} }
inline bool startsWith(string s, string p) { return startsWith(s.c_ str(), p.c_str()); } inline bool startsWith(string s, string p) { return startsWith(s.c_ str(), p.c_str()); }
inline bool endsWith(string s, string p) { inline bool endsWith(string s, string p) {
int l = p.size(); int l = p.size();
int x = s.size(); int x = s.size();
if( x < l ) return false; if( x < l ) return false;
return strncmp(s.c_str()+x-l, p.c_str(), l) == 0; return strncmp(s.c_str()+x-l, p.c_str(), l) == 0;
} }
inline bool equals( const char * a , const char * b ) { return strc mp( a , b ) == 0; }
/** find char x, and return rest of string thereafter, or "" if not found */ /** find char x, and return rest of string thereafter, or "" if not found */
inline const char * after(const char *s, char x) { inline const char * after(const char *s, char x) {
const char *p = strchr(s, x); const char *p = strchr(s, x);
return (p != 0) ? p+1 : ""; } return (p != 0) ? p+1 : "";
}
inline string after(const string& s, char x) { inline string after(const string& s, char x) {
const char *p = strchr(s.c_str(), x); const char *p = strchr(s.c_str(), x);
return (p != 0) ? string(p+1) : ""; } return (p != 0) ? string(p+1) : "";
}
/** find string x, and return rest of string thereafter, or "" if n ot found */
inline const char * after(const char *s, const char *x) { inline const char * after(const char *s, const char *x) {
const char *p = strstr(s, x); const char *p = strstr(s, x);
return (p != 0) ? p+strlen(x) : ""; } return (p != 0) ? p+strlen(x) : "";
}
inline string after(string s, string x) { inline string after(string s, string x) {
const char *p = strstr(s.c_str(), x.c_str()); const char *p = strstr(s.c_str(), x.c_str());
return (p != 0) ? string(p+x.size()) : ""; } return (p != 0) ? string(p+x.size()) : "";
}
/** @return true if s contains x */
inline bool contains(string s, string x) { inline bool contains(string s, string x) {
return strstr(s.c_str(), x.c_str()) != 0; } return strstr(s.c_str(), x.c_str()) != 0;
}
inline bool contains(string s, char x) {
return strchr(s.c_str(), x) != 0;
}
/** @return everything befor the character x, else entire string */ /** @return everything before the character x, else entire string * /
inline string before(const string& s, char x) { inline string before(const string& s, char x) {
const char *p = strchr(s.c_str(), x); const char *p = strchr(s.c_str(), x);
return (p != 0) ? s.substr(0, p-s.c_str()) : s; } return (p != 0) ? s.substr(0, p-s.c_str()) : s;
}
/** @return everything before the string x, else entire string */
inline string before(const string& s, const string& x) {
const char *p = strstr(s.c_str(), x.c_str());
return (p != 0) ? s.substr(0, p-s.c_str()) : s;
}
/** check if if strings share a common starting prefix /** check if if strings share a common starting prefix
@return offset of divergence (or length if equal). 0=nothing i n common. */ @return offset of divergence (or length if equal). 0=nothing i n common. */
inline int shareCommonPrefix(const char *p, const char *q) { inline int shareCommonPrefix(const char *p, const char *q) {
int ofs = 0; int ofs = 0;
while( 1 ) { while( 1 ) {
if( *p == 0 || *q == 0 ) if( *p == 0 || *q == 0 )
break; break;
if( *p != *q ) if( *p != *q )
break; break;
p++; q++; ofs++; p++; q++; ofs++;
} }
return ofs; } return ofs;
}
inline int shareCommonPrefix(const string &a, const string &b) inline int shareCommonPrefix(const string &a, const string &b)
{ return shareCommonPrefix(a.c_str(), b.c_str()); } { return shareCommonPrefix(a.c_str(), b.c_str()); }
/** string to unsigned. zero if not a number. can end with non-num chars */
inline unsigned toUnsigned(const string& a) {
unsigned x = 0;
const char *p = a.c_str();
while( 1 ) {
if( !isdigit(*p) )
break;
x = x * 10 + (*p - '0');
p++;
}
return x;
}
/** split a string on a specific char. We don't split N times, jus t once
on the first occurrence. If char not present entire string is in L
and R is empty.
@return true if char found
*/
inline bool splitOn(const string &s, char c, string& L, string& R) {
const char *start = s.c_str();
const char *p = strchr(start, c);
if( p == 0 ) {
L = s; R.clear();
return false;
}
L = string(start, p-start);
R = string(p+1);
return true;
}
/** split scanning reverse direction. Splits ONCE ONLY. */
inline bool rSplitOn(const string &s, char c, string& L, string& R) {
const char *start = s.c_str();
const char *p = strrchr(start, c);
if( p == 0 ) {
L = s; R.clear();
return false;
}
L = string(start, p-start);
R = string(p+1);
return true;
}
/** @return number of occurrences of c in s */
inline unsigned count( const string& s , char c ) {
unsigned n=0;
for ( unsigned i=0; i<s.size(); i++ )
if ( s[i] == c )
n++;
return n;
}
/** trim leading spaces. spaces only, not tabs etc. */
inline string ltrim(const string& s) {
const char *p = s.c_str();
while( *p == ' ' ) p++;
return p;
}
/** remove trailing chars in place */
inline void stripTrailing(string& s, const char *chars) {
string::iterator i = s.end();
while( s.begin() != i ) {
i--;
if( contains(chars, *i) ) {
s.erase(i);
}
}
}
} }
} }
 End of changes. 16 change blocks. 
12 lines changed or deleted 102 lines changed or added


 strategy.h   strategy.h 
skipping to change at line 28 skipping to change at line 28
#pragma once #pragma once
#include "../pch.h" #include "../pch.h"
#include "chunk.h" #include "chunk.h"
#include "request.h" #include "request.h"
namespace mongo { namespace mongo {
class Strategy { class Strategy {
public: public:
Strategy(){} Strategy() {}
virtual ~Strategy() {} virtual ~Strategy() {}
virtual void queryOp( Request& r ) = 0; virtual void queryOp( Request& r ) = 0;
virtual void getMore( Request& r ) = 0; virtual void getMore( Request& r ) = 0;
virtual void writeOp( int op , Request& r ) = 0; virtual void writeOp( int op , Request& r ) = 0;
virtual void insertSharded( DBConfigPtr conf, const char* ns, BSONO bj& o, int flags, bool safe=false, const char* nsChunkLookup=0 ) = 0;
virtual void updateSharded( DBConfigPtr conf, const char* ns, BSONO bj& query, BSONObj& toupdate, int flags, bool safe=false ) = 0;
protected: protected:
void doWrite( int op , Request& r , const Shard& shard , bool check Version = true ); void doWrite( int op , Request& r , const Shard& shard , bool check Version = true );
void doQuery( Request& r , const Shard& shard ); void doQuery( Request& r , const Shard& shard );
void insert( const Shard& shard , const char * ns , const BSONObj& obj ); void insert( const Shard& shard , const char * ns , const BSONObj& obj , int flags=0 , bool safe=false );
void update( const Shard& shard , const char * ns , const BSONObj& query , const BSONObj& toupdate , int flags=0, bool safe=false );
}; };
extern Strategy * SINGLE; extern Strategy * SINGLE;
extern Strategy * SHARDED; extern Strategy * SHARDED;
bool setShardVersion( DBClientBase & conn , const string& ns , ShardChu nkVersion version , bool authoritative , BSONObj& result );
void waitForWriteback( const OID& oid );
} }
 End of changes. 4 change blocks. 
5 lines changed or deleted 6 lines changed or added


 stringdata.h   stringdata.h 
skipping to change at line 18 skipping to change at line 18
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef BSON_STRINDATA_HEADER #pragma once
#define BSON_STRINDATA_HEADER
#include <string> #include <string>
#include <cstring> #include <cstring>
namespace mongo { namespace mongo {
using std::string; using std::string;
/** A StringData object wraps a 'const string&' or a 'const char*' with out
* copying its contents. The most common usage is as a function argumen t that
* takes any of the two forms of strings above. Fundamentally, this cla ss tries
* go around the fact that string literals in C++ are char[N]'s.
*
* Note that the object StringData wraps around must be alive while the StringData
* is.
*/
class StringData { class StringData {
public: public:
/** Construct a StringData, for the case where the length of
* string is not known. 'c' must be a pointer to a null-terminated string.
*/
StringData( const char* c ) StringData( const char* c )
: _data(c), _size((unsigned) strlen(c)) {} : _data(c), _size((unsigned) strlen(c)) {}
/** Construct a StringData explicitly, for the case where the lengt h of the string
* is already known. 'c' must be a pointer to a null-terminated str ing, and strlenOfc
* must be the length that std::strlen(c) would return, a.k.a the i ndex of the
* terminator in c.
*/
StringData( const char* c, unsigned len )
: _data(c), _size(len) {}
/** Construct a StringData, for the case of a std::string. */
StringData( const string& s ) StringData( const string& s )
: _data(s.c_str()), _size((unsigned) s.size()) {} : _data(s.c_str()), _size((unsigned) s.size()) {}
// Construct a StringData explicitly, for the case of a literal who se size is
// known at compile time.
struct LiteralTag {}; struct LiteralTag {};
template<size_t N> template<size_t N>
StringData( const char (&val)[N], LiteralTag ) StringData( const char (&val)[N], LiteralTag )
: _data(&val[0]), _size(N-1) {} : _data(&val[0]), _size(N-1) {}
// Construct a StringData explicitly, for the case where the // accessors
// length of the string is already known. 'c' must be a const char* data() const { return _data; }
// pointer to a null-terminated string, and strlenOfc must be
// the length that std::strlen(c) would return, a.k.a the
// index of the terminator in c.
StringData( const char* c, size_t strlenOfc )
: _data(c), _size((unsigned) strlenOfc) {}
const char* const data() const { return _data; }
const unsigned size() const { return _size; } const unsigned size() const { return _size; }
private: private:
// TODO - Hook this class up in the BSON machinery const char* const _data; // is always null terminated
// There are two assumptions here that we may want to review then. const unsigned _size; // 'size' does not include the null termi nator
// '_data' *always* finishes with a null terminator
// 'size' does *not* account for the null terminator
// These assumptions may make it easier to minimize changes to exis ting code
const char* const _data;
const unsigned _size;
}; };
} // namespace mongo } // namespace mongo
#endif // BSON_STRINGDATA_HEADER
 End of changes. 8 change blocks. 
18 lines changed or deleted 27 lines changed or added


 stringutils.h   stringutils.h 
skipping to change at line 18 skipping to change at line 18
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef UTIL_STRING_UTILS_HEADER #pragma once
#define UTIL_STRING_UTILS_HEADER
namespace mongo { namespace mongo {
// see also mongoutils/str.h - perhaps move these there?
// see also text.h
void splitStringDelim( const string& str , vector<string>* res , char d elim ); void splitStringDelim( const string& str , vector<string>* res , char d elim );
void joinStringDelim( const vector<string>& strs , string* res , char d elim ); void joinStringDelim( const vector<string>& strs , string* res , char d elim );
inline string tolowerString( const string& input ){ inline string tolowerString( const string& input ) {
string::size_type sz = input.size(); string::size_type sz = input.size();
boost::scoped_array<char> line(new char[sz+1]); boost::scoped_array<char> line(new char[sz+1]);
char * copy = line.get(); char * copy = line.get();
for ( string::size_type i=0; i<sz; i++ ){ for ( string::size_type i=0; i<sz; i++ ) {
char c = input[i]; char c = input[i];
copy[i] = (char)tolower( (int)c ); copy[i] = (char)tolower( (int)c );
} }
copy[sz] = 0; copy[sz] = 0;
return string(copy); return string(copy);
} }
} // namespace mongo /**
* Non numeric characters are compared lexicographically; numeric subst rings
* are compared numerically; dots separate ordered comparable subunits.
* For convenience, character 255 is greater than anything else.
*/
inline int lexNumCmp( const char *s1, const char *s2 ) {
//cout << "START : " << s1 << "\t" << s2 << endl;
#endif // UTIL_STRING_UTILS_HEADER bool startWord = true;
while( *s1 && *s2 ) {
bool d1 = ( *s1 == '.' );
bool d2 = ( *s2 == '.' );
if ( d1 && !d2 )
return -1;
if ( d2 && !d1 )
return 1;
if ( d1 && d2 ) {
++s1; ++s2;
startWord = true;
continue;
}
bool p1 = ( *s1 == (char)255 );
bool p2 = ( *s2 == (char)255 );
//cout << "\t\t " << p1 << "\t" << p2 << endl;
if ( p1 && !p2 )
return 1;
if ( p2 && !p1 )
return -1;
bool n1 = isNumber( *s1 );
bool n2 = isNumber( *s2 );
if ( n1 && n2 ) {
// get rid of leading 0s
if ( startWord ) {
while ( *s1 == '0' ) s1++;
while ( *s2 == '0' ) s2++;
}
char * e1 = (char*)s1;
char * e2 = (char*)s2;
// find length
// if end of string, will break immediately ('\0')
while ( isNumber (*e1) ) e1++;
while ( isNumber (*e2) ) e2++;
int len1 = (int)(e1-s1);
int len2 = (int)(e2-s2);
int result;
// if one is longer than the other, return
if ( len1 > len2 ) {
return 1;
}
else if ( len2 > len1 ) {
return -1;
}
// if the lengths are equal, just strcmp
else if ( (result = strncmp(s1, s2, len1)) != 0 ) {
return result;
}
// otherwise, the numbers are equal
s1 = e1;
s2 = e2;
startWord = false;
continue;
}
if ( n1 )
return 1;
if ( n2 )
return -1;
if ( *s1 > *s2 )
return 1;
if ( *s2 > *s1 )
return -1;
s1++; s2++;
startWord = false;
}
if ( *s1 )
return 1;
if ( *s2 )
return -1;
return 0;
}
} // namespace mongo
 End of changes. 6 change blocks. 
5 lines changed or deleted 13 lines changed or added


 syncclusterconnection.h   syncclusterconnection.h 
skipping to change at line 19 skipping to change at line 19
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once
#include "../pch.h" #include "../pch.h"
#include "dbclient.h" #include "dbclient.h"
#include "redef_macros.h" #include "redef_macros.h"
namespace mongo { namespace mongo {
/** /**
* This is a connection to a cluster of servers that operate as one * This is a connection to a cluster of servers that operate as one
* for super high durability. * for super high durability.
* *
skipping to change at line 44 skipping to change at line 46
* Read operations are sent to a single random node. * Read operations are sent to a single random node.
* *
* The class checks if a command is read or write style, and sends to a single * The class checks if a command is read or write style, and sends to a single
* node if a read lock command and to all in two phases with a write st yle command. * node if a read lock command and to all in two phases with a write st yle command.
*/ */
class SyncClusterConnection : public DBClientBase { class SyncClusterConnection : public DBClientBase {
public: public:
/** /**
* @param commaSeparated should be 3 hosts comma separated * @param commaSeparated should be 3 hosts comma separated
*/ */
SyncClusterConnection( const list<HostAndPort> & ); SyncClusterConnection( const list<HostAndPort> &, double socketTime out = 0);
SyncClusterConnection( string commaSeparated ); SyncClusterConnection( string commaSeparated, double socketTimeout = 0);
SyncClusterConnection( string a , string b , string c ); SyncClusterConnection( string a , string b , string c, double socke tTimeout = 0 );
~SyncClusterConnection(); ~SyncClusterConnection();
/** /**
* @return true if all servers are up and ready for writes * @return true if all servers are up and ready for writes
*/ */
bool prepare( string& errmsg ); bool prepare( string& errmsg );
/** /**
* runs fsync on all servers * runs fsync on all servers
*/ */
skipping to change at line 68 skipping to change at line 70
// --- from DBClientInterface // --- from DBClientInterface
virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions); virtual BSONObj findOne(const string &ns, const Query& query, const BSONObj *fieldsToReturn, int queryOptions);
virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn, int nToSkip, virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer y, int nToReturn, int nToSkip,
const BSONObj *fieldsToRetur n, int queryOptions, int batchSize ); const BSONObj *fieldsToRetur n, int queryOptions, int batchSize );
virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo ng cursorId, int nToReturn, int options ); virtual auto_ptr<DBClientCursor> getMore( const string &ns, long lo ng cursorId, int nToReturn, int options );
virtual void insert( const string &ns, BSONObj obj ); virtual void insert( const string &ns, BSONObj obj, int flags=0);
virtual void insert( const string &ns, const vector< BSONObj >& v ) ; virtual void insert( const string &ns, const vector< BSONObj >& v, int flags=0);
virtual void remove( const string &ns , Query query, bool justOne ) ; virtual void remove( const string &ns , Query query, bool justOne ) ;
virtual void update( const string &ns , Query query , BSONObj obj , bool upsert , bool multi ); virtual void update( const string &ns , Query query , BSONObj obj , bool upsert , bool multi );
virtual bool call( Message &toSend, Message &response, bool assertO k ); virtual bool call( Message &toSend, Message &response, bool assertO k , string * actualServer );
virtual void say( Message &toSend ); virtual void say( Message &toSend, bool isRetry = false );
virtual void sayPiggyBack( Message &toSend ); virtual void sayPiggyBack( Message &toSend );
virtual void killCursor( long long cursorID ); virtual void killCursor( long long cursorID );
virtual string getServerAddress() const { return _address; } virtual string getServerAddress() const { return _address; }
virtual bool isFailed() const { return false; } virtual bool isFailed() const { return false; }
virtual string toString() { return _toString(); } virtual string toString() { return _toString(); }
virtual BSONObj getLastErrorDetailed(); virtual BSONObj getLastErrorDetailed();
virtual bool callRead( Message& toSend , Message& response ); virtual bool callRead( Message& toSend , Message& response );
virtual ConnectionString::ConnectionType type() const { return Conn ectionString::SYNC; } virtual ConnectionString::ConnectionType type() const { return Conn ectionString::SYNC; }
virtual bool isMember( const DBConnector * conn ) const; void setAllSoTimeouts( double socketTimeout );
double getSoTimeout() const { return _socketTimeout; }
virtual bool auth(const string &dbname, const string &username, con st string &password_text, string& errmsg, bool digestPassword);
virtual bool lazySupported() const { return false; }
private: private:
SyncClusterConnection( SyncClusterConnection& prev ); SyncClusterConnection( SyncClusterConnection& prev, double socketTi meout = 0 );
string _toString() const; string _toString() const;
bool _commandOnActive(const string &dbname, const BSONObj& cmd, BSO NObj &info, int options=0); bool _commandOnActive(const string &dbname, const BSONObj& cmd, BSO NObj &info, int options=0);
auto_ptr<DBClientCursor> _queryOnActive(const string &ns, Query que ry, int nToReturn, int nToSkip, auto_ptr<DBClientCursor> _queryOnActive(const string &ns, Query que ry, int nToReturn, int nToSkip,
const BSONObj *fieldsToRetu rn, int queryOptions, int batchSize ); const BSONObj *fieldsToRetu rn, int queryOptions, int batchSize );
int _lockType( const string& name ); int _lockType( const string& name );
void _checkLast(); void _checkLast();
void _connect( string host ); void _connect( string host );
string _address; string _address;
vector<string> _connAddresses; vector<string> _connAddresses;
vector<DBClientConnection*> _conns; vector<DBClientConnection*> _conns;
map<string,int> _lockTypes; map<string,int> _lockTypes;
mongo::mutex _mutex; mongo::mutex _mutex;
vector<BSONObj> _lastErrors; vector<BSONObj> _lastErrors;
double _socketTimeout;
}; };
class UpdateNotTheSame : public UserException { class UpdateNotTheSame : public UserException {
public: public:
UpdateNotTheSame( int code , const string& msg , const vector<strin g>& addrs , const vector<BSONObj>& lastErrors ) UpdateNotTheSame( int code , const string& msg , const vector<strin g>& addrs , const vector<BSONObj>& lastErrors )
: UserException( code , msg ) , _addrs( addrs ) , _lastErrors( lastErrors ){ : UserException( code , msg ) , _addrs( addrs ) , _lastErrors( lastErrors ) {
assert( _addrs.size() == _lastErrors.size() ); assert( _addrs.size() == _lastErrors.size() );
} }
virtual ~UpdateNotTheSame() throw() { virtual ~UpdateNotTheSame() throw() {
} }
unsigned size() const { unsigned size() const {
return _addrs.size(); return _addrs.size();
} }
 End of changes. 11 change blocks. 
11 lines changed or deleted 19 lines changed or added


 synchronization.h   synchronization.h 
skipping to change at line 21 skipping to change at line 21
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <boost/thread/condition.hpp> #include <boost/thread/condition.hpp>
#include "mutex.h" #include "mutex.h"
namespace mongo { namespace mongo {
/* /*
* A class to establish a sinchronization point between two threads. On e thread is the waiter and one is * A class to establish a synchronization point between two threads. On e thread is the waiter and one is
* the notifier. After the notification event, both proceed normally. * the notifier. After the notification event, both proceed normally.
* *
* This class is thread-safe. * This class is thread-safe.
*/ */
class Notification { class Notification {
public: public:
Notification(); Notification();
~Notification(); ~Notification();
/* /*
* Blocks until the method 'notifyOne()' is called. * Blocks until the method 'notifyOne()' is called.
*/ */
void waitToBeNotified(); void waitToBeNotified();
/* /*
* Notifies the waiter of '*this' that it can proceed. * Notifies the waiter of '*this' that it can proceed. Can only be called once.
*/ */
void notifyOne(); void notifyOne();
private: private:
mongo::mutex _mutex; // protects state below mongo::mutex _mutex; // protects state below
bool _notified; // was notifyOne() issued? bool _notified; // was notifyOne() issued?
boost::condition _condition; // cond over _notified being true boost::condition _condition; // cond over _notified being true
}; };
/** establishes a synchronization point between threads. N threads are waits and one is notifier.
threadsafe.
*/
class NotifyAll : boost::noncopyable {
public:
NotifyAll();
typedef unsigned long long When;
When now();
/** awaits the next notifyAll() call by another thread. notificatio ns that precede this
call are ignored -- we are looking for a fresh event.
*/
void waitFor(When);
/** a bit faster than waitFor( now() ) */
void awaitBeyondNow();
/** may be called multiple times. notifies all waiters */
void notifyAll(When);
/** indicates how many threads are waiting for a notify. */
unsigned nWaiting() const { return _nWaiting; }
private:
mongo::mutex _mutex;
boost::condition _condition;
When _lastDone;
When _lastReturned;
unsigned _nWaiting;
};
} // namespace mongo } // namespace mongo
 End of changes. 4 change blocks. 
3 lines changed or deleted 35 lines changed or added


 task.h   task.h 
skipping to change at line 33 skipping to change at line 33
namespace mongo { namespace mongo {
namespace task { namespace task {
/** abstraction around threads. simpler than BackgroundJob which i s used behind the scenes. /** abstraction around threads. simpler than BackgroundJob which i s used behind the scenes.
allocate the Task dynamically. when the thread terminates, the Task object will delete itself. allocate the Task dynamically. when the thread terminates, the Task object will delete itself.
*/ */
class Task : private BackgroundJob { class Task : private BackgroundJob {
protected: protected:
virtual void doWork() = 0; // implement the ta sk here. virtual void doWork() = 0; // implement the ta sk here.
virtual string name() = 0; // name the threada virtual string name() const = 0; // name the threada
public: public:
Task(); Task();
/** for a repeating task, stop after current invocation ends. c an be called by other threads /** for a repeating task, stop after current invocation ends. c an be called by other threads
as long as the Task is still in scope. as long as the Task is still in scope.
*/ */
void halt(); void halt();
private: private:
unsigned n, repeat; unsigned n, repeat;
friend void fork(Task* t); friend void fork(Task* t);
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 text.h   text.h 
skipping to change at line 39 skipping to change at line 39
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
namespace mongo { namespace mongo {
class StringSplitter { class StringSplitter {
public: public:
/** @param big the string to be split
@param splitter the delimiter
*/
StringSplitter( const char * big , const char * splitter ) StringSplitter( const char * big , const char * splitter )
: _big( big ) , _splitter( splitter ){ : _big( big ) , _splitter( splitter ) {
} }
bool more(){ /** @return true if more to be taken via next() */
bool more() {
return _big[0]; return _big[0];
} }
string next(){ /** get next split string fragment */
string next() {
const char * foo = strstr( _big , _splitter ); const char * foo = strstr( _big , _splitter );
if ( foo ){ if ( foo ) {
string s( _big , foo - _big ); string s( _big , foo - _big );
_big = foo + 1; _big = foo + 1;
while ( *_big && strstr( _big , _splitter ) == _big ) while ( *_big && strstr( _big , _splitter ) == _big )
_big++; _big++;
return s; return s;
} }
string s = _big; string s = _big;
_big += strlen( _big ); _big += strlen( _big );
return s; return s;
} }
void split( vector<string>& l ){ void split( vector<string>& l ) {
while ( more() ){ while ( more() ) {
l.push_back( next() ); l.push_back( next() );
} }
} }
vector<string> split(){ vector<string> split() {
vector<string> l; vector<string> l;
split( l ); split( l );
return l; return l;
} }
static vector<string> split( const string& big , const string& spli tter ){ static vector<string> split( const string& big , const string& spli tter ) {
StringSplitter ss( big.c_str() , splitter.c_str() ); StringSplitter ss( big.c_str() , splitter.c_str() );
return ss.split(); return ss.split();
} }
static string join( vector<string>& l , const string& split ){ static string join( vector<string>& l , const string& split ) {
stringstream ss; stringstream ss;
for ( unsigned i=0; i<l.size(); i++ ){ for ( unsigned i=0; i<l.size(); i++ ) {
if ( i > 0 ) if ( i > 0 )
ss << split; ss << split;
ss << l[i]; ss << l[i];
} }
return ss.str(); return ss.str();
} }
private: private:
const char * _big; const char * _big;
const char * _splitter; const char * _splitter;
skipping to change at line 127 skipping to change at line 132
// expect that n contains a base ten number and nothing else after it // expect that n contains a base ten number and nothing else after it
// NOTE win version hasn't been tested directly // NOTE win version hasn't been tested directly
inline long long parseLL( const char *n ) { inline long long parseLL( const char *n ) {
long long ret; long long ret;
uassert( 13307, "cannot convert empty string to long long", *n != 0 ); uassert( 13307, "cannot convert empty string to long long", *n != 0 );
#if !defined(_WIN32) #if !defined(_WIN32)
char *endPtr = 0; char *endPtr = 0;
errno = 0; errno = 0;
ret = strtoll( n, &endPtr, 10 ); ret = strtoll( n, &endPtr, 10 );
uassert( 13305, "could not convert string to long long", *endPtr == 0 && errno == 0 ); uassert( 13305, "could not convert string to long long", *endPtr == 0 && errno == 0 );
#elif _MSC_VER>=1600 // 1600 is VS2k10 1500 is VS2k8 #elif _MSC_VER>=1600 // 1600 is VS2k10 1500 is VS2k8
size_t endLen = 0; size_t endLen = 0;
try { try {
ret = stoll( n, &endLen, 10 ); ret = stoll( n, &endLen, 10 );
} catch ( ... ) { }
catch ( ... ) {
endLen = 0; endLen = 0;
} }
uassert( 13306, "could not convert string to long long", endLen != 0 && n[ endLen ] == 0 ); uassert( 13306, "could not convert string to long long", endLen != 0 && n[ endLen ] == 0 );
#else // stoll() wasn't introduced until VS 2010. #else // stoll() wasn't introduced until VS 2010.
char* endPtr = 0; char* endPtr = 0;
ret = _strtoi64( n, &endPtr, 10 ); ret = _strtoi64( n, &endPtr, 10 );
uassert( 13310, "could not convert string to long long", (*endPtr = = 0) && (ret != _I64_MAX) && (ret != _I64_MIN) ); uassert( 13310, "could not convert string to long long", (*endPtr = = 0) && (ret != _I64_MAX) && (ret != _I64_MIN) );
#endif // !defined(_WIN32) #endif // !defined(_WIN32)
return ret; return ret;
} }
 End of changes. 12 change blocks. 
12 lines changed or deleted 18 lines changed or added


 thread_pool.h   thread_pool.h 
skipping to change at line 18 skipping to change at line 18
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli ed.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#pragma once
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#undef assert #undef assert
#define assert MONGO_assert #define assert MONGO_assert
namespace mongo { namespace mongo {
namespace threadpool { namespace threadpool {
class Worker; class Worker;
typedef boost::function<void(void)> Task; //nullary function or functor typedef boost::function<void(void)> Task; //nullary function or fun ctor
// exported to the mongo namespace // exported to the mongo namespace
class ThreadPool : boost::noncopyable{ class ThreadPool : boost::noncopyable {
public: public:
explicit ThreadPool(int nThreads=8); explicit ThreadPool(int nThreads=8);
// blocks until all tasks are complete (tasks_remaining() == 0) // blocks until all tasks are complete (tasks_remaining() == 0)
// You should not call schedule while in the destructor // You should not call schedule while in the destructor
~ThreadPool(); ~ThreadPool();
// blocks until all tasks are complete (tasks_remaining() == 0) // blocks until all tasks are complete (tasks_remaining() == 0)
// does not prevent new tasks from being scheduled so could wait fo rever. // does not prevent new tasks from being scheduled so could wai t forever.
// Also, new tasks could be scheduled after this returns. // Also, new tasks could be scheduled after this returns.
void join(); void join();
// task will be copied a few times so make sure it's relatively che ap // task will be copied a few times so make sure it's relatively cheap
void schedule(Task task); void schedule(Task task);
// Helpers that wrap schedule and boost::bind. // Helpers that wrap schedule and boost::bind.
// Functor and args will be copied a few times so make sure it's re latively cheap // Functor and args will be copied a few times so make sure it' s relatively cheap
template<typename F, typename A> template<typename F, typename A>
void schedule(F f, A a){ schedule(boost::bind(f,a)); } void schedule(F f, A a) { schedule(boost::bind(f,a)); }
template<typename F, typename A, typename B> template<typename F, typename A, typename B>
void schedule(F f, A a, B b){ schedule(boost::bind(f,a,b)); } void schedule(F f, A a, B b) { schedule(boost::bind(f,a,b)); }
template<typename F, typename A, typename B, typename C> template<typename F, typename A, typename B, typename C>
void schedule(F f, A a, B b, C c){ schedule(boost::bind(f,a,b,c)); } void schedule(F f, A a, B b, C c) { schedule(boost::bind(f,a,b, c)); }
template<typename F, typename A, typename B, typename C, typename D > template<typename F, typename A, typename B, typename C, typena me D>
void schedule(F f, A a, B b, C c, D d){ schedule(boost::bind(f,a,b, c,d)); } void schedule(F f, A a, B b, C c, D d) { schedule(boost::bind(f ,a,b,c,d)); }
template<typename F, typename A, typename B, typename C, typename D , typename E> template<typename F, typename A, typename B, typename C, typena me D, typename E>
void schedule(F f, A a, B b, C c, D d, E e){ schedule(boost::bind(f ,a,b,c,d,e)); } void schedule(F f, A a, B b, C c, D d, E e) { schedule(boost::b ind(f,a,b,c,d,e)); }
int tasks_remaining() { return _tasksRemaining; } int tasks_remaining() { return _tasksRemaining; }
private: private:
mongo::mutex _mutex; mongo::mutex _mutex;
boost::condition _condition; boost::condition _condition;
list<Worker*> _freeWorkers; //used as LIFO stack (always front) list<Worker*> _freeWorkers; //used as LIFO stack (always front)
list<Task> _tasks; //used as FIFO queue (push_back, pop_front) list<Task> _tasks; //used as FIFO queue (push_back, pop_front)
int _tasksRemaining; // in queue + currently processing int _tasksRemaining; // in queue + currently processing
int _nThreads; // only used for sanity checking. could be removed i n the future. int _nThreads; // only used for sanity checking. could be remov ed in the future.
// should only be called by a worker from the worker's thread // should only be called by a worker from the worker's thread
void task_done(Worker* worker); void task_done(Worker* worker);
friend class Worker; friend class Worker;
}; };
} //namespace threadpool } //namespace threadpool
using threadpool::ThreadPool; using threadpool::ThreadPool;
} //namespace mongo } //namespace mongo
 End of changes. 14 change blocks. 
42 lines changed or deleted 44 lines changed or added


 tool.h   tool.h 
skipping to change at line 31 skipping to change at line 31
#include <string> #include <string>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#if defined(_WIN32) #if defined(_WIN32)
#include <io.h> #include <io.h>
#endif #endif
#include "client/dbclient.h" #include "client/dbclient.h"
#include "db/instance.h" #include "db/instance.h"
#include "db/matcher.h"
using std::string; using std::string;
namespace mongo { namespace mongo {
class Tool { class Tool {
public: public:
enum DBAccess{ enum DBAccess {
NONE, NONE = 0 ,
ALL, REMOTE_SERVER = 1 << 1 ,
NO_LOCAL LOCAL_SERVER = 1 << 2 ,
SPECIFY_DBCOL = 1 << 3 ,
ALL = REMOTE_SERVER | LOCAL_SERVER | SPECIFY_DBCOL
}; };
Tool( string name , DBAccess access=ALL, string defaultDB="test" , Tool( string name , DBAccess access=ALL, string defaultDB="test" ,
string defaultCollection="", bool usesstdout=true); string defaultCollection="", bool usesstdout=true);
virtual ~Tool(); virtual ~Tool();
int main( int argc , char ** argv ); int main( int argc , char ** argv );
boost::program_options::options_description_easy_init add_options() { boost::program_options::options_description_easy_init add_options() {
return _options->add_options(); return _options->add_options();
} }
boost::program_options::options_description_easy_init add_hidden_op tions(){ boost::program_options::options_description_easy_init add_hidden_op tions() {
return _hidden_options->add_options(); return _hidden_options->add_options();
} }
void addPositionArg( const char * name , int pos ){ void addPositionArg( const char * name , int pos ) {
_positonalOptions.add( name , pos ); _positonalOptions.add( name , pos );
} }
string getParam( string name , string def="" ){ string getParam( string name , string def="" ) {
if ( _params.count( name ) ) if ( _params.count( name ) )
return _params[name.c_str()].as<string>(); return _params[name.c_str()].as<string>();
return def; return def;
} }
int getParam( string name , int def ){ int getParam( string name , int def ) {
if ( _params.count( name ) ) if ( _params.count( name ) )
return _params[name.c_str()].as<int>(); return _params[name.c_str()].as<int>();
return def; return def;
} }
bool hasParam( string name ){ bool hasParam( string name ) {
return _params.count( name ); return _params.count( name );
} }
string getNS(){ string getNS() {
if ( _coll.size() == 0 ){ if ( _coll.size() == 0 ) {
cerr << "no collection specified!" << endl; cerr << "no collection specified!" << endl;
throw -1; throw -1;
} }
return _db + "." + _coll; return _db + "." + _coll;
} }
virtual void preSetup(){} bool isMaster();
virtual void preSetup() {}
virtual int run() = 0; virtual int run() = 0;
virtual void printHelp(ostream &out); virtual void printHelp(ostream &out);
virtual void printExtraHelp( ostream & out ){} virtual void printExtraHelp( ostream & out ) {}
virtual void printExtraHelpAfter( ostream & out ){} virtual void printExtraHelpAfter( ostream & out ) {}
virtual void printVersion(ostream &out);
protected: protected:
mongo::DBClientBase &conn( bool slaveIfPaired = false ); mongo::DBClientBase &conn( bool slaveIfPaired = false );
void auth( string db = "" ); void auth( string db = "" );
string _name; string _name;
string _db; string _db;
string _coll; string _coll;
string _fileName;
string _username; string _username;
string _password; string _password;
bool _usesstdout; bool _usesstdout;
bool _noconnection; bool _noconnection;
bool _autoreconnect; bool _autoreconnect;
void addFieldOptions(); void addFieldOptions();
void needFields(); void needFields();
vector<string> _fields; vector<string> _fields;
BSONObj _fieldsObj; BSONObj _fieldsObj;
string _host; string _host;
protected: protected:
mongo::DBClientBase * _conn; mongo::DBClientBase * _conn;
mongo::DBClientBase * _slaveConn;
bool _paired; bool _paired;
boost::program_options::options_description * _options; boost::program_options::options_description * _options;
boost::program_options::options_description * _hidden_options; boost::program_options::options_description * _hidden_options;
boost::program_options::positional_options_description _positonalOp tions; boost::program_options::positional_options_description _positonalOp tions;
boost::program_options::variables_map _params; boost::program_options::variables_map _params;
}; };
 End of changes. 13 change blocks. 
15 lines changed or deleted 24 lines changed or added


 top.h   top.h 
skipping to change at line 34 skipping to change at line 34
namespace mongo { namespace mongo {
/** /**
* tracks usage by collection * tracks usage by collection
*/ */
class Top { class Top {
public: public:
Top() : _lock("Top") { } Top() : _lock("Top") { }
class UsageData { struct UsageData {
public: UsageData() : time(0) , count(0) {}
UsageData() : time(0) , count(0){}
UsageData( const UsageData& older , const UsageData& newer ); UsageData( const UsageData& older , const UsageData& newer );
long long time; long long time;
long long count; long long count;
void inc( long long micros ){ void inc( long long micros ) {
count++; count++;
time += micros; time += micros;
} }
}; };
class CollectionData { struct CollectionData {
public:
/** /**
* constructs a diff * constructs a diff
*/ */
CollectionData(){} CollectionData() {}
CollectionData( const CollectionData& older , const CollectionD ata& newer ); CollectionData( const CollectionData& older , const CollectionD ata& newer );
UsageData total; UsageData total;
UsageData readLock; UsageData readLock;
UsageData writeLock; UsageData writeLock;
UsageData queries; UsageData queries;
UsageData getmore; UsageData getmore;
UsageData insert; UsageData insert;
UsageData update; UsageData update;
UsageData remove; UsageData remove;
UsageData commands; UsageData commands;
}; };
typedef map<string,CollectionData> UsageMap; typedef map<string,CollectionData> UsageMap;
public: public:
void record( const string& ns , int op , int lockType , long long m icros , bool command ); void record( const string& ns , int op , int lockType , long long m icros , bool command );
void append( BSONObjBuilder& b ); void append( BSONObjBuilder& b );
void cloneMap(UsageMap& out); void cloneMap(UsageMap& out) const;
CollectionData getGlobalData(){ return _global; } CollectionData getGlobalData() const { return _global; }
void collectionDropped( const string& ns ); void collectionDropped( const string& ns );
public: // static stuff public: // static stuff
static Top global; static Top global;
void append( BSONObjBuilder& b , const char * name , const UsageDat a& map );
void append( BSONObjBuilder& b , const UsageMap& map );
private: private:
void _appendToUsageMap( BSONObjBuilder& b , const UsageMap& map ) c onst;
void _appendStatsEntry( BSONObjBuilder& b , const char * statsName , const UsageData& map ) const;
void _record( CollectionData& c , int op , int lockType , long long micros , bool command ); void _record( CollectionData& c , int op , int lockType , long long micros , bool command );
mongo::mutex _lock; mutable mongo::mutex _lock;
CollectionData _global; CollectionData _global;
UsageMap _usage; UsageMap _usage;
string _lastDropped; string _lastDropped;
}; };
/* 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 TopOld { class TopOld {
typedef boost::posix_time::ptime T; typedef boost::posix_time::ptime T;
skipping to change at line 184 skipping to change at line 180
res.push_back( u ); res.push_back( u );
} }
} }
static void completeSnapshot() { static void completeSnapshot() {
scoped_lock L(topMutex); scoped_lock 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; _snapshotDuration = currentTime() - _snapshotStart;
_snapshotStart = currentTime(); _snapshotStart = currentTime();
_nextSnapshot.clear(); _nextSnapshot.clear();
} }
private: private:
static mongo::mutex topMutex; static mongo::mutex topMutex;
 End of changes. 9 change blocks. 
15 lines changed or deleted 12 lines changed or added


 undef_macros.h   undef_macros.h 
/** @file undef_macros.h - remove mongo-specific macros that might cause is sues */ /** @file undef_macros.h remove mongo implementation macros after using */
/* Copyright 2009 10gen Inc. /* Copyright 2009 10gen Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
skipping to change at line 56 skipping to change at line 56
#undef ctime #undef ctime
// util/debug_util.h // util/debug_util.h
#undef DEV #undef DEV
#undef DEBUGGING #undef DEBUGGING
#undef SOMETIMES #undef SOMETIMES
#undef OCCASIONALLY #undef OCCASIONALLY
#undef RARELY #undef RARELY
#undef ONCE #undef ONCE
// util/log.h
#undef LOG
#define MONGO_MACROS_CLEANED #define MONGO_MACROS_CLEANED
#endif #endif
 End of changes. 2 change blocks. 
1 lines changed or deleted 4 lines changed or added


 unittest.h   unittest.h 
skipping to change at line 28 skipping to change at line 28
#pragma once #pragma once
namespace mongo { namespace mongo {
/* The idea here is to let all initialization of global variables (clas ses inheriting from UnitTest) /* The idea here is to let all initialization of global variables (clas ses inheriting from UnitTest)
complete before we run the tests -- otherwise order of initilization being arbitrary may mess complete before we run the tests -- otherwise order of initilization being arbitrary may mess
us up. The app's main() function should call runTests(). us up. The app's main() function should call runTests().
To define a unit test, inherit from this and implement run. instanti ate one object for the new class To define a unit test, inherit from this and implement run. instanti ate one object for the new class
as a global. as a global.
These tests are ran on *every* startup of mongod, so they have to be very lightweight. But it is a
good quick check for a bad build.
*/ */
struct UnitTest { struct UnitTest {
UnitTest() { UnitTest() {
registerTest(this); registerTest(this);
} }
virtual ~UnitTest() {} virtual ~UnitTest() {}
// assert if fails // assert if fails
virtual void run() = 0; virtual void run() = 0;
 End of changes. 1 change blocks. 
0 lines changed or deleted 3 lines changed or added


 update.h   update.h 
skipping to change at line 19 skipping to change at line 19
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 Lice nse * You should have received a copy of the GNU Affero General Public Lice nse
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "../pch.h" #include "../../pch.h"
#include "jsobj.h" #include "../jsobj.h"
#include "../util/embedded_builder.h" #include "../../util/embedded_builder.h"
#include "matcher.h" #include "../matcher.h"
namespace mongo { namespace mongo {
// ---------- public -------------
struct UpdateResult {
bool existing; // if existing objects were modified
bool mod; // was this a $ mod
long long num; // how many objects touched
OID upserted; // if something was upserted, the new _id of the obj ect
UpdateResult( bool e, bool m, unsigned long long n , const BSONObj& upsertedObject = BSONObj() )
: existing(e) , mod(m), num(n) {
upserted.clear();
BSONElement id = upsertedObject["_id"];
if ( ! e && n == 1 && id.type() == jstOID ) {
upserted = id.OID();
}
}
};
class RemoveSaver;
/* returns true if an existing object was updated, false if no existing object was found.
multi - update multiple objects - mostly useful with things like $se t
god - allow access to system namespaces
*/
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BS ONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug );
UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& up dateobj, BSONObj pattern,
bool upsert, bool multi , bool logop , OpDe bug& debug , RemoveSaver * rs = 0 );
// ---------- private -------------
class ModState; class ModState;
class ModSetState; class ModSetState;
/* Used for modifiers such as $inc, $set, $push, ... /* Used for modifiers such as $inc, $set, $push, ...
* stores the info about a single operation * stores the info about a single operation
* once created should never be modified * once created should never be modified
*/ */
struct Mod { struct Mod {
// See opFromStr below // See opFromStr below
// 0 1 2 3 4 5 6 7 8 9 10 11 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI TAND, BITOR , BIT , ADDTOSET } op; enum Op { INC, SET, PUSH, PUSH_ALL, PULL, PULL_ALL , POP, UNSET, BI TAND, BITOR , BIT , ADDTOSET, RENAME_FROM, RENAME_TO } op;
static const char* modNames[]; static const char* modNames[];
static unsigned modNamesNum; static unsigned modNamesNum;
const char *fieldName; const char *fieldName;
const char *shortFieldName; const char *shortFieldName;
BSONElement elt; // x:5 note: this is the actual element from the u pdateobj BSONElement elt; // x:5 note: this is the actual element from the u pdateobj
boost::shared_ptr<Matcher> matcher; boost::shared_ptr<Matcher> matcher;
bool matcherOnPrimitive;
void init( Op o , BSONElement& e ){ void init( Op o , BSONElement& e ) {
op = o; op = o;
elt = e; elt = e;
if ( op == PULL && e.type() == Object ) if ( op == PULL && e.type() == Object ) {
matcher.reset( new Matcher( e.embeddedObject() ) ); BSONObj t = e.embeddedObject();
if ( t.firstElement().getGtLtOp() == 0 ) {
matcher.reset( new Matcher( t ) );
matcherOnPrimitive = false;
}
else {
matcher.reset( new Matcher( BSON( "" << t ) ) );
matcherOnPrimitive = true;
}
}
} }
void setFieldName( const char * s ){ void setFieldName( const char * s ) {
fieldName = s; fieldName = s;
shortFieldName = strrchr( fieldName , '.' ); shortFieldName = strrchr( fieldName , '.' );
if ( shortFieldName ) if ( shortFieldName )
shortFieldName++; shortFieldName++;
else else
shortFieldName = fieldName; shortFieldName = fieldName;
} }
/** /**
* @param in incrememnts the actual value inside in * @param in incrememnts the actual value inside in
*/ */
void incrementMe( BSONElement& in ) const { void incrementMe( BSONElement& in ) const {
BSONElementManipulator manip( in ); BSONElementManipulator manip( in );
switch ( in.type() ) {
switch ( in.type() ){
case NumberDouble: case NumberDouble:
manip.setNumber( elt.numberDouble() + in.numberDouble() ); manip.setNumber( elt.numberDouble() + in.numberDouble() );
break; break;
case NumberLong: case NumberLong:
manip.setLong( elt.numberLong() + in.numberLong() ); manip.setLong( elt.numberLong() + in.numberLong() );
break; break;
case NumberInt: case NumberInt:
manip.setInt( elt.numberInt() + in.numberInt() ); manip.setInt( elt.numberInt() + in.numberInt() );
break; break;
default: default:
assert(0); assert(0);
} }
}
void IncrementMe( BSONElement& in ) const {
BSONElementManipulator manip( in );
switch ( in.type() ) {
case NumberDouble:
manip.SetNumber( elt.numberDouble() + in.numberDouble() );
break;
case NumberLong:
manip.SetLong( elt.numberLong() + in.numberLong() );
break;
case NumberInt:
manip.SetInt( elt.numberInt() + in.numberInt() );
break;
default:
assert(0);
}
} }
template< class Builder > template< class Builder >
void appendIncremented( Builder& bb , const BSONElement& in, ModSta te& ms ) const; void appendIncremented( Builder& bb , const BSONElement& in, ModSta te& ms ) const;
bool operator<( const Mod &other ) const { bool operator<( const Mod &other ) const {
return strcmp( fieldName, other.fieldName ) < 0; return strcmp( fieldName, other.fieldName ) < 0;
} }
bool arrayDep() const { bool arrayDep() const {
switch (op){ switch (op) {
case PUSH: case PUSH:
case PUSH_ALL: case PUSH_ALL:
case POP: case POP:
return true; return true;
default: default:
return false; return false;
} }
} }
static bool isIndexed( const string& fullName , const set<string>& idxKeys ){ static bool isIndexed( const string& fullName , const set<string>& idxKeys ) {
const char * fieldName = fullName.c_str(); const char * fieldName = fullName.c_str();
// check if there is an index key that is a parent of mod // check if there is an index key that is a parent of mod
for( const char *dot = strchr( fieldName, '.' ); dot; dot = str chr( dot + 1, '.' ) ) for( const char *dot = strchr( fieldName, '.' ); dot; dot = str chr( dot + 1, '.' ) )
if ( idxKeys.count( string( fieldName, dot - fieldName ) ) ) if ( idxKeys.count( string( fieldName, dot - fieldName ) ) )
return true; return true;
// check if there is an index key equal to mod // check if there is an index key equal to mod
if ( idxKeys.count(fullName) ) if ( idxKeys.count(fullName) )
return true; return true;
// check if there is an index key that is a child of mod // check if there is an index key that is a child of mod
skipping to change at line 127 skipping to change at line 183
return false; return false;
} }
bool isIndexed( const set<string>& idxKeys ) const { bool isIndexed( const set<string>& idxKeys ) const {
string fullName = fieldName; string fullName = fieldName;
if ( isIndexed( fullName , idxKeys ) ) if ( isIndexed( fullName , idxKeys ) )
return true; return true;
if ( strstr( fieldName , "." ) ){ if ( strstr( fieldName , "." ) ) {
// check for a.0.1 // check for a.0.1
StringBuilder buf( fullName.size() + 1 ); StringBuilder buf( fullName.size() + 1 );
for ( size_t i=0; i<fullName.size(); i++ ){ for ( size_t i=0; i<fullName.size(); i++ ) {
char c = fullName[i]; char c = fullName[i];
if ( c == '$' && if ( c == '$' &&
i > 0 && fullName[i-1] == '.' && i > 0 && fullName[i-1] == '.' &&
i+1<fullName.size() && i+1<fullName.size() &&
fullName[i+1] == '.' ){ fullName[i+1] == '.' ) {
i++; i++;
continue; continue;
} }
buf << c; buf << c;
if ( c != '.' ) if ( c != '.' )
continue; continue;
if ( ! isdigit( fullName[i+1] ) ) if ( ! isdigit( fullName[i+1] ) )
continue; continue;
bool possible = true; bool possible = true;
size_t j=i+2; size_t j=i+2;
for ( ; j<fullName.size(); j++ ){ for ( ; j<fullName.size(); j++ ) {
char d = fullName[j]; char d = fullName[j];
if ( d == '.' ) if ( d == '.' )
break; break;
if ( isdigit( d ) ) if ( isdigit( d ) )
continue; continue;
possible = false; possible = false;
break; break;
} }
if ( possible ) if ( possible )
skipping to change at line 181 skipping to change at line 237
template< class Builder > template< class Builder >
void apply( Builder& b , BSONElement in , ModState& ms ) const; void apply( Builder& b , BSONElement in , ModState& ms ) const;
/** /**
* @return true iff toMatch should be removed from the array * @return true iff toMatch should be removed from the array
*/ */
bool _pullElementMatch( BSONElement& toMatch ) const; bool _pullElementMatch( BSONElement& toMatch ) const;
void _checkForAppending( const BSONElement& e ) const { void _checkForAppending( const BSONElement& e ) const {
if ( e.type() == Object ){ if ( e.type() == Object ) {
// this is a tiny bit slow, but rare and important // this is a tiny bit slow, but rare and important
// only when setting something TO an object, not setting so mething in an object // only when setting something TO an object, not setting so mething in an object
// and it checks for { $set : { x : { 'a.b' : 1 } } } // and it checks for { $set : { x : { 'a.b' : 1 } } }
// which is feel has been common // which is feel has been common
uassert( 12527 , "not okForStorage" , e.embeddedObject().ok ForStorage() ); uassert( 12527 , "not okForStorage" , e.embeddedObject().ok ForStorage() );
} }
} }
bool isEach() const { bool isEach() const {
if ( elt.type() != Object ) if ( elt.type() != Object )
skipping to change at line 205 skipping to change at line 261
return false; return false;
return strcmp( e.fieldName() , "$each" ) == 0; return strcmp( e.fieldName() , "$each" ) == 0;
} }
BSONObj getEach() const { BSONObj getEach() const {
return elt.embeddedObjectUserCheck().firstElement().embeddedObj ectUserCheck(); return elt.embeddedObjectUserCheck().firstElement().embeddedObj ectUserCheck();
} }
void parseEach( BSONElementSet& s ) const { void parseEach( BSONElementSet& s ) const {
BSONObjIterator i(getEach()); BSONObjIterator i(getEach());
while ( i.more() ){ while ( i.more() ) {
s.insert( i.next() ); s.insert( i.next() );
} }
} }
const char *renameFrom() const {
massert( 13492, "mod must be RENAME_TO type", op == Mod::RENAME _TO );
return elt.fieldName();
}
}; };
/** /**
* stores a set of Mods * stores a set of Mods
* once created, should never be changed * once created, should never be changed
*/ */
class ModSet : boost::noncopyable { class ModSet : boost::noncopyable {
typedef map<string,Mod> ModHolder; typedef map<string,Mod> ModHolder;
ModHolder _mods; ModHolder _mods;
int _isIndexed; int _isIndexed;
skipping to change at line 242 skipping to change at line 302
if ( mDone ) if ( mDone )
return RIGHT_BEFORE; return RIGHT_BEFORE;
if ( pDone ) if ( pDone )
return LEFT_BEFORE; return LEFT_BEFORE;
return compareDottedFieldNames( m->first, p->first.c_str() ); return compareDottedFieldNames( m->first, p->first.c_str() );
} }
bool mayAddEmbedded( map< string, BSONElement > &existing, string r ight ) { bool mayAddEmbedded( map< string, BSONElement > &existing, string r ight ) {
for( string left = EmbeddedBuilder::splitDot( right ); for( string left = EmbeddedBuilder::splitDot( right );
left.length() > 0 && left[ left.length() - 1 ] != '.'; left.length() > 0 && left[ left.length() - 1 ] != '.';
left += "." + EmbeddedBuilder::splitDot( right ) ) { left += "." + EmbeddedBuilder::splitDot( right ) ) {
if ( existing.count( left ) > 0 && existing[ left ].type() != Object ) if ( existing.count( left ) > 0 && existing[ left ].type() != Object )
return false; return false;
if ( haveModForField( left.c_str() ) ) if ( haveModForField( left.c_str() ) )
return false; return false;
} }
return true; return true;
} }
static Mod::Op opFromStr( const char *fn ) { static Mod::Op opFromStr( const char *fn ) {
assert( fn[0] == '$' ); assert( fn[0] == '$' );
switch( fn[1] ){ switch( fn[1] ) {
case 'i': { case 'i': {
if ( fn[2] == 'n' && fn[3] == 'c' && fn[4] == 0 ) if ( fn[2] == 'n' && fn[3] == 'c' && fn[4] == 0 )
return Mod::INC; return Mod::INC;
break; break;
} }
case 's': { case 's': {
if ( fn[2] == 'e' && fn[3] == 't' && fn[4] == 0 ) if ( fn[2] == 'e' && fn[3] == 't' && fn[4] == 0 )
return Mod::SET; return Mod::SET;
break; break;
} }
case 'p': { case 'p': {
if ( fn[2] == 'u' ){ if ( fn[2] == 'u' ) {
if ( fn[3] == 's' && fn[4] == 'h' ){ if ( fn[3] == 's' && fn[4] == 'h' ) {
if ( fn[5] == 0 ) if ( fn[5] == 0 )
return Mod::PUSH; return Mod::PUSH;
if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' & & fn[8] == 0 ) if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' & & fn[8] == 0 )
return Mod::PUSH_ALL; return Mod::PUSH_ALL;
} }
else if ( fn[3] == 'l' && fn[4] == 'l' ){ else if ( fn[3] == 'l' && fn[4] == 'l' ) {
if ( fn[5] == 0 ) if ( fn[5] == 0 )
return Mod::PULL; return Mod::PULL;
if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' & & fn[8] == 0 ) if ( fn[5] == 'A' && fn[6] == 'l' && fn[7] == 'l' & & fn[8] == 0 )
return Mod::PULL_ALL; return Mod::PULL_ALL;
} }
} }
else if ( fn[2] == 'o' && fn[3] == 'p' && fn[4] == 0 ) else if ( fn[2] == 'o' && fn[3] == 'p' && fn[4] == 0 )
return Mod::POP; return Mod::POP;
break; break;
} }
case 'u': { case 'u': {
if ( fn[2] == 'n' && fn[3] == 's' && fn[4] == 'e' && fn[5] == 't' && fn[6] == 0 ) if ( fn[2] == 'n' && fn[3] == 's' && fn[4] == 'e' && fn[5] == 't' && fn[6] == 0 )
return Mod::UNSET; return Mod::UNSET;
break; break;
} }
case 'b': { case 'b': {
if ( fn[2] == 'i' && fn[3] == 't' ){ if ( fn[2] == 'i' && fn[3] == 't' ) {
if ( fn[4] == 0 ) if ( fn[4] == 0 )
return Mod::BIT; return Mod::BIT;
if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn [7] == 0 ) if ( fn[4] == 'a' && fn[5] == 'n' && fn[6] == 'd' && fn [7] == 0 )
return Mod::BITAND; return Mod::BITAND;
if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 ) if ( fn[4] == 'o' && fn[5] == 'r' && fn[6] == 0 )
return Mod::BITOR; return Mod::BITOR;
} }
break; break;
} }
case 'a': { case 'a': {
if ( fn[2] == 'd' && fn[3] == 'd' ){ if ( fn[2] == 'd' && fn[3] == 'd' ) {
// add // add
if ( fn[4] == 'T' && fn[5] == 'o' && fn[6] == 'S' && fn [7] == 'e' && fn[8] == 't' && fn[9] == 0 ) if ( fn[4] == 'T' && fn[5] == 'o' && fn[6] == 'S' && fn [7] == 'e' && fn[8] == 't' && fn[9] == 0 )
return Mod::ADDTOSET; return Mod::ADDTOSET;
} }
break;
}
case 'r': {
if ( fn[2] == 'e' && fn[3] == 'n' && fn[4] == 'a' && fn[5] == 'm' && fn[6] =='e' ) {
return Mod::RENAME_TO; // with this return code we hand le both RENAME_TO and RENAME_FROM
}
break;
} }
default: break; default: break;
} }
uassert( 10161 , "Invalid modifier specified " + string( fn ), false ); uassert( 10161 , "Invalid modifier specified " + string( fn ), false );
return Mod::INC; return Mod::INC;
} }
ModSet(){} ModSet() {}
void updateIsIndexed( const Mod &m, const set<string> &idxKeys, con st set<string> *backgroundKeys ) {
if ( m.isIndexed( idxKeys ) ||
(backgroundKeys && m.isIndexed(*backgroundKeys)) ) {
_isIndexed++;
}
}
public: public:
ModSet( const BSONObj &from , ModSet( const BSONObj &from ,
const set<string>& idxKeys = set<string>(), const set<string>& idxKeys = set<string>(),
const set<string>* backgroundKeys = 0 const set<string>* backgroundKeys = 0
); );
// TODO: this is inefficient - should probably just handle when ite rating // TODO: this is inefficient - should probably just handle when ite rating
ModSet * fixDynamicArray( const char * elemMatchKey ) const; ModSet * fixDynamicArray( const char * elemMatchKey ) const;
bool hasDynamicArray() const { return _hasDynamicArray; } bool hasDynamicArray() const { return _hasDynamicArray; }
/** /**
* creates a ModSetState suitable for operation on obj * creates a ModSetState suitable for operation on obj
* doesn't change or modify this ModSet or any underying Mod * doesn't change or modify this ModSet or any underying Mod
*/ */
skipping to change at line 352 skipping to change at line 426
int isIndexed() const { int isIndexed() const {
return _isIndexed; return _isIndexed;
} }
unsigned size() const { return _mods.size(); } unsigned size() const { return _mods.size(); }
bool haveModForField( const char *fieldName ) const { bool haveModForField( const char *fieldName ) const {
return _mods.find( fieldName ) != _mods.end(); return _mods.find( fieldName ) != _mods.end();
} }
bool haveConflictingMod( const string& fieldName ){ bool haveConflictingMod( const string& fieldName ) {
size_t idx = fieldName.find( '.' ); size_t idx = fieldName.find( '.' );
if ( idx == string::npos ) if ( idx == string::npos )
idx = fieldName.size(); idx = fieldName.size();
ModHolder::const_iterator start = _mods.lower_bound(fieldName.s ubstr(0,idx)); ModHolder::const_iterator start = _mods.lower_bound(fieldName.s ubstr(0,idx));
for ( ; start != _mods.end(); start++ ){ for ( ; start != _mods.end(); start++ ) {
FieldCompareResult r = compareDottedFieldNames( fieldName , start->first ); FieldCompareResult r = compareDottedFieldNames( fieldName , start->first );
switch ( r ){ switch ( r ) {
case LEFT_SUBFIELD: return true; case LEFT_SUBFIELD: return true;
case LEFT_BEFORE: return false; case LEFT_BEFORE: return false;
case SAME: return true; case SAME: return true;
case RIGHT_BEFORE: return false; case RIGHT_BEFORE: return false;
case RIGHT_SUBFIELD: return true; case RIGHT_SUBFIELD: return true;
} }
} }
return false; return false;
} }
}; };
/** /**
* stores any information about a single Mod operating on a single Obje ct * stores any information about a single Mod operating on a single Obje ct
*/ */
class ModState { class ModState {
public: public:
const Mod * m; const Mod * m;
BSONElement old; BSONElement old;
BSONElement newVal;
BSONObj _objData;
const char * fixedOpName; const char * fixedOpName;
BSONElement * fixed; BSONElement * fixed;
int pushStartSize; int pushStartSize;
BSONType incType; BSONType incType;
int incint; int incint;
double incdouble; double incdouble;
long long inclong; long long inclong;
ModState(){ bool dontApply;
ModState() {
fixedOpName = 0; fixedOpName = 0;
fixed = 0; fixed = 0;
pushStartSize = -1; pushStartSize = -1;
incType = EOO; incType = EOO;
dontApply = false;
} }
Mod::Op op() const { Mod::Op op() const {
return m->op; return m->op;
} }
const char * fieldName() const { const char * fieldName() const {
return m->fieldName; return m->fieldName;
} }
bool needOpLogRewrite() const { bool needOpLogRewrite() const {
if ( dontApply )
return false;
if ( fixed || fixedOpName || incType ) if ( fixed || fixedOpName || incType )
return true; return true;
switch( op() ){ switch( op() ) {
case Mod::RENAME_FROM:
case Mod::RENAME_TO:
return true;
case Mod::BIT: case Mod::BIT:
case Mod::BITAND: case Mod::BITAND:
case Mod::BITOR: case Mod::BITOR:
// TODO: should we convert this to $set? // TODO: should we convert this to $set?
return false; return false;
default: default:
return false; return false;
} }
} }
void appendForOpLog( BSONObjBuilder& b ) const; void appendForOpLog( BSONObjBuilder& b ) const;
template< class Builder > template< class Builder >
void apply( Builder& b , BSONElement in ){ void apply( Builder& b , BSONElement in ) {
m->apply( b , in , *this ); m->apply( b , in , *this );
} }
template< class Builder > template< class Builder >
void appendIncValue( Builder& b , bool useFullName ) const { void appendIncValue( Builder& b , bool useFullName ) const {
const char * n = useFullName ? m->fieldName : m->shortFieldName ; const char * n = useFullName ? m->fieldName : m->shortFieldName ;
switch ( incType ){ switch ( incType ) {
case NumberDouble: case NumberDouble:
b.append( n , incdouble ); break; b.append( n , incdouble ); break;
case NumberLong: case NumberLong:
b.append( n , inclong ); break; b.append( n , inclong ); break;
case NumberInt: case NumberInt:
b.append( n , incint ); break; b.append( n , incint ); break;
default: default:
assert(0); assert(0);
} }
} }
string toString() const; string toString() const;
template< class Builder >
void handleRename( Builder &newObjBuilder, const char *shortFieldNa me );
}; };
/** /**
* this is used to hold state, meta data while applying a ModSet to a B SONObj * this is used to hold state, meta data while applying a ModSet to a B SONObj
* the goal is to make ModSet const so its re-usable * the goal is to make ModSet const so its re-usable
*/ */
class ModSetState : boost::noncopyable { class ModSetState : boost::noncopyable {
struct FieldCmp { struct FieldCmp {
bool operator()( const string &l, const string &r ) const { bool operator()( const string &l, const string &r ) const;
return lexNumCmp( l.c_str(), r.c_str() ) < 0;
}
}; };
typedef map<string,ModState,FieldCmp> ModStateHolder; typedef map<string,ModState,FieldCmp> ModStateHolder;
const BSONObj& _obj; const BSONObj& _obj;
ModStateHolder _mods; ModStateHolder _mods;
bool _inPlacePossible; bool _inPlacePossible;
BSONObj _newFromMods; // keep this data alive, as oplog generation may depend on it
ModSetState( const BSONObj& obj ) ModSetState( const BSONObj& obj )
: _obj( obj ) , _inPlacePossible(true){ : _obj( obj ) , _inPlacePossible(true) {
} }
/** /**
* @return if in place is still possible * @return if in place is still possible
*/ */
bool amIInPlacePossible( bool inPlacePossible ){ bool amIInPlacePossible( bool inPlacePossible ) {
if ( ! inPlacePossible ) if ( ! inPlacePossible )
_inPlacePossible = false; _inPlacePossible = false;
return _inPlacePossible; return _inPlacePossible;
} }
template< class Builder > template< class Builder >
void createNewFromMods( const string& root , Builder& b , const BSO NObj &obj ); void createNewFromMods( const string& root , Builder& b , const BSO NObj &obj );
template< class Builder > template< class Builder >
void _appendNewFromMods( const string& root , ModState& m , Builder & b , set<string>& onedownseen ); void _appendNewFromMods( const string& root , ModState& m , Builder & b , set<string>& onedownseen );
template< class Builder > template< class Builder >
void appendNewFromMod( ModState& ms , Builder& b ){ void appendNewFromMod( ModState& ms , Builder& b ) {
if ( ms.dontApply ) {
return;
}
//const Mod& m = *(ms.m); // HACK //const Mod& m = *(ms.m); // HACK
Mod& m = *((Mod*)(ms.m)); // HACK Mod& m = *((Mod*)(ms.m)); // HACK
switch ( m.op ){ switch ( m.op ) {
case Mod::PUSH: case Mod::PUSH:
case Mod::ADDTOSET: { case Mod::ADDTOSET: {
if ( m.isEach() ){ if ( m.isEach() ) {
b.appendArray( m.shortFieldName , m.getEach() ); b.appendArray( m.shortFieldName , m.getEach() );
} }
else { else {
BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) ); BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) );
arr.appendAs( m.elt, "0" ); arr.appendAs( m.elt, "0" );
arr.done(); arr.done();
} }
break; break;
} }
skipping to change at line 519 skipping to change at line 610
// no-op b/c unset/pull of nothing does nothing // no-op b/c unset/pull of nothing does nothing
break; break;
case Mod::INC: case Mod::INC:
ms.fixedOpName = "$set"; ms.fixedOpName = "$set";
case Mod::SET: { case Mod::SET: {
m._checkForAppending( m.elt ); m._checkForAppending( m.elt );
b.appendAs( m.elt, m.shortFieldName ); b.appendAs( m.elt, m.shortFieldName );
break; break;
} }
// shouldn't see RENAME_FROM here
case Mod::RENAME_TO:
ms.handleRename( b, m.shortFieldName );
break;
default: default:
stringstream ss; stringstream ss;
ss << "unknown mod in appendNewFromMod: " << m.op; ss << "unknown mod in appendNewFromMod: " << m.op;
throw UserException( 9015, ss.str() ); throw UserException( 9015, ss.str() );
} }
} }
public: public:
bool canApplyInPlace() const { bool canApplyInPlace() const {
return _inPlacePossible; return _inPlacePossible;
} }
/** /**
* modified underlying _obj * modified underlying _obj
* @param isOnDisk - true means this is an on disk object, and this update needs to be made durable
*/ */
void applyModsInPlace(); void applyModsInPlace( bool isOnDisk );
BSONObj createNewFromMods(); BSONObj createNewFromMods();
// re-writing for oplog // re-writing for oplog
bool needOpLogRewrite() const { bool needOpLogRewrite() const {
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ )
if ( i->second.needOpLogRewrite() ) if ( i->second.needOpLogRewrite() )
return true; return true;
return false; return false;
skipping to change at line 566 skipping to change at line 662
bool haveArrayDepMod() const { bool haveArrayDepMod() const {
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ )
if ( i->second.m->arrayDep() ) if ( i->second.m->arrayDep() )
return true; return true;
return false; return false;
} }
void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const { void appendSizeSpecForArrayDepMods( BSONObjBuilder &b ) const {
for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) { for ( ModStateHolder::const_iterator i = _mods.begin(); i != _m ods.end(); i++ ) {
const ModState& m = i->second; const ModState& m = i->second;
if ( m.m->arrayDep() ){ if ( m.m->arrayDep() ) {
if ( m.pushStartSize == -1 ) if ( m.pushStartSize == -1 )
b.appendNull( m.fieldName() ); b.appendNull( m.fieldName() );
else else
b << m.fieldName() << BSON( "$size" << m.pushStartS ize ); b << m.fieldName() << BSON( "$size" << m.pushStartS ize );
} }
} }
} }
string toString() const; string toString() const;
 End of changes. 49 change blocks. 
52 lines changed or deleted 148 lines changed or added


 util.h   util.h 
skipping to change at line 41 skipping to change at line 41
struct ShardChunkVersion { struct ShardChunkVersion {
union { union {
struct { struct {
int _minor; int _minor;
int _major; int _major;
}; };
unsigned long long _combined; unsigned long long _combined;
}; };
ShardChunkVersion( int major=0, int minor=0 ) ShardChunkVersion( int major=0, int minor=0 )
: _minor(minor),_major(major){ : _minor(minor),_major(major) {
} }
ShardChunkVersion( unsigned long long ll ) ShardChunkVersion( unsigned long long ll )
: _combined( ll ){ : _combined( ll ) {
} }
ShardChunkVersion( const BSONElement& e ){ ShardChunkVersion( const BSONElement& e ) {
if ( e.type() == Date || e.type() == Timestamp ){ if ( e.type() == Date || e.type() == Timestamp ) {
_combined = e._numberLong(); _combined = e._numberLong();
} }
else if ( e.eoo() ){ else if ( e.eoo() ) {
_combined = 0; _combined = 0;
} }
else { else {
_combined = 0;
log() << "ShardChunkVersion can't handle type (" << (int)(e .type()) << ") " << e << endl; log() << "ShardChunkVersion can't handle type (" << (int)(e .type()) << ") " << e << endl;
assert(0); assert(0);
} }
} }
void inc( bool major ){ void inc( bool major ) {
if ( major ) if ( major )
incMajor(); incMajor();
else else
incMinor(); incMinor();
} }
void incMajor() { void incMajor() {
_major++; _major++;
_minor = 0; _minor = 0;
} }
skipping to change at line 96 skipping to change at line 97
stringstream ss; stringstream ss;
ss << _major << "|" << _minor; ss << _major << "|" << _minor;
return ss.str(); return ss.str();
} }
int majorVersion() const { return _major; } int majorVersion() const { return _major; }
int minorVersion() const { return _minor; } int minorVersion() const { return _minor; }
operator unsigned long long() const { return _combined; } operator unsigned long long() const { return _combined; }
ShardChunkVersion& operator=( const BSONElement& elem ){ ShardChunkVersion& operator=( const BSONElement& elem ) {
switch ( elem.type() ){ switch ( elem.type() ) {
case Timestamp: case Timestamp:
case NumberLong: case NumberLong:
case Date: case Date:
_combined = elem._numberLong(); _combined = elem._numberLong();
break; break;
case EOO: case EOO:
_combined = 0; _combined = 0;
break; break;
default: default:
assert(0); massert( 13657 , str::stream() << "unknown type for ShardCh unkVersion: " << elem , 0 );
} }
return *this; return *this;
} }
}; };
inline ostream& operator<<( ostream &s , const ShardChunkVersion& v){ inline ostream& operator<<( ostream &s , const ShardChunkVersion& v) {
s << v._major << "|" << v._minor; s << v._major << "|" << v._minor;
return s; return s;
} }
/** /**
* your config info for a given shard/chunk is out of date * your config info for a given shard/chunk is out of date
*/ */
class StaleConfigException : public AssertionException { class StaleConfigException : public AssertionException {
public: public:
StaleConfigException( const string& ns , const string& raw , bool j ustConnection = false ) StaleConfigException( const string& ns , const string& raw , bool j ustConnection = false )
: AssertionException( (string)"ns: " + ns + " " + raw , 9996 ) , : AssertionException( (string)"ns: " + ns + " " + raw , 9996 ) ,
_justConnection(justConnection) , _justConnection(justConnection) ,
_ns(ns){ _ns(ns) {
} }
virtual ~StaleConfigException() throw(){} virtual ~StaleConfigException() throw() {}
virtual void appendPrefix( stringstream& ss ) const { ss << "StaleC onfigException: "; } virtual void appendPrefix( stringstream& ss ) const { ss << "stale sharding config exception: "; }
bool justConnection() const { return _justConnection; } bool justConnection() const { return _justConnection; }
string getns() const { return _ns; } string getns() const { return _ns; }
static bool parse( const string& big , string& ns , string& raw ){ static bool parse( const string& big , string& ns , string& raw ) {
string::size_type start = big.find( '[' ); string::size_type start = big.find( '[' );
if ( start == string::npos ) if ( start == string::npos )
return false; return false;
string::size_type end = big.find( ']' ,start ); string::size_type end = big.find( ']' ,start );
if ( end == string::npos ) if ( end == string::npos )
return false; return false;
ns = big.substr( start + 1 , ( end - start ) - 1 ); ns = big.substr( start + 1 , ( end - start ) - 1 );
raw = big.substr( end + 1 ); raw = big.substr( end + 1 );
return true; return true;
} }
private: private:
bool _justConnection; bool _justConnection;
string _ns; string _ns;
}; };
bool checkShardVersion( DBClientBase & conn , const string& ns , bool a uthoritative = false , int tryNumber = 1 ); extern boost::function4<bool, DBClientBase&, const string&, bool, int> checkShardVersionCB;
void resetShardVersion( DBClientBase * conn ); extern boost::function1<void, DBClientBase*> resetShardVersionCB;
} }
 End of changes. 14 change blocks. 
16 lines changed or deleted 18 lines changed or added


 v8_db.h   v8_db.h 
skipping to change at line 25 skipping to change at line 25
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <v8.h> #include <v8.h>
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include "engine_v8.h"
#include "../client/dbclient.h" #include "../client/dbclient.h"
namespace mongo { namespace mongo {
// These functions may depend on the caller creating a handle scope and context scope. // These functions may depend on the caller creating a handle scope and context scope.
v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( bool local ) ; v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate( V8Scope * sc ope, bool local );
void installDBTypes( v8::Handle<v8::ObjectTemplate>& global ); // void installDBTypes( V8Scope * scope, v8::Handle<v8::ObjectTemplate>& global );
void installDBTypes( v8::Handle<v8::Object>& global ); void installDBTypes( V8Scope * scope, v8::Handle<v8::Object>& global );
// the actual globals // the actual globals
mongo::DBClientBase * getConnection( const v8::Arguments& args ); mongo::DBClientBase * getConnection( const v8::Arguments& args );
// Mongo members // Mongo members
v8::Handle<v8::Value> mongoConsLocal(const v8::Arguments& args); v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Argument s& args);
v8::Handle<v8::Value> mongoConsExternal(const v8::Arguments& args); v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8::Argum ents& args);
v8::Handle<v8::Value> mongoFind(const v8::Arguments& args); v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& ar gs);
v8::Handle<v8::Value> mongoInsert(const v8::Arguments& args); v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments& args);
v8::Handle<v8::Value> mongoRemove(const v8::Arguments& args); v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments& args);
v8::Handle<v8::Value> mongoUpdate(const v8::Arguments& args); v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments& args);
v8::Handle<v8::Value> internalCursorCons(const v8::Arguments& args); v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Argu ments& args);
v8::Handle<v8::Value> internalCursorNext(const v8::Arguments& args); v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Argu ments& args);
v8::Handle<v8::Value> internalCursorHasNext(const v8::Arguments& args); v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::A rguments& args);
v8::Handle<v8::Value> internalCursorObjsLeftInBatch(const v8::Arguments & args); v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, con st v8::Arguments& args);
// DB members // DB members
v8::Handle<v8::Value> dbInit(const v8::Arguments& args); v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args) ;
v8::Handle<v8::Value> collectionInit( const v8::Arguments& args ); v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Argument s& args );
v8::Handle<v8::Value> objectIdInit( const v8::Arguments& args ); v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments & args );
v8::Handle<v8::Value> dbRefInit( const v8::Arguments& args ); v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& a rgs );
v8::Handle<v8::Value> dbPointerInit( const v8::Arguments& args ); v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Argument s& args );
v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Argume nts& args );
v8::Handle<v8::Value> binDataInit( const v8::Arguments& args ); v8::Handle<v8::Value> binDataInit( V8Scope* scope, const v8::Arguments& args );
v8::Handle<v8::Value> binDataToString( const v8::Arguments& args ); v8::Handle<v8::Value> binDataToString( V8Scope* scope, const v8::Argume nts& args );
v8::Handle<v8::Value> binDataToBase64( V8Scope* scope, const v8::Argume nts& args );
v8::Handle<v8::Value> binDataToHex( V8Scope* scope, const v8::Arguments & args );
v8::Handle<v8::Value> numberLongInit( const v8::Arguments& args ); v8::Handle<v8::Value> uuidInit( V8Scope* scope, const v8::Arguments& ar gs );
v8::Handle<v8::Value> numberLongToNumber(const v8::Arguments& args); v8::Handle<v8::Value> md5Init( V8Scope* scope, const v8::Arguments& arg s );
v8::Handle<v8::Value> numberLongValueOf(const v8::Arguments& args); v8::Handle<v8::Value> hexDataInit( V8Scope* scope, const v8::Arguments& args );
v8::Handle<v8::Value> numberLongToString(const v8::Arguments& args);
v8::Handle<v8::Value> dbQueryInit( const v8::Arguments& args ); v8::Handle<v8::Value> numberLongInit( V8Scope* scope, const v8::Argumen ts& args );
v8::Handle<v8::Value> numberLongToNumber(V8Scope* scope, const v8::Argu ments& args);
v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Argum ents& args);
v8::Handle<v8::Value> numberLongToString(V8Scope* scope, const v8::Argu ments& args);
v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Argument s& args );
v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Argum ents& args);
v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Argume nts& args);
v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Argum ents& args);
v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments& args );
v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::Ac cessorInfo& info ); v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::Ac cessorInfo& info );
v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, c onst v8::AccessorInfo &info); v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, c onst v8::AccessorInfo &info);
v8::Handle<v8::Value> bsonsize( const v8::Arguments& args ); v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& ar gs );
// call with v8 mutex:
void enableV8Interrupt();
void disableV8Interrupt();
} }
 End of changes. 11 change blocks. 
26 lines changed or deleted 43 lines changed or added


 v8_utils.h   v8_utils.h 
skipping to change at line 30 skipping to change at line 30
#include <v8.h> #include <v8.h>
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <assert.h> #include <assert.h>
#include <iostream> #include <iostream>
namespace mongo { namespace mongo {
v8::Handle<v8::Value> Print(const v8::Arguments& args);
v8::Handle<v8::Value> Version(const v8::Arguments& args);
v8::Handle<v8::Value> GCV8(const v8::Arguments& args);
void ReportException(v8::TryCatch* handler); void ReportException(v8::TryCatch* handler);
#define jsassert(x,msg) assert(x) #define jsassert(x,msg) assert(x)
std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o ); std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::Value> & o );
std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatc h> * try_catch ); std::ostream& operator<<( std::ostream &s, const v8::Handle<v8::TryCatc h> * try_catch );
std::string toSTLString( const v8::Handle<v8::Value> & o ); std::string toSTLString( const v8::Handle<v8::Value> & o );
std::string toSTLString( const v8::TryCatch * try_catch ); std::string toSTLString( const v8::TryCatch * try_catch );
class V8Scope; class V8Scope;
void installFork( v8::Handle< v8::Object > &global, v8::Handle< v8::Con text > &context ); void installFork( V8Scope* scope, v8::Handle< v8::Object > &global, v8: :Handle< v8::Context > &context );
} }
 End of changes. 2 change blocks. 
5 lines changed or deleted 1 lines changed or added


 v8_wrapper.h   v8_wrapper.h 
skipping to change at line 25 skipping to change at line 25
* limitations under the License. * limitations under the License.
*/ */
#pragma once #pragma once
#include <v8.h> #include <v8.h>
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include "../db/jsobj.h" #include "../db/jsobj.h"
#include "engine_v8.h"
namespace mongo { namespace mongo {
v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool array = 0 , bool readOnly = false ); v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate(V8Scope* scop e);
mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0 );
void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> name ,
const string sname , v8::Handle<v8::Value> value , int depth = 0 );
v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f );
v8::Function * getNamedCons( const char * name );
v8::Function * getObjectIdCons();
v8::Handle<v8::FunctionTemplate> getObjectWrapperTemplate();
class WrapperHolder; class WrapperHolder;
WrapperHolder * createWrapperHolder( const BSONObj * o , bool readOnly , bool iDelete ); WrapperHolder * createWrapperHolder( V8Scope* scope, const BSONObj * o , bool readOnly , bool iDelete );
} }
 End of changes. 3 change blocks. 
12 lines changed or deleted 3 lines changed or added


 value.h   value.h 
/* @file value.h /* @file value.h
concurrency helpers Atomic<T> and DiagStr concurrency helpers DiagStr, Guarded
*/ */
/** /**
* Copyright (C) 2008 10gen Inc. * Copyright (C) 2008 10gen Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3 , * it under the terms of the GNU Affero General Public License, version 3 ,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* This program is distributed in the hope that it will be useful,b * This program is distributed in the hope that it will be useful,b
* 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
namespace mongo { #include "mutex.h"
extern mutex _atomicMutex; namespace mongo {
/** atomic wrapper for a value. enters a mutex on each access. must /** declare that a variable that is "guarded" by a mutex.
be copyable.
*/
template<typename T>
class Atomic : boost::noncopyable {
T val;
public:
Atomic<T>() { }
void operator=(const T& a) { The decl documents the rule. For example "counta and countb are gu arded by xyzMutex":
scoped_lock lk(_atomicMutex);
val = a; }
operator T() const { Guarded<int, xyzMutex> counta;
scoped_lock lk(_atomicMutex); Guarded<int, xyzMutex> countb;
return val; }
/** example: Upon use, specify the scoped_lock object. This makes it hard for s omeone
Atomic<int> q; later to forget to be in the lock. Check is made that it is the ri ght lock in _DEBUG
... builds at runtime.
{ */
Atomic<int>::tran t(q); template <typename T, mutex& BY>;
if( q.ref() > 0 ) class Guarded {
q.ref()--; T _val;
} public:
*/ T& ref(const scoped_lock& lk) {
class tran : private scoped_lock { dassert( lk._mut == &BY );
Atomic<T>& _a; return _val;
public: }
tran(Atomic<T>& a) : scoped_lock(_atomicMutex), _a(a) { }
T& ref() { return _a.val; }
};
}; };
/** this string COULD be mangled but with the double buffering, assumin g writes
are infrequent, it's unlikely. thus, this is reasonable for lockless s etting of
diagnostic strings, where their content isn't critical.
*/
class DiagStr { class DiagStr {
char buf1[256]; string _s;
char buf2[256]; static mutex m;
char *p;
public: public:
DiagStr() { DiagStr(const DiagStr& r) : _s(r.get()) { }
memset(buf1, 0, 256); DiagStr() { }
memset(buf2, 0, 256); bool empty() const {
p = buf1; mutex::scoped_lock lk(m);
return _s.empty();
}
string get() const {
mutex::scoped_lock lk(m);
return _s;
} }
const char * get() const { return p; }
void set(const char *s) { void set(const char *s) {
char *q = (p==buf1) ? buf2 : buf1; mutex::scoped_lock lk(m);
strncpy(q, s, 255); _s = s;
p = q; }
void set(const string& s) {
mutex::scoped_lock lk(m);
_s = s;
} }
operator string() const { return get(); }
void operator=(const string& s) { set(s); }
}; };
} }
 End of changes. 13 change blocks. 
48 lines changed or deleted 38 lines changed or added


 version.h   version.h 
#ifndef UTIL_VERSION_HEADER #ifndef UTIL_VERSION_HEADER
#define UTIL_VERSION_HEADER #define UTIL_VERSION_HEADER
#include <string> #include <string>
namespace mongo { namespace mongo {
struct BSONArray;
using std::string; using std::string;
// mongo version // mongo version
extern const char versionString[]; extern const char versionString[];
extern const BSONArray versionArray;
string mongodVersion(); string mongodVersion();
int versionCmp(StringData rhs, StringData lhs); // like strcmp
const char * gitVersion(); const char * gitVersion();
void printGitVersion(); void printGitVersion();
string sysInfo(); string sysInfo();
void printSysInfo(); void printSysInfo();
void show_warnings(); void show_warnings();
} // namespace mongo } // namespace mongo
 End of changes. 3 change blocks. 
0 lines changed or deleted 3 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/